| 
				
				
				
				 | 
			
			 | 
			@@ -1,6 +1,9 @@ | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#include <assert.h> | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#include <mutex> | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#include <chrono> | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#include <thread> | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#include <mutex> | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#include <condition_variable> | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#include "core.hpp" | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#include "audio.hpp" | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#include "dsp/samplerate.hpp" | 
		
		
	
	
		
			
				| 
				
				
				
					
				
				 | 
			
			 | 
			@@ -12,33 +15,93 @@ | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#pragma GCC diagnostic pop | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#define MAX_OUTPUTS 8 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#define MAX_INPUTS 8 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			static auto audioTimeout = std::chrono::milliseconds(100); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			using namespace rack; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			struct AudioInterfaceIO : AudioIO { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				std::mutex engineMutex; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				std::condition_variable engineCv; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				std::mutex audioMutex; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				std::condition_variable audioCv; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				// Audio thread produces, engine thread consumes | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				DoubleRingBuffer<Frame<MAX_INPUTS>, (1<<15)> inputBuffer; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				// Audio thread consumes, engine thread produces | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				DoubleRingBuffer<Frame<MAX_OUTPUTS>, (1<<15)> outputBuffer; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				AudioInterfaceIO() { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					maxOutputs = MAX_OUTPUTS; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					maxInputs = MAX_INPUTS; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				void processStream(const float *input, float *output, int length) override { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					if (numInputs > 0) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						// TODO Do we need to wait on the input to be consumed here? | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						for (int i = 0; i < length; i++) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							if (inputBuffer.full()) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
								break; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							Frame<MAX_INPUTS> f; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							memset(&f, 0, sizeof(f)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							memcpy(&f, &input[numInputs * i], numInputs * sizeof(float)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							inputBuffer.push(f); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					if (numOutputs > 0) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						std::unique_lock<std::mutex> lock(audioMutex); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						auto cond = [&] { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							return outputBuffer.size() >= length; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						}; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						if (audioCv.wait_for(lock, audioTimeout, cond)) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							// Consume audio block | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							for (int i = 0; i < length; i++) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
								Frame<MAX_OUTPUTS> f = outputBuffer.shift(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
								memcpy(&output[numOutputs * i], &f, numOutputs * sizeof(float)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						else { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							// Timed out, fill output with zeros | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							memset(output, 0, length * numOutputs * sizeof(float)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					// Notify engine when finished processing | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					engineCv.notify_all(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				void onCloseStream() override { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					inputBuffer.clear(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					outputBuffer.clear(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			}; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			struct AudioInterface : Module { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				enum ParamIds { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					NUM_PARAMS | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				}; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				enum InputIds { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					AUDIO1_INPUT, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					NUM_INPUTS = AUDIO1_INPUT + 8 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					ENUMS(AUDIO_INPUT, MAX_INPUTS), | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					NUM_INPUTS | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				}; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				enum OutputIds { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					AUDIO1_OUTPUT, | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					NUM_OUTPUTS = AUDIO1_OUTPUT + 8 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					ENUMS(AUDIO_OUTPUT, MAX_OUTPUTS), | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					NUM_OUTPUTS | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				}; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				AudioIO audioIO; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				AudioInterfaceIO audioIO; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				SampleRateConverter<8> inputSrc; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				SampleRateConverter<8> outputSrc; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				// in rack's sample rate | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				DoubleRingBuffer<Frame<8>, 16> inputBuffer; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				DoubleRingBuffer<Frame<8>, (1<<15)> outputBuffer; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				// in device's sample rate | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				DoubleRingBuffer<Frame<8>, (1<<15)> inputSrcBuffer; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				DoubleRingBuffer<Frame<MAX_INPUTS>, 16> inputBuffer; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				DoubleRingBuffer<Frame<MAX_OUTPUTS>, 16> outputBuffer; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				AudioInterface() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
	
		
			
				| 
				
				
				
					
				
				 | 
			
			 | 
			@@ -49,31 +112,40 @@ struct AudioInterface : Module { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				json_t *toJson() override { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					json_t *rootJ = json_object(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					// json_object_set_new(rootJ, "driver", json_integer(getDriver())); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					// json_object_set_new(rootJ, "device", json_integer(device)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					// json_object_set_new(rootJ, "audioIO.sampleRate", json_real(audioIO.sampleRate)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					// json_object_set_new(rootJ, "audioIO.blockSize", json_integer(audioIO.blockSize)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					json_object_set_new(rootJ, "driver", json_integer(audioIO.getDriver())); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					std::string deviceName = audioIO.getDeviceName(audioIO.device); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					json_object_set_new(rootJ, "deviceName", json_string(deviceName.c_str())); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					json_object_set_new(rootJ, "sampleRate", json_integer(audioIO.sampleRate)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					json_object_set_new(rootJ, "blockSize", json_integer(audioIO.blockSize)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					return rootJ; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				void fromJson(json_t *rootJ) override { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					// json_t *driverJ = json_object_get(rootJ, "driver"); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					// if (driverJ) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					// 	setDriver(json_number_value(driverJ)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					// json_t *deviceJ = json_object_get(rootJ, "device"); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					// if (deviceJ) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					// 	device = json_number_value(deviceJ); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					json_t *driverJ = json_object_get(rootJ, "driver"); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					if (driverJ) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						audioIO.setDriver(json_number_value(driverJ)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					json_t *deviceNameJ = json_object_get(rootJ, "deviceName"); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					if (deviceNameJ) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						std::string deviceName = json_string_value(deviceNameJ); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						// Search for device ID with equal name | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						for (int device = 0; device < audioIO.getDeviceCount(); device++) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							if (audioIO.getDeviceName(device) == deviceName) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
								audioIO.device = device; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
								break; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					// json_t *sampleRateJ = json_object_get(rootJ, "audioIO.sampleRate"); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					// if (sampleRateJ) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					// 	audioIO.sampleRate = json_number_value(sampleRateJ); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					json_t *sampleRateJ = json_object_get(rootJ, "sampleRate"); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					if (sampleRateJ) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						audioIO.sampleRate = json_integer_value(sampleRateJ); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					// json_t *blockSizeJ = json_object_get(rootJ, "audioIO.blockSize"); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					// if (blockSizeJ) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					// 	audioIO.blockSize = json_integer_value(blockSizeJ); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					json_t *blockSizeJ = json_object_get(rootJ, "blockSize"); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					if (blockSizeJ) | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						audioIO.blockSize = json_integer_value(blockSizeJ); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					// openStream(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					audioIO.openStream(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				void onReset() override { | 
		
		
	
	
		
			
				| 
				
				
				
					
				
				 | 
			
			 | 
			@@ -82,117 +154,61 @@ struct AudioInterface : Module { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			}; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			#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() { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				// debug("inputBuffer %d inputSrcBuffer %d outputBuffer %d", inputBuffer.size(), inputSrcBuffer.size(), outputBuffer.size()); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				// Read/write stream if we have enough input, OR the output buffer is empty if we have no input | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				if (audioIO.numOutputs > 0) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					TIMED_SLEEP_LOCK(inputSrcBuffer.size() < audioIO.blockSize, 100e-6, 0.2); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				else if (audioIO.numInputs > 0) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					TIMED_SLEEP_LOCK(!outputBuffer.empty(), 100e-6, 0.2); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				Frame<MAX_INPUTS> inputFrame; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				memset(&inputFrame, 0, sizeof(inputFrame)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				// Get input and pass it through the sample rate converter | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				if (audioIO.numOutputs > 0) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					if (!inputBuffer.full()) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						Frame<8> f; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						for (int i = 0; i < 8; i++) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							f.samples[i] = inputs[AUDIO1_INPUT + i].value / 10.0; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						inputBuffer.push(f); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					// Once full, sample rate convert the input | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					// inputBuffer -> SRC -> inputSrcBuffer | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					if (inputBuffer.full()) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						inputSrc.setRates(engineGetSampleRate(), audioIO.sampleRate); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						int inLen = inputBuffer.size(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						int outLen = inputSrcBuffer.capacity(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						inputSrc.process(inputBuffer.startData(), &inLen, inputSrcBuffer.endData(), &outLen); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						inputBuffer.startIncr(inLen); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						inputSrcBuffer.endIncr(outLen); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				if (audioIO.numInputs > 0) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					if (inputBuffer.empty()) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						inputSrc.setRates(audioIO.sampleRate, engineGetSampleRate()); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						int inLen = audioIO.inputBuffer.size(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						int outLen = inputBuffer.capacity(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						inputSrc.process(audioIO.inputBuffer.startData(), &inLen, inputBuffer.endData(), &outLen); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						audioIO.inputBuffer.startIncr(inLen); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						inputBuffer.endIncr(outLen); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				// Set output | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				if (!outputBuffer.empty()) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					Frame<8> f = outputBuffer.shift(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					for (int i = 0; i < 8; i++) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						outputs[AUDIO1_OUTPUT + i].value = 10.0 * f.samples[i]; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				if (!inputBuffer.empty()) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					inputFrame = inputBuffer.shift(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			void AudioInterface::stepStream(const float *input, float *output, int numFrames) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				if (gPaused) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					memset(output, 0, sizeof(float) * audioIO.numOutputs * numFrames); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					return; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				for (int i = 0; i < MAX_INPUTS; i++) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					outputs[AUDIO_OUTPUT + i].value = 10.0 * inputFrame.samples[i]; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				if (audioIO.numOutputs > 0) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					// Wait for enough input before proceeding | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					TIMED_SLEEP_LOCK(inputSrcBuffer.size() >= numFrames, 100e-6, 0.2); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				else if (audioIO.numInputs > 0) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					TIMED_SLEEP_LOCK(outputBuffer.empty(), 100e-6, 0.2); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				// input stream -> output buffer | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				if (audioIO.numInputs > 0) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					Frame<8> inputFrames[numFrames]; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					for (int i = 0; i < numFrames; i++) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						for (int c = 0; c < 8; c++) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							inputFrames[i].samples[c] = (c < audioIO.numInputs) ? input[i*audioIO.numInputs + c] : 0.0; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					// Get and push output SRC frame | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					if (!outputBuffer.full()) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						Frame<MAX_OUTPUTS> f; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						for (int i = 0; i < audioIO.numOutputs; i++) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							f.samples[i] = inputs[AUDIO_INPUT + i].value / 10.0; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						outputBuffer.push(f); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					// Pass output through sample rate converter | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					outputSrc.setRates(audioIO.sampleRate, engineGetSampleRate()); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					int inLen = numFrames; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					int outLen = outputBuffer.capacity(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					outputSrc.process(inputFrames, &inLen, outputBuffer.endData(), &outLen); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					outputBuffer.endIncr(outLen); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			 | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				// input buffer -> output stream | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				if (audioIO.numOutputs > 0) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					for (int i = 0; i < numFrames; i++) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						Frame<8> f; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						if (inputSrcBuffer.empty()) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							memset(&f, 0, sizeof(f)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					if (outputBuffer.full()) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						// Wait until outputs are needed | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						std::unique_lock<std::mutex> lock(audioIO.engineMutex); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						auto cond = [&] { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							return audioIO.outputBuffer.size() < audioIO.blockSize; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						}; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						if (audioIO.engineCv.wait_for(lock, audioTimeout, cond)) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							// Push converted output | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							outputSrc.setRates(engineGetSampleRate(), audioIO.sampleRate); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							int inLen = outputBuffer.size(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							int outLen = audioIO.outputBuffer.capacity(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							outputSrc.process(outputBuffer.startData(), &inLen, audioIO.outputBuffer.endData(), &outLen); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							outputBuffer.startIncr(inLen); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							audioIO.outputBuffer.endIncr(outLen); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						else { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							f = inputSrcBuffer.shift(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						for (int c = 0; c < audioIO.numOutputs; c++) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							output[i*audioIO.numOutputs + c] = clampf(f.samples[c], -1.0, 1.0); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
							// Give up on pushing output | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
						} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			// void AudioInterface::closeStream() { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			// 	// Clear buffers | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			// 	inputBuffer.clear(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			// 	outputBuffer.clear(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			// 	inputSrcBuffer.clear(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			// 	inputSrc.reset(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			// 	outputSrc.reset(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			// } | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				audioIO.audioCv.notify_all(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			} | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			
  | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
			AudioInterfaceWidget::AudioInterfaceWidget() { | 
		
		
	
	
		
			
				| 
				
					
				
				
					
				
				
				 | 
			
			 | 
			@@ -226,7 +242,7 @@ AudioInterfaceWidget::AudioInterfaceWidget() { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				yPos += 5; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				xPos = 10; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				for (int i = 0; i < 4; i++) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					addInput(createInput<PJ3410Port>(Vec(xPos, yPos), module, AudioInterface::AUDIO1_INPUT + i)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					addInput(createInput<PJ3410Port>(Vec(xPos, yPos), module, AudioInterface::AUDIO_INPUT + i)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					Label *label = new Label(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					label->box.pos = Vec(xPos + 4, yPos + 28); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					label->text = stringf("%d", i + 1); | 
		
		
	
	
		
			
				| 
				
				
				
					
				
				 | 
			
			 | 
			@@ -239,7 +255,7 @@ AudioInterfaceWidget::AudioInterfaceWidget() { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				yPos += 5; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				xPos = 10; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				for (int i = 4; i < 8; i++) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					addInput(createInput<PJ3410Port>(Vec(xPos, yPos), module, AudioInterface::AUDIO1_INPUT + i)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					addInput(createInput<PJ3410Port>(Vec(xPos, yPos), module, AudioInterface::AUDIO_INPUT + i)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					Label *label = new Label(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					label->box.pos = Vec(xPos + 4, yPos + 28); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					label->text = stringf("%d", i + 1); | 
		
		
	
	
		
			
				| 
				
				
				
					
				
				 | 
			
			 | 
			@@ -260,7 +276,7 @@ AudioInterfaceWidget::AudioInterfaceWidget() { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				yPos += 5; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				xPos = 10; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				for (int i = 0; i < 4; i++) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					addOutput(createOutput<PJ3410Port>(Vec(xPos, yPos), module, AudioInterface::AUDIO1_OUTPUT + i)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					addOutput(createOutput<PJ3410Port>(Vec(xPos, yPos), module, AudioInterface::AUDIO_OUTPUT + i)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					Label *label = new Label(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					label->box.pos = Vec(xPos + 4, yPos + 28); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					label->text = stringf("%d", i + 1); | 
		
		
	
	
		
			
				| 
				
				
				
					
				
				 | 
			
			 | 
			@@ -273,7 +289,7 @@ AudioInterfaceWidget::AudioInterfaceWidget() { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				yPos += 5; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				xPos = 10; | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
				for (int i = 4; i < 8; i++) { | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					addOutput(createOutput<PJ3410Port>(Vec(xPos, yPos), module, AudioInterface::AUDIO1_OUTPUT + i)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					addOutput(createOutput<PJ3410Port>(Vec(xPos, yPos), module, AudioInterface::AUDIO_OUTPUT + i)); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					Label *label = new Label(); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					label->box.pos = Vec(xPos + 4, yPos + 28); | 
		
		
	
		
			
			 | 
			 | 
			
			 | 
					label->text = stringf("%d", i + 1); | 
		
		
	
	
		
			
				| 
				
					
				
				
				
				 | 
			
			 | 
			
  |