Skip to content

Commit

Permalink
Merge pull request #8 from mxmxmx/dev
Browse files Browse the repository at this point in the history
[v1.1]
  • Loading branch information
mxmxmx authored Apr 24, 2019
2 parents 81a6cb7 + a0c1f67 commit 9f5422d
Show file tree
Hide file tree
Showing 9 changed files with 168 additions and 93 deletions.
2 changes: 1 addition & 1 deletion hardware/BOM.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# BOM for cv | midi

### resistors 0603:
### resistors 0603 (1%):

| value | # | note |
| --- | ---: | --- |
Expand Down
148 changes: 77 additions & 71 deletions software/C2M_X/APP_CV2MIDI.ino
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,17 @@

#include "util/util_settings.h"
#include "util/util_trigger_delay.h"
#include <MIDI.h>
#include "C2M_LED.h"
#include "C2M_options.h"
#include "braids_quantizer.h"
#include "braids_quantizer_scales.h"
#include "channel_message_quantizer.h"
#include "extern/dspinst.h"

/* rate limit */
#define TX_LIMIT_IN_TICKS 100 // 100 ticks ~= 6ms


enum CV2MIDISettings {
CV2MIDI_SETTING_MIDI_CHANNEL,
CV2MIDI_SETTING_DEFAULT_PITCH,
Expand All @@ -45,12 +49,14 @@ enum ControlBitMask {
CONTROL_GATE_FALLING = 4
};

MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI);
const int32_t CM_SCALE = 71000; // scale factor --> velocity (~ x 1.05)
const int32_t NUM_CHANNELS = 5;
const int32_t DOUBLE_CLICK_TICKS = 3000;
const uint8_t NUM_CHANNELS = 5;
const int32_t DOUBLE_CLICK_TICKS = 2500;
const uint8_t trigger_delay_ticks[NUM_CHANNELS + 0x1] = { 0, 4, 8, 12, 16, 24 };

const uint8_t DEFAULT_NOTE_NUMBERS[NUM_CHANNELS] = { NOTE_NUMBER_1, NOTE_NUMBER_2, NOTE_NUMBER_3, NOTE_NUMBER_4, NOTE_NUMBER_5 };
const uint8_t DEFAULT_MIDI_CHANNEL[NUM_CHANNELS] = { MIDI_CHANNEL_1, MIDI_CHANNEL_2, MIDI_CHANNEL_3, MIDI_CHANNEL_4, MIDI_CHANNEL_5 };

class CV2MIDI : public settings::SettingsBase<CV2MIDI, CV2MIDI_SETTING_LAST> {
public:

Expand Down Expand Up @@ -153,8 +159,11 @@ public:
_gate_state |= CONTROL_GATE_FALLING;
gate_raised_ = _gate_raised;

if (_triggered && (active_note_ < 0)) {
rate_limit_++;

if (_triggered && (active_note_ < 0) && (rate_limit_ > TX_LIMIT_IN_TICKS)) {

rate_limit_ = 0x0;
// make note value:
int32_t sample = C2M::ADC::pitch_value(adc_channel);
sample = (quantizer_.Process(sample) >> 7) + get_default_pitch();
Expand All @@ -167,30 +176,26 @@ public:
case 0x90: // note on + velocity
break;
case 0xB0: // control change
MIDI.sendControlChange(get_cc_number(), cm_sample, get_midi_channel());
cm_sample = get_default_velocity(); // velocity
break;
/*
case 0xA0: // aftertouch ... (probably should be sent later, not here / on trigger)
MIDI.sendAfterTouch(cm_sample, get_midi_channel());
C2M::MIDI::send_data(0xB0 + get_midi_channel(), get_cc_number(), cm_sample);
cm_sample = get_default_velocity(); // velocity
break;
*/
default: break;
}
// send note on
if (cm_sample) {
MIDI.sendNoteOn(sample, cm_sample, get_midi_channel());
C2M::MIDI::send_data(0x90 + get_midi_channel(), sample, cm_sample);
active_note_ = sample;
digitalWriteFast(C2M::LEDs::LED_num[channel_id_], HIGH);
}
}
else if ((_gate_state == CONTROL_GATE_FALLING) && (active_note_ >= 0)) {
MIDI.sendNoteOn(active_note_, 0x0, get_midi_channel());
/* alternative would be to use sendNoteOff ... could make this a setting, but there's no ui left really ? */
/* MIDI.sendNoteOff(active_note_, 0x0, _midi_channel); */

/* note off: once gate goes low, or is low ... */
else if (((_gate_state == CONTROL_GATE_FALLING) || !_gate_raised) && (active_note_ >= 0)) {
/* alternative would be to send 0x80 / note off ... */
C2M::MIDI::send_data(0x90 + get_midi_channel(), active_note_, 0x0);
digitalWriteFast(C2M::LEDs::LED_num[channel_id_], LOW);
active_note_ = -0xFF;
gate_raised_ = false;
}
}

Expand All @@ -207,6 +212,7 @@ int32_t Scale127(int32_t sample) {
private:
bool gate_raised_;
uint8_t channel_id_;
uint16_t rate_limit_;
int32_t active_note_;
braids::Quantizer quantizer_;
SemitoneQuantizer cm_quantizer_;
Expand All @@ -219,10 +225,14 @@ void CV2MIDI::Init(C2M::DigitalInput default_trigger) {

InitDefaults();
channel_id_ = static_cast<int8_t>(default_trigger);
// default to midi channels 1-5 ... only do this if defaults were not loaded in the first place:
if (C2M::apps::using_defaults) set_midi_channel(channel_id_ + 0x1);
/* load defaults from C2M_options.h ... only do this if defaults were not loaded in the first place: */
if (C2M::apps::using_defaults) {
set_midi_channel(DEFAULT_MIDI_CHANNEL[channel_id_]);
set_default_pitch(DEFAULT_NOTE_NUMBERS[channel_id_]);
}
active_note_ = -0xFF;
gate_raised_ = false;
rate_limit_ = 0x0;
quantizer_.Init();
cm_quantizer_.Init();
quantizer_.Configure(braids::scales[0x1], 0xFFFF);
Expand All @@ -232,8 +242,8 @@ void CV2MIDI::Init(C2M::DigitalInput default_trigger) {

SETTINGS_DECLARE(CV2MIDI, CV2MIDI_SETTING_LAST) {
{ 0x0, 0x0, 0xF, "midi ch ", NULL, settings::STORAGE_TYPE_U4 },
{ 60, 0, 127, "default pitch", NULL, settings::STORAGE_TYPE_U8 },
{ 60, 0, 127, "default velocity", NULL, settings::STORAGE_TYPE_U8 }, // used when sending CC or aftertouch
{ DEFAULT_NOTE_NUMBERS[0], 0, 127, "default pitch", NULL, settings::STORAGE_TYPE_U8 },
{ DEFAULT_NOTE_NUMBERS[0], 0, 127, "default velocity", NULL, settings::STORAGE_TYPE_U8 }, // used when sending CC
{ 0x90, 0x0, 0xFF, "message type", NULL, settings::STORAGE_TYPE_U8 },
{ 0, 0, 127, "cc number", NULL, settings::STORAGE_TYPE_U8 },
{ 2, 0, NUM_CHANNELS, "trigger delay", NULL, settings::STORAGE_TYPE_U4 }
Expand All @@ -254,17 +264,18 @@ public:
ui.arm = false;
ui.save = false;
ui.ticks = 0x0;
ui.rx_status = 0x0;
ui.pressed = 0x0;
MIDI.begin(MIDI_CHANNEL_OMNI);
}

uint8_t delay_settings() { return c2m_[0].get_trigger_delay(); }

void reInit() {
for (int i = 0; i < NUM_CHANNELS; i++)
{
c2m_[i].set_midi_channel(0x1 + i);
c2m_[i].set_trigger_delay(0x0);
c2m_[i].set_midi_channel(DEFAULT_MIDI_CHANNEL[i]);
c2m_[i].set_default_pitch(DEFAULT_NOTE_NUMBERS[i]);
c2m_[i].set_trigger_delay(0x0);
}
}

Expand Down Expand Up @@ -321,61 +332,55 @@ public:
else
ui.ticks = 0x0;

// and listen to incoming midi data:
if (MIDI.read()) {
/* and listen to incoming midi data: */

if (C2M::MIDI::rx()) {

uint8_t statusByte = Serial1.read();
uint8_t msgType = statusByte & 0xF0;

switch(MIDI.getType()) {
switch(msgType) {

case midi::NoteOn:
case 0x90: // note on
case 0xB0: // control change
{
// set default pitch + midi channel
c2m_[channel].set_midi_channel(MIDI.getChannel());
c2m_[channel].set_default_pitch(MIDI.getData1());
SERIAL_PRINTLN("received: channel %d, note on: %d", c2m_[channel].get_midi_channel(), c2m_[channel].get_default_pitch());
uint8_t param1 = Serial1.read();
uint8_t param2 = Serial1.read();

/* set channel */
c2m_[channel].set_midi_channel(statusByte & 0xF);
/* set type */
c2m_[channel].set_channel_message(msgType);

/* if velocity > 0, set default pitch */
if ((msgType == 0x90) && param2) {
c2m_[channel].set_default_pitch(param1);
SERIAL_PRINTLN("received: channel %d, note on: %d", c2m_[channel].get_midi_channel(), c2m_[channel].get_default_pitch());
}
else if (msgType == 0xB0) {
c2m_[channel].set_cc_number(param1);
/* set default velocity via pot */
int32_t velocity = c2m_[channel].Scale127(C2M::ADC::pitch_value(static_cast<ADC_CHANNEL>(channel + ADC_CHANNEL_NUM)));
c2m_[channel].set_default_velocity(velocity);
SERIAL_PRINTLN("received: channel %d, CC: %d / %d", c2m_[channel].get_midi_channel(), c2m_[channel].get_cc_number(), param2);
}

ui.save = true;
ui.rx_status = millis();
digitalWriteFast(LEDX, LOW);

/* echo the note */
C2M::MIDI::send_data(statusByte, param1, param2);
}
break;
case midi::ControlChange:
{
c2m_[channel].set_midi_channel(MIDI.getChannel());
c2m_[channel].set_channel_message(0xB0); // set status
c2m_[channel].set_cc_number(MIDI.getData1()); // set CC number
// set default velocity via pot
int32_t velocity = c2m_[channel].Scale127(C2M::ADC::pitch_value(static_cast<ADC_CHANNEL>(channel + ADC_CHANNEL_NUM)));
c2m_[channel].set_default_velocity(velocity);
SERIAL_PRINTLN("received: channel %d, CC: %d / %d", c2m_[channel].get_midi_channel(), c2m_[channel].get_cc_number(), MIDI.getData2());
ui.save = true;
}
break;
/*
case midi::AfterTouchChannel:
{
c2m_[channel].set_midi_channel(MIDI.getChannel());
c2m_[channel].set_channel_message(0xA0);
// set velocity via pot
int32_t velocity = C2M::ADC::pitch_value(static_cast<ADC_CHANNEL>(channel + ADC_CHANNEL_NUM));
// scale to 0-127
velocity = c2m_[channel].Scale127(velocity);
c2m_[channel].set_default_velocity(velocity);
SERIAL_PRINTLN("received: channel %d, aftertouch: %d / %d", c2m_[channel].get_midi_channel(), MIDI.getData1(), MIDI.getData2());
ui.save = true;
}
break;
*/
/*
// add ? ... could just send the MSB, or not scale to 127
case midi::PitchBend:
c2m_[channel].set_midi_channel(MIDI.getChannel());
c2m_[channel].set_channel_message(0xE0);
SERIAL_PRINTLN("received: channel %d, pitchbend: %d / %d", c2m_[channel].get_midi_channel(), MIDI.getData1(), MIDI.getData2());
ui.save = true;
break;
*/
/* to do ... dump channel configurations into module w/ sysex ? */
default:
SERIAL_PRINTLN("(ignored): channel %d, type: %d", MIDI.getChannel(), MIDI.getType());
break;
}
}
}
/* RX feedback ... */
if (ui.rx_status && (millis() - ui.rx_status > 250)) {
digitalWriteFast(LEDX, HIGH);
ui.rx_status = 0x0;
}
}
}
Expand All @@ -395,6 +400,7 @@ public:
int8_t selected_channel;
int8_t trigger_settings;
uint32_t ticks;
uint32_t rx_status;
bool arm;
bool save;
bool pressed;
Expand Down
9 changes: 0 additions & 9 deletions software/C2M_X/C2M_LED.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,6 @@ class LEDs {
static void on_up_to(uint8_t num);

static constexpr uint8_t LED_num[LED_CHANNEL_LAST] = { LED1, LED2, LED3, LED4, LED5, LEDX };

//template <LED_CHANNEL channel>
//static void set(uint32_t value) {
// values_[channel] = USAT16(value);
//}

//static void set(LED_CHANNEL channel, uint32_t value) {
// values_[channel] = USAT16(value);
//}

private:
static uint32_t ticks_;
Expand Down
33 changes: 33 additions & 0 deletions software/C2M_X/C2M_MIDI.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#include "C2M_MIDI.h"
#include "C2M_options.h"
#include "Arduino.h"

namespace C2M {

/*static*/
void MIDI::Init() {
Serial1.begin(31250);
}

int MIDI::rx() {
return (Serial1.available() > 0);
}

void MIDI::send_data(uint8_t statusByte, uint8_t param1, uint8_t param2) {

#ifdef USE_TX_BUFFER
uint8_t tx_buf[0x3];

tx_buf[0] = statusByte;
tx_buf[1] = param1;
tx_buf[2] = param2;

Serial1.write(tx_buf, 0x3);
#else
Serial1.write(statusByte); Serial1.write(param1); Serial1.write(param2);
#endif
}

}; // namespace 2CM

// C2M_MIDI
20 changes: 20 additions & 0 deletions software/C2M_X/C2M_MIDI.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#ifndef C2M_MIDI_H_
#define C2M_MIDI_H_

#include <stdint.h>

namespace C2M {

class MIDI {

public:

static void Init();
static int rx();
static void send_data(uint8_t statusByte, uint8_t param1, uint8_t param2);

};

}; // namespace 2CM

#endif // C2M_MIDI_H_
4 changes: 3 additions & 1 deletion software/C2M_X/C2M_X.ino
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "C2M_apps.h"
#include "C2M_core.h"
#include "C2M_LED.h"
#include "C2M_MIDI.h"
#include "C2M_gpio.h"
#include "C2M_ADC.h"
#include "C2M_calibration.h"
Expand Down Expand Up @@ -78,7 +79,8 @@ void setup() {
C2M::DigitalInputs::Init();
C2M::ADC::Init(&C2M::calibration_data.adc);
C2M::ADC::Init_DMA();
C2M::LEDs::Init();
C2M::LEDs::Init();
C2M::MIDI::Init();
C2M::ui.Init();
calibration_load();

Expand Down
Loading

0 comments on commit 9f5422d

Please sign in to comment.