-
-
Notifications
You must be signed in to change notification settings - Fork 52
refactor: stable render quantum size #1008
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 | ||||
|---|---|---|---|---|---|---|
|
|
@@ -4,11 +4,14 @@ | |||||
| #import <NativeAudioPlayer.h> | ||||||
| #else // when compiled as C++ | ||||||
| typedef struct objc_object NativeAudioPlayer; | ||||||
| typedef struct objc_object AudioBufferList; | ||||||
|
||||||
| typedef struct objc_object AudioBufferList; | |
| struct AudioBufferList; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,11 @@ | ||
| #import <AVFoundation/AVFoundation.h> | ||
|
|
||
| #include <algorithm> | ||
| #include <cstring> | ||
|
|
||
| #include <audioapi/core/utils/Constants.h> | ||
| #include <audioapi/dsp/VectorMath.h> | ||
| #include <audioapi/ios/core/IOSAudioPlayer.h> | ||
| #include <audioapi/ios/system/AudioEngine.h> | ||
| #include <audioapi/utils/AudioArray.hpp> | ||
| #include <audioapi/utils/AudioBuffer.hpp> | ||
|
|
||
| namespace audioapi { | ||
|
|
@@ -13,35 +14,20 @@ | |
| const std::function<void(std::shared_ptr<DSPAudioBuffer>, int)> &renderAudio, | ||
| float sampleRate, | ||
| int channelCount) | ||
| : renderAudio_(renderAudio), channelCount_(channelCount), audioBuffer_(0), isRunning_(false) | ||
| : audioBuffer_(nullptr), | ||
| audioPlayer_(nullptr), | ||
| renderAudio_(renderAudio), | ||
| channelCount_(channelCount), | ||
| isRunning_(false), | ||
| pendingSaved_(RENDER_QUANTUM_SIZE, channelCount_, sampleRate) | ||
| { | ||
| RenderAudioBlock renderAudioBlock = ^(AudioBufferList *outputData, int numFrames) { | ||
| int processedFrames = 0; | ||
|
|
||
| while (processedFrames < numFrames) { | ||
| int framesToProcess = std::min(numFrames - processedFrames, RENDER_QUANTUM_SIZE); | ||
|
|
||
| if (isRunning_.load(std::memory_order_acquire)) { | ||
| renderAudio_(audioBuffer_, framesToProcess); | ||
| } else { | ||
| audioBuffer_->zero(); | ||
| } | ||
|
|
||
| for (size_t channel = 0; channel < channelCount_; channel += 1) { | ||
| float *outputChannel = (float *)outputData->mBuffers[channel].mData; | ||
|
|
||
| audioBuffer_->getChannel(channel)->copyTo( | ||
| outputChannel, 0, processedFrames, framesToProcess); | ||
| } | ||
|
|
||
| processedFrames += framesToProcess; | ||
| } | ||
| deliverOutputBuffers(outputData, numFrames); | ||
| }; | ||
|
|
||
| audioPlayer_ = [[NativeAudioPlayer alloc] initWithRenderAudio:renderAudioBlock | ||
| sampleRate:sampleRate | ||
| channelCount:channelCount_]; | ||
|
|
||
| audioBuffer_ = std::make_shared<DSPAudioBuffer>(RENDER_QUANTUM_SIZE, channelCount_, sampleRate); | ||
| } | ||
|
|
||
|
|
@@ -50,13 +36,90 @@ | |
| cleanup(); | ||
| } | ||
|
|
||
| void IOSAudioPlayer::clearPendingSaved() | ||
| { | ||
| pendingSavedCount_ = 0; | ||
| pendingSaved_.zero(); | ||
| } | ||
|
|
||
| void IOSAudioPlayer::deliverOutputBuffers(AudioBufferList *outputData, int numFrames) | ||
| { | ||
| // If requested, clear any saved overflow before continuing normal rendering. | ||
| if (flushOverflowNextPull_.exchange(false, std::memory_order_acq_rel)) { | ||
| clearPendingSaved(); | ||
| } | ||
|
|
||
| // if not running, set output to 0 | ||
| if (!isRunning_.load(std::memory_order_acquire)) { | ||
| for (int channel = 0; channel < channelCount_; ++channel) { | ||
| auto *outputChannel = static_cast<float *>(outputData->mBuffers[channel].mData); | ||
| std::memset(outputChannel, 0, static_cast<size_t>(numFrames) * sizeof(float)); | ||
| } | ||
| return; | ||
| } | ||
|
|
||
| int outPos = 0; | ||
| while (outPos < numFrames) { | ||
| const int need = numFrames - outPos; | ||
|
|
||
| if (pendingSavedCount_ > 0) { | ||
| const int fromPending = std::min(need, pendingSavedCount_); | ||
|
|
||
| // populate output with pendingSaved | ||
| for (int ch = 0; ch < channelCount_; ++ch) { | ||
| float *dst = static_cast<float *>(outputData->mBuffers[ch].mData) + outPos; | ||
| const float *src = pendingSaved_[ch].begin(); | ||
| std::memcpy(dst, src, fromPending * sizeof(float)); | ||
|
|
||
| // move the remaining samples to the beginning of the pendingSaved buffer | ||
| const int remain = pendingSavedCount_ - fromPending; | ||
| if (remain > 0) { | ||
| float *buf = pendingSaved_[ch].begin(); | ||
| std::memmove(buf, buf + fromPending, remain * sizeof(float)); | ||
| } | ||
| } | ||
|
|
||
| pendingSavedCount_ -= fromPending; | ||
| outPos += fromPending; | ||
| continue; | ||
| } | ||
|
|
||
| renderAudio_(audioBuffer_, RENDER_QUANTUM_SIZE); | ||
|
|
||
|
Comment on lines
+52
to
+88
|
||
| // normal rendering - take RENDER_QUANTUM_SIZE frames from the graph and copy to output | ||
| const int stillNeed = numFrames - outPos; | ||
| if (stillNeed >= RENDER_QUANTUM_SIZE) { | ||
| for (int ch = 0; ch < channelCount_; ++ch) { | ||
| auto *src = (*audioBuffer_)[ch].begin(); | ||
| float *dst = static_cast<float *>(outputData->mBuffers[ch].mData) + outPos; | ||
| std::memcpy(dst, src, RENDER_QUANTUM_SIZE * sizeof(float)); | ||
| } | ||
| outPos += RENDER_QUANTUM_SIZE; | ||
| } else { | ||
| // when output will be sliced, copy the remaining frames to pendingSaved | ||
| const int tail = RENDER_QUANTUM_SIZE - stillNeed; | ||
| for (int ch = 0; ch < channelCount_; ++ch) { | ||
| auto *src = (*audioBuffer_)[ch].begin(); | ||
| float *dst = static_cast<float *>(outputData->mBuffers[ch].mData) + outPos; | ||
| std::memcpy(dst, src, stillNeed * sizeof(float)); | ||
| } | ||
| pendingSaved_.copy(*audioBuffer_, stillNeed, 0, tail); | ||
| pendingSavedCount_ = tail; | ||
| outPos += stillNeed; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| bool IOSAudioPlayer::start() | ||
| { | ||
| if (isRunning()) { | ||
| return true; | ||
| } | ||
|
|
||
| bool success = [audioPlayer_ start]; | ||
| if (success) { | ||
| flushOverflowNextPull_.store(true, std::memory_order_release); | ||
| } | ||
| isRunning_.store(success, std::memory_order_release); | ||
| return success; | ||
| } | ||
|
|
@@ -74,6 +137,9 @@ | |
| } | ||
|
|
||
| bool success = [audioPlayer_ resume]; | ||
| if (success) { | ||
| flushOverflowNextPull_.store(true, std::memory_order_release); | ||
| } | ||
| isRunning_.store(success, std::memory_order_release); | ||
| return success; | ||
| } | ||
|
|
||
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.
printfis used inprocessNodebut this file doesn’t include<cstdio>/<stdio.h>, which can be a build break in C++. Also, emittingprintffrom the audio thread is not real-time safe and can cause glitches. If you need diagnostics here, prefer a debug-only assert or a one-time atomic flag that can be surfaced/logged off the audio thread; and if the intent is to validate quantum sizing, the check should likely beframesToProcess != RENDER_QUANTUM_SIZErather thanprocessingBuffer->getSize()(buffer capacity).