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.

553 lines
14KB

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