#include "amuse/BooBackend.hpp" #include "amuse/Voice.hpp" #include "amuse/Submix.hpp" #include "amuse/Engine.hpp" namespace amuse { size_t BooBackendVoice::VoiceCallback::supplyAudio(boo::IAudioVoice&, size_t frames, int16_t* data) { return m_parent.m_clientVox.supplyAudio(frames, data); } BooBackendVoice::BooBackendVoice(boo::IAudioVoiceEngine& engine, Voice& clientVox, double sampleRate, bool dynamicPitch) : m_clientVox(clientVox), m_cb(*this), m_booVoice(engine.allocateNewMonoVoice(sampleRate, &m_cb, dynamicPitch)) {} BooBackendVoice::BooBackendVoice(boo::IAudioSubmix& submix, Voice& clientVox, double sampleRate, bool dynamicPitch) : m_clientVox(clientVox), m_cb(*this), m_booVoice(submix.allocateNewMonoVoice(sampleRate, &m_cb, dynamicPitch)) {} void BooBackendVoice::resetSampleRate(double sampleRate) { m_booVoice->resetSampleRate(sampleRate); } void BooBackendVoice::setMatrixCoefficients(const float coefs[8]) { m_booVoice->setMonoMatrixCoefficients(coefs); } void BooBackendVoice::setPitchRatio(double ratio, bool slew) { m_booVoice->setPitchRatio(ratio, slew); } void BooBackendVoice::start() { m_booVoice->start(); } void BooBackendVoice::stop() { m_booVoice->stop(); } bool BooBackendSubmix::SubmixCallback::canApplyEffect() const { return m_parent.m_clientSmx.canApplyEffect(); } void BooBackendSubmix::SubmixCallback::applyEffect(int16_t* audio, size_t frameCount, const boo::ChannelMap& chanMap, double) const { return m_parent.m_clientSmx.applyEffect(audio, frameCount, reinterpret_cast(chanMap)); } void BooBackendSubmix::SubmixCallback::applyEffect(int32_t* audio, size_t frameCount, const boo::ChannelMap& chanMap, double) const { return m_parent.m_clientSmx.applyEffect(audio, frameCount, reinterpret_cast(chanMap)); } void BooBackendSubmix::SubmixCallback::applyEffect(float* audio, size_t frameCount, const boo::ChannelMap& chanMap, double) const { return m_parent.m_clientSmx.applyEffect(audio, frameCount, reinterpret_cast(chanMap)); } BooBackendSubmix::BooBackendSubmix(boo::IAudioVoiceEngine& engine, Submix& clientSmx) : m_clientSmx(clientSmx), m_cb(*this), m_booSubmix(engine.allocateNewSubmix(&m_cb)) {} BooBackendSubmix::BooBackendSubmix(boo::IAudioSubmix& parent, Submix& clientSmx) : m_clientSmx(clientSmx), m_cb(*this), m_booSubmix(parent.allocateNewSubmix(&m_cb)) {} void BooBackendSubmix::setChannelGains(const float gains[8]) { m_booSubmix->setChannelGains(gains); } std::unique_ptr BooBackendSubmix::allocateVoice(Voice& clientVox, double sampleRate, bool dynamicPitch) { return std::make_unique(*m_booSubmix, clientVox, sampleRate, dynamicPitch); } double BooBackendSubmix::getSampleRate() const { return m_booSubmix->getSampleRate(); } SubmixFormat BooBackendSubmix::getSampleFormat() const { return SubmixFormat(m_booSubmix->getSampleFormat()); } std::string BooBackendMIDIReader::description() { return m_midiIn->description(); } BooBackendMIDIReader::~BooBackendMIDIReader() {} BooBackendMIDIReader::BooBackendMIDIReader(Engine& engine, const char* name) : m_engine(engine), m_decoder(*this) { BooBackendVoiceAllocator& voxAlloc = static_cast(engine.getBackend()); if (!name) { auto devices = voxAlloc.m_booEngine.enumerateMIDIDevices(); for (const auto& dev : devices) { m_midiIn = voxAlloc.m_booEngine.newRealMIDIIn(dev.first.c_str(), std::bind(&BooBackendMIDIReader::_MIDIReceive, this, std::placeholders::_1)); if (m_midiIn) return; } 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&& bytes) { std::unique_lock 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 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 (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) { for (std::shared_ptr& seq : m_engine.getActiveSequencers()) seq->keyOff(chan, key, velocity); } void BooBackendMIDIReader::noteOn(uint8_t chan, uint8_t key, uint8_t velocity) { for (std::shared_ptr& seq : m_engine.getActiveSequencers()) seq->keyOn(chan, key, velocity); } void BooBackendMIDIReader::notePressure(uint8_t /*chan*/, uint8_t /*key*/, uint8_t /*pressure*/) { } void BooBackendMIDIReader::controlChange(uint8_t chan, uint8_t control, uint8_t value) { for (std::shared_ptr& seq : m_engine.getActiveSequencers()) seq->setCtrlValue(chan, control, value); } void BooBackendMIDIReader::programChange(uint8_t chan, uint8_t program) { for (std::shared_ptr& seq : m_engine.getActiveSequencers()) seq->setChanProgram(chan, program); } void BooBackendMIDIReader::channelPressure(uint8_t /*chan*/, uint8_t /*pressure*/) { } void BooBackendMIDIReader::pitchBend(uint8_t chan, int16_t pitch) { for (std::shared_ptr& seq : m_engine.getActiveSequencers()) seq->setPitchWheel(chan, (pitch - 0x2000) / float(0x2000)); } void BooBackendMIDIReader::allSoundOff(uint8_t chan) { for (std::shared_ptr& seq : m_engine.getActiveSequencers()) seq->allOff(chan, true); } void BooBackendMIDIReader::resetAllControllers(uint8_t /*chan*/) { } void BooBackendMIDIReader::localControl(uint8_t /*chan*/, bool /*on*/) { } void BooBackendMIDIReader::allNotesOff(uint8_t chan) { for (std::shared_ptr& seq : m_engine.getActiveSequencers()) seq->allOff(chan, false); } void BooBackendMIDIReader::omniMode(uint8_t /*chan*/, bool /*on*/) { } void BooBackendMIDIReader::polyMode(uint8_t /*chan*/, bool /*on*/) { } void BooBackendMIDIReader::sysex(const void* /*data*/, size_t /*len*/) { } void BooBackendMIDIReader::timeCodeQuarterFrame(uint8_t /*message*/, uint8_t /*value*/) { } void BooBackendMIDIReader::songPositionPointer(uint16_t /*pointer*/) { } void BooBackendMIDIReader::songSelect(uint8_t /*song*/) { } void BooBackendMIDIReader::tuneRequest() { } void BooBackendMIDIReader::startSeq() { } void BooBackendMIDIReader::continueSeq() { } void BooBackendMIDIReader::stopSeq() { } void BooBackendMIDIReader::reset() { } BooBackendVoiceAllocator::BooBackendVoiceAllocator(boo::IAudioVoiceEngine& booEngine) : m_booEngine(booEngine) {} std::unique_ptr BooBackendVoiceAllocator::allocateVoice(Voice& clientVox, double sampleRate, bool dynamicPitch) { return std::make_unique(m_booEngine, clientVox, sampleRate, dynamicPitch); } std::unique_ptr BooBackendVoiceAllocator::allocateSubmix(Submix& clientSmx) { return std::make_unique(m_booEngine, clientSmx); } std::vector> BooBackendVoiceAllocator::enumerateMIDIDevices() { return m_booEngine.enumerateMIDIDevices(); } std::unique_ptr BooBackendVoiceAllocator::allocateMIDIReader(Engine& engine, const char* name) { std::unique_ptr ret = std::make_unique(engine, name); if (!static_cast(*ret).m_midiIn) return {}; return ret; } void BooBackendVoiceAllocator::register5MsCallback(std::function&& callback) { m_booEngine.register5MsCallback(std::move(callback)); } AudioChannelSet BooBackendVoiceAllocator::getAvailableSet() { return AudioChannelSet(m_booEngine.getAvailableSet()); } void BooBackendVoiceAllocator::pumpAndMixVoices() { m_booEngine.pumpAndMixVoices(); } }