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.

554 lines
14KB

  1. #include <assert.h>
  2. #include <mutex>
  3. #include <thread>
  4. #include "core.hpp"
  5. #include "dsp/samplerate.hpp"
  6. #include "dsp/ringbuffer.hpp"
  7. #pragma GCC diagnostic push
  8. #pragma GCC diagnostic ignored "-Wsuggest-override"
  9. #include <rtaudio/RtAudio.h>
  10. #pragma GCC diagnostic pop
  11. using namespace rack;
  12. struct AudioInterface : Module {
  13. enum ParamIds {
  14. NUM_PARAMS
  15. };
  16. enum InputIds {
  17. AUDIO1_INPUT,
  18. NUM_INPUTS = AUDIO1_INPUT + 8
  19. };
  20. enum OutputIds {
  21. AUDIO1_OUTPUT,
  22. NUM_OUTPUTS = AUDIO1_OUTPUT + 8
  23. };
  24. RtAudio stream;
  25. // Stream properties
  26. int deviceId = -1;
  27. float sampleRate = 44100.0;
  28. int blockSize = 256;
  29. int numOutputs = 0;
  30. int numInputs = 0;
  31. // Used because the GUI thread and Rack thread can both interact with this class
  32. std::mutex bufferMutex;
  33. SampleRateConverter<8> inputSrc;
  34. SampleRateConverter<8> outputSrc;
  35. // in rack's sample rate
  36. DoubleRingBuffer<Frame<8>, 16> inputBuffer;
  37. DoubleRingBuffer<Frame<8>, (1<<15)> outputBuffer;
  38. // in device's sample rate
  39. DoubleRingBuffer<Frame<8>, (1<<15)> inputSrcBuffer;
  40. AudioInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) {}
  41. ~AudioInterface() {
  42. closeDevice();
  43. }
  44. void step() override;
  45. void stepStream(const float *input, float *output, int numFrames);
  46. int getDeviceCount();
  47. std::string getDeviceName(int deviceId);
  48. void openDevice(int deviceId, float sampleRate, int blockSize);
  49. void closeDevice();
  50. void setDeviceId(int deviceId) {
  51. openDevice(deviceId, sampleRate, blockSize);
  52. }
  53. void setSampleRate(float sampleRate) {
  54. openDevice(deviceId, sampleRate, blockSize);
  55. }
  56. void setBlockSize(int blockSize) {
  57. openDevice(deviceId, sampleRate, blockSize);
  58. }
  59. json_t *toJson() override {
  60. json_t *rootJ = json_object();
  61. if (deviceId >= 0) {
  62. std::string deviceName = getDeviceName(deviceId);
  63. json_object_set_new(rootJ, "deviceName", json_string(deviceName.c_str()));
  64. json_object_set_new(rootJ, "sampleRate", json_real(sampleRate));
  65. json_object_set_new(rootJ, "blockSize", json_integer(blockSize));
  66. }
  67. return rootJ;
  68. }
  69. void fromJson(json_t *rootJ) override {
  70. json_t *deviceNameJ = json_object_get(rootJ, "deviceName");
  71. if (deviceNameJ) {
  72. std::string deviceName = json_string_value(deviceNameJ);
  73. for (int i = 0; i < getDeviceCount(); i++) {
  74. if (deviceName == getDeviceName(i)) {
  75. setDeviceId(i);
  76. break;
  77. }
  78. }
  79. }
  80. json_t *sampleRateJ = json_object_get(rootJ, "sampleRate");
  81. if (sampleRateJ) {
  82. setSampleRate(json_number_value(sampleRateJ));
  83. }
  84. json_t *blockSizeJ = json_object_get(rootJ, "blockSize");
  85. if (blockSizeJ) {
  86. setBlockSize(json_integer_value(blockSizeJ));
  87. }
  88. }
  89. void reset() override {
  90. closeDevice();
  91. }
  92. };
  93. void AudioInterface::step() {
  94. // Read/write stream if we have enough input, OR the output buffer is empty if we have no input
  95. if (numOutputs > 0) {
  96. while (inputSrcBuffer.size() >= blockSize && stream.isStreamRunning()) {
  97. std::this_thread::sleep_for(std::chrono::duration<float>(100e-6));
  98. }
  99. }
  100. else if (numInputs > 0) {
  101. while (outputBuffer.empty() && stream.isStreamRunning()) {
  102. std::this_thread::sleep_for(std::chrono::duration<float>(100e-6));
  103. }
  104. }
  105. std::lock_guard<std::mutex> lock(bufferMutex);
  106. // Get input and pass it through the sample rate converter
  107. if (numOutputs > 0) {
  108. if (!inputBuffer.full()) {
  109. Frame<8> f;
  110. for (int i = 0; i < 8; i++) {
  111. f.samples[i] = inputs[AUDIO1_INPUT + i].value / 10.0;
  112. }
  113. inputBuffer.push(f);
  114. }
  115. // Once full, sample rate convert the input
  116. // inputBuffer -> SRC -> inputSrcBuffer
  117. if (inputBuffer.full()) {
  118. inputSrc.setRatio(sampleRate / engineGetSampleRate());
  119. int inLen = inputBuffer.size();
  120. int outLen = inputSrcBuffer.capacity();
  121. inputSrc.process(inputBuffer.startData(), &inLen, inputSrcBuffer.endData(), &outLen);
  122. inputBuffer.startIncr(inLen);
  123. inputSrcBuffer.endIncr(outLen);
  124. }
  125. }
  126. // Set output
  127. if (!outputBuffer.empty()) {
  128. Frame<8> f = outputBuffer.shift();
  129. for (int i = 0; i < 8; i++) {
  130. outputs[AUDIO1_OUTPUT + i].value = 10.0 * f.samples[i];
  131. }
  132. }
  133. }
  134. void AudioInterface::stepStream(const float *input, float *output, int numFrames) {
  135. if (gPaused) {
  136. memset(output, 0, sizeof(float) * numOutputs * numFrames);
  137. return;
  138. }
  139. if (numOutputs > 0) {
  140. // Wait for enough input before proceeding
  141. while (inputSrcBuffer.size() < numFrames) {
  142. if (!stream.isStreamRunning())
  143. return;
  144. std::this_thread::sleep_for(std::chrono::duration<float>(100e-6));
  145. }
  146. }
  147. std::lock_guard<std::mutex> lock(bufferMutex);
  148. // input stream -> output buffer
  149. if (numInputs > 0) {
  150. Frame<8> inputFrames[numFrames];
  151. for (int i = 0; i < numFrames; i++) {
  152. for (int c = 0; c < 8; c++) {
  153. inputFrames[i].samples[c] = (c < numInputs) ? input[i*numInputs + c] : 0.0;
  154. }
  155. }
  156. // Pass output through sample rate converter
  157. outputSrc.setRatio(engineGetSampleRate() / sampleRate);
  158. int inLen = numFrames;
  159. int outLen = outputBuffer.capacity();
  160. outputSrc.process(inputFrames, &inLen, outputBuffer.endData(), &outLen);
  161. outputBuffer.endIncr(outLen);
  162. }
  163. // input buffer -> output stream
  164. if (numOutputs > 0) {
  165. for (int i = 0; i < numFrames; i++) {
  166. if (inputSrcBuffer.empty())
  167. break;
  168. Frame<8> f = inputSrcBuffer.shift();
  169. for (int c = 0; c < numOutputs; c++) {
  170. output[i*numOutputs + c] = (c < 8) ? clampf(f.samples[c], -1.0, 1.0) : 0.0;
  171. }
  172. }
  173. }
  174. }
  175. int AudioInterface::getDeviceCount() {
  176. return stream.getDeviceCount();
  177. }
  178. std::string AudioInterface::getDeviceName(int deviceId) {
  179. if (deviceId < 0)
  180. return "";
  181. try {
  182. RtAudio::DeviceInfo deviceInfo = stream.getDeviceInfo(deviceId);
  183. return stringf("%s (%d in, %d out)", deviceInfo.name.c_str(), deviceInfo.inputChannels, deviceInfo.outputChannels);
  184. }
  185. catch (RtAudioError &e) {
  186. warn("Failed to query audio device: %s", e.what());
  187. return "";
  188. }
  189. }
  190. static int rtCallback(void *outputBuffer, void *inputBuffer, unsigned int nFrames, double streamTime, RtAudioStreamStatus status, void *userData) {
  191. AudioInterface *audioInterface = (AudioInterface *) userData;
  192. assert(audioInterface);
  193. audioInterface->stepStream((const float *) inputBuffer, (float *) outputBuffer, nFrames);
  194. return 0;
  195. }
  196. void AudioInterface::openDevice(int deviceId, float sampleRate, int blockSize) {
  197. closeDevice();
  198. std::lock_guard<std::mutex> lock(bufferMutex);
  199. this->sampleRate = sampleRate;
  200. this->blockSize = blockSize;
  201. // Open new device
  202. if (deviceId >= 0) {
  203. RtAudio::DeviceInfo deviceInfo;
  204. try {
  205. deviceInfo = stream.getDeviceInfo(deviceId);
  206. }
  207. catch (RtAudioError &e) {
  208. warn("Failed to query audio device: %s", e.what());
  209. return;
  210. }
  211. numOutputs = mini(deviceInfo.outputChannels, 8);
  212. numInputs = mini(deviceInfo.inputChannels, 8);
  213. RtAudio::StreamParameters outParameters;
  214. outParameters.deviceId = deviceId;
  215. outParameters.nChannels = numOutputs;
  216. RtAudio::StreamParameters inParameters;
  217. inParameters.deviceId = deviceId;
  218. inParameters.nChannels = numInputs;
  219. RtAudio::StreamOptions options;
  220. // options.flags |= RTAUDIO_SCHEDULE_REALTIME;
  221. try {
  222. // Don't use stream parameters if 0 input or output channels
  223. stream.openStream(
  224. numOutputs == 0 ? NULL : &outParameters,
  225. numInputs == 0 ? NULL : &inParameters,
  226. RTAUDIO_FLOAT32, sampleRate, (unsigned int*) &blockSize, &rtCallback, this, &options, NULL);
  227. }
  228. catch (RtAudioError &e) {
  229. warn("Failed to open audio stream: %s", e.what());
  230. return;
  231. }
  232. try {
  233. stream.startStream();
  234. }
  235. catch (RtAudioError &e) {
  236. warn("Failed to start audio stream: %s", e.what());
  237. return;
  238. }
  239. this->sampleRate = stream.getStreamSampleRate();
  240. this->deviceId = deviceId;
  241. }
  242. }
  243. void AudioInterface::closeDevice() {
  244. std::lock_guard<std::mutex> lock(bufferMutex);
  245. if (stream.isStreamOpen()) {
  246. try {
  247. stream.abortStream();
  248. stream.closeStream();
  249. }
  250. catch (RtAudioError &e) {
  251. warn("Failed to abort stream %s", e.what());
  252. return;
  253. }
  254. }
  255. // Reset stream settings
  256. deviceId = -1;
  257. numOutputs = 0;
  258. numInputs = 0;
  259. // Clear buffers
  260. inputBuffer.clear();
  261. outputBuffer.clear();
  262. inputSrcBuffer.clear();
  263. inputSrc.reset();
  264. outputSrc.reset();
  265. }
  266. struct AudioItem : MenuItem {
  267. AudioInterface *audioInterface;
  268. int deviceId;
  269. void onAction(EventAction &e) override {
  270. audioInterface->setDeviceId(deviceId);
  271. }
  272. };
  273. struct AudioChoice : ChoiceButton {
  274. int lastDeviceId = -1;
  275. AudioInterface *audioInterface;
  276. void onAction(EventAction &e) override {
  277. Menu *menu = gScene->createMenu();
  278. menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round();
  279. menu->box.size.x = box.size.x;
  280. int deviceCount = audioInterface->getDeviceCount();
  281. {
  282. AudioItem *audioItem = new AudioItem();
  283. audioItem->audioInterface = audioInterface;
  284. audioItem->deviceId = -1;
  285. audioItem->text = "No device";
  286. menu->pushChild(audioItem);
  287. }
  288. for (int deviceId = 0; deviceId < deviceCount; deviceId++) {
  289. AudioItem *audioItem = new AudioItem();
  290. audioItem->audioInterface = audioInterface;
  291. audioItem->deviceId = deviceId;
  292. audioItem->text = audioInterface->getDeviceName(deviceId);
  293. menu->pushChild(audioItem);
  294. }
  295. }
  296. void step() override {
  297. if (lastDeviceId != audioInterface->deviceId) {
  298. std::string name = audioInterface->getDeviceName(audioInterface->deviceId);
  299. text = ellipsize(name, 24);
  300. lastDeviceId = audioInterface->deviceId;
  301. }
  302. }
  303. };
  304. struct SampleRateItem : MenuItem {
  305. AudioInterface *audioInterface;
  306. float sampleRate;
  307. void onAction(EventAction &e) override {
  308. audioInterface->setSampleRate(sampleRate);
  309. }
  310. };
  311. struct SampleRateChoice : ChoiceButton {
  312. AudioInterface *audioInterface;
  313. void onAction(EventAction &e) override {
  314. Menu *menu = gScene->createMenu();
  315. menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round();
  316. menu->box.size.x = box.size.x;
  317. const float sampleRates[6] = {44100, 48000, 88200, 96000, 176400, 192000};
  318. int sampleRatesLen = sizeof(sampleRates) / sizeof(sampleRates[0]);
  319. for (int i = 0; i < sampleRatesLen; i++) {
  320. SampleRateItem *item = new SampleRateItem();
  321. item->audioInterface = audioInterface;
  322. item->sampleRate = sampleRates[i];
  323. item->text = stringf("%.0f Hz", sampleRates[i]);
  324. menu->pushChild(item);
  325. }
  326. }
  327. void step() override {
  328. this->text = stringf("%.0f Hz", audioInterface->sampleRate);
  329. }
  330. };
  331. struct BlockSizeItem : MenuItem {
  332. AudioInterface *audioInterface;
  333. int blockSize;
  334. void onAction(EventAction &e) override {
  335. audioInterface->setBlockSize(blockSize);
  336. }
  337. };
  338. struct BlockSizeChoice : ChoiceButton {
  339. AudioInterface *audioInterface;
  340. void onAction(EventAction &e) override {
  341. Menu *menu = gScene->createMenu();
  342. menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round();
  343. menu->box.size.x = box.size.x;
  344. const int blockSizes[] = {64, 128, 256, 512, 1024, 2048, 4096};
  345. int blockSizesLen = sizeof(blockSizes) / sizeof(blockSizes[0]);
  346. for (int i = 0; i < blockSizesLen; i++) {
  347. BlockSizeItem *item = new BlockSizeItem();
  348. item->audioInterface = audioInterface;
  349. item->blockSize = blockSizes[i];
  350. item->text = stringf("%d", blockSizes[i]);
  351. menu->pushChild(item);
  352. }
  353. }
  354. void step() override {
  355. this->text = stringf("%d", audioInterface->blockSize);
  356. }
  357. };
  358. AudioInterfaceWidget::AudioInterfaceWidget() {
  359. AudioInterface *module = new AudioInterface();
  360. setModule(module);
  361. box.size = Vec(15*12, 380);
  362. {
  363. Panel *panel = new LightPanel();
  364. panel->box.size = box.size;
  365. addChild(panel);
  366. }
  367. // addChild(createScrew<ScrewSilver>(Vec(15, 0)));
  368. // addChild(createScrew<ScrewSilver>(Vec(box.size.x-30, 0)));
  369. // addChild(createScrew<ScrewSilver>(Vec(15, 365)));
  370. // addChild(createScrew<ScrewSilver>(Vec(box.size.x-30, 365)));
  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. }