| @@ -40,7 +40,9 @@ struct AudioIO { | |||
| std::string getDeviceDetail(int device, int offset); | |||
| void setDevice(int device, int offset); | |||
| std::vector<int> getSampleRates(); | |||
| void setSampleRate(int sampleRate); | |||
| std::vector<int> getBlockSizes(); | |||
| void setBlockSize(int blockSize); | |||
| void setChannels(int numOutputs, int numInputs); | |||
| @@ -49,8 +51,6 @@ struct AudioIO { | |||
| void openStream(); | |||
| void closeStream(); | |||
| std::vector<int> getSampleRates(); | |||
| virtual void processStream(const float *input, float *output, int frames) {} | |||
| virtual void onCloseStream() {} | |||
| virtual void onOpenStream() {} | |||
| @@ -98,7 +98,11 @@ struct AudioSampleRateChoice : LedDisplayChoice { | |||
| void onAction(EventAction &e) override { | |||
| Menu *menu = gScene->createMenu(); | |||
| menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Sample rate")); | |||
| for (int sampleRate : audioWidget->audioIO->getSampleRates()) { | |||
| std::vector<int> sampleRates = audioWidget->audioIO->getSampleRates(); | |||
| if (sampleRates.empty()) { | |||
| menu->addChild(construct<MenuLabel>(&MenuLabel::text, "(None available)")); | |||
| } | |||
| for (int sampleRate : sampleRates) { | |||
| AudioSampleRateItem *item = new AudioSampleRateItem(); | |||
| item->audioIO = audioWidget->audioIO; | |||
| item->sampleRate = sampleRate; | |||
| @@ -126,7 +130,10 @@ struct AudioBlockSizeChoice : LedDisplayChoice { | |||
| void onAction(EventAction &e) override { | |||
| Menu *menu = gScene->createMenu(); | |||
| menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Block size")); | |||
| std::vector<int> blockSizes = {64, 128, 256, 512, 1024, 2048, 4096}; | |||
| std::vector<int> blockSizes = audioWidget->audioIO->getBlockSizes(); | |||
| if (blockSizes.empty()) { | |||
| menu->addChild(construct<MenuLabel>(&MenuLabel::text, "(None available)")); | |||
| } | |||
| for (int blockSize : blockSizes) { | |||
| AudioBlockSizeItem *item = new AudioBlockSizeItem(); | |||
| item->audioIO = audioWidget->audioIO; | |||
| @@ -70,7 +70,7 @@ int AudioIO::getDeviceCount() { | |||
| if (rtAudio) { | |||
| return rtAudio->getDeviceCount(); | |||
| } | |||
| if (driver == BRIDGE_DRIVER) { | |||
| else if (driver == BRIDGE_DRIVER) { | |||
| return BRIDGE_NUM_PORTS; | |||
| } | |||
| return 0; | |||
| @@ -108,7 +108,7 @@ int AudioIO::getDeviceChannels(int device) { | |||
| if (getDeviceInfo(device, &deviceInfo)) | |||
| return max((int) deviceInfo.inputChannels, (int) deviceInfo.outputChannels); | |||
| } | |||
| if (driver == BRIDGE_DRIVER) { | |||
| else if (driver == BRIDGE_DRIVER) { | |||
| return 2; | |||
| } | |||
| return 0; | |||
| @@ -123,7 +123,7 @@ std::string AudioIO::getDeviceName(int device) { | |||
| if (getDeviceInfo(device, &deviceInfo)) | |||
| return deviceInfo.name; | |||
| } | |||
| if (driver == BRIDGE_DRIVER) { | |||
| else if (driver == BRIDGE_DRIVER) { | |||
| return stringf("%d", device + 1); | |||
| } | |||
| return ""; | |||
| @@ -147,7 +147,7 @@ std::string AudioIO::getDeviceDetail(int device, int offset) { | |||
| return deviceDetail; | |||
| } | |||
| } | |||
| if (driver == BRIDGE_DRIVER) { | |||
| else if (driver == BRIDGE_DRIVER) { | |||
| return stringf("Port %d", device + 1); | |||
| } | |||
| return ""; | |||
| @@ -160,13 +160,38 @@ void AudioIO::setDevice(int device, int offset) { | |||
| openStream(); | |||
| } | |||
| std::vector<int> AudioIO::getSampleRates() { | |||
| if (rtAudio) { | |||
| try { | |||
| RtAudio::DeviceInfo deviceInfo = rtAudio->getDeviceInfo(device); | |||
| std::vector<int> sampleRates(deviceInfo.sampleRates.begin(), deviceInfo.sampleRates.end()); | |||
| return sampleRates; | |||
| } | |||
| catch (RtAudioError &e) { | |||
| warn("Failed to query RtAudio device: %s", e.what()); | |||
| } | |||
| } | |||
| return {}; | |||
| } | |||
| void AudioIO::setSampleRate(int sampleRate) { | |||
| if (sampleRate == this->sampleRate) | |||
| return; | |||
| closeStream(); | |||
| this->sampleRate = sampleRate; | |||
| openStream(); | |||
| } | |||
| std::vector<int> AudioIO::getBlockSizes() { | |||
| if (rtAudio) { | |||
| return {64, 128, 256, 512, 1024, 2048, 4096}; | |||
| } | |||
| return {}; | |||
| } | |||
| void AudioIO::setBlockSize(int blockSize) { | |||
| if (blockSize == this->blockSize) | |||
| return; | |||
| closeStream(); | |||
| this->blockSize = blockSize; | |||
| openStream(); | |||
| @@ -256,11 +281,8 @@ void AudioIO::openStream() { | |||
| this->sampleRate = rtAudio->getStreamSampleRate(); | |||
| onOpenStream(); | |||
| } | |||
| if (driver == BRIDGE_DRIVER) { | |||
| else if (driver == BRIDGE_DRIVER) { | |||
| setChannels(0, 0); | |||
| // TEMP | |||
| sampleRate = 44100; | |||
| blockSize = 256; | |||
| bridgeAudioSubscribe(device, this); | |||
| } | |||
| } | |||
| @@ -289,31 +311,13 @@ void AudioIO::closeStream() { | |||
| } | |||
| deviceInfo = RtAudio::DeviceInfo(); | |||
| } | |||
| if (driver == BRIDGE_DRIVER) { | |||
| else if (driver == BRIDGE_DRIVER) { | |||
| bridgeAudioUnsubscribe(device, this); | |||
| } | |||
| onCloseStream(); | |||
| } | |||
| std::vector<int> AudioIO::getSampleRates() { | |||
| if (rtAudio) { | |||
| try { | |||
| RtAudio::DeviceInfo deviceInfo = rtAudio->getDeviceInfo(device); | |||
| std::vector<int> sampleRates(deviceInfo.sampleRates.begin(), deviceInfo.sampleRates.end()); | |||
| return sampleRates; | |||
| } | |||
| catch (RtAudioError &e) { | |||
| warn("Failed to query RtAudio device: %s", e.what()); | |||
| } | |||
| } | |||
| if (driver == BRIDGE_DRIVER) { | |||
| return {44100, 48000, 88200, 96000, 176400, 192000}; | |||
| } | |||
| return {}; | |||
| } | |||
| json_t *AudioIO::toJson() { | |||
| json_t *rootJ = json_object(); | |||
| json_object_set_new(rootJ, "driver", json_integer(driver)); | |||
| @@ -21,14 +21,11 @@ | |||
| namespace rack { | |||
| static const int RECV_BUFFER_SIZE = (1<<13); | |||
| static const int RECV_QUEUE_SIZE = (1<<17); | |||
| struct BridgeClientConnection; | |||
| static BridgeClientConnection *connections[BRIDGE_NUM_PORTS] = {}; | |||
| static AudioIO *audioListeners[BRIDGE_NUM_PORTS] = {}; | |||
| static std::thread serverThread; | |||
| static bool running; | |||
| static bool serverRunning = false; | |||
| struct BridgeClientConnection { | |||
| @@ -138,7 +135,7 @@ struct BridgeClientConnection { | |||
| case QUIT_COMMAND: { | |||
| debug("Bridge client quitting"); | |||
| ready = true; | |||
| ready = false; | |||
| } break; | |||
| case PORT_SET_COMMAND: { | |||
| @@ -150,7 +147,7 @@ struct BridgeClientConnection { | |||
| case MIDI_MESSAGE_SEND_COMMAND: { | |||
| uint8_t midiBuffer[3]; | |||
| recv(&midiBuffer, 3); | |||
| // debug("MIDI: %02x %02x %02x", midiBuffer[0], midiBuffer[1], midiBuffer[2]); | |||
| debug("MIDI: %02x %02x %02x", midiBuffer[0], midiBuffer[1], midiBuffer[2]); | |||
| } break; | |||
| case AUDIO_SAMPLE_RATE_SET_COMMAND: { | |||
| @@ -174,9 +171,13 @@ struct BridgeClientConnection { | |||
| } | |||
| float input[length]; | |||
| recv(&input, length * sizeof(float)); | |||
| if (!recv(&input, length * sizeof(float))) { | |||
| ready = false; | |||
| return; | |||
| } | |||
| float output[length]; | |||
| int frames = length / 2; | |||
| memset(&output, 0, sizeof(output)); | |||
| processStream(input, output, frames); | |||
| send(&output, length * sizeof(float)); | |||
| flush(); | |||
| @@ -184,12 +185,12 @@ struct BridgeClientConnection { | |||
| case AUDIO_ACTIVATE: { | |||
| audioActive = true; | |||
| refreshAudioActive(); | |||
| refreshAudio(); | |||
| } break; | |||
| case AUDIO_DEACTIVATE: { | |||
| audioActive = false; | |||
| refreshAudioActive(); | |||
| refreshAudio(); | |||
| } break; | |||
| } | |||
| } | |||
| @@ -206,7 +207,7 @@ struct BridgeClientConnection { | |||
| if ((0 <= port && port < BRIDGE_NUM_PORTS) && !connections[port]) { | |||
| this->port = port; | |||
| connections[this->port] = this; | |||
| refreshAudioActive(); | |||
| refreshAudio(); | |||
| } | |||
| else { | |||
| this->port = -1; | |||
| @@ -214,8 +215,8 @@ struct BridgeClientConnection { | |||
| } | |||
| void setSampleRate(int sampleRate) { | |||
| // TODO | |||
| this->sampleRate = sampleRate; | |||
| refreshAudio(); | |||
| } | |||
| void processStream(const float *input, float *output, int frames) { | |||
| @@ -223,19 +224,22 @@ struct BridgeClientConnection { | |||
| return; | |||
| if (!audioListeners[port]) | |||
| return; | |||
| audioListeners[port]->setBlockSize(frames); | |||
| audioListeners[port]->processStream(input, output, frames); | |||
| debug("%d frames", frames); | |||
| } | |||
| void refreshAudioActive() { | |||
| void refreshAudio() { | |||
| if (!(0 <= port && port < BRIDGE_NUM_PORTS)) | |||
| return; | |||
| if (connections[port] != this) | |||
| return; | |||
| if (!audioListeners[port]) | |||
| return; | |||
| if (audioActive) | |||
| audioListeners[port]->setChannels(2, 2); | |||
| else | |||
| audioListeners[port]->setChannels(0, 0); | |||
| audioListeners[port]->setSampleRate(sampleRate); | |||
| } | |||
| }; | |||
| @@ -267,13 +271,13 @@ static void clientRun(int client) { | |||
| } | |||
| static void serverRun() { | |||
| static void serverConnect() { | |||
| int err; | |||
| // Initialize sockets | |||
| #ifdef ARCH_WIN | |||
| WSADATA wsaData; | |||
| err = WSAStartup(MAKEWORD(2,2), &wsaData); | |||
| err = WSAStartup(MAKEWORD(2, 2), &wsaData); | |||
| defer({ | |||
| WSACleanup(); | |||
| }); | |||
| @@ -352,8 +356,7 @@ static void serverRun() { | |||
| #endif | |||
| // Accept clients | |||
| running = true; | |||
| while (running) { | |||
| while (serverRunning) { | |||
| int client = accept(server, NULL, NULL); | |||
| if (client < 0) { | |||
| // Wait a bit before attempting to accept another client | |||
| @@ -369,13 +372,20 @@ static void serverRun() { | |||
| info("Bridge server closed"); | |||
| } | |||
| static void serverRun() { | |||
| while (serverRunning) { | |||
| std::this_thread::sleep_for(std::chrono::duration<double>(0.1)); | |||
| serverConnect(); | |||
| } | |||
| } | |||
| void bridgeInit() { | |||
| serverRunning = true; | |||
| serverThread = std::thread(serverRun); | |||
| } | |||
| void bridgeDestroy() { | |||
| running = false; | |||
| serverRunning = false; | |||
| serverThread.join(); | |||
| } | |||
| @@ -387,7 +397,7 @@ void bridgeAudioSubscribe(int port, AudioIO *audio) { | |||
| return; | |||
| audioListeners[port] = audio; | |||
| if (connections[port]) | |||
| connections[port]->refreshAudioActive(); | |||
| connections[port]->refreshAudio(); | |||
| } | |||
| void bridgeAudioUnsubscribe(int port, AudioIO *audio) { | |||