Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix linux build, add a portaudio and a pulseaudio backend #4

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

jcelerier
Copy link

@jcelerier jcelerier commented Apr 11, 2019

This gives a more complete implementation to null backend and adds two backends :

  • one based on portaudio, so that the API can easily be tested on most platforms, both major and minor (hello Haiku, hello ASIO).
  • one based on pulseaudio which is the "canonical" desktop audio server on linux (e.g. that's what web browsers & stuff like SDL and music players use underneath). This one only does playback, not recording, because pulseaudio does not have a duplex API so you need a ringbuffer at some point to pass the input data to the output callback.

@jcelerier jcelerier changed the title Fix linux build Fix linux build, add a portaudio backend Apr 11, 2019
@jcelerier jcelerier changed the title Fix linux build, add a portaudio backend Fix linux build, add a portaudio and a pulseaudio backend Apr 15, 2019
@jcelerier
Copy link
Author

jcelerier commented Apr 15, 2019

Here are so far my comments on the whole endeavour :

Dynamic libraries

  • Before having this, it is necessary to have some way to load dynamic libraries - on linux, this ensures that software built on machine A with pulseaudio will also work on machine B without pulseaudio, and on machine C without any kind of audio package. You don't want your libstdc++ depending on that stuff at link time.
  • Likewise, ASIO on windows requires loading dynamic libraries that you go look for in the registry.

Graph APIs

  • Most recent audio APIs (e.g. WASAPI, JACK, PulseAudio, etc.) don't work with devices but in a audio-graph-like manner - in particular this means that there's not much point in having a "device" which supports N inputs and M outputs because it's the API which will do magic with your audio channels so that it fits in the output you're routing it into in your OS sound widget - e.g. with pulseaudio you can play a 5.1 96khz stream and it will downmix & resample correctly to 2.0 44100 if you only have stereo sound, and with JACK there is no relationship between the I/O of your software, and the I/O of your soundcards... you can even send your audio channels over the network.
    So far the only API that I see which actually follows the paradigm provided by libstdaudio is ASIO.

  • This also raises the question of how to handle APIs that support multiple soundcards (e.g. MME if I'm not mistaken allows to give you a callback with one soundcard for the inputs, and one for the outputs ?)

  • Likewise, how should the buffer size / samplerate be presented ?
    In my experience you can request an audio API to provide e.g. 44100 at 512 samples but again unless it's ASIO you've got a good chance of having different buffer sizes in practices - so should the library perform buffering ? (in which case it will start looking a lot like PortAudio).
    PulseAudio does not care at all about your buffer size / sample rate - should the get_supported_* family of functions return nothing in that case ? Or a sentinel value maybe ?
    In contrast, JACK only supports what is currently being set on the JACK server.

Contexts

  • How is this handled ? multiple APIs need to have some kind of global context which is persisted between calls - else many things go awry. In my experience devices can change indices - or sometimes APIs uses pointers as indices which are invalidated when this global context is destroyed, so you have to store the context in an object which will be kept alive by both the device list and the actual devices.

Multiple APIs

  • How does it work ? should the library provide hints as to when handling multiple APIs work ?
    A simple example : suppose that libstdaudio provides an ALSA and a JACK backend. If you initialize one before the other - e.g. by getting a device list - then the other won't work until the context of the other is cleaned (and that's a rather good case - IIRC if you initialize portaudio while you already have a JACK context, you can get a crash :-) )

Likewise for ASIO and WASAPI on windows.

Error handling

This is targeting at least C++2x, why not using exceptions / outcome / etc rather than returning bools ?

Other remarks

  • Not being able to do connect at runtime seems pretty restrictive for me.
  • There should be an SDL backend : it is the standard way to access audio devices from webassembly if you are using Emscripten (e.g. emscripten exposes the SDL API but then implements it in terms of JS WebAudioAPI calls).

@ghost
Copy link

ghost commented Apr 27, 2019

Totally agree about returning bools. I've voiced some of design comments here: stdcpp-audio/proposals#1

@ghost
Copy link

ghost commented May 16, 2019

It looks like there is r2 of the paper somewhere private because API changed significantly. Driver is no longer a template parameter.

@jcelerier
Copy link
Author

yep, I'll try to update it this week

@milasudril
Copy link

Here are so far my comments on the whole endeavour :

Dynamic libraries

  • Before having this, it is necessary to have some way to load dynamic libraries - on linux, this ensures that software built on machine A with pulseaudio will also work on machine B without pulseaudio, and on machine C without any kind of audio package. You don't want your libstdc++ depending on that stuff at link time.

Really good point: C++ needs standardized support for shared libraries first. Aim for C++29, at the time there is only one executable format. As for now, different formats have different defaults for symbol visibility. Side-note: The same holds true for graphics.

@ghost
Copy link

ghost commented May 29, 2019

This is solved in OpenCL using "platforms". Platform list is generated at runtime. I hope R3 of this paper will return the concept of drivers and will change it so drivers are loaded at runtime.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants