Browse Source

Made AudioInterface and MidiInterface a bit prettier, added audio input

to AudioInterface
tags/v0.3.0
Andrew Belt 7 years ago
parent
commit
0669bf5dd7
10 changed files with 188 additions and 69 deletions
  1. +3
    -0
      Makefile
  2. +1
    -3
      README.md
  3. +12
    -3
      include/widgets.hpp
  4. +121
    -34
      src/core/AudioInterface.cpp
  5. +45
    -16
      src/core/MidiInterface.cpp
  6. +3
    -0
      src/core/core.cpp
  7. +3
    -0
      src/core/core.hpp
  8. +0
    -4
      src/widgets/Button.cpp
  9. +0
    -5
      src/widgets/ScrollBar.cpp
  10. +0
    -4
      src/widgets/Slider.cpp

+ 3
- 0
Makefile View File

@@ -29,6 +29,9 @@ CFLAGS += -DNOC_FILE_DIALOG_OSX
CXXFLAGS += -DAPPLE -stdlib=libc++ -I$(HOME)/local/include CXXFLAGS += -DAPPLE -stdlib=libc++ -I$(HOME)/local/include
LDFLAGS += -stdlib=libc++ -L$(HOME)/local/lib -lpthread -lglew -lglfw3 -framework Cocoa -framework OpenGL -framework IOKit -framework CoreVideo -ldl -ljansson -lportaudio -lportmidi LDFLAGS += -stdlib=libc++ -L$(HOME)/local/lib -lpthread -lglew -lglfw3 -framework Cocoa -framework OpenGL -framework IOKit -framework CoreVideo -ldl -ljansson -lportaudio -lportmidi
TARGET = Rack TARGET = Rack

Rack.app: $(TARGET)
./bundle.sh
endif endif


# Windows # Windows


+ 1
- 3
README.md View File

@@ -1,8 +1,6 @@
*Note: This software is in semi-public alpha. If you have stumbled upon this project, feel free to try it out and report bugs to the GitHub issue tracker. However, with more users it becomes difficult to make breaking changes, so please don't spread this to your friends and the Internet just yet, until the official announcement has been made.* *Note: This software is in semi-public alpha. If you have stumbled upon this project, feel free to try it out and report bugs to the GitHub issue tracker. However, with more users it becomes difficult to make breaking changes, so please don't spread this to your friends and the Internet just yet, until the official announcement has been made.*


░█▀▄░█▀█░█▀▀░█░█
░█▀▄░█▀█░█░░░█▀▄
░▀░▀░▀░▀░▀▀▀░▀░▀
# Rack


Eurorack-style modular DAW Eurorack-style modular DAW




+ 12
- 3
include/widgets.hpp View File

