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
LDFLAGS += -stdlib=libc++ -L$(HOME)/local/lib -lpthread -lglew -lglfw3 -framework Cocoa -framework OpenGL -framework IOKit -framework CoreVideo -ldl -ljansson -lportaudio -lportmidi
TARGET = Rack

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

# 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.*

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

Eurorack-style modular DAW



+ 12
- 3
include/widgets.hpp View File

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

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

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

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

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

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


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

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


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

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

void AudioInterface::step() {
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;
}

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;
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) {
// Ignore buffer underflows
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) {
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) {
PaError err;
std::lock_guard<std::mutex> lock(mutex);

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

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

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

PaStreamParameters outputParameters;
outputParameters.device = deviceId;
outputParameters.channelCount = 2;
outputParameters.channelCount = numOutputs;
outputParameters.sampleFormat = paFloat32;
outputParameters.suggestedLatency = info->defaultLowOutputLatency;
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) {
printf("Failed to open audio stream: %s\n", Pa_GetErrorText(err));
return;
@@ -177,17 +221,60 @@ struct AudioChoice : ChoiceButton {
AudioInterfaceWidget::AudioInterfaceWidget() : ModuleWidget(new AudioInterface()) {
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->audioInterface = dynamic_cast<AudioInterface*>(module);
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);
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) {


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

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

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 {
@@ -49,16 +56,6 @@ MidiInterface::MidiInterface() {
params.resize(NUM_PARAMS);
inputs.resize(NUM_INPUTS);
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() {
@@ -93,7 +90,11 @@ int MidiInterface::getPortCount() {

std::string MidiInterface::getPortName(int 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) {
@@ -223,16 +224,44 @@ struct MidiChoice : ChoiceButton {
MidiInterfaceWidget::MidiInterfaceWidget() : ModuleWidget(new MidiInterface()) {
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->midiInterface = dynamic_cast<MidiInterface*>(module);
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);
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;

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

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


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

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

rack::Plugin *coreInit();

void audioInit();
void midiInit();

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


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

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

namespace rack {

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

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());
}


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

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

namespace rack {

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

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


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

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

#define SLIDER_SENSITIVITY 0.001

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

void Slider::draw(NVGcontext *vg) {
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);


Loading…
Cancel
Save