MIDI API refactor

This commit is contained in:
Jack Andersen 2016-05-20 12:56:25 -10:00
parent 2a5823d6de
commit 64ed5e2ea5
6 changed files with 77 additions and 18 deletions

View File

@ -174,6 +174,7 @@ struct AppCallback : boo::IApplicationCallback
int m_sfxId = -1; int m_sfxId = -1;
std::shared_ptr<amuse::Voice> m_vox; std::shared_ptr<amuse::Voice> m_vox;
size_t m_lastVoxCount = 0; size_t m_lastVoxCount = 0;
int8_t m_lastChanProg = -1;
/* Control state */ /* Control state */
float m_volume = 0.5f; float m_volume = 0.5f;
@ -297,14 +298,22 @@ struct AppCallback : boo::IApplicationCallback
m_engine->pumpEngine(); m_engine->pumpEngine();
size_t voxCount; size_t voxCount;
int8_t progId;
if (m_seq) if (m_seq)
{
voxCount = m_seq->getVoiceCount(); voxCount = m_seq->getVoiceCount();
progId = m_seq->getChanProgram(m_chanId);
}
else else
{
voxCount = 0; voxCount = 0;
progId = -1;
}
if (m_lastVoxCount != voxCount) if (m_lastVoxCount != voxCount || m_lastChanProg != progId)
{ {
m_lastVoxCount = voxCount; m_lastVoxCount = voxCount;
m_lastChanProg = progId;
UpdateSongDisplay(); UpdateSongDisplay();
} }

View File

@ -8,6 +8,9 @@
#include "IBackendVoice.hpp" #include "IBackendVoice.hpp"
#include "IBackendSubmix.hpp" #include "IBackendSubmix.hpp"
#include "IBackendVoiceAllocator.hpp" #include "IBackendVoiceAllocator.hpp"
#include <mutex>
#include <list>
#include <chrono>
namespace amuse namespace amuse
{ {
@ -66,15 +69,21 @@ public:
/** Backend MIDI event reader for controlling sequencer with external hardware / software */ /** Backend MIDI event reader for controlling sequencer with external hardware / software */
class BooBackendMIDIReader : public IMIDIReader, public boo::IMIDIReader class BooBackendMIDIReader : public IMIDIReader, public boo::IMIDIReader
{ {
friend class BooBackendVoiceAllocator;
Engine& m_engine; Engine& m_engine;
std::unique_ptr<boo::IMIDIIn> m_midiIn; std::unique_ptr<boo::IMIDIIn> m_midiIn;
boo::MIDIDecoder m_decoder; boo::MIDIDecoder m_decoder;
std::list<std::pair<std::chrono::steady_clock::time_point, std::vector<uint8_t>>> m_queue;
std::mutex m_midiMutex;
void _MIDIReceive(std::vector<uint8_t>&& bytes);
public: public:
BooBackendMIDIReader(Engine& engine, std::unique_ptr<boo::IMIDIIn>&& in) ~BooBackendMIDIReader();
: m_engine(engine), m_midiIn(std::move(in)), m_decoder(*m_midiIn, *this) {} BooBackendMIDIReader(Engine& engine, const char* name);
std::string description(); std::string description();
void pumpReader(); void pumpReader(double dt);
void noteOff(uint8_t chan, uint8_t key, uint8_t velocity); void noteOff(uint8_t chan, uint8_t key, uint8_t velocity);
void noteOn(uint8_t chan, uint8_t key, uint8_t velocity); void noteOn(uint8_t chan, uint8_t key, uint8_t velocity);
@ -107,6 +116,7 @@ public:
/** Backend voice allocator implementation for boo mixer */ /** Backend voice allocator implementation for boo mixer */
class BooBackendVoiceAllocator : public IBackendVoiceAllocator class BooBackendVoiceAllocator : public IBackendVoiceAllocator
{ {
friend class BooBackendMIDIReader;
boo::IAudioVoiceEngine& m_booEngine; boo::IAudioVoiceEngine& m_booEngine;
public: public:
BooBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine); BooBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine);

View File

@ -26,7 +26,7 @@ class Engine
friend class Voice; friend class Voice;
friend class Emitter; friend class Emitter;
friend class Sequencer; friend class Sequencer;
friend class Sequencer::ChannelState; friend struct Sequencer::ChannelState;
IBackendVoiceAllocator& m_backend; IBackendVoiceAllocator& m_backend;
std::unique_ptr<IMIDIReader> m_midiReader; std::unique_ptr<IMIDIReader> m_midiReader;

View File

@ -29,7 +29,7 @@ class IMIDIReader
public: public:
virtual ~IMIDIReader()=default; virtual ~IMIDIReader()=default;
virtual std::string description()=0; virtual std::string description()=0;
virtual void pumpReader()=0; virtual void pumpReader(double dt)=0;
}; };
/** Client-implemented voice allocator */ /** Client-implemented voice allocator */

View File

@ -107,9 +107,55 @@ std::string BooBackendMIDIReader::description()
return m_midiIn->description(); return m_midiIn->description();
} }
void BooBackendMIDIReader::pumpReader() BooBackendMIDIReader::~BooBackendMIDIReader() {}
BooBackendMIDIReader::BooBackendMIDIReader(Engine& engine, const char* name)
: m_engine(engine), m_decoder(*this)
{ {
while (m_decoder.receiveBytes()) {} BooBackendVoiceAllocator& voxAlloc = static_cast<BooBackendVoiceAllocator&>(engine.getBackend());
if (!name)
m_midiIn = voxAlloc.m_booEngine.newVirtualMIDIIn(std::bind(&BooBackendMIDIReader::_MIDIReceive, this,
std::placeholders::_1));
else
m_midiIn = voxAlloc.m_booEngine.newRealMIDIIn(name,
std::bind(&BooBackendMIDIReader::_MIDIReceive, this,
std::placeholders::_1));
}
void BooBackendMIDIReader::_MIDIReceive(std::vector<uint8_t>&& bytes)
{
std::unique_lock<std::mutex> lk(m_midiMutex);
m_queue.emplace_back(std::chrono::steady_clock::now(), std::move(bytes));
}
void BooBackendMIDIReader::pumpReader(double dt)
{
dt += 0.001; /* Add 1ms to ensure consumer keeps up with producer */
std::unique_lock<std::mutex> lk(m_midiMutex);
if (m_queue.empty())
return;
/* Determine range of buffer updates within this period */
auto periodEnd = m_queue.cbegin();
std::chrono::steady_clock::time_point startPt = m_queue.front().first;
for (; periodEnd != m_queue.cend() ; ++periodEnd)
{
double delta = std::chrono::duration_cast<std::chrono::microseconds>
(periodEnd->first - startPt).count() / 1000000.0;
if (delta > dt)
break;
}
if (m_queue.cbegin() == periodEnd)
return;
/* Dispatch buffers */
for (auto it = m_queue.begin() ; it != periodEnd ;)
{
m_decoder.receiveBytes(it->second.cbegin(), it->second.cend());
it = m_queue.erase(it);
}
} }
void BooBackendMIDIReader::noteOff(uint8_t chan, uint8_t key, uint8_t velocity) void BooBackendMIDIReader::noteOff(uint8_t chan, uint8_t key, uint8_t velocity)
@ -241,16 +287,10 @@ std::vector<std::pair<std::string, std::string>> BooBackendVoiceAllocator::enume
std::unique_ptr<IMIDIReader> BooBackendVoiceAllocator::allocateMIDIReader(Engine& engine, const char* name) std::unique_ptr<IMIDIReader> BooBackendVoiceAllocator::allocateMIDIReader(Engine& engine, const char* name)
{ {
std::unique_ptr<boo::IMIDIIn> inPort; std::unique_ptr<IMIDIReader> ret = std::make_unique<BooBackendMIDIReader>(engine, name);
if (!name) if (!static_cast<BooBackendMIDIReader&>(*ret).m_midiIn)
inPort = m_booEngine.newVirtualMIDIIn();
else
inPort = m_booEngine.newRealMIDIIn(name);
if (!inPort)
return {}; return {};
return ret;
return std::make_unique<BooBackendMIDIReader>(engine, std::move(inPort));
} }
void BooBackendVoiceAllocator::register5MsCallback(std::function<void(double)>&& callback) void BooBackendVoiceAllocator::register5MsCallback(std::function<void(double)>&& callback)

View File

@ -158,7 +158,7 @@ void Engine::_bringOutYourDead()
void Engine::_5MsCallback(double dt) void Engine::_5MsCallback(double dt)
{ {
if (m_midiReader) if (m_midiReader)
m_midiReader->pumpReader(); m_midiReader->pumpReader(dt);
for (std::shared_ptr<Sequencer>& seq : m_activeSequencers) for (std::shared_ptr<Sequencer>& seq : m_activeSequencers)
seq->advance(dt); seq->advance(dt);
} }