You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

204 lines
5.2KB

  1. #include "core.hpp"
  2. #include <assert.h>
  3. #include <mutex>
  4. #include <condition_variable>
  5. #include <rtaudio/RtAudio.h>
  6. #define AUDIO_BUFFER_SIZE 16384
  7. struct AudioInterface : Module {
  8. enum ParamIds {
  9. NUM_PARAMS
  10. };
  11. enum InputIds {
  12. AUDIO1_INPUT,
  13. AUDIO2_INPUT,
  14. NUM_INPUTS
  15. };
  16. enum OutputIds {
  17. NUM_OUTPUTS
  18. };
  19. float audio1Buffer[AUDIO_BUFFER_SIZE] = {};
  20. float audio2Buffer[AUDIO_BUFFER_SIZE] = {};
  21. // Current frame for step(), called by the rack thread
  22. long bufferFrame = 0;
  23. // Current frame for processAudio(), called by audio thread
  24. long audioFrame = 0;
  25. long audioFrameNeeded = -1;
  26. RtAudio audio;
  27. // The audio thread should wait on the rack thread until the buffer has enough samples
  28. std::mutex mutex;
  29. std::condition_variable cv;
  30. bool running;
  31. AudioInterface();
  32. ~AudioInterface();
  33. void step();
  34. void openDevice(int deviceId);
  35. void closeDevice();
  36. // Blocks until the buffer has enough samples
  37. void processAudio(float *outputBuffer, int frameCount);
  38. };
  39. AudioInterface::AudioInterface() {
  40. params.resize(NUM_PARAMS);
  41. inputs.resize(NUM_INPUTS);
  42. outputs.resize(NUM_OUTPUTS);
  43. }
  44. AudioInterface::~AudioInterface() {
  45. closeDevice();
  46. }
  47. void AudioInterface::step() {
  48. int i = bufferFrame % AUDIO_BUFFER_SIZE;
  49. audio1Buffer[i] = getf(inputs[AUDIO1_INPUT]);
  50. audio2Buffer[i] = getf(inputs[AUDIO2_INPUT]);
  51. std::unique_lock<std::mutex> lock(mutex);
  52. bufferFrame++;
  53. if (bufferFrame == audioFrameNeeded) {
  54. lock.unlock();
  55. cv.notify_all();
  56. }
  57. }
  58. int audioCallback(void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames, double streamTime, RtAudioStreamStatus status, void *userData) {
  59. AudioInterface *that = (AudioInterface*) userData;
  60. that->processAudio((float*) outputBuffer, nBufferFrames);
  61. return 0;
  62. }
  63. void AudioInterface::openDevice(int deviceId) {
  64. assert(!audio.isStreamOpen());
  65. if (deviceId < 0) {
  66. deviceId = audio.getDefaultOutputDevice();
  67. }
  68. RtAudio::StreamParameters streamParams;
  69. streamParams.deviceId = deviceId;
  70. streamParams.nChannels = 2;
  71. streamParams.firstChannel = 0;
  72. unsigned int sampleRate = SAMPLE_RATE;
  73. unsigned int bufferFrames = 256;
  74. audioFrame = -1;
  75. running = true;
  76. try {
  77. audio.openStream(&streamParams, NULL, RTAUDIO_FLOAT32, sampleRate, &bufferFrames, &audioCallback, this);
  78. audio.startStream();
  79. }
  80. catch (RtAudioError &e) {
  81. printf("Could not open audio stream: %s\n", e.what());
  82. }
  83. }
  84. void AudioInterface::closeDevice() {
  85. if (!audio.isStreamOpen()) {
  86. return;
  87. }
  88. {
  89. std::unique_lock<std::mutex> lock(mutex);
  90. running = false;
  91. }
  92. cv.notify_all();
  93. try {
  94. // Blocks until stream thread exits
  95. audio.stopStream();
  96. audio.closeStream();
  97. }
  98. catch (RtAudioError &e) {
  99. printf("Could not close audio stream: %s\n", e.what());
  100. }
  101. }
  102. void AudioInterface::processAudio(float *outputBuffer, int frameCount) {
  103. std::unique_lock<std::mutex> lock(mutex);
  104. if (audioFrame < 0) {
  105. // This audio thread is new. Reset the frame positions
  106. audioFrame = rackGetFrame();
  107. bufferFrame = audioFrame;
  108. }
  109. audioFrameNeeded = audioFrame + frameCount;
  110. rackRequestFrame(audioFrameNeeded);
  111. // Wait for needed frames
  112. while (running && bufferFrame < audioFrameNeeded) {
  113. cv.wait(lock);
  114. }
  115. // Copy values from internal buffer to audio buffer, while holding the mutex just in case our audio buffer wraps around
  116. for (int frame = 0; frame < frameCount; frame++) {
  117. int i = audioFrame % AUDIO_BUFFER_SIZE;
  118. outputBuffer[2*frame + 0] = audio1Buffer[i] / 5.0;
  119. outputBuffer[2*frame + 1] = audio2Buffer[i] / 5.0;
  120. audioFrame++;
  121. }
  122. }
  123. struct AudioItem : MenuItem {
  124. AudioInterface *audioInterface;
  125. int deviceId;
  126. void onAction() {
  127. audioInterface->closeDevice();
  128. audioInterface->openDevice(deviceId);
  129. }
  130. };
  131. struct AudioChoice : ChoiceButton {
  132. AudioInterface *audioInterface;
  133. void onAction() {
  134. MenuOverlay *overlay = new MenuOverlay();
  135. Menu *menu = new Menu();
  136. menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y));
  137. int deviceCount = audioInterface->audio.getDeviceCount();
  138. if (deviceCount == 0) {
  139. MenuLabel *label = new MenuLabel();
  140. label->text = "No audio devices";
  141. menu->pushChild(label);
  142. }
  143. for (int deviceId = 0; deviceId < deviceCount; deviceId++) {
  144. RtAudio::DeviceInfo info = audioInterface->audio.getDeviceInfo(deviceId);
  145. if (!info.probed)
  146. continue;
  147. char text[100];
  148. snprintf(text, 100, "%s (%d in, %d out)", info.name.c_str(), info.inputChannels, info.outputChannels);
  149. AudioItem *audioItem = new AudioItem();
  150. audioItem->audioInterface = audioInterface;
  151. audioItem->deviceId = deviceId;
  152. audioItem->text = text;
  153. menu->pushChild(audioItem);
  154. }
  155. overlay->addChild(menu);
  156. gScene->addChild(overlay);
  157. }
  158. };
  159. AudioInterfaceWidget::AudioInterfaceWidget() : ModuleWidget(new AudioInterface()) {
  160. box.size = Vec(15*4, 380);
  161. inputs.resize(AudioInterface::NUM_INPUTS);
  162. createInputPort(this, AudioInterface::AUDIO1_INPUT, Vec(15, 120));
  163. createInputPort(this, AudioInterface::AUDIO2_INPUT, Vec(15, 170));
  164. AudioChoice *audioChoice = new AudioChoice();
  165. audioChoice->audioInterface = dynamic_cast<AudioInterface*>(module);
  166. audioChoice->text = "Audio Interface";
  167. audioChoice->box.pos = Vec(0, 0);
  168. audioChoice->box.size.x = box.size.x;
  169. addChild(audioChoice);
  170. }
  171. void AudioInterfaceWidget::draw(NVGcontext *vg) {
  172. bndBackground(vg, box.pos.x, box.pos.y, box.size.x, box.size.y);
  173. ModuleWidget::draw(vg);
  174. }