CoreAudioSound.cpp 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. 
  2. #include "../DFPSR/api/soundAPI.h"
  3. #include "../DFPSR/api/timeAPI.h"
  4. #include "../DFPSR/base/noSimd.h"
  5. //#include <CoreAudio/CoreAudio.h>
  6. #include <AudioUnit/AudioUnit.h>
  7. namespace dsr {
  8. static AudioUnit audioUnit;
  9. static bool playing = false;
  10. static void terminateSound() {
  11. AudioOutputUnitStop(audioUnit);
  12. AudioUnitUninitialize(audioUnit);
  13. AudioComponentInstanceDispose(audioUnit);
  14. }
  15. static uint32_t allocatedElements = 0u;
  16. static Buffer floatBuffer;
  17. static SafePointer<float> floatData;
  18. static void allocateBuffers(int neededElements) {
  19. if (neededElements > allocatedElements) {
  20. floatBuffer = buffer_create(neededElements * sizeof(float));
  21. floatData = buffer_getSafeData<float>(floatBuffer, "Output data");
  22. allocatedElements = neededElements;
  23. }
  24. }
  25. int32_t engineChannels;
  26. int32_t engineSampleRate;
  27. static std::function<bool(SafePointer<float> data, int32_t length)> engineCallback;
  28. OSStatus coreAudioCallback(void*, AudioUnitRenderActionFlags*, const AudioTimeStamp*, uint32_t, uint32_t samplesPerChannel, AudioBufferList *outputBuffers) {
  29. if (playing) {
  30. // Calculate the number of samples from all channels.
  31. uint32_t totalSamples = samplesPerChannel * engineChannels;
  32. // Make sure that we have enough memory in the float buffer to get sounds from the engine.
  33. allocateBuffers(totalSamples);
  34. // Set all elements to zero, so that the engine does not have to do it before accumulating sounds.
  35. safeMemorySet(floatData, 0, totalSamples * sizeof(float));
  36. // Get samples from the sound engine and check if we are done playing sounds.
  37. bool keepRunning = engineCallback(floatData, samplesPerChannel);
  38. // Convert from float to 16-bit signed PCM format.
  39. int16_t *outputData = (int16_t*)(outputBuffers->mBuffers[0].mData);
  40. for (uint32_t t = 0; t < samplesPerChannel * engineChannels; t++) {
  41. outputData[t] = truncateToI32(clamp(-32768.0f, floatData[t] * 32767.0f, 32767.0f));
  42. }
  43. // If the engine is done taking requests, then this backend can terminate.
  44. if (!keepRunning) {
  45. playing = false;
  46. }
  47. }
  48. return noErr;
  49. }
  50. static void initializeSound() {
  51. OSErr errorCode;
  52. AudioComponentDescription audioComponentDescription;
  53. audioComponentDescription.componentType = kAudioUnitType_Output;
  54. audioComponentDescription.componentSubType = kAudioUnitSubType_DefaultOutput;
  55. audioComponentDescription.componentManufacturer = kAudioUnitManufacturer_Apple;
  56. AudioComponent audioOutputDevice = AudioComponentFindNext(nullptr, &audioComponentDescription);
  57. if (audioOutputDevice == nullptr) {
  58. throwError("Failed to find any CoreAudio output device!\n");
  59. return;
  60. }
  61. errorCode = AudioComponentInstanceNew(audioOutputDevice, &audioUnit);
  62. if (errorCode != 0) {
  63. throwError("Failed to create the CoreAudio audio unit! Error code: ", errorCode, "!\n");
  64. return;
  65. }
  66. AURenderCallbackStruct audioUnitCallbackStruct;
  67. audioUnitCallbackStruct.inputProc = coreAudioCallback;
  68. errorCode = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &audioUnitCallbackStruct, sizeof(audioUnitCallbackStruct));
  69. if (errorCode != 0) {
  70. throwError("Failed to assign the CoreAudio audio unit callback! Error code: ", errorCode, "!\n");
  71. return;
  72. }
  73. AudioStreamBasicDescription audioStreamBasicDescription;
  74. audioStreamBasicDescription.mFormatID = kAudioFormatLinearPCM;
  75. audioStreamBasicDescription.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
  76. audioStreamBasicDescription.mSampleRate = engineSampleRate;
  77. audioStreamBasicDescription.mBitsPerChannel = 16;
  78. audioStreamBasicDescription.mChannelsPerFrame = engineChannels;
  79. audioStreamBasicDescription.mFramesPerPacket = 1;
  80. audioStreamBasicDescription.mBytesPerFrame = engineChannels * sizeof(int16_t);
  81. audioStreamBasicDescription.mBytesPerPacket = engineChannels * sizeof(int16_t);
  82. errorCode = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &audioStreamBasicDescription, sizeof(audioStreamBasicDescription));
  83. if (errorCode != 0) {
  84. throwError("Failed to set the CoreAudio audio properties! Error code: ", errorCode, "!\n");
  85. return;
  86. }
  87. errorCode = AudioUnitInitialize(audioUnit);
  88. if (errorCode != 0) {
  89. throwError("Failed to initialize the CoreAudio audio unit! Error code: ", errorCode, "!\n");
  90. return;
  91. }
  92. errorCode = AudioOutputUnitStart(audioUnit);
  93. if (errorCode != 0) {
  94. throwError("Failed to start the CoreAudio audio unit! Error code: ", errorCode, "!\n");
  95. return;
  96. }
  97. playing = true;
  98. }
  99. bool sound_streamToSpeakers(int32_t channels, int32_t sampleRate, std::function<bool(SafePointer<float> data, int32_t length)> soundOutput) {
  100. engineChannels = channels;
  101. engineSampleRate = sampleRate;
  102. engineCallback = soundOutput;
  103. initializeSound();
  104. // For consistent thread behavior between operating systems and letting callbacks finish before terminating CoreAudio's audio unit,
  105. // a loop will check if it is time to terminate the sound engine's thread once in a while.
  106. // TODO: Make it faster and more efficient, by using a mutex that is locked while playing.
  107. while (playing) {
  108. time_sleepSeconds(0.2);
  109. }
  110. terminateSound();
  111. return true;
  112. }
  113. }