-
Notifications
You must be signed in to change notification settings - Fork 247
src/sound/README.md: start documenting the sound design #3873
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -48,4 +48,56 @@ This folder contains the related files for all sound APIs. | |
|
|
||
| ## Documentation of sound design | ||
|
|
||
| **Fixme:** The sound design is not yet documented. | ||
| This describes how the code behaves today. It covers the device lifecycle and the threading rules | ||
| around the audio callback; the areas still missing are listed at the end. | ||
|
|
||
| Each platform provides one `CSound` class deriving from `CSoundBase`, and exactly one of the | ||
| subdirectories is compiled in: `asio/` (Windows), `coreaudio-mac/`, `coreaudio-ios/`, `oboe/` | ||
| (Android) and `jack/` (where JACK is enabled). | ||
|
|
||
| ### Buffer size negotiation | ||
|
|
||
| `Init ( iNewPrefMonoBufferSize )` returns the mono buffer size the device actually accepted, | ||
| which may differ from the one requested. `CClient::Init()` uses that return value to find out | ||
| which sizes a device supports, so it calls `Init()` four times per invocation: once for each of | ||
| `FRAME_SIZE_FACTOR_PREFERRED`, `FRAME_SIZE_FACTOR_DEFAULT` and `FRAME_SIZE_FACTOR_SAFE` to fill | ||
| `bFraSiFactPrefSupported`, `bFraSiFactDefSupported` and `bFraSiFactSafeSupported`, then once with | ||
| the size selected in the settings. Those three flags drive the enabled state of the buffer delay | ||
| radio buttons, and the settings dialog polls them once a second; no signal runs from the sound | ||
| device to that dialog. | ||
|
|
||
| A driver may also change the buffer size on its own. `kAsioBufferSizeChange` in the ASIO backend | ||
| and JACK's buffer size callback both report that by calling | ||
| `EmitReinitRequestSignal ( RS_ONLY_RESTART_AND_INIT )`, which reaches | ||
| `CClient::OnSndCrdReinitRequest` and repeats the negotiation above. | ||
|
|
||
| ### Start, stop and the audio callback | ||
|
|
||
| `Init()` is only ever entered with the device stopped. Callers that may be running stop it first | ||
| and restart it afterwards, which is the `bWasRunning` pattern throughout `client.cpp`. | ||
|
|
||
| The audio callback runs on a thread owned by the driver. `CSoundBase` inherits `QThread`, but | ||
| nothing here overrides `run()` or calls `start()`, so no such thread exists. Backends keep the | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is worth investigating. If the architectural framework was put in place to put this on a thread, it's probably meant to be on a thread or the code should be cleaned up. The architecture is meant to have one of the following:
|
||
| callback away from a device that is being re-initialised in two ways, and the ASIO backend is the | ||
| exception to both: | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I make that three ways, then. It should be written plainly. |
||
|
|
||
| | backend | audio callback | ignores the callback while stopped | takes `MutexAudioProcessCallback` | | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It might be worth seeing why this variation exists, anyway. |
||
| |---|---|---|---| | ||
| | JACK | `process()` | yes, `IsRunning()` | yes | | ||
| | CoreAudio (macOS) | `callbackIO()` | yes, `bRun` | yes | | ||
| | CoreAudio (iOS) | `processBufferList()` | no | yes | | ||
| | Oboe | `onAudioReady()` | yes, `!bRun` | yes | | ||
| | ASIO | `bufferSwitch()` | no | no, it uses its own `ASIOMutex` | | ||
|
|
||
| `CSoundBase::Stop()` clears `bRun` and then takes `MutexAudioProcessCallback` to wait for a | ||
| callback that is already in flight. The ASIO backend never takes that mutex, so on Windows that | ||
| wait returns immediately and `CSound::Stop()` waits on `ASIOMutex` instead. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Make it clear whether |
||
|
|
||
| ### Not yet documented | ||
|
|
||
| - device enumeration, and what `SetDev()` does when a device cannot be used | ||
| - input and output channel selection, and the input channel mixing in the callbacks | ||
| - MIDI: device selection, controller mapping and `ParseMIDIMessage()` | ||
| - latency reporting via `GetInOutLatencyMs()` | ||
| - the sound card conversion buffer used when a device's buffer size is not a multiple of the | ||
| system frame size | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd turn this around to state what can be done, how it can be done and then state which drivers currently do it (ideally, "and why" as well as "and why none of the others").