-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2854 from maksutovic/v6-c++
V6 c++
- Loading branch information
Showing
9 changed files
with
486 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
#pragma once | ||
#include <vector> | ||
#include <functional> | ||
#include <atomic> | ||
#include <memory> | ||
#include <AudioUnit/AudioUnit.h> | ||
#include <AudioToolbox/AudioToolbox.h> | ||
#include <AVFoundation/AVFoundation.h> | ||
#include "WorkStealingQueue.hpp" | ||
|
||
namespace AudioKit { | ||
|
||
using std::vector; | ||
using std::shared_ptr; | ||
using std::atomic; | ||
using RenderJobVector = vector<shared_ptr<RenderJob>>; | ||
class AudioProgram { | ||
|
||
public: | ||
vector<int> generatorIndices; | ||
|
||
AudioProgram(const RenderJobVector& jobs, vector<int> generatorIndices) | ||
: jobs(jobs), generatorIndices(generatorIndices), finished(new atomic<int>[jobs.size()]) | ||
{ | ||
reset(); | ||
} | ||
|
||
void reset() { | ||
for (int i=0;i < jobs.size(); ++i) { | ||
finished[i].store(0); | ||
} | ||
remaining.store(static_cast<int>(jobs.size())); | ||
} | ||
|
||
~AudioProgram() { | ||
delete[] finished; | ||
} | ||
|
||
void run(AudioUnitRenderActionFlags* actionFlags, | ||
const AudioTimeStamp* timeStamp, | ||
AUAudioFrameCount frameCount, | ||
AudioBufferList* outputBufferList, | ||
int workerIndex, | ||
std::vector<WorkStealingQueue<int>>& runQueues) { | ||
|
||
auto exec = [&](int index) { | ||
auto& job = jobs[index]; | ||
|
||
job->render(actionFlags, timeStamp, frameCount, | ||
(index == jobs.size() - 1) ? outputBufferList : nullptr); | ||
|
||
// Increment outputs. | ||
for (int outputIndex : job->outputIndices) { | ||
if (finished[outputIndex].fetch_add(1, std::memory_order_relaxed) == jobs[outputIndex]->inputCount()) { | ||
runQueues[workerIndex].push(outputIndex); | ||
} | ||
} | ||
|
||
remaining.fetch_sub(1, std::memory_order_relaxed); | ||
}; | ||
|
||
while (remaining.load(std::memory_order_relaxed) > 0) { | ||
// Pop an index off our queue. | ||
if (auto index = runQueues[workerIndex].pop()) { | ||
exec(*index); | ||
} else { | ||
// Try to steal an index. Start with the next worker and wrap around, | ||
// but don't steal from ourselves. | ||
for (int i = 0; i < runQueues.size() - 1; ++i) { | ||
int victim = (workerIndex + i) % runQueues.size(); | ||
if (auto index = runQueues[victim].steal()) { | ||
exec(*index); | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
private: | ||
vector<shared_ptr<RenderJob>> jobs; | ||
atomic<int>* finished; | ||
atomic<int> remaining = {0}; | ||
}; | ||
|
||
} //namespace AudioKit |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
// Copyright AudioKit. All Rights Reserved. Revision History at http://github.com/AudioKit/AudioKit/ | ||
#pragma once | ||
#include <AudioToolbox/AudioToolbox.h> | ||
|
||
/// Handles the ickyness of accessing AURenderEvents without reading off the end of the struct. | ||
/// | ||
/// - Parameters: | ||
/// - events: render event list | ||
/// - midi: callback for midi events | ||
/// - sysex: callback for sysex events | ||
/// - param: callback for param events | ||
template<typename MidiFunc, typename SysexFunc, typename ParamFunc> | ||
void processRenderEvents(AURenderEvent* events, | ||
MidiFunc midiFunc, | ||
SysexFunc sysexFunc, | ||
ParamFunc paramFunc) { | ||
|
||
while(events) { | ||
switch(events->head.eventType) { | ||
case AURenderEventMIDI: | ||
midiFunc(&events->MIDI); | ||
break; | ||
case AURenderEventMIDISysEx: | ||
sysexFunc(&events->MIDI); | ||
break; | ||
case AURenderEventParameter: | ||
paramFunc(&events->parameter); | ||
break; | ||
case AURenderEventParameterRamp: | ||
paramFunc(&events->parameter); | ||
break; | ||
case AURenderEventMIDIEventList: | ||
midiFunc(&events->MIDI); | ||
break; | ||
} | ||
events = events->head.next; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
// Copyright AudioKit. All Rights Reserved. Revision History at http://github.com/AudioKit/AudioKit/ | ||
#pragma once | ||
#include <vector> | ||
#include <functional> | ||
#include <atomic> | ||
#include <memory> | ||
#include <AudioUnit/AudioUnit.h> | ||
#include <AudioToolbox/AudioToolbox.h> | ||
#include <AVFoundation/AVFoundation.h> | ||
#include "SynchronizedAudioBufferList.h" | ||
|
||
typedef int RenderJobIndex; | ||
|
||
namespace AudioKit { | ||
|
||
class RenderJob { | ||
private: | ||
/// Buffer we're writing to, unless overridden by buffer passed to render. | ||
std::shared_ptr<SynrchonizedAudioBufferList2> outputBuffer; | ||
|
||
/// Block called to render. | ||
AURenderBlock renderBlock; | ||
|
||
/// Input block passed to the renderBlock. We don't chain AUs recursively. | ||
AURenderPullInputBlock inputBlock; | ||
|
||
/// Indices of jobs feeding this one | ||
std::vector<int> inputIndices; | ||
|
||
public: | ||
|
||
/// Indices of jobs that this one feeds. | ||
std::vector<int> outputIndices; | ||
|
||
RenderJob(std::shared_ptr<SynrchonizedAudioBufferList2> outputBuffer, | ||
AURenderBlock renderBlock, | ||
AURenderPullInputBlock inputBlock, | ||
std::vector<int> inputIndices) | ||
: outputBuffer(outputBuffer), renderBlock(renderBlock), inputBlock(inputBlock), inputIndices(inputIndices) { | ||
} | ||
|
||
/// Number of inputs feeding this AU. | ||
int32_t inputCount() { | ||
return static_cast<int32_t>(inputIndices.size()); | ||
} | ||
|
||
void render(AudioUnitRenderActionFlags* actionFlags, const AudioTimeStamp* timeStamp, AUAudioFrameCount frameCount, AudioBufferList* outputBufferList=nullptr) { | ||
|
||
AudioBufferList* out = outputBufferList ? outputBufferList : outputBuffer->abl; | ||
|
||
// AUs may change the output size, so reset it. | ||
out->mBuffers[0].mDataByteSize = frameCount * sizeof(float); | ||
out->mBuffers[1].mDataByteSize = frameCount * sizeof(float); | ||
|
||
// Do the actual DSP. | ||
AUAudioUnitStatus status = renderBlock(actionFlags, timeStamp, frameCount, 0, out, inputBlock); | ||
|
||
// Propagate errors. | ||
if (status != noErr) { | ||
switch (status) { | ||
case kAudioUnitErr_NoConnection: | ||
printf("got kAudioUnitErr_NoConnection\n"); | ||
break; | ||
case kAudioUnitErr_TooManyFramesToProcess: | ||
printf("got kAudioUnitErr_TooManyFramesToProcess\n"); | ||
break; | ||
case AVAudioEngineManualRenderingErrorNotRunning: | ||
printf("got AVAudioEngineManualRenderingErrorNotRunning\n"); | ||
break; | ||
case kAudio_ParamError: | ||
printf("got kAudio_ParamError\n"); | ||
break; | ||
default: | ||
printf("unkown rendering error\n"); | ||
break; | ||
} | ||
} | ||
|
||
// Indicate that we're done writing to the output. | ||
outputBuffer->endWriting(); | ||
} | ||
}; | ||
|
||
} // namespace AudioKit | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
// Copyright AudioKit. All Rights Reserved. Revision History at http://github.com/AudioKit/AudioKit/ | ||
#pragma once | ||
#include <vector> | ||
#include <cstdint> | ||
#include <cassert> | ||
#include <type_traits> | ||
#include <AudioUnit/AudioUnit.h> | ||
|
||
|
||
|
||
/// Encode a value in a MIDI sysex message. Value must be plain-old-data. | ||
template<typename T> | ||
std::vector<uint8_t> encodeSysex(T value) { | ||
// static_assert to check if T is POD at compile time. | ||
static_assert(std::is_pod<T>::value, "T must be plain-old-data"); | ||
|
||
// Start with a sysex header. | ||
std::vector<uint8_t> result {0xF0, 0x00}; | ||
|
||
// Encode the value as a sequence of nibbles. | ||
// There might be some more efficient way to do this, | ||
// but we can't clash with the 0xF7 end-of-message. | ||
// We may not actually need to encode a valid MIDI sysex | ||
// message, but that could be implementation dependent | ||
// and change over time. Best to be safe. | ||
uint8_t* value_ptr = reinterpret_cast<uint8_t*>(&value); | ||
for(size_t i = 0; i < sizeof(T); i++) { | ||
uint8_t byte = value_ptr[i]; | ||
result.push_back(byte >> 4); | ||
result.push_back(byte & 0xF); | ||
} | ||
|
||
result.push_back(0xF7); | ||
return result; | ||
} | ||
|
||
/// Decode a sysex message into a value. Value must be plain-old-data. | ||
/// | ||
/// We can't return a value because we can't assume the value can be | ||
/// default constructed. | ||
/// | ||
/// - Parameters: | ||
/// - bytes: the sysex message | ||
/// - count: number of bytes in message | ||
/// - value: the value we're writing to | ||
/// | ||
template<typename T> | ||
void decodeSysex(const uint8_t* bytes, size_t count, T& value) { | ||
// static_assert to check if T is POD at compile time. | ||
static_assert(std::is_pod<T>::value, "T must be plain-old-data"); | ||
|
||
assert(count == 2 * sizeof(T) + 3); | ||
|
||
uint8_t* value_ptr = reinterpret_cast<uint8_t*>(&value); | ||
for(size_t i = 0; i < sizeof(T); i++) { | ||
value_ptr[i] = (bytes[2 * i + 2] << 4) | bytes[2 * i + 3]; | ||
} | ||
} | ||
|
||
/// Call a function with a pointer to the midi data in the AURenderEvent. | ||
/// | ||
/// We need this function because event.pointee.MIDI.data is just a tuple of three midi bytes. This is | ||
/// fine for simple midi messages like note on/off, but some messages are longer, so we need | ||
/// access to the full array, which extends off the end of the structure (one of those variable-length C structs). | ||
/// | ||
/// - Parameters: | ||
/// - event: pointer to the AURenderEvent | ||
/// - f: function to call | ||
|
||
void withMidiData(const AUMIDIEvent* event, void (*f)(const uint8_t*)) { | ||
assert(event->eventType == AURenderEventMIDISysEx || event->eventType == AURenderEventMIDI); | ||
|
||
intptr_t offset = reinterpret_cast<intptr_t>(&(event->data)) - reinterpret_cast<intptr_t>(event); | ||
const uint8_t* raw = reinterpret_cast<const uint8_t*>(event) + offset; | ||
|
||
f(raw); | ||
} | ||
|
||
|
||
/// Decode a value from a sysex AURenderEvent. | ||
/// | ||
/// We can't return a value because we can't assume the value can be | ||
/// default constructed. | ||
/// | ||
/// - Parameters: | ||
/// - event: pointer to the AURenderEvent | ||
/// - value: where we will store the value | ||
template<typename T> | ||
void decodeSysex(const AUMIDIEvent* event, T& value) { | ||
static_assert(std::is_trivial<T>::value && std::is_standard_layout<T>::value, "Type T must be POD"); | ||
|
||
assert(event->eventType == AURenderEventMIDISysEx); | ||
|
||
uint16_t length = event->length; | ||
|
||
withMidiData(event, [&](const uint8_t* ptr) { | ||
decodeSysex(ptr, static_cast<int>(length), value); | ||
}); | ||
} | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
#pragma once |
Oops, something went wrong.