| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122 |
-
- #include "../DFPSR/api/soundAPI.h"
- #include "../DFPSR/api/timeAPI.h"
- #include "../DFPSR/base/noSimd.h"
- //#include <CoreAudio/CoreAudio.h>
- #include <AudioUnit/AudioUnit.h>
- namespace dsr {
- static AudioUnit audioUnit;
- static bool playing = false;
- static void terminateSound() {
- AudioOutputUnitStop(audioUnit);
- AudioUnitUninitialize(audioUnit);
- AudioComponentInstanceDispose(audioUnit);
- }
- static uint32_t allocatedElements = 0u;
- static Buffer floatBuffer;
- static SafePointer<float> floatData;
- static void allocateBuffers(int neededElements) {
- if (neededElements > allocatedElements) {
- floatBuffer = buffer_create(neededElements * sizeof(float));
- floatData = buffer_getSafeData<float>(floatBuffer, "Output data");
- allocatedElements = neededElements;
- }
- }
- int32_t engineChannels;
- int32_t engineSampleRate;
- static std::function<bool(SafePointer<float> data, int32_t length)> engineCallback;
- OSStatus coreAudioCallback(void*, AudioUnitRenderActionFlags*, const AudioTimeStamp*, uint32_t, uint32_t samplesPerChannel, AudioBufferList *outputBuffers) {
- if (playing) {
- // Calculate the number of samples from all channels.
- uint32_t totalSamples = samplesPerChannel * engineChannels;
- // Make sure that we have enough memory in the float buffer to get sounds from the engine.
- allocateBuffers(totalSamples);
- // Set all elements to zero, so that the engine does not have to do it before accumulating sounds.
- safeMemorySet(floatData, 0, totalSamples * sizeof(float));
- // Get samples from the sound engine and check if we are done playing sounds.
- bool keepRunning = engineCallback(floatData, samplesPerChannel);
- // Convert from float to 16-bit signed PCM format.
- int16_t *outputData = (int16_t*)(outputBuffers->mBuffers[0].mData);
- for (uint32_t t = 0; t < samplesPerChannel * engineChannels; t++) {
- outputData[t] = truncateToI32(clamp(-32768.0f, floatData[t] * 32767.0f, 32767.0f));
- }
- // If the engine is done taking requests, then this backend can terminate.
- if (!keepRunning) {
- playing = false;
- }
- }
- return noErr;
- }
- static void initializeSound() {
- OSErr errorCode;
- AudioComponentDescription audioComponentDescription;
- audioComponentDescription.componentType = kAudioUnitType_Output;
- audioComponentDescription.componentSubType = kAudioUnitSubType_DefaultOutput;
- audioComponentDescription.componentManufacturer = kAudioUnitManufacturer_Apple;
- AudioComponent audioOutputDevice = AudioComponentFindNext(nullptr, &audioComponentDescription);
- if (audioOutputDevice == nullptr) {
- throwError("Failed to find any CoreAudio output device!\n");
- return;
- }
- errorCode = AudioComponentInstanceNew(audioOutputDevice, &audioUnit);
- if (errorCode != 0) {
- throwError("Failed to create the CoreAudio audio unit! Error code: ", errorCode, "!\n");
- return;
- }
- AURenderCallbackStruct audioUnitCallbackStruct;
- audioUnitCallbackStruct.inputProc = coreAudioCallback;
- errorCode = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &audioUnitCallbackStruct, sizeof(audioUnitCallbackStruct));
- if (errorCode != 0) {
- throwError("Failed to assign the CoreAudio audio unit callback! Error code: ", errorCode, "!\n");
- return;
- }
- AudioStreamBasicDescription audioStreamBasicDescription;
- audioStreamBasicDescription.mFormatID = kAudioFormatLinearPCM;
- audioStreamBasicDescription.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
- audioStreamBasicDescription.mSampleRate = engineSampleRate;
- audioStreamBasicDescription.mBitsPerChannel = 16;
- audioStreamBasicDescription.mChannelsPerFrame = engineChannels;
- audioStreamBasicDescription.mFramesPerPacket = 1;
- audioStreamBasicDescription.mBytesPerFrame = engineChannels * sizeof(int16_t);
- audioStreamBasicDescription.mBytesPerPacket = engineChannels * sizeof(int16_t);
- errorCode = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &audioStreamBasicDescription, sizeof(audioStreamBasicDescription));
- if (errorCode != 0) {
- throwError("Failed to set the CoreAudio audio properties! Error code: ", errorCode, "!\n");
- return;
- }
- errorCode = AudioUnitInitialize(audioUnit);
- if (errorCode != 0) {
- throwError("Failed to initialize the CoreAudio audio unit! Error code: ", errorCode, "!\n");
- return;
- }
- errorCode = AudioOutputUnitStart(audioUnit);
- if (errorCode != 0) {
- throwError("Failed to start the CoreAudio audio unit! Error code: ", errorCode, "!\n");
- return;
- }
- playing = true;
- }
- bool sound_streamToSpeakers(int32_t channels, int32_t sampleRate, std::function<bool(SafePointer<float> data, int32_t length)> soundOutput) {
- engineChannels = channels;
- engineSampleRate = sampleRate;
- engineCallback = soundOutput;
- initializeSound();
- // For consistent thread behavior between operating systems and letting callbacks finish before terminating CoreAudio's audio unit,
- // a loop will check if it is time to terminate the sound engine's thread once in a while.
- // TODO: Make it faster and more efficient, by using a mutex that is locked while playing.
- while (playing) {
- time_sleepSeconds(0.2);
- }
- terminateSound();
- return true;
- }
- }
|