diff --git a/source/native-plugins/midi-base.hpp b/source/native-plugins/midi-base.hpp index 0b5509de3..70039e5e8 100644 --- a/source/native-plugins/midi-base.hpp +++ b/source/native-plugins/midi-base.hpp @@ -1,6 +1,6 @@ /* * Carla Native Plugins - * Copyright (C) 2013-2014 Filipe Coelho + * Copyright (C) 2012-2015 Filipe Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -25,10 +25,14 @@ #include "CarlaJuceUtils.hpp" #include "CarlaMathUtils.hpp" +// ----------------------------------------------------------------------- + #define MAX_EVENT_DATA_SIZE 4 #define MIN_PREALLOCATED_EVENT_COUNT 100 #define MAX_PREALLOCATED_EVENT_COUNT 1000 +// ----------------------------------------------------------------------- + struct RawMidiEvent { uint64_t time; uint8_t size; @@ -42,53 +46,60 @@ struct RawMidiEvent { } }; +// ----------------------------------------------------------------------- + class AbstractMidiPlayer { public: virtual ~AbstractMidiPlayer() {} - virtual void writeMidiEvent(const uint64_t timePosFrame, const RawMidiEvent* const event) = 0; + virtual void writeMidiEvent(const uint8_t port, const uint64_t timePosFrame, const RawMidiEvent* const event) = 0; }; +// ----------------------------------------------------------------------- + class MidiPattern { public: - MidiPattern(AbstractMidiPlayer* const player) + MidiPattern(AbstractMidiPlayer* const player) noexcept : kPlayer(player), + fMidiPort(0), + fStartTime(0), fMutex(), fData(), leakDetector_MidiPattern() - //fStartTime(0), - //fDuration(0) { - CARLA_ASSERT(kPlayer != nullptr); + CARLA_SAFE_ASSERT(kPlayer != nullptr); } - ~MidiPattern() + ~MidiPattern() noexcept { - fData.clear(); + clear(); } + // ------------------------------------------------------------------- + // add data, time always counts from 0 + void addControl(const uint64_t time, const uint8_t channel, const uint8_t control, const uint8_t value) { - RawMidiEvent* ctrlEvent(new RawMidiEvent()); + RawMidiEvent* const ctrlEvent(new RawMidiEvent()); ctrlEvent->time = time; ctrlEvent->size = 3; ctrlEvent->data[0] = uint8_t(MIDI_STATUS_CONTROL_CHANGE | (channel & 0x0F)); ctrlEvent->data[1] = control; ctrlEvent->data[2] = value; - append(ctrlEvent); + appendSorted(ctrlEvent); } void addChannelPressure(const uint64_t time, const uint8_t channel, const uint8_t pressure) { - RawMidiEvent* pressureEvent(new RawMidiEvent()); + RawMidiEvent* const pressureEvent(new RawMidiEvent()); pressureEvent->time = time; pressureEvent->size = 2; pressureEvent->data[0] = uint8_t(MIDI_STATUS_CHANNEL_PRESSURE | (channel & 0x0F)); pressureEvent->data[1] = pressure; - append(pressureEvent); + appendSorted(pressureEvent); } void addNote(const uint64_t time, const uint8_t channel, const uint8_t pitch, const uint8_t velocity, const uint32_t duration) @@ -99,143 +110,173 @@ public: void addNoteOn(const uint64_t time, const uint8_t channel, const uint8_t pitch, const uint8_t velocity) { - RawMidiEvent* noteOnEvent(new RawMidiEvent()); + RawMidiEvent* const noteOnEvent(new RawMidiEvent()); noteOnEvent->time = time; noteOnEvent->size = 3; noteOnEvent->data[0] = uint8_t(MIDI_STATUS_NOTE_ON | (channel & 0x0F)); noteOnEvent->data[1] = pitch; noteOnEvent->data[2] = velocity; - append(noteOnEvent); + appendSorted(noteOnEvent); } void addNoteOff(const uint64_t time, const uint8_t channel, const uint8_t pitch, const uint8_t velocity = 0) { - RawMidiEvent* noteOffEvent(new RawMidiEvent()); + RawMidiEvent* const noteOffEvent(new RawMidiEvent()); noteOffEvent->time = time; noteOffEvent->size = 3; noteOffEvent->data[0] = uint8_t(MIDI_STATUS_NOTE_OFF | (channel & 0x0F)); noteOffEvent->data[1] = pitch; noteOffEvent->data[2] = velocity; - append(noteOffEvent); + appendSorted(noteOffEvent); } void addNoteAftertouch(const uint64_t time, const uint8_t channel, const uint8_t pitch, const uint8_t pressure) { - RawMidiEvent* noteAfterEvent(new RawMidiEvent()); + RawMidiEvent* const noteAfterEvent(new RawMidiEvent()); noteAfterEvent->time = time; noteAfterEvent->size = 3; noteAfterEvent->data[0] = uint8_t(MIDI_STATUS_POLYPHONIC_AFTERTOUCH | (channel & 0x0F)); noteAfterEvent->data[1] = pitch; noteAfterEvent->data[2] = pressure; - append(noteAfterEvent); + appendSorted(noteAfterEvent); } void addProgram(const uint64_t time, const uint8_t channel, const uint8_t bank, const uint8_t program) { - RawMidiEvent* bankEvent(new RawMidiEvent()); + RawMidiEvent* const bankEvent(new RawMidiEvent()); bankEvent->time = time; bankEvent->size = 3; bankEvent->data[0] = uint8_t(MIDI_STATUS_CONTROL_CHANGE | (channel & 0x0F)); bankEvent->data[1] = MIDI_CONTROL_BANK_SELECT; bankEvent->data[2] = bank; - RawMidiEvent* programEvent(new RawMidiEvent()); + RawMidiEvent* const programEvent(new RawMidiEvent()); programEvent->time = time; programEvent->size = 2; programEvent->data[0] = uint8_t(MIDI_STATUS_PROGRAM_CHANGE | (channel & 0x0F)); programEvent->data[1] = program; - append(bankEvent); - append(programEvent); + appendSorted(bankEvent); + appendSorted(programEvent); } void addPitchbend(const uint64_t time, const uint8_t channel, const uint8_t lsb, const uint8_t msb) { - RawMidiEvent* pressureEvent(new RawMidiEvent()); + RawMidiEvent* const pressureEvent(new RawMidiEvent()); pressureEvent->time = time; pressureEvent->size = 3; pressureEvent->data[0] = uint8_t(MIDI_STATUS_PITCH_WHEEL_CONTROL | (channel & 0x0F)); pressureEvent->data[1] = lsb; pressureEvent->data[2] = msb; - append(pressureEvent); + appendSorted(pressureEvent); } - void addRaw(const uint64_t time, const uint8_t* data, const uint8_t size) + void addRaw(const uint64_t time, const uint8_t* const data, const uint8_t size) { - RawMidiEvent* rawEvent(new RawMidiEvent()); + RawMidiEvent* const rawEvent(new RawMidiEvent()); rawEvent->time = time; rawEvent->size = size; carla_copy(rawEvent->data, data, size); - append(rawEvent); + appendSorted(rawEvent); + } + + // ------------------------------------------------------------------- + // clear + + void clear() noexcept + { + const CarlaMutexLocker sl(fMutex); + + for (LinkedList::Itenerator it = fData.begin(); it.valid(); it.next()) + delete it.getValue(nullptr); + + fData.clear(); } - void play(uint64_t timePosFrame, uint32_t frames) + // ------------------------------------------------------------------- + // play on time + + void play(uint64_t timePosFrame, const uint32_t frames) { if (! fMutex.tryLock()) return; + timePosFrame += fStartTime; + for (LinkedList::Itenerator it = fData.begin(); it.valid(); it.next()) { - const RawMidiEvent* const rawMidiEvent(it.getValue()); + const RawMidiEvent* const rawMidiEvent(it.getValue(nullptr)); + CARLA_SAFE_ASSERT_CONTINUE(rawMidiEvent != nullptr); if (timePosFrame > rawMidiEvent->time) continue; if (timePosFrame + frames <= rawMidiEvent->time) continue; - kPlayer->writeMidiEvent(timePosFrame, rawMidiEvent); + kPlayer->writeMidiEvent(fMidiPort, timePosFrame, rawMidiEvent); } fMutex.unlock(); } - void clear() + // ------------------------------------------------------------------- + // configure + + void setMidiPort(const uint8_t port) noexcept { - const CarlaMutexLocker sl(fMutex); - fData.clear(); + fMidiPort = port; + } + + void setStartTime(const uint64_t time) noexcept + { + fStartTime = time; } + // ------------------------------------------------------------------- + private: AbstractMidiPlayer* const kPlayer; - //uint32_t fStartTime; // unused - //uint32_t fDuration; // unused + uint8_t fMidiPort; + uint64_t fStartTime; CarlaMutex fMutex; LinkedList fData; - void append(const RawMidiEvent* const event) + void appendSorted(const RawMidiEvent* const event) { + const CarlaMutexLocker sl(fMutex); + if (fData.isEmpty()) { - const CarlaMutexLocker sl(fMutex); fData.append(event); return; } for (LinkedList::Itenerator it = fData.begin(); it.valid(); it.next()) { - const RawMidiEvent* const oldEvent(it.getValue()); + const RawMidiEvent* const oldEvent(it.getValue(nullptr)); + CARLA_SAFE_ASSERT_CONTINUE(oldEvent != nullptr); if (event->time >= oldEvent->time) continue; - const CarlaMutexLocker sl(fMutex); fData.insertAt(event, it); return; } - const CarlaMutexLocker sl(fMutex); fData.append(event); } CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MidiPattern) }; +// ----------------------------------------------------------------------- + #endif // MIDI_BASE_HPP_INCLUDED diff --git a/source/native-plugins/midi-file.cpp b/source/native-plugins/midi-file.cpp index 56c23160c..7c7190ca8 100644 --- a/source/native-plugins/midi-file.cpp +++ b/source/native-plugins/midi-file.cpp @@ -1,6 +1,6 @@ /* * Carla Native Plugins - * Copyright (C) 2012-2014 Filipe Coelho + * Copyright (C) 2012-2015 Filipe Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -91,9 +91,7 @@ protected: return; if (const char* const filename = uiOpenFile(false, "Open Audio File", "MIDI Files *.mid;*.midi;;")) - { uiCustomDataChanged("file", filename); - } uiClosed(); } @@ -101,11 +99,11 @@ protected: // ------------------------------------------------------------------- // AbstractMidiPlayer calls - void writeMidiEvent(const uint64_t timePosFrame, const RawMidiEvent* const event) override + void writeMidiEvent(const uint8_t port, const uint64_t timePosFrame, const RawMidiEvent* const event) override { NativeMidiEvent midiEvent; - midiEvent.port = 0; + midiEvent.port = port; midiEvent.time = uint32_t(event->time-timePosFrame); midiEvent.size = event->size; midiEvent.data[0] = event->data[0]; @@ -116,6 +114,8 @@ protected: NativePluginClass::writeMidiEvent(&midiEvent); } + // ------------------------------------------------------------------- + private: MidiPattern fMidiOut; bool fWasPlayingBefore; diff --git a/source/native-plugins/midi-sequencer.cpp b/source/native-plugins/midi-sequencer.cpp index affdef4c2..133c5856d 100644 --- a/source/native-plugins/midi-sequencer.cpp +++ b/source/native-plugins/midi-sequencer.cpp @@ -1,6 +1,6 @@ /* * Carla Native Plugins - * Copyright (C) 2012-2014 Filipe Coelho + * Copyright (C) 2012-2015 Filipe Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -18,6 +18,8 @@ #include "CarlaNative.hpp" #include "midi-base.hpp" +// ----------------------------------------------------------------------- + class MidiSequencerPlugin : public NativePluginClass, public AbstractMidiPlayer { @@ -172,12 +174,12 @@ protected: // ------------------------------------------------------------------- // Plugin process calls - void writeMidiEvent(const uint32_t timePosFrame, const RawMidiEvent* const event) override + void writeMidiEvent(const uint8_t port, const uint32_t timePosFrame, const RawMidiEvent* const event) override { NativeMidiEvent midiEvent; - midiEvent.port = 0; - midiEvent.time = event->time-timePosFrame; + midiEvent.port = port; + midiEvent.time = uint32_t(event->time-timePosFrame); midiEvent.data[0] = event->data[0]; midiEvent.data[1] = event->data[1]; midiEvent.data[2] = event->data[2];