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.

479 lines
13KB

  1. #include <assert.h>
  2. #include <mutex>
  3. #include <thread>
  4. #include <portaudio.h>
  5. #include "core.hpp"
  6. #include "dsp.hpp"
  7. using namespace rack;
  8. void audioInit() {
  9. PaError err = Pa_Initialize();
  10. if (err) {
  11. fprintf(stderr, "Failed to initialize PortAudio: %s\n", Pa_GetErrorText(err));
  12. return;
  13. }
  14. }
  15. struct AudioInterface : Module {
  16. enum ParamIds {
  17. NUM_PARAMS
  18. };
  19. enum InputIds {
  20. AUDIO1_INPUT,
  21. AUDIO2_INPUT,
  22. NUM_INPUTS
  23. };
  24. enum OutputIds {
  25. AUDIO1_OUTPUT,
  26. AUDIO2_OUTPUT,
  27. NUM_OUTPUTS
  28. };
  29. PaStream *stream = NULL;
  30. // Stream properties
  31. int deviceId = -1;
  32. float sampleRate = 44100.0;
  33. int blockSize = 256;
  34. int numOutputs = 0;
  35. int numInputs = 0;
  36. // Used because the GUI thread and Rack thread can both interact with this class
  37. std::mutex bufferMutex;
  38. bool streamRunning;
  39. SampleRateConverter<2> inputSrc;
  40. SampleRateConverter<2> outputSrc;
  41. // in rack's sample rate
  42. DoubleRingBuffer<Frame<2>, 16> inputBuffer;
  43. DoubleRingBuffer<Frame<2>, (1<<15)> outputBuffer;
  44. // in device's sample rate
  45. DoubleRingBuffer<Frame<2>, (1<<15)> inputSrcBuffer;
  46. AudioInterface();
  47. ~AudioInterface();
  48. void step();
  49. void stepStream(const float *input, float *output, int numFrames);
  50. int getDeviceCount();
  51. std::string getDeviceName(int deviceId);
  52. void openDevice(int deviceId, float sampleRate, int blockSize);
  53. void closeDevice();
  54. void setDeviceId(int deviceId) {
  55. openDevice(deviceId, sampleRate, blockSize);
  56. }
  57. void setSampleRate(float sampleRate) {
  58. openDevice(deviceId, sampleRate, blockSize);
  59. }
  60. void setBlockSize(int blockSize) {
  61. openDevice(deviceId, sampleRate, blockSize);
  62. }
  63. };
  64. AudioInterface::AudioInterface() {
  65. params.resize(NUM_PARAMS);
  66. inputs.resize(NUM_INPUTS);
  67. outputs.resize(NUM_OUTPUTS);
  68. }
  69. AudioInterface::~AudioInterface() {
  70. closeDevice();
  71. }
  72. void AudioInterface::step() {
  73. if (!stream)
  74. return;
  75. // Read/write stream if we have enough input, OR the output buffer is empty if we have no input
  76. if (numOutputs > 0) {
  77. while (inputSrcBuffer.size() >= blockSize && streamRunning) {
  78. std::this_thread::sleep_for(std::chrono::duration<float>(100e-6));
  79. }
  80. }
  81. else if (numInputs > 0) {
  82. while (outputBuffer.empty() && streamRunning) {
  83. std::this_thread::sleep_for(std::chrono::duration<float>(100e-6));
  84. }
  85. }
  86. std::lock_guard<std::mutex> lock(bufferMutex);
  87. // Get input and pass it through the sample rate converter
  88. if (numOutputs > 0) {
  89. if (!inputBuffer.full()) {
  90. Frame<2> f;
  91. f.samples[0] = getf(inputs[AUDIO1_INPUT]) / 5.0;
  92. f.samples[1] = getf(inputs[AUDIO2_INPUT]) / 5.0;
  93. inputBuffer.push(f);
  94. }
  95. // Once full, sample rate convert the input
  96. // inputBuffer -> SRC -> inputSrcBuffer
  97. if (inputBuffer.full()) {
  98. inputSrc.setRatio(sampleRate / gSampleRate);
  99. int inLen = inputBuffer.size();
  100. int outLen = inputSrcBuffer.capacity();
  101. inputSrc.process(inputBuffer.startData(), &inLen, inputSrcBuffer.endData(), &outLen);
  102. inputBuffer.startIncr(inLen);
  103. inputSrcBuffer.endIncr(outLen);
  104. }
  105. }
  106. // Set output
  107. if (!outputBuffer.empty()) {
  108. Frame<2> f = outputBuffer.shift();
  109. setf(outputs[AUDIO1_OUTPUT], 5.0 * f.samples[0]);
  110. setf(outputs[AUDIO2_OUTPUT], 5.0 * f.samples[1]);
  111. }
  112. }
  113. void AudioInterface::stepStream(const float *input, float *output, int numFrames) {
  114. if (numOutputs > 0) {
  115. // Wait for enough input before proceeding
  116. while (inputSrcBuffer.size() < numFrames) {
  117. if (!streamRunning)
  118. return;
  119. std::this_thread::sleep_for(std::chrono::duration<float>(100e-6));
  120. }
  121. }
  122. std::lock_guard<std::mutex> lock(bufferMutex);
  123. // input stream -> output buffer
  124. if (numInputs > 0) {
  125. Frame<2> inputFrames[numFrames];
  126. for (int i = 0; i < numFrames; i++) {
  127. for (int c = 0; c < 2; c++) {
  128. inputFrames[i].samples[c] = (numInputs > c) ? input[i*numInputs + c] : 0.0;
  129. }
  130. }
  131. // Pass output through sample rate converter
  132. outputSrc.setRatio(gSampleRate / sampleRate);
  133. int inLen = numFrames;
  134. int outLen = outputBuffer.capacity();
  135. outputSrc.process(inputFrames, &inLen, outputBuffer.endData(), &outLen);
  136. outputBuffer.endIncr(outLen);
  137. }
  138. // input buffer -> output stream
  139. if (numOutputs > 0) {
  140. for (int i = 0; i < numFrames; i++) {
  141. if (inputSrcBuffer.empty())
  142. break;
  143. Frame<2> f = inputSrcBuffer.shift();
  144. for (int c = 0; c < numOutputs; c++) {
  145. output[i*numOutputs + c] = f.samples[c];
  146. }
  147. }
  148. }
  149. }
  150. int AudioInterface::getDeviceCount() {
  151. return Pa_GetDeviceCount();
  152. }
  153. std::string AudioInterface::getDeviceName(int deviceId) {
  154. const PaDeviceInfo *info = Pa_GetDeviceInfo(deviceId);
  155. if (!info)
  156. return "";
  157. const PaHostApiInfo *apiInfo = Pa_GetHostApiInfo(info->hostApi);
  158. return stringf("%s: %s (%d in, %d out)", apiInfo->name, info->name, info->maxInputChannels, info->maxOutputChannels);
  159. }
  160. static int paCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo *timeInfo, PaStreamCallbackFlags statusFlags, void *userData) {
  161. AudioInterface *p = (AudioInterface *) userData;
  162. p->stepStream((const float *) inputBuffer, (float *) outputBuffer, framesPerBuffer);
  163. return paContinue;
  164. }
  165. void AudioInterface::openDevice(int deviceId, float sampleRate, int blockSize) {
  166. closeDevice();
  167. std::lock_guard<std::mutex> lock(bufferMutex);
  168. this->sampleRate = sampleRate;
  169. this->blockSize = blockSize;
  170. // Open new device
  171. if (deviceId >= 0) {
  172. PaError err;
  173. const PaDeviceInfo *deviceInfo = Pa_GetDeviceInfo(deviceId);
  174. if (!deviceInfo) {
  175. fprintf(stderr, "Failed to query audio device\n");
  176. return;
  177. }
  178. numOutputs = mini(deviceInfo->maxOutputChannels, 2);
  179. numInputs = mini(deviceInfo->maxInputChannels, 2);
  180. PaStreamParameters outputParameters;
  181. outputParameters.device = deviceId;
  182. outputParameters.channelCount = numOutputs;
  183. outputParameters.sampleFormat = paFloat32;
  184. outputParameters.suggestedLatency = deviceInfo->defaultLowOutputLatency;
  185. outputParameters.hostApiSpecificStreamInfo = NULL;
  186. PaStreamParameters inputParameters;
  187. inputParameters.device = deviceId;
  188. inputParameters.channelCount = numInputs;
  189. inputParameters.sampleFormat = paFloat32;
  190. inputParameters.suggestedLatency = deviceInfo->defaultLowInputLatency;
  191. inputParameters.hostApiSpecificStreamInfo = NULL;
  192. // Don't use stream parameters if 0 input or output channels
  193. err = Pa_OpenStream(&stream,
  194. numInputs == 0 ? NULL : &inputParameters,
  195. numOutputs == 0 ? NULL : &outputParameters,
  196. sampleRate, blockSize, paNoFlag, paCallback, this);
  197. if (err) {
  198. fprintf(stderr, "Failed to open audio stream: %s\n", Pa_GetErrorText(err));
  199. return;
  200. }
  201. err = Pa_StartStream(stream);
  202. if (err) {
  203. fprintf(stderr, "Failed to start audio stream: %s\n", Pa_GetErrorText(err));
  204. return;
  205. }
  206. // This should go after Pa_StartStream because sometimes it will call the callback once synchronously, and that time it should return early
  207. streamRunning = true;
  208. // Correct sample rate
  209. const PaStreamInfo *streamInfo = Pa_GetStreamInfo(stream);
  210. this->sampleRate = streamInfo->sampleRate;
  211. this->deviceId = deviceId;
  212. }
  213. }
  214. void AudioInterface::closeDevice() {
  215. std::lock_guard<std::mutex> lock(bufferMutex);
  216. if (stream) {
  217. PaError err;
  218. streamRunning = false;
  219. err = Pa_AbortStream(stream);
  220. // err = Pa_StopStream(stream);
  221. if (err) {
  222. fprintf(stderr, "Failed to stop audio stream: %s\n", Pa_GetErrorText(err));
  223. }
  224. err = Pa_CloseStream(stream);
  225. if (err) {
  226. fprintf(stderr, "Failed to close audio stream: %s\n", Pa_GetErrorText(err));
  227. }
  228. }
  229. // Reset stream settings
  230. stream = NULL;
  231. deviceId = -1;
  232. numOutputs = 0;
  233. numInputs = 0;
  234. // Clear buffers
  235. inputBuffer.clear();
  236. outputBuffer.clear();
  237. inputSrcBuffer.clear();
  238. inputSrc.reset();
  239. outputSrc.reset();
  240. }
  241. struct AudioItem : MenuItem {
  242. AudioInterface *audioInterface;
  243. int deviceId;
  244. void onAction() {
  245. audioInterface->setDeviceId(deviceId);
  246. }
  247. };
  248. struct AudioChoice : ChoiceButton {
  249. AudioInterface *audioInterface;
  250. void onAction() {
  251. MenuOverlay *overlay = new MenuOverlay();
  252. Menu *menu = new Menu();
  253. menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y));
  254. int deviceCount = audioInterface->getDeviceCount();
  255. {
  256. AudioItem *audioItem = new AudioItem();
  257. audioItem->audioInterface = audioInterface;
  258. audioItem->deviceId = -1;
  259. audioItem->text = "No device";
  260. menu->pushChild(audioItem);
  261. }
  262. for (int deviceId = 0; deviceId < deviceCount; deviceId++) {
  263. AudioItem *audioItem = new AudioItem();
  264. audioItem->audioInterface = audioInterface;
  265. audioItem->deviceId = deviceId;
  266. audioItem->text = audioInterface->getDeviceName(deviceId);
  267. menu->pushChild(audioItem);
  268. }
  269. overlay->addChild(menu);
  270. gScene->setOverlay(overlay);
  271. }
  272. void step() {
  273. std::string name = audioInterface->getDeviceName(audioInterface->deviceId);
  274. text = name.empty() ? "(no device)" : ellipsize(name, 14);
  275. }
  276. };
  277. struct SampleRateItem : MenuItem {
  278. AudioInterface *audioInterface;
  279. float sampleRate;
  280. void onAction() {
  281. audioInterface->setSampleRate(sampleRate);
  282. }
  283. };
  284. struct SampleRateChoice : ChoiceButton {
  285. AudioInterface *audioInterface;
  286. void onAction() {
  287. MenuOverlay *overlay = new MenuOverlay();
  288. Menu *menu = new Menu();
  289. menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y));
  290. const float sampleRates[6] = {44100, 48000, 88200, 96000, 176400, 192000};
  291. int sampleRatesLen = sizeof(sampleRates) / sizeof(sampleRates[0]);
  292. for (int i = 0; i < sampleRatesLen; i++) {
  293. SampleRateItem *item = new SampleRateItem();
  294. item->audioInterface = audioInterface;
  295. item->sampleRate = sampleRates[i];
  296. item->text = stringf("%.0f Hz", sampleRates[i]);
  297. menu->pushChild(item);
  298. }
  299. overlay->addChild(menu);
  300. gScene->setOverlay(overlay);
  301. }
  302. void step() {
  303. this->text = stringf("%.0f Hz", audioInterface->sampleRate);
  304. }
  305. };
  306. struct BlockSizeItem : MenuItem {
  307. AudioInterface *audioInterface;
  308. int blockSize;
  309. void onAction() {
  310. audioInterface->setBlockSize(blockSize);
  311. }
  312. };
  313. struct BlockSizeChoice : ChoiceButton {
  314. AudioInterface *audioInterface;
  315. void onAction() {
  316. MenuOverlay *overlay = new MenuOverlay();
  317. Menu *menu = new Menu();
  318. menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y));
  319. const int blockSizes[] = {64, 128, 256, 512, 1024, 2048, 4096};
  320. int blockSizesLen = sizeof(blockSizes) / sizeof(blockSizes[0]);
  321. for (int i = 0; i < blockSizesLen; i++) {
  322. BlockSizeItem *item = new BlockSizeItem();
  323. item->audioInterface = audioInterface;
  324. item->blockSize = blockSizes[i];
  325. item->text = stringf("%d", blockSizes[i]);
  326. menu->pushChild(item);
  327. }
  328. overlay->addChild(menu);
  329. gScene->setOverlay(overlay);
  330. }
  331. void step() {
  332. this->text = stringf("%d", audioInterface->blockSize);
  333. }
  334. };
  335. AudioInterfaceWidget::AudioInterfaceWidget() {
  336. AudioInterface *module = new AudioInterface();
  337. setModule(module);
  338. box.size = Vec(15*8, 380);
  339. {
  340. Panel *panel = new LightPanel();
  341. panel->box.size = box.size;
  342. addChild(panel);
  343. }
  344. float margin = 5;
  345. float yPos = margin;
  346. {
  347. Label *label = new Label();
  348. label->box.pos = Vec(margin, yPos);
  349. label->text = "Audio device";
  350. addChild(label);
  351. yPos += label->box.size.y + margin;
  352. AudioChoice *choice = new AudioChoice();
  353. choice->audioInterface = dynamic_cast<AudioInterface*>(module);
  354. choice->box.pos = Vec(margin, yPos);
  355. choice->box.size.x = box.size.x - 10;
  356. addChild(choice);
  357. yPos += choice->box.size.y + 2*margin;
  358. }
  359. {
  360. Label *label = new Label();
  361. label->box.pos = Vec(margin, yPos);
  362. label->text = "Sample rate";
  363. addChild(label);
  364. yPos += label->box.size.y + margin;
  365. SampleRateChoice *choice = new SampleRateChoice();
  366. choice->audioInterface = dynamic_cast<AudioInterface*>(module);
  367. choice->box.pos = Vec(margin, yPos);
  368. choice->box.size.x = box.size.x - 10;
  369. addChild(choice);
  370. yPos += choice->box.size.y + 2*margin;
  371. }
  372. {
  373. Label *label = new Label();
  374. label->box.pos = Vec(margin, yPos);
  375. label->text = "Block size";
  376. addChild(label);
  377. yPos += label->box.size.y + margin;
  378. BlockSizeChoice *choice = new BlockSizeChoice();
  379. choice->audioInterface = dynamic_cast<AudioInterface*>(module);
  380. choice->box.pos = Vec(margin, yPos);
  381. choice->box.size.x = box.size.x - 10;
  382. addChild(choice);
  383. yPos += choice->box.size.y + 2*margin;
  384. }
  385. {
  386. Label *label = new Label();
  387. label->box.pos = Vec(margin, yPos);
  388. label->text = "Outputs";
  389. addChild(label);
  390. yPos += label->box.size.y + margin;
  391. }
  392. yPos += 5;
  393. addInput(createInput<InputPortPJ3410>(Vec(20, yPos), module, AudioInterface::AUDIO1_INPUT));
  394. addInput(createInput<InputPortPJ3410>(Vec(70, yPos), module, AudioInterface::AUDIO2_INPUT));
  395. yPos += 35 + margin;
  396. {
  397. Label *label = new Label();
  398. label->box.pos = Vec(margin, yPos);
  399. label->text = "Inputs";
  400. addChild(label);
  401. yPos += label->box.size.y + margin;
  402. }
  403. yPos += 5;
  404. addOutput(createOutput<OutputPortPJ3410>(Vec(20, yPos), module, AudioInterface::AUDIO1_OUTPUT));
  405. addOutput(createOutput<OutputPortPJ3410>(Vec(70, yPos), module, AudioInterface::AUDIO2_OUTPUT));
  406. yPos += 35 + margin;
  407. }