Add audio data exchange and waveform visualization (AudioDataExchange + JS API + VST3 DataExchange) - #8
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2c838c6fbd
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| { | ||
| auto configCallback = [] (Vst::DataExchangeHandler::Config& config, const Vst::ProcessSetup&) { | ||
| config.blockSize = sizeof(Singularity::AudioDataExchange::AudioDataBlock); | ||
| config.numBlocks = 8; |
There was a problem hiding this comment.
Avoid dropping VST waveform slices after 128 frames
In the VST path the plugin is invoked by ProcessDataSlicer slicer(16), and the new example calls sendAudioDataToUI once per plugin process call. With this exchange queue limited to 8 blocks, any host buffer larger than 128 frames (for example 256 or 512 samples) fills the DataExchange queue during a single audio callback; subsequent getCurrentOrNewBlock() calls return invalid and the later slices are never delivered, so the UI waveform only reflects the beginning of each block. Accumulate into one block per host buffer or size/throttle the exchange around the slicer.
Useful? React with 👍 / 👎.
7dfd73b to
f4a10d4
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f4a10d4fc8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| break; | ||
| } | ||
|
|
||
| template<typename SampleT> |
There was a problem hiding this comment.
Close resetOutputParameters before declaring helpers
Standalone APP targets that include this header will fail to compile because the new member template declaration is still inside resetOutputParameters(); the function's closing brace was not restored after the loop, so C++ sees a template declaration at block scope. Add the missing } before these helpers so the new audio wrappers are class members.
Useful? React with 👍 / 👎.
| if (audioDataQueue_) | ||
| while (audioDataQueue_->popAudioDataBlock(latestAudioData_)) | ||
| receivedNewBlock = true; |
There was a problem hiding this comment.
Return every drained audio block to JavaScript
When the audio thread produces more than one block between UI draws, this loop pops all queued blocks into latestAudioData_ but the API returns only the last one and increments the revision once. The new Waveform calls getAudioData() once per draw, so at normal audio/UI rates the intermediate blocks are discarded and the waveform history becomes gapped/too slow; either return all drained blocks or pop only one block per call.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7e69408593
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (Math.abs(candidate) > Math.abs(sample)) | ||
| sample = candidate; | ||
| } | ||
| history.push(Math.max(-1, Math.min(1, sample))); |
There was a problem hiding this comment.
When getAudioData() advances its revision, it returns QuickJSEngine::latestAudioData_ as a rolling snapshot, not just the newly drained block. This history.push(...) therefore re-appends the previous snapshot on every revision; with consecutive 128-frame updates the widget records 128 frames, then appends all 256 frames, then all 384, and once the native buffer is full each UI tick injects about 2048 old samples again. That makes the waveform repeat old audio and scroll at the wrong time scale during normal continuous playback, so the widget needs to track which frames were already consumed or the native API should return deltas.
Useful? React with 👍 / 👎.
Motivation
Description
AudioDataExchange.himplementingAudioDataBlock,IDataSink,AudioDataQueue,appendInterleavedFloatData,ScopedSendContextandsendAudioDataToUIhelpers for producing and queuing interleaved float audio.AudioDataQueuethrough the stack by adding queue accessors and plumbing:ISingularityAudio, standalone backends (PipeWire,ASIO),SingularityController,IJSEngine/QuickJSEngine, and VST3VST3Processor/VST3ControllerwithDataExchangeHandlerintegration andIDataSinkimplementations to receive blocks.getAudioDatainQuickJSEngineto return the latestAudioDataBlockto JS, and include theWaveformwidget (widgets/waveform.js) plus example usage inexamples/ExampleEffect/App.jsand plugin exampleExampleEffect.hcallingsendAudioDataToUI.AudioDataQueue*(e.g.IJSEngine::createJSEngine,SingularityController) and wire VST3 queue configuration and lifecycle (connect,disconnect,queueOpened,onDataExchangeBlocksReceived).Testing
Waveformwidget andgetAudioDataJS call; the widget rendered without blocking the audio thread.DataExchangeintegration and validated that the host-target build completes and the exchange handler compiles; build checks succeeded.Codex Task