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