@@ -162,6 +162,9 @@ struct QuantityWidget : virtual Widget {


struct Label : Widget { struct Label : Widget {
std::string text; std::string text;
Label() {
box.size.y = BND_WIDGET_HEIGHT;
}
void draw(NVGcontext *vg); void draw(NVGcontext *vg);
}; };


@@ -209,7 +212,9 @@ struct Button : OpaqueWidget {
std::string text; std::string text;
BNDwidgetState state = BND_DEFAULT; BNDwidgetState state = BND_DEFAULT;


Button();
Button() {
box.size.y = BND_WIDGET_HEIGHT;
}
void draw(NVGcontext *vg); void draw(NVGcontext *vg);
void onMouseEnter(); void onMouseEnter();
void onMouseLeave() ; void onMouseLeave() ;
@@ -223,7 +228,9 @@ struct ChoiceButton : Button {
struct Slider : OpaqueWidget, QuantityWidget { struct Slider : OpaqueWidget, QuantityWidget {
BNDwidgetState state = BND_DEFAULT; BNDwidgetState state = BND_DEFAULT;


Slider();
Slider() {
box.size.y = BND_WIDGET_HEIGHT;
}
void draw(NVGcontext *vg); void draw(NVGcontext *vg);
void onDragStart(); void onDragStart();
void onDragMove(Vec mouseRel); void onDragMove(Vec mouseRel);
@@ -236,7 +243,9 @@ struct ScrollBar : OpaqueWidget {
float containerSize = 0.0; float containerSize = 0.0;
BNDwidgetState state = BND_DEFAULT; BNDwidgetState state = BND_DEFAULT;


ScrollBar();
ScrollBar() {
box.size = Vec(BND_SCROLLBAR_WIDTH, BND_SCROLLBAR_HEIGHT);
}
void draw(NVGcontext *vg); void draw(NVGcontext *vg);
void move(float delta); void move(float delta);
void onDragStart(); void onDragStart();


+ 121
- 34
src/core/AudioInterface.cpp View File

@@ -3,10 +3,16 @@
#include <portaudio.h> #include <portaudio.h>
#include "core.hpp" #include "core.hpp"



using namespace rack; using namespace rack;


static bool audioInitialized = false;

void audioInit() {
PaError err = Pa_Initialize();
if (err) {
printf("Failed to initialize PortAudio: %s\n", Pa_GetErrorText(err));
return;
}
}




struct AudioInterface : Module { struct AudioInterface : Module {
@@ -16,15 +22,24 @@ struct AudioInterface : Module {
enum InputIds { enum InputIds {
AUDIO1_INPUT, AUDIO1_INPUT,
AUDIO2_INPUT, AUDIO2_INPUT,
AUDIO3_INPUT,
AUDIO4_INPUT,
NUM_INPUTS NUM_INPUTS
}; };
enum OutputIds { enum OutputIds {
AUDIO1_OUTPUT,
AUDIO2_OUTPUT,
AUDIO3_OUTPUT,
AUDIO4_OUTPUT,
NUM_OUTPUTS NUM_OUTPUTS
}; };


PaStream *stream = NULL; PaStream *stream = NULL;
float *buffer;
int bufferFrames;
int numOutputs;
int numInputs;
int numFrames;
float outputBuffer[1<<14] = {};
float inputBuffer[1<<14] = {};
int bufferFrame; int bufferFrame;
// Used because the GUI thread and Rack thread can both interact with this class // Used because the GUI thread and Rack thread can both interact with this class
std::mutex mutex; std::mutex mutex;
@@ -44,42 +59,52 @@ AudioInterface::AudioInterface() {
params.resize(NUM_PARAMS); params.resize(NUM_PARAMS);
inputs.resize(NUM_INPUTS); inputs.resize(NUM_INPUTS);
outputs.resize(NUM_OUTPUTS); outputs.resize(NUM_OUTPUTS);

buffer = new float[1<<14];

// Lazy initialize PulseAudio
if (!audioInitialized) {
PaError err = Pa_Initialize();
if (err) {
printf("Failed to initialize PortAudio: %s\n", Pa_GetErrorText(err));
return;
}
audioInitialized = true;
}
} }


AudioInterface::~AudioInterface() { AudioInterface::~AudioInterface() {
openDevice(-1); openDevice(-1);
delete[] buffer;
} }


void AudioInterface::step() { void AudioInterface::step() {
std::lock_guard<std::mutex> lock(mutex); std::lock_guard<std::mutex> lock(mutex);


if (!stream)
if (!stream) {
setf(inputs[AUDIO1_OUTPUT], 0.0);
setf(inputs[AUDIO2_OUTPUT], 0.0);
setf(inputs[AUDIO3_OUTPUT], 0.0);
setf(inputs[AUDIO4_OUTPUT], 0.0);
return; return;
}


buffer[2*bufferFrame + 0] = getf(inputs[AUDIO1_INPUT]) / 5.0;
buffer[2*bufferFrame + 1] = getf(inputs[AUDIO2_INPUT]) / 5.0;
// Input ports -> Output buffer
for (int i = 0; i < numOutputs; i++) {
outputBuffer[numOutputs * bufferFrame + i] = getf(inputs[i]) / 5.0;
}
// Input buffer -> Output ports
for (int i = 0; i < numInputs; i++) {
setf(outputs[i], inputBuffer[numOutputs * bufferFrame + i] * 5.0);
}


if (++bufferFrame >= bufferFrames) {
if (++bufferFrame >= numFrames) {
bufferFrame = 0; bufferFrame = 0;
PaError err = Pa_WriteStream(stream, buffer, bufferFrames);
PaError err;

// Input
// (for some reason, if you write the output stream before you read the input stream, PortAudio can segfault on Windows.)
err = Pa_ReadStream(stream, inputBuffer, numFrames);
if (err) {
// Ignore buffer underflows
if (err != paInputOverflowed) {
printf("Audio input buffer underflow\n");
}
}

// Output
err = Pa_WriteStream(stream, outputBuffer, numFrames);
if (err) { if (err) {
// Ignore buffer underflows // Ignore buffer underflows
if (err != paOutputUnderflowed) { if (err != paOutputUnderflowed) {
printf("Failed to write buffer to audio stream: %s\n", Pa_GetErrorText(err));
return;
printf("Audio output buffer underflow\n");
} }
} }
} }
@@ -91,40 +116,59 @@ int AudioInterface::getDeviceCount() {


std::string AudioInterface::getDeviceName(int deviceId) { std::string AudioInterface::getDeviceName(int deviceId) {
const PaDeviceInfo *info = Pa_GetDeviceInfo(deviceId); const PaDeviceInfo *info = Pa_GetDeviceInfo(deviceId);
return info ? std::string(info->name) : "";
if (!info)
return "";
const PaHostApiInfo *apiInfo = Pa_GetHostApiInfo(info->hostApi);
char name[1024];
snprintf(name, sizeof(name), "%s: %s (%d in, %d out)", apiInfo->name, info->name, info->maxInputChannels, info->maxOutputChannels);
return name;
} }


void AudioInterface::openDevice(int deviceId) { void AudioInterface::openDevice(int deviceId) {
PaError err;
std::lock_guard<std::mutex> lock(mutex); std::lock_guard<std::mutex> lock(mutex);


// Close existing device // Close existing device
if (stream) { if (stream) {
PaError err;
err = Pa_CloseStream(stream); err = Pa_CloseStream(stream);
if (err) { if (err) {
// This shouldn't happen:
printf("Failed to close audio stream: %s\n", Pa_GetErrorText(err)); printf("Failed to close audio stream: %s\n", Pa_GetErrorText(err));
} }
stream = NULL; stream = NULL;
} }
numOutputs = 0;
numInputs = 0;


// Open new device
bufferFrames = 256;
numFrames = 256;
bufferFrame = 0; bufferFrame = 0;
// Open new device
if (deviceId >= 0) { if (deviceId >= 0) {
PaError err;
const PaDeviceInfo *info = Pa_GetDeviceInfo(deviceId); const PaDeviceInfo *info = Pa_GetDeviceInfo(deviceId);
if (!info) { if (!info) {
printf("Failed to query audio device\n"); printf("Failed to query audio device\n");
return; return;
} }


numOutputs = mini(info->maxOutputChannels, 4);
numInputs = mini(info->maxInputChannels, 4);

PaStreamParameters outputParameters; PaStreamParameters outputParameters;
outputParameters.device = deviceId; outputParameters.device = deviceId;
outputParameters.channelCount = 2;
outputParameters.channelCount = numOutputs;
outputParameters.sampleFormat = paFloat32; outputParameters.sampleFormat = paFloat32;
outputParameters.suggestedLatency = info->defaultLowOutputLatency; outputParameters.suggestedLatency = info->defaultLowOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL; outputParameters.hostApiSpecificStreamInfo = NULL;


err = Pa_OpenStream(&stream, NULL, &outputParameters, SAMPLE_RATE, bufferFrames, paNoFlag, NULL, NULL);
PaStreamParameters inputParameters;
inputParameters.device = deviceId;
inputParameters.channelCount = numInputs;
inputParameters.sampleFormat = paFloat32;
inputParameters.suggestedLatency = info->defaultLowInputLatency;
inputParameters.hostApiSpecificStreamInfo = NULL;

err = Pa_OpenStream(&stream, &inputParameters, &outputParameters, SAMPLE_RATE, numFrames, paNoFlag, NULL, NULL);
if (err) { if (err) {
printf("Failed to open audio stream: %s\n", Pa_GetErrorText(err)); printf("Failed to open audio stream: %s\n", Pa_GetErrorText(err));
return; return;
@@ -177,17 +221,60 @@ struct AudioChoice : ChoiceButton {
AudioInterfaceWidget::AudioInterfaceWidget() : ModuleWidget(new AudioInterface()) { AudioInterfaceWidget::AudioInterfaceWidget() : ModuleWidget(new AudioInterface()) {
box.size = Vec(15*8, 380); box.size = Vec(15*8, 380);


addInput(createInput(Vec(15, 100), module, AudioInterface::AUDIO1_INPUT));
addInput(createInput(Vec(70, 100), module, AudioInterface::AUDIO2_INPUT));
float margin = 5;
float yPos = margin;

{
Label *label = new Label();
label->box.pos = Vec(margin, yPos);
label->text = "Audio Interface";
addChild(label);
yPos += label->box.size.y + margin;
}


{ {
AudioChoice *audioChoice = new AudioChoice(); AudioChoice *audioChoice = new AudioChoice();
audioChoice->audioInterface = dynamic_cast<AudioInterface*>(module); audioChoice->audioInterface = dynamic_cast<AudioInterface*>(module);
audioChoice->text = "Audio Interface"; audioChoice->text = "Audio Interface";
audioChoice->box.pos = Vec(0, 0);
audioChoice->box.size.x = box.size.x;
audioChoice->box.pos = Vec(margin, yPos);
audioChoice->box.size.x = box.size.x - 10;
addChild(audioChoice); addChild(audioChoice);
yPos += audioChoice->box.size.y + 2*margin;
}

{
Label *label = new Label();
label->box.pos = Vec(margin, yPos);
label->text = "Outputs";
addChild(label);
yPos += label->box.size.y + margin;
} }

yPos += 5;
addInput(createInput(Vec(25, yPos), module, AudioInterface::AUDIO1_INPUT));
addInput(createInput(Vec(75, yPos), module, AudioInterface::AUDIO2_INPUT));
yPos += 35 + margin;

addInput(createInput(Vec(25, yPos), module, AudioInterface::AUDIO3_INPUT));
addInput(createInput(Vec(75, yPos), module, AudioInterface::AUDIO4_INPUT));
yPos += 35 + margin;

{
Label *label = new Label();
label->box.pos = Vec(margin, yPos);
label->text = "Inputs";
addChild(label);
yPos += label->box.size.y + margin;
}

yPos += 5;
addOutput(createOutput(Vec(25, yPos), module, AudioInterface::AUDIO1_OUTPUT));
addOutput(createOutput(Vec(75, yPos), module, AudioInterface::AUDIO2_OUTPUT));
yPos += 35 + margin;

addOutput(createOutput(Vec(25, yPos), module, AudioInterface::AUDIO3_OUTPUT));
addOutput(createOutput(Vec(75, yPos), module, AudioInterface::AUDIO4_OUTPUT));
yPos += 35 + margin;
} }


void AudioInterfaceWidget::draw(NVGcontext *vg) { void AudioInterfaceWidget::draw(NVGcontext *vg) {


+ 45
- 16
src/core/MidiInterface.cpp View File

@@ -7,7 +7,14 @@


using namespace rack; using namespace rack;


static bool midiInitialized = false;

void midiInit() {
PmError err = Pm_Initialize();
if (err) {
printf("Failed to initialize PortMidi: %s\n", Pm_GetErrorText(err));
return;
}
}




struct MidiInterface : Module { struct MidiInterface : Module {
@@ -49,16 +56,6 @@ MidiInterface::MidiInterface() {
params.resize(NUM_PARAMS); params.resize(NUM_PARAMS);
inputs.resize(NUM_INPUTS); inputs.resize(NUM_INPUTS);
outputs.resize(NUM_OUTPUTS); outputs.resize(NUM_OUTPUTS);

// Lazy initialize PortMidi
if (!midiInitialized) {
PmError err = Pm_Initialize();
if (err) {
printf("Failed to initialize PortMidi: %s\n", Pm_GetErrorText(err));
return;
}
midiInitialized = true;
}
} }


MidiInterface::~MidiInterface() { MidiInterface::~MidiInterface() {
@@ -93,7 +90,11 @@ int MidiInterface::getPortCount() {


std::string MidiInterface::getPortName(int portId) { std::string MidiInterface::getPortName(int portId) {
const PmDeviceInfo *info = Pm_GetDeviceInfo(portId); const PmDeviceInfo *info = Pm_GetDeviceInfo(portId);
return info ? std::string(info->name) : "";
if (!info)
return "";
char name[1024];
snprintf(name, sizeof(name), "%s: %s (%s)", info->interf, info->name, info->input ? "input" : "output");
return name;
} }


void MidiInterface::openPort(int portId) { void MidiInterface::openPort(int portId) {
@@ -223,16 +224,44 @@ struct MidiChoice : ChoiceButton {
MidiInterfaceWidget::MidiInterfaceWidget() : ModuleWidget(new MidiInterface()) { MidiInterfaceWidget::MidiInterfaceWidget() : ModuleWidget(new MidiInterface()) {
box.size = Vec(15*8, 380); box.size = Vec(15*8, 380);


addOutput(createOutput(Vec(15, 100), module, MidiInterface::GATE_OUTPUT));
addOutput(createOutput(Vec(70, 100), module, MidiInterface::PITCH_OUTPUT));
float margin = 5;
float yPos = margin;

{
Label *label = new Label();
label->box.pos = Vec(margin, yPos);
label->text = "MIDI Interface";
addChild(label);
yPos += label->box.size.y + margin;
}


{ {
MidiChoice *midiChoice = new MidiChoice(); MidiChoice *midiChoice = new MidiChoice();
midiChoice->midiInterface = dynamic_cast<MidiInterface*>(module); midiChoice->midiInterface = dynamic_cast<MidiInterface*>(module);
midiChoice->text = "MIDI Interface"; midiChoice->text = "MIDI Interface";
midiChoice->box.pos = Vec(0, 0);
midiChoice->box.size.x = box.size.x;
midiChoice->box.pos = Vec(margin, yPos);
midiChoice->box.size.x = box.size.x - 10;
addChild(midiChoice); addChild(midiChoice);
yPos += midiChoice->box.size.y + margin;
}

yPos += 5;
addOutput(createOutput(Vec(25, yPos), module, MidiInterface::PITCH_OUTPUT));
addOutput(createOutput(Vec(75, yPos), module, MidiInterface::GATE_OUTPUT));
yPos += 25 + margin;

{
Label *pitchLabel = new Label();
pitchLabel->box.pos = Vec(25-12, yPos);
pitchLabel->text = "Pitch";
addChild(pitchLabel);

Label *gateLabel = new Label();
gateLabel->box.pos = Vec(75-12, yPos);
gateLabel->text = "Gate";
addChild(gateLabel);

yPos += pitchLabel->box.size.y + margin;
} }
} }




+ 3
- 0
src/core/core.cpp View File

@@ -4,6 +4,9 @@
using namespace rack; using namespace rack;


Plugin *coreInit() { Plugin *coreInit() {
audioInit();
midiInit();

Plugin *plugin = createPlugin("Core", "Core"); Plugin *plugin = createPlugin("Core", "Core");
createModel<AudioInterfaceWidget>(plugin, "AudioInterface", "Audio Interface"); createModel<AudioInterfaceWidget>(plugin, "AudioInterface", "Audio Interface");
createModel<MidiInterfaceWidget>(plugin, "MidiInterface", "MIDI Interface"); createModel<MidiInterfaceWidget>(plugin, "MidiInterface", "MIDI Interface");


+ 3
- 0
src/core/core.hpp View File

@@ -3,6 +3,9 @@


rack::Plugin *coreInit(); rack::Plugin *coreInit();


void audioInit();
void midiInit();

//////////////////// ////////////////////
// module widgets // module widgets
//////////////////// ////////////////////


+ 0
- 4
src/widgets/Button.cpp View File

@@ -3,10 +3,6 @@


namespace rack { namespace rack {


Button::Button() {
box.size.y = BND_WIDGET_HEIGHT;
}

void Button::draw(NVGcontext *vg) { void Button::draw(NVGcontext *vg) {
bndToolButton(vg, box.pos.x, box.pos.y, box.size.x, box.size.y, BND_CORNER_NONE, state, -1, text.c_str()); bndToolButton(vg, box.pos.x, box.pos.y, box.size.x, box.size.y, BND_CORNER_NONE, state, -1, text.c_str());
} }


+ 0
- 5
src/widgets/ScrollBar.cpp View File

@@ -3,11 +3,6 @@


namespace rack { namespace rack {


ScrollBar::ScrollBar() {
box.size.x = BND_SCROLLBAR_WIDTH;
box.size.y = BND_SCROLLBAR_HEIGHT;
}

void ScrollBar::draw(NVGcontext *vg) { void ScrollBar::draw(NVGcontext *vg) {
float boxSize = (orientation == VERTICAL ? box.size.y : box.size.x); float boxSize = (orientation == VERTICAL ? box.size.y : box.size.x);
float maxOffset = containerSize - boxSize; float maxOffset = containerSize - boxSize;


+ 0
- 4
src/widgets/Slider.cpp View File

@@ -5,10 +5,6 @@ namespace rack {


#define SLIDER_SENSITIVITY 0.001 #define SLIDER_SENSITIVITY 0.001


Slider::Slider() {
box.size.y = BND_WIDGET_HEIGHT;
}

void Slider::draw(NVGcontext *vg) { void Slider::draw(NVGcontext *vg) {
float progress = mapf(value, minValue, maxValue, 0.0, 1.0); float progress = mapf(value, minValue, maxValue, 0.0, 1.0);
bndSlider(vg, box.pos.x, box.pos.y, box.size.x, box.size.y, BND_CORNER_NONE, state, progress, getText().c_str(), NULL); bndSlider(vg, box.pos.x, box.pos.y, box.size.x, box.size.y, BND_CORNER_NONE, state, progress, getText().c_str(), NULL);


Loading…
Cancel
Save