| @@ -29,7 +29,7 @@ struct AudioInterface : Module { | |||||
| NUM_OUTPUTS = AUDIO1_OUTPUT + 8 | NUM_OUTPUTS = AUDIO1_OUTPUT + 8 | ||||
| }; | }; | ||||
| RtAudio stream; | |||||
| RtAudio *stream = NULL; | |||||
| // Stream properties | // Stream properties | ||||
| int deviceId = -1; | int deviceId = -1; | ||||
| float sampleRate = 44100.0; | float sampleRate = 44100.0; | ||||
| @@ -46,9 +46,11 @@ struct AudioInterface : Module { | |||||
| // in device's sample rate | // in device's sample rate | ||||
| DoubleRingBuffer<Frame<8>, (1<<15)> inputSrcBuffer; | DoubleRingBuffer<Frame<8>, (1<<15)> inputSrcBuffer; | ||||
| AudioInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) {} | |||||
| AudioInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) { | |||||
| setDriver(RtAudio::UNSPECIFIED); | |||||
| } | |||||
| ~AudioInterface() { | ~AudioInterface() { | ||||
| closeDevice(); | |||||
| closeStream(); | |||||
| } | } | ||||
| void step() override; | void step() override; | ||||
| @@ -57,18 +59,44 @@ struct AudioInterface : Module { | |||||
| int getDeviceCount(); | int getDeviceCount(); | ||||
| std::string getDeviceName(int deviceId); | std::string getDeviceName(int deviceId); | ||||
| void openDevice(int deviceId, float sampleRate, int blockSize); | |||||
| void closeDevice(); | |||||
| void setDeviceId(int deviceId) { | |||||
| openDevice(deviceId, sampleRate, blockSize); | |||||
| } | |||||
| void setSampleRate(float sampleRate) { | |||||
| openDevice(deviceId, sampleRate, blockSize); | |||||
| } | |||||
| void setBlockSize(int blockSize) { | |||||
| openDevice(deviceId, sampleRate, blockSize); | |||||
| void openStream(); | |||||
| void closeStream(); | |||||
| void setDriver(int driver) { | |||||
| closeStream(); | |||||
| if (stream) | |||||
| delete stream; | |||||
| stream = new RtAudio((RtAudio::Api) driver); | |||||
| } | |||||
| int getDriver() { | |||||
| if (!stream) | |||||
| return RtAudio::UNSPECIFIED; | |||||
| return stream->getCurrentApi(); | |||||
| } | |||||
| std::vector<int> getAvailableDrivers() { | |||||
| std::vector<RtAudio::Api> apis; | |||||
| RtAudio::getCompiledApi(apis); | |||||
| std::vector<int> drivers; | |||||
| for (RtAudio::Api api : apis) | |||||
| drivers.push_back(api); | |||||
| return drivers; | |||||
| } | |||||
| std::string getDriverName(int driver) { | |||||
| switch (driver) { | |||||
| case RtAudio::UNSPECIFIED: return "Unspecified"; | |||||
| case RtAudio::LINUX_ALSA: return "ALSA"; | |||||
| case RtAudio::LINUX_PULSE: return "PulseAudio"; | |||||
| case RtAudio::LINUX_OSS: return "OSS"; | |||||
| case RtAudio::UNIX_JACK: return "JACK"; | |||||
| case RtAudio::MACOSX_CORE: return "Core Audio"; | |||||
| case RtAudio::WINDOWS_WASAPI: return "WASAPI"; | |||||
| case RtAudio::WINDOWS_ASIO: return "ASIO"; | |||||
| case RtAudio::WINDOWS_DS: return "DirectSound"; | |||||
| case RtAudio::RTAUDIO_DUMMY: return "Dummy"; | |||||
| default: return "Unknown"; | |||||
| } | |||||
| } | } | ||||
| std::vector<float> getSampleRates(); | std::vector<float> getSampleRates(); | ||||
| json_t *toJson() override { | json_t *toJson() override { | ||||
| @@ -88,7 +116,7 @@ struct AudioInterface : Module { | |||||
| std::string deviceName = json_string_value(deviceNameJ); | std::string deviceName = json_string_value(deviceNameJ); | ||||
| for (int i = 0; i < getDeviceCount(); i++) { | for (int i = 0; i < getDeviceCount(); i++) { | ||||
| if (deviceName == getDeviceName(i)) { | if (deviceName == getDeviceName(i)) { | ||||
| setDeviceId(i); | |||||
| deviceId = i; | |||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| @@ -96,40 +124,42 @@ struct AudioInterface : Module { | |||||
| json_t *sampleRateJ = json_object_get(rootJ, "sampleRate"); | json_t *sampleRateJ = json_object_get(rootJ, "sampleRate"); | ||||
| if (sampleRateJ) { | if (sampleRateJ) { | ||||
| setSampleRate(json_number_value(sampleRateJ)); | |||||
| sampleRate = json_number_value(sampleRateJ); | |||||
| } | } | ||||
| json_t *blockSizeJ = json_object_get(rootJ, "blockSize"); | json_t *blockSizeJ = json_object_get(rootJ, "blockSize"); | ||||
| if (blockSizeJ) { | if (blockSizeJ) { | ||||
| setBlockSize(json_integer_value(blockSizeJ)); | |||||
| blockSize = json_integer_value(blockSizeJ); | |||||
| } | } | ||||
| openStream(); | |||||
| } | } | ||||
| void reset() override { | void reset() override { | ||||
| closeDevice(); | |||||
| closeStream(); | |||||
| } | } | ||||
| }; | }; | ||||
| #define TIMED_SLEEP_LOCK(_cond, _spinTime, _totalTime) { \ | |||||
| auto startTime = std::chrono::high_resolution_clock::now(); \ | |||||
| while (!(_cond)) { \ | |||||
| std::this_thread::sleep_for(std::chrono::duration<float>(_spinTime)); \ | |||||
| auto currTime = std::chrono::high_resolution_clock::now(); \ | |||||
| float totalTime = std::chrono::duration<float>(currTime - startTime).count(); \ | |||||
| if (totalTime > (_totalTime)) \ | |||||
| break; \ | |||||
| } \ | |||||
| } | |||||
| void AudioInterface::step() { | void AudioInterface::step() { | ||||
| // Read/write stream if we have enough input, OR the output buffer is empty if we have no input | // Read/write stream if we have enough input, OR the output buffer is empty if we have no input | ||||
| if (numOutputs > 0) { | if (numOutputs > 0) { | ||||
| const float maxTime = 10e-3; | |||||
| const float spinTime = 100e-6; | |||||
| for (float time = 0.0; time < maxTime; time += spinTime) { | |||||
| if (inputSrcBuffer.size() < blockSize) | |||||
| break; | |||||
| std::this_thread::sleep_for(std::chrono::duration<float>(spinTime)); | |||||
| } | |||||
| TIMED_SLEEP_LOCK(inputSrcBuffer.size() < blockSize, 100e-6, 0.2); | |||||
| } | } | ||||
| else if (numInputs > 0) { | else if (numInputs > 0) { | ||||
| const float maxTime = 10e-3; | |||||
| const float spinTime = 100e-6; | |||||
| for (float time = 0.0; time < maxTime; time += spinTime) { | |||||
| if (!outputBuffer.empty()) | |||||
| break; | |||||
| std::this_thread::sleep_for(std::chrono::duration<float>(spinTime)); | |||||
| } | |||||
| TIMED_SLEEP_LOCK(!outputBuffer.empty(), 100e-6, 0.2); | |||||
| } | } | ||||
| // Get input and pass it through the sample rate converter | // Get input and pass it through the sample rate converter | ||||
| @@ -164,20 +194,14 @@ void AudioInterface::step() { | |||||
| } | } | ||||
| void AudioInterface::stepStream(const float *input, float *output, int numFrames) { | void AudioInterface::stepStream(const float *input, float *output, int numFrames) { | ||||
| // if (gPaused) { | |||||
| // memset(output, 0, sizeof(float) * numOutputs * numFrames); | |||||
| // return; | |||||
| // } | |||||
| if (gPaused) { | |||||
| memset(output, 0, sizeof(float) * numOutputs * numFrames); | |||||
| return; | |||||
| } | |||||
| if (numOutputs > 0) { | if (numOutputs > 0) { | ||||
| // Wait for enough input before proceeding | // Wait for enough input before proceeding | ||||
| const float maxTime = 10e-3; | |||||
| const float spinTime = 100e-6; | |||||
| for (float time = 0.0; time < maxTime; time += spinTime) { | |||||
| if (inputSrcBuffer.size() >= numFrames) | |||||
| break; | |||||
| std::this_thread::sleep_for(std::chrono::duration<float>(spinTime)); | |||||
| } | |||||
| TIMED_SLEEP_LOCK(inputSrcBuffer.size() >= numFrames, 100e-6, 0.2); | |||||
| } | } | ||||
| // input stream -> output buffer | // input stream -> output buffer | ||||
| @@ -215,15 +239,17 @@ void AudioInterface::stepStream(const float *input, float *output, int numFrames | |||||
| } | } | ||||
| int AudioInterface::getDeviceCount() { | int AudioInterface::getDeviceCount() { | ||||
| return stream.getDeviceCount(); | |||||
| if (!stream) | |||||
| return 0; | |||||
| return stream->getDeviceCount(); | |||||
| } | } | ||||
| std::string AudioInterface::getDeviceName(int deviceId) { | std::string AudioInterface::getDeviceName(int deviceId) { | ||||
| if (deviceId < 0) | |||||
| if (!stream || deviceId < 0) | |||||
| return ""; | return ""; | ||||
| try { | try { | ||||
| RtAudio::DeviceInfo deviceInfo = stream.getDeviceInfo(deviceId); | |||||
| RtAudio::DeviceInfo deviceInfo = stream->getDeviceInfo(deviceId); | |||||
| return stringf("%s (%d in, %d out)", deviceInfo.name.c_str(), deviceInfo.inputChannels, deviceInfo.outputChannels); | return stringf("%s (%d in, %d out)", deviceInfo.name.c_str(), deviceInfo.inputChannels, deviceInfo.outputChannels); | ||||
| } | } | ||||
| catch (RtAudioError &e) { | catch (RtAudioError &e) { | ||||
| @@ -239,17 +265,17 @@ static int rtCallback(void *outputBuffer, void *inputBuffer, unsigned int nFrame | |||||
| return 0; | return 0; | ||||
| } | } | ||||
| void AudioInterface::openDevice(int deviceId, float sampleRate, int blockSize) { | |||||
| closeDevice(); | |||||
| this->sampleRate = sampleRate; | |||||
| this->blockSize = blockSize; | |||||
| void AudioInterface::openStream() { | |||||
| int deviceId = this->deviceId; | |||||
| closeStream(); | |||||
| if (!stream) | |||||
| return; | |||||
| // Open new device | // Open new device | ||||
| if (deviceId >= 0) { | if (deviceId >= 0) { | ||||
| RtAudio::DeviceInfo deviceInfo; | RtAudio::DeviceInfo deviceInfo; | ||||
| try { | try { | ||||
| deviceInfo = stream.getDeviceInfo(deviceId); | |||||
| deviceInfo = stream->getDeviceInfo(deviceId); | |||||
| } | } | ||||
| catch (RtAudioError &e) { | catch (RtAudioError &e) { | ||||
| warn("Failed to query audio device: %s", e.what()); | warn("Failed to query audio device: %s", e.what()); | ||||
| @@ -259,6 +285,11 @@ void AudioInterface::openDevice(int deviceId, float sampleRate, int blockSize) { | |||||
| numOutputs = mini(deviceInfo.outputChannels, 8); | numOutputs = mini(deviceInfo.outputChannels, 8); | ||||
| numInputs = mini(deviceInfo.inputChannels, 8); | numInputs = mini(deviceInfo.inputChannels, 8); | ||||
| if (numOutputs == 0 && numInputs == 0) { | |||||
| warn("Audio device %d has 0 inputs and 0 outputs"); | |||||
| return; | |||||
| } | |||||
| RtAudio::StreamParameters outParameters; | RtAudio::StreamParameters outParameters; | ||||
| outParameters.deviceId = deviceId; | outParameters.deviceId = deviceId; | ||||
| outParameters.nChannels = numOutputs; | outParameters.nChannels = numOutputs; | ||||
| @@ -268,15 +299,23 @@ void AudioInterface::openDevice(int deviceId, float sampleRate, int blockSize) { | |||||
| inParameters.nChannels = numInputs; | inParameters.nChannels = numInputs; | ||||
| RtAudio::StreamOptions options; | RtAudio::StreamOptions options; | ||||
| options.flags |= RTAUDIO_SCHEDULE_REALTIME; | |||||
| // options.flags |= RTAUDIO_SCHEDULE_REALTIME; | |||||
| // Find closest sample rate | |||||
| unsigned int closestSampleRate = 0; | |||||
| for (unsigned int sr : deviceInfo.sampleRates) { | |||||
| if (fabsf(sr - sampleRate) < fabsf(closestSampleRate - sampleRate)) { | |||||
| closestSampleRate = sr; | |||||
| } | |||||
| } | |||||
| try { | try { | ||||
| // Don't use stream parameters if 0 input or output channels | // Don't use stream parameters if 0 input or output channels | ||||
| debug("Opening audio stream %d", deviceId); | debug("Opening audio stream %d", deviceId); | ||||
| stream.openStream( | |||||
| stream->openStream( | |||||
| numOutputs == 0 ? NULL : &outParameters, | numOutputs == 0 ? NULL : &outParameters, | ||||
| numInputs == 0 ? NULL : &inParameters, | numInputs == 0 ? NULL : &inParameters, | ||||
| RTAUDIO_FLOAT32, sampleRate, (unsigned int*) &blockSize, &rtCallback, this, &options, NULL); | |||||
| RTAUDIO_FLOAT32, closestSampleRate, (unsigned int*) &blockSize, &rtCallback, this, &options, NULL); | |||||
| } | } | ||||
| catch (RtAudioError &e) { | catch (RtAudioError &e) { | ||||
| warn("Failed to open audio stream: %s", e.what()); | warn("Failed to open audio stream: %s", e.what()); | ||||
| @@ -285,29 +324,38 @@ void AudioInterface::openDevice(int deviceId, float sampleRate, int blockSize) { | |||||
| try { | try { | ||||
| debug("Starting audio stream %d", deviceId); | debug("Starting audio stream %d", deviceId); | ||||
| stream.startStream(); | |||||
| stream->startStream(); | |||||
| } | } | ||||
| catch (RtAudioError &e) { | catch (RtAudioError &e) { | ||||
| warn("Failed to start audio stream: %s", e.what()); | warn("Failed to start audio stream: %s", e.what()); | ||||
| return; | return; | ||||
| } | } | ||||
| this->sampleRate = stream.getStreamSampleRate(); | |||||
| // Update sample rate because this may have changed | |||||
| this->sampleRate = stream->getStreamSampleRate(); | |||||
| this->deviceId = deviceId; | this->deviceId = deviceId; | ||||
| } | } | ||||
| } | } | ||||
| void AudioInterface::closeDevice() { | |||||
| if (stream.isStreamOpen()) { | |||||
| try { | |||||
| void AudioInterface::closeStream() { | |||||
| if (stream) { | |||||
| if (stream->isStreamRunning()) { | |||||
| debug("Aborting audio stream %d", deviceId); | debug("Aborting audio stream %d", deviceId); | ||||
| stream.abortStream(); | |||||
| debug("Closing audio stream %d", deviceId); | |||||
| stream.closeStream(); | |||||
| try { | |||||
| stream->abortStream(); | |||||
| } | |||||
| catch (RtAudioError &e) { | |||||
| warn("Failed to abort stream %s", e.what()); | |||||
| } | |||||
| } | } | ||||
| catch (RtAudioError &e) { | |||||
| warn("Failed to abort stream %s", e.what()); | |||||
| return; | |||||
| if (stream->isStreamOpen()) { | |||||
| debug("Closing audio stream %d", deviceId); | |||||
| try { | |||||
| stream->closeStream(); | |||||
| } | |||||
| catch (RtAudioError &e) { | |||||
| warn("Failed to close stream %s", e.what()); | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -326,12 +374,12 @@ void AudioInterface::closeDevice() { | |||||
| std::vector<float> AudioInterface::getSampleRates() { | std::vector<float> AudioInterface::getSampleRates() { | ||||
| std::vector<float> allowedSampleRates = {44100, 48000, 88200, 96000, 176400, 192000}; | std::vector<float> allowedSampleRates = {44100, 48000, 88200, 96000, 176400, 192000}; | ||||
| if (deviceId < 0) | |||||
| if (!stream || deviceId < 0) | |||||
| return allowedSampleRates; | return allowedSampleRates; | ||||
| try { | try { | ||||
| std::vector<float> sampleRates; | std::vector<float> sampleRates; | ||||
| RtAudio::DeviceInfo deviceInfo = stream.getDeviceInfo(deviceId); | |||||
| RtAudio::DeviceInfo deviceInfo = stream->getDeviceInfo(deviceId); | |||||
| for (int sr : deviceInfo.sampleRates) { | for (int sr : deviceInfo.sampleRates) { | ||||
| float sampleRate = sr; | float sampleRate = sr; | ||||
| auto allowedIt = std::find(allowedSampleRates.begin(), allowedSampleRates.end(), sampleRate); | auto allowedIt = std::find(allowedSampleRates.begin(), allowedSampleRates.end(), sampleRate); | ||||
| @@ -349,15 +397,45 @@ std::vector<float> AudioInterface::getSampleRates() { | |||||
| struct AudioItem : MenuItem { | |||||
| struct AudioDriverItem : MenuItem { | |||||
| AudioInterface *audioInterface; | |||||
| int driver; | |||||
| void onAction(EventAction &e) override { | |||||
| audioInterface->setDriver(driver); | |||||
| } | |||||
| }; | |||||
| struct AudioDriverChoice : ChoiceButton { | |||||
| AudioInterface *audioInterface; | |||||
| void onAction(EventAction &e) override { | |||||
| Menu *menu = gScene->createMenu(); | |||||
| menu->box.pos = getAbsoluteOffset(Vec(0, box.size.y)).round(); | |||||
| menu->box.size.x = box.size.x; | |||||
| for (int driver : audioInterface->getAvailableDrivers()) { | |||||
| AudioDriverItem *audioItem = new AudioDriverItem(); | |||||
| audioItem->audioInterface = audioInterface; | |||||
| audioItem->driver = driver; | |||||
| audioItem->text = audioInterface->getDriverName(driver); | |||||
| menu->pushChild(audioItem); | |||||
| } | |||||
| } | |||||
| void step() override { | |||||
| text = audioInterface->getDriverName(audioInterface->getDriver()); | |||||
| } | |||||
| }; | |||||
| struct AudioDeviceItem : MenuItem { | |||||
| AudioInterface *audioInterface; | AudioInterface *audioInterface; | ||||
| int deviceId; | int deviceId; | ||||
| void onAction(EventAction &e) override { | void onAction(EventAction &e) override { | ||||
| audioInterface->setDeviceId(deviceId); | |||||
| audioInterface->deviceId = deviceId; | |||||
| audioInterface->openStream(); | |||||
| } | } | ||||
| }; | }; | ||||
| struct AudioChoice : ChoiceButton { | |||||
| struct AudioDeviceChoice : ChoiceButton { | |||||
| int lastDeviceId = -1; | int lastDeviceId = -1; | ||||
| AudioInterface *audioInterface; | AudioInterface *audioInterface; | ||||
| void onAction(EventAction &e) override { | void onAction(EventAction &e) override { | ||||
| @@ -367,14 +445,14 @@ struct AudioChoice : ChoiceButton { | |||||
| int deviceCount = audioInterface->getDeviceCount(); | int deviceCount = audioInterface->getDeviceCount(); | ||||
| { | { | ||||
| AudioItem *audioItem = new AudioItem(); | |||||
| AudioDeviceItem *audioItem = new AudioDeviceItem(); | |||||
| audioItem->audioInterface = audioInterface; | audioItem->audioInterface = audioInterface; | ||||
| audioItem->deviceId = -1; | audioItem->deviceId = -1; | ||||
| audioItem->text = "No device"; | audioItem->text = "No device"; | ||||
| menu->pushChild(audioItem); | menu->pushChild(audioItem); | ||||
| } | } | ||||
| for (int deviceId = 0; deviceId < deviceCount; deviceId++) { | for (int deviceId = 0; deviceId < deviceCount; deviceId++) { | ||||
| AudioItem *audioItem = new AudioItem(); | |||||
| AudioDeviceItem *audioItem = new AudioDeviceItem(); | |||||
| audioItem->audioInterface = audioInterface; | audioItem->audioInterface = audioInterface; | ||||
| audioItem->deviceId = deviceId; | audioItem->deviceId = deviceId; | ||||
| audioItem->text = audioInterface->getDeviceName(deviceId); | audioItem->text = audioInterface->getDeviceName(deviceId); | ||||
| @@ -395,7 +473,8 @@ struct SampleRateItem : MenuItem { | |||||
| AudioInterface *audioInterface; | AudioInterface *audioInterface; | ||||
| float sampleRate; | float sampleRate; | ||||
| void onAction(EventAction &e) override { | void onAction(EventAction &e) override { | ||||
| audioInterface->setSampleRate(sampleRate); | |||||
| audioInterface->sampleRate = sampleRate; | |||||
| audioInterface->openStream(); | |||||
| } | } | ||||
| }; | }; | ||||
| @@ -424,7 +503,8 @@ struct BlockSizeItem : MenuItem { | |||||
| AudioInterface *audioInterface; | AudioInterface *audioInterface; | ||||
| int blockSize; | int blockSize; | ||||
| void onAction(EventAction &e) override { | void onAction(EventAction &e) override { | ||||
| audioInterface->setBlockSize(blockSize); | |||||
| audioInterface->blockSize = blockSize; | |||||
| audioInterface->openStream(); | |||||
| } | } | ||||
| }; | }; | ||||
| @@ -471,6 +551,21 @@ AudioInterfaceWidget::AudioInterfaceWidget() { | |||||
| float yPos = margin; | float yPos = margin; | ||||
| float xPos; | float xPos; | ||||
| { | |||||
| Label *label = new Label(); | |||||
| label->box.pos = Vec(margin, yPos); | |||||
| label->text = "Audio driver"; | |||||
| addChild(label); | |||||
| yPos += labelHeight + margin; | |||||
| AudioDriverChoice *choice = new AudioDriverChoice(); | |||||
| choice->audioInterface = module; | |||||
| choice->box.pos = Vec(margin, yPos); | |||||
| choice->box.size.x = box.size.x - 2*margin; | |||||
| addChild(choice); | |||||
| yPos += choice->box.size.y + margin; | |||||
| } | |||||
| { | { | ||||
| Label *label = new Label(); | Label *label = new Label(); | ||||
| label->box.pos = Vec(margin, yPos); | label->box.pos = Vec(margin, yPos); | ||||
| @@ -478,8 +573,8 @@ AudioInterfaceWidget::AudioInterfaceWidget() { | |||||
| addChild(label); | addChild(label); | ||||
| yPos += labelHeight + margin; | yPos += labelHeight + margin; | ||||
| AudioChoice *choice = new AudioChoice(); | |||||
| choice->audioInterface = dynamic_cast<AudioInterface*>(module); | |||||
| AudioDeviceChoice *choice = new AudioDeviceChoice(); | |||||
| choice->audioInterface = module; | |||||
| choice->box.pos = Vec(margin, yPos); | choice->box.pos = Vec(margin, yPos); | ||||
| choice->box.size.x = box.size.x - 2*margin; | choice->box.size.x = box.size.x - 2*margin; | ||||
| addChild(choice); | addChild(choice); | ||||
| @@ -494,7 +589,7 @@ AudioInterfaceWidget::AudioInterfaceWidget() { | |||||
| yPos += labelHeight + margin; | yPos += labelHeight + margin; | ||||
| SampleRateChoice *choice = new SampleRateChoice(); | SampleRateChoice *choice = new SampleRateChoice(); | ||||
| choice->audioInterface = dynamic_cast<AudioInterface*>(module); | |||||
| choice->audioInterface = module; | |||||
| choice->box.pos = Vec(margin, yPos); | choice->box.pos = Vec(margin, yPos); | ||||
| choice->box.size.x = box.size.x - 2*margin; | choice->box.size.x = box.size.x - 2*margin; | ||||
| addChild(choice); | addChild(choice); | ||||
| @@ -509,7 +604,7 @@ AudioInterfaceWidget::AudioInterfaceWidget() { | |||||
| yPos += labelHeight + margin; | yPos += labelHeight + margin; | ||||
| BlockSizeChoice *choice = new BlockSizeChoice(); | BlockSizeChoice *choice = new BlockSizeChoice(); | ||||
| choice->audioInterface = dynamic_cast<AudioInterface*>(module); | |||||
| choice->audioInterface = module; | |||||
| choice->box.pos = Vec(margin, yPos); | choice->box.pos = Vec(margin, yPos); | ||||
| choice->box.size.x = box.size.x - 2*margin; | choice->box.size.x = box.size.x - 2*margin; | ||||
| addChild(choice); | addChild(choice); | ||||