// Copyright 2012 Olivier Gillet.
//
// Author: Olivier Gillet (ol.gillet@gmail.com)
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
//
// -----------------------------------------------------------------------------
#ifndef EDGES_MIDI_HANDLER_H_
#define EDGES_MIDI_HANDLER_H_
#include "avrlibx/avrlibx.h"
#include "edges/hardware_config.h"
#include "edges/midi.h"
#include "edges/note_stack.h"
#include "edges/settings.h"
#include "edges/voice_allocator.h"
namespace edges {
enum MidiMode {
MIDI_MODE_MULTITIMBRAL,
MIDI_MODE_POLYPHONIC
};
class MidiHandler : public midi::MidiDevice {
public:
MidiHandler() { }
static inline void Init() {
for (uint8_t channel = 0; channel < kNumChannels; ++channel) {
stack_[channel].Init();
}
allocator_.Init();
allocator_.set_size(4);
learning_ = false;
memset(gate_, false, sizeof(gate_));
memset(pitch_, -1, sizeof(pitch_));
}
static void ToggleMidiMode() {
settings.ToggleMidiMode();
Init();
}
static void DisableMidiCoupling() {
Init();
}
static void ResetAllControllers(uint8_t channel) {
channel = (channel - base_channel()) & 0xf;
pitch_bend_[channel] = 0;
}
static inline void NoteOn(uint8_t channel, uint8_t note, uint8_t velocity) {
channel = (channel - base_channel()) & 0xf;
if (velocity == 0) {
NoteOff(channel, note, 0);
}
if (midi_mode() == MIDI_MODE_MULTITIMBRAL) {
stack_[channel].NoteOn(note, velocity);
pitch_[channel] = stack_[channel].most_recent_note().note << 7;
} else {
channel = allocator_.NoteOn(note);
pitch_[channel] = note << 7;
}
gate_[channel] = true;
}
static inline void NoteOff(uint8_t channel, uint8_t note, uint8_t velocity) {
channel = (channel - base_channel()) & 0xf;
if (midi_mode() == MIDI_MODE_MULTITIMBRAL) {
stack_[channel].NoteOff(note);
if (stack_[channel].size()) {
pitch_[channel] = stack_[channel].most_recent_note().note << 7;
}
gate_[channel] = stack_[channel].size() != 0;
} else {
channel = allocator_.NoteOff(note);
if (channel != 0xff) {
gate_[channel] = false;
}
}
}
static inline void PitchBend(uint8_t channel, uint16_t value) {
channel = (channel - base_channel()) & 0xf;
int16_t v = value;
v -= 8192;
v >>= 5;
if (midi_mode() == MIDI_MODE_MULTITIMBRAL) {
pitch_bend_[channel] = v;
} else {
for (uint8_t i = 0; i < kNumChannels; ++i) {
pitch_bend_[i] = v;
}
}
}
static inline void RawMidiData(
uint8_t status,
uint8_t* data,
uint8_t data_size,
uint8_t accepted_channel) {
if (learning_ && (status & 0xf0) == 0x90) {
settings.set_midi_channel(status & 0xf);
learning_ = false;
}
}
static uint8_t CheckChannel(uint8_t channel) {
if (midi_mode() == MIDI_MODE_MULTITIMBRAL) {
return ((channel - base_channel()) & 0xf) < kNumChannels;
} else {
return channel == base_channel();
}
}
static void Learn() {
learning_ = true;
}
static inline bool learning() { return learning_; }
static inline bool gate(uint8_t channel) { return gate_[channel]; }
static int16_t shift_pitch(uint8_t channel, int16_t pitch) {
if (pitch_[channel] == -1) {
return pitch;
} else {
pitch -= (60 << 7);
pitch += pitch_[channel];
pitch += pitch_bend_[channel];
if (pitch < 0) {
pitch = 0;
} else if (pitch > 16383) {
pitch = 16383;
}
return pitch;
}
return pitch;
}
static inline uint8_t base_channel() {
return settings.midi_channel();
}
static inline MidiMode midi_mode() {
return static_cast(settings.midi_mode());
}
private:
static bool learning_;
static bool gate_[kNumChannels];
static int16_t pitch_[kNumChannels];
static int16_t pitch_bend_[kNumChannels];
static NoteStack<10> stack_[kNumChannels];
static VoiceAllocator allocator_;
DISALLOW_COPY_AND_ASSIGN(MidiHandler);
};
extern MidiHandler midi_handler;
} // namespace edges
#endif // EDGES_MIDI_HANDLER_H_