@@ -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) { | ||||