void MidiSelectProcessor::processBlock (AudioSampleBuffer &buffer, MidiBuffer &midiMessages) { // Do nothing to buffer; just let it pass through MidiBuffer inputMessages(midiMessages); midiMessages.clear(); MidiBuffer::Iterator it(inputMessages); while (true) { MidiMessage message(0xf0); int samplePosition; if (!it.getNextEvent(message, samplePosition)) { break; } if (message.isNoteOn()) { if (findNextNote()) { // We know it's safe to add to the list if findNextNote() returns true transformations[message.getNoteNumber()] = currentNote; message.setNoteNumber(currentNote); midiMessages.addEvent(message, samplePosition); } else { // This will just get skipped, but we must make note of that to also skip // the upcomming note off event transformations[message.getNoteNumber()] = -1; } } else if (message.isNoteOff()) { auto transformIt = transformations.find(message.getNoteNumber()); if (transformIt == transformations.end()) { // I have no recollection of this note continue; } if (transformIt->second == -1) { // We discarded the note on, discard the note off too transformations.erase(transformIt); continue; } // Okay, make the note off match the note on, then add message.setNoteNumber(transformIt->second); midiMessages.addEvent(message, samplePosition); transformations.erase(transformIt); } else { // We don't mess with other events (yet), so pass on through midiMessages.addEvent(message, samplePosition); } } }
void GenericProcessor::addEvent(MidiBuffer& eventBuffer, uint8 type, int sampleNum, uint8 eventId, uint8 eventChannel, uint8 numBytes, uint8* eventData) { uint8 *data = new uint8[4+numBytes]; data[0] = type; // event type data[1] = nodeId; // processor ID automatically added data[2] = eventId; // event ID data[3] = eventChannel; // event channel memcpy(&data[4], eventData, numBytes); eventBuffer.addEvent(data, // spike data sizeof(data), // total bytes sampleNum); // sample index //if (type == TTL) // std::cout << "Adding event for channel " << (int) eventChannel << " with ID " << (int) eventId << std::endl; delete data; }
void MidiKeyboardState::processNextMidiBuffer (MidiBuffer& buffer, const int startSample, const int numSamples, const bool injectIndirectEvents) { MidiBuffer::Iterator i (buffer); MidiMessage message; int time; const ScopedLock sl (lock); while (i.getNextEvent (message, time)) processNextMidiEvent (message); if (injectIndirectEvents) { MidiBuffer::Iterator i2 (eventsToAdd); const int firstEventToAdd = eventsToAdd.getFirstEventTime(); const double scaleFactor = numSamples / (double) (eventsToAdd.getLastEventTime() + 1 - firstEventToAdd); while (i2.getNextEvent (message, time)) { const int pos = jlimit (0, numSamples - 1, roundToInt ((time - firstEventToAdd) * scaleFactor)); buffer.addEvent (message, startSample + pos); } } eventsToAdd.clear(); }
void addCtrlrMidiMessageToBuffer (MidiBuffer &bufferToAddTo, CtrlrMidiMessage &m) { for (int i=0; i<m.getNumMessages(); i++) { bufferToAddTo.addEvent (m.getReference(i).m, 1); } }
void SoftSynthAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) { MidiBuffer processedMidi; int time; MidiMessage m; for (MidiBuffer::Iterator i(midiMessages); i.getNextEvent(m, time);) { if (m.isNoteOn()) { m = MidiMessage::noteOn(m.getChannel(), m.getNoteNumber(), m.getVelocity()); synth.keyPressed(m.getNoteNumber(), m.getVelocity()); } else if (m.isNoteOff()) { m = MidiMessage::noteOff(m.getChannel(), m.getNoteNumber(), m.getVelocity()); synth.keyReleased(m.getNoteNumber()); } processedMidi.addEvent(m, time); } auto synthBuffer = synth.getNextBuffer(buffer.getNumSamples()); float *leftData = buffer.getWritePointer(0); float *rightData = buffer.getWritePointer(1); for (int i = 0; i < buffer.getNumSamples(); ++i) { leftData[i] = synthBuffer[i]; rightData[i] = synthBuffer[i]; } midiMessages.swapWith(processedMidi); }
//============================================================================== void MidiManipulator::processEvents (MidiBuffer& midiMessages, const int blockSize) { MidiBuffer midiOutput; if (! midiMessages.isEmpty ()) { int timeStamp; MidiMessage message (0xf4, 0.0); MidiBuffer::Iterator it (midiMessages); if (filter) { while (it.getNextEvent (message, timeStamp)) { if (filter->filterEvent (message)) midiOutput.addEvent (message, timeStamp); } } else { midiOutput = midiMessages; } midiMessages.clear (); } if (transform) { transform->processEvents (midiOutput, blockSize); } midiMessages = midiOutput; }
void CfpluginAudioProcessor::MidiPanic(MidiBuffer& midiMessages) { for(int i = 1; i <= 16; ++i) { midiMessages.addEvent(MidiMessage::allNotesOff(i), 0); midiMessages.addEvent(MidiMessage::allSoundOff(i), 0); } }
//============================================================================== void BeatboxVoxAudioProcessor::processBlock(AudioSampleBuffer& buffer, MidiBuffer& midiMessages) { const auto totalNumInputChannels = getTotalNumInputChannels(); const auto totalNumOutputChannels = getTotalNumOutputChannels(); const auto sampleRate = getSampleRate(); const auto numSamples = buffer.getNumSamples(); //Reset the noise synth if triggered midiMessages.addEvent(MidiMessage::noteOff(1, noiseNoteNumber), 0); classifier.processAudioBuffer(buffer.getReadPointer(0), numSamples); //This is used for configuring the onset detector settings from the GUI if (classifier.noteOnsetDetected()) { if (usingOSDTestSound.load()) { triggerOSDTestSound(midiMessages); } else if (classifier.getNumBuffersDelayed() > 0) { triggerNoise(midiMessages); } } const auto sound = classifier.classify(); switch (sound) { case soundLabel::KickDrum: triggerKickDrum(midiMessages); break; case soundLabel::SnareDrum: triggerSnareDrum(midiMessages); break; case soundLabel::HiHat: triggerHiHat(midiMessages); break; default: break; } /** Now classification complete clear the input buffer/signal. * We only want synth response output, no a blend of input vocal * signal + synth output. **/ buffer.clear(); if (usingOSDTestSound.load()) osdTestSynth.renderNextBlock(buffer, midiMessages, 0, buffer.getNumSamples()); else drumSynth.renderNextBlock(buffer, midiMessages, 0, buffer.getNumSamples()); //Not outputting midi so clear after rendering synths midiMessages.clear(); }
/** * Send midi note off message into buffer at given sample position. */ void StepSequencer::sendMidiNoteOffMessage(MidiBuffer& midiMessages, int sample) { if (currStepOnOff[static_cast<int>(params.seqLastPlayedStep.get())]->getStep() == eOnOffToggle::eOn) { MidiMessage m = MidiMessage::noteOff(1, lastPlayedNote); midiMessages.addEvent(m, sample); } seqNoteIsPlaying = false; }
void Loop::sendCurrentNoteToBuffer(MidiBuffer& buffer, int sample_number, int velocity) { MidiMessage m = this->getEventPointer(currentIndex)->message; //playingNote[m.getNoteNumber()][m.getChannel()-1][0] = jlimit(0,127,m.getNoteNumber()+getTransposition()); m.setNoteNumber(jlimit(0,127,m.getNoteNumber()+getTransposition())); m.setVelocity((((float)velocity*midiScaler * velocitySensitivity) + (1.f-velocitySensitivity))); if (outChannel>0) m.setChannel(outChannel); buffer.addEvent(m,sample_number); indexOfLastNoteOn=currentIndex; }
void GenericProcessor::setNumSamples(MidiBuffer& events, int sampleIndex) { uint8 data[2]; data[0] = BUFFER_SIZE; // most-significant byte data[1] = nodeId; // least-significant byte events.addEvent(data, // spike data 2, // total bytes sampleIndex); // sample index }
void MidiMessageCollector::removeNextBlockOfMessages (MidiBuffer& destBuffer, const int numSamples) { #if JUCE_DEBUG jassert (hasCalledReset); // you need to call reset() to set the correct sample rate before using this object #endif jassert (numSamples > 0); auto timeNow = Time::getMillisecondCounterHiRes(); auto msElapsed = timeNow - lastCallbackTime; const ScopedLock sl (midiCallbackLock); lastCallbackTime = timeNow; if (! incomingMessages.isEmpty()) { int numSourceSamples = jmax (1, roundToInt (msElapsed * 0.001 * sampleRate)); int startSample = 0; int scale = 1 << 16; const uint8* midiData; int numBytes, samplePosition; MidiBuffer::Iterator iter (incomingMessages); if (numSourceSamples > numSamples) { // if our list of events is longer than the buffer we're being // asked for, scale them down to squeeze them all in.. const int maxBlockLengthToUse = numSamples << 5; if (numSourceSamples > maxBlockLengthToUse) { startSample = numSourceSamples - maxBlockLengthToUse; numSourceSamples = maxBlockLengthToUse; iter.setNextSamplePosition (startSample); } scale = (numSamples << 10) / numSourceSamples; while (iter.getNextEvent (midiData, numBytes, samplePosition)) { samplePosition = ((samplePosition - startSample) * scale) >> 10; destBuffer.addEvent (midiData, numBytes, jlimit (0, numSamples - 1, samplePosition)); } } else {
/** * Send midi note on message into buffer at given sample position. */ void StepSequencer::sendMidiNoteOnMessage(MidiBuffer & midiMessages, int sample) { if (currStepOnOff[currSeqNote]->getStep() == eOnOffToggle::eOn) { // emphasis on step 1 float seqVelocity = 0.5f; //if (seqNote == 0) //{ // seqVelocity = 0.85f; //} MidiMessage m = MidiMessage::noteOn(1, static_cast<int>(currMidiStepSeq[currSeqNote]->get()), seqVelocity); midiMessages.addEvent(m, sample); } seqNoteIsPlaying = true; params.seqLastPlayedStep.set(jmax(0.0f, jmin(static_cast<float>(currSeqNote), 7.0f))); lastPlayedNote = static_cast<int>(currMidiStepSeq[currSeqNote]->get()); }
void MetronomeProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) { const int numSamples = buffer.getNumSamples(); int channel; // convert special metronome bip and bop (0xf2 00 and 0xf2 01) into note on/off messages MidiBuffer metronomeMidiBuffer; MidiBuffer::Iterator iter(midiMessages); MidiMessage message; int pos; while (iter.getNextEvent(message,pos)) { if (message.getRawDataSize()==2 && message.isSongPositionPointer()) { char raw[4] = {0,0,0,0}; char* data = (char*)message.getRawData(); if (data[1]==0) { // bip MidiMessage b = MidiMessage::noteOn(1,80,64.0f); memcpy(raw,b.getRawData(),4); } else { // bop MidiMessage b = MidiMessage::noteOn(1,70,64.0f); memcpy(raw,b.getRawData(),4); } if (raw[0]) { MidiMessage m(raw[0],raw[1],raw[2]); //printf("m %d %d %d at %d\n",m.getRawData()[0],m.getRawData()[1],m.getRawData()[2],pos); metronomeMidiBuffer.addEvent(m,pos); } } } // and now get the synth to process these midi events and generate its output. synth.renderNextBlock (buffer, metronomeMidiBuffer, 0, numSamples); }
void Pfm2AudioProcessor::handleIncomingMidiBuffer(MidiBuffer &buffer, int numberOfSamples) { if (!buffer.isEmpty()) { MidiBuffer newBuffer; MidiMessage midiMessage; int samplePosition; MidiBuffer::Iterator midiIterator(buffer); while (midiIterator.getNextEvent(midiMessage, samplePosition)) { bool copyMessageInNewBuffer = true; if (midiMessage.isController() && midiMessage.getChannel() == currentMidiChannel) { switch (midiMessage.getControllerNumber()) { case 99: currentNrpn.paramMSB = midiMessage.getControllerValue(); copyMessageInNewBuffer = false; break; case 98: currentNrpn.paramLSB = midiMessage.getControllerValue(); copyMessageInNewBuffer = false; break; case 6: currentNrpn.valueMSB = midiMessage.getControllerValue(); copyMessageInNewBuffer = false; break; case 38: { currentNrpn.valueLSB = midiMessage.getControllerValue(); copyMessageInNewBuffer = false; int param = (int)(currentNrpn.paramMSB << 7) + currentNrpn.paramLSB; int value = (int)(currentNrpn.valueMSB << 7) + currentNrpn.valueLSB; const MessageManagerLock mmLock; handleIncomingNrpn(param, value); break; } } } if (copyMessageInNewBuffer) { newBuffer.addEvent(midiMessage, samplePosition); } } buffer.swapWith(newBuffer); } }
void getNextAudioBlock (AudioSourceChannelInfo const& bufferToFill) { int const numSamples = bufferToFill.numSamples; // the synth always adds its output //bufferToFill.clearActiveBufferRegion(); m_midi.clear (); if (m_active) { double const samplesPerBeat = m_sampleRate * 60 / m_tempo; // Adjust phase so the beat is on or after the beginning of the output double beat; if (m_phase > 0) beat = 1 - m_phase; else beat = 0 - m_phase; // Set notes in midi buffer for (;;beat += 1) { // Calc beat pos int pos = static_cast <int> (beat * samplesPerBeat); if (pos < numSamples) { m_midi.addEvent (MidiMessage::noteOn (1, 84, 1.f), pos); } else { break; } } } m_synth.renderNextBlock (*bufferToFill.buffer, m_midi, 0, bufferToFill.numSamples); }
/** * If any seqStepNote changed or is muted then send noteOff message for the old note. */ void StepSequencer::midiNoteChanged(MidiBuffer & midiMessages) { for (int i = 0; i < seqNumSteps; ++i) { // compare with current sequence if ((prevMidiStepSeq[i] != static_cast<int>(currMidiStepSeq[i]->get())) || (prevStepOnOff[i] != currStepOnOff[i]->getStep())) { if (i == static_cast<int>(params.seqLastPlayedStep.get())) { MidiMessage m = MidiMessage::noteOff(1, prevMidiStepSeq[i]); midiMessages.addEvent(m, 0); } } } // save new sequence for (int i = 0; i < 8; ++i) { prevMidiStepSeq[i] = static_cast<int>(currMidiStepSeq[i]->get()); prevStepOnOff[i] = currStepOnOff[i]->getStep(); } }
void CtrlrPanelProcessor::processBlock(MidiBuffer &midiMessages, MidiBuffer &leftoverBuffer, const AudioPlayHead::CurrentPositionInfo &info) { if (owner.getMidiOptionBool(panelMidiInputFromHostCompare)) { owner.getMIDIInputThread().handleMIDIFromHost(midiMessages); } leftoverBuffer.clear(); processLua(midiMessages, info); MidiBuffer::Iterator i(midiMessages); MidiMessage m; int time; while (i.getNextEvent(m,time)) { _MIN("VST INPUT", m, time); if (owner.getMidiOptionBool(panelMidiThruH2D) == true) { if (owner.getMidiOptionBool(panelMidiThruH2DChannelize)) { m.setChannel (owner.getMidiChannel(panelMidiOutputChannelDevice)); } owner.sendMidi(m); } if (owner.getMidiOptionBool(panelMidiThruH2H) == true) { if (owner.getMidiOptionBool(panelMidiThruH2HChannelize)) { m.setChannel (owner.getMidiChannel(panelMidiOutputChannelHost)); } leftoverBuffer.addEvent (m, time); } } }
/** * Stop stepSequencer and reset not GUI variables. */ void StepSequencer::stopSeq(MidiBuffer& midiMessages) { // stop and reset only if not already stopped if (!seqStopped) { params.seqLastPlayedStep.set(0.0f); currSeqNote = 0; lastPlayedNote = 0; seqNoteAdd = 1; nextPlaySample = 0; noteOffSample = 0; seqNextStep = 0.0; stopNoteTime = 0.0; lastPlayHeadPosition = 0.0; seqStopped = true; seqNoteIsPlaying = false; // stop all midimessages from sequencer MidiMessage m = MidiMessage::allNotesOff(1); midiMessages.addEvent(m, 0); } }
void GateTrigger::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) { float threshold = library->getThreshold(); int64 releaseTicks = library->getReleaseTicks(); float velocityScale = library->getVelocityScale(); int windowSize = buffer.getNumSamples(); for (int i = 0; i < buffer.getNumSamples(); i += windowSize) { float rms = 0; for (int chan = 0; chan < buffer.getNumChannels(); chan++) { rms += buffer.getMagnitude (chan, i, jmin(windowSize, buffer.getNumSamples() - i)); } rms = rms / buffer.getNumChannels() * 100; if (rms - lastRms > threshold) { if (Time::getHighResolutionTicks() - lastTriggerTick > releaseTicks) { Pattern* pattern = sequencer->getPattern(); Instrument* instrument = pattern->getActiveInstrument(); // play note float velocity = (rms - lastRms) / velocityScale; DBG("RMS: " + String(rms) + " lastRMS: " + String(lastRms) + " velocity: " + String(velocity)); int noteNumber = instrument->getNoteNumber(); MidiMessage m = MidiMessage::noteOn (1, noteNumber, velocity); midiMessages.addEvent (m, i); // insert into sequencer pattern int step = round(sequencer->getPreciseStep()); step = step % pattern->getNumSteps(); Cell* cell = pattern->getCellAt (0, step); delayedInserterThread->insertNote (cell, velocity, instrument); // Retrigger the reset timer resetTimer->retrigger(); lastTriggerTick = Time::getHighResolutionTicks(); } } lastRms = rms; } }
void CtrlrPanelProcessor::processBlock(MidiBuffer &midiMessages, MidiBuffer &leftoverBuffer) { if (owner.getMidiOptionBool(panelMidiInputFromHostCompare)) { owner.getMIDIInputThread().handleMIDIFromHost(midiMessages); } MidiBuffer::Iterator i(midiMessages); MidiMessage m; int time; while (i.getNextEvent(m,time)) { _MIN("VST INPUT", m); if (owner.getMidiOptionBool(panelMidiThruH2D) == true) { if (owner.getMidiOptionBool(panelMidiThruH2DChannelize)) { m.setChannel (owner.getMidiChannel(panelMidiOutputChannelDevice)); } owner.sendMidi(m); } if (owner.getMidiOptionBool(panelMidiThruH2H) == true) { if (owner.getMidiOptionBool(panelMidiThruH2HChannelize)) { m.setChannel (owner.getMidiChannel(panelMidiOutputChannelHost)); } leftoverBuffer.addEvent (m, m.getTimeStamp()); } } }
void Sequencer::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) { AudioPlayHead::CurrentPositionInfo pos (pluginAudioProcessor->lastPosInfo); // If we aren't playing... if (! pos.isPlaying) { if (noteOffs.size() > 0) { // Send any upcoming note-off events Array<NoteOff*> notesToRemove; for (int i = 0; i < noteOffs.size(); i++) { int noteNumber = noteOffs[i]->noteNumber; MidiMessage m2 = MidiMessage::noteOff (1, noteNumber); midiMessages.addEvent (m2, 0); playingNotes.set (noteNumber, false); notesToRemove.add (noteOffs[i]); } for (int i = 0; i < notesToRemove.size(); i++) { // Remove the event from the note-off event list // (We do this in two steps so that the noteOffs array isn't modified inside a loop) noteOffs.removeObject (notesToRemove[i], true); } } return; } double ppq = pos.ppqPosition; double timeInSeconds = pos.timeInSeconds; double bpm = pos.bpm; //if (primary) { SharedState::getInstance()->setPpqPosition (ppq); SharedState::getInstance()->setTimeInSeconds (timeInSeconds); SharedState::getInstance()->setBpm (bpm); //} /* int numerator = pos.timeSigNumerator; int denominator = pos.timeSigDenominator; const int ppqPerBar = (numerator * 4 / denominator); // e.g. 4 if 4/4 const double beats = (fmod (ppq, ppqPerBar) / ppqPerBar) * numerator; const int bar = ((int) ppq) / ppqPerBar + 1; const int beat = ((int) beats) + 1; const int ticks = ((int) (fmod (beats, 1.0) * 960.0)); */ double tickCountPrecise = fmod (ppq * speed * ticksPerCol, getTotalCols() * ticksPerCol); tickCount = (int)tickCountPrecise; lastPlayheadColPrecise = tickCountPrecise / ticksPerCol; jassert (lastPlayheadColPrecise >= 0.0) jassert (lastPlayheadColPrecise <= 16.0) //if (primary) { SharedState::getInstance()->setPlayheadColPrecise (lastPlayheadColPrecise); //} if (tickCount != lastTickCount) { lastTickCount = tickCount; // Check if we should be degrading... int panelIndex = pluginAudioProcessor->getPanelIndex(); int state = SharedState::getInstance()->getState(panelIndex); if (state == Panel::DEGRADING_SLOW || state == Panel::DEGRADING_FAST) { // degrade at most once per tempo sweep, in column 0 if (getPlayheadCol() == 0) { if (!columnZeroDegradeUpdate) { SharedState::getInstance()->degradeStep(panelIndex); columnZeroDegradeUpdate = true; } } else { columnZeroDegradeUpdate = false; } } else { columnZeroDegradeUpdate = false; if (state == Panel::ACTIVE) { if (SharedState::kDegradeAfterInactiveSec > 0 && (SharedState::getInstance()->getLastTouchElapsedMs(panelIndex) >= SharedState::kDegradeAfterInactiveSec * 1000)) { DBG(String(Time::currentTimeMillis()) + " " + "Start degrading panel " + String(panelIndex)); SharedState::getInstance()->startDegrade(panelIndex); } } } // Update starfield if necessary if (SharedState::getInstance()->getStarFieldActive()) { // bug:67 - for production running, we could assert that the following is true: // SharedState::getInstance()->allAttracting() // but that won't work if someone turns on the star field by hand via the sequencer GUI if (pluginAudioProcessor->getPanelIndex() == 0) { SharedState::getInstance()->updateStarField(); } } bool playCol = false; // play the current column of notes? float velocity = 0.9f; // Swing... // If we're on an odd column if (getPlayheadCol() % 2 != 0) { // If we've waited for enough ticks for the swing if (tickCount == (getPlayheadCol() * ticksPerCol) + swingTicks) { playCol = true; } } else { // Else we're on an even column // If we're on the first tick of the column... if (tickCount % ticksPerCol == 0) { playCol = true; } } // Calculate the latency double beatsPerSec = bpm * speed * ticksPerCol / 60.0; double secPerBeat = 1.0 / beatsPerSec; double tickOffset = tickCountPrecise - tickCount; int tickOffsetSamples = tickOffset * secPerBeat * sampleRate; tickOffsetSamples = jmax (buffer.getNumSamples() - tickOffsetSamples - 1, 0); // Send any upcoming note-off events Array<NoteOff*> notesToRemove; for (int i = 0; i < noteOffs.size(); i++) { if (noteOffs[i]->tick == tickCount) { int noteNumber = noteOffs[i]->noteNumber; MidiMessage m2 = MidiMessage::noteOff (1, noteNumber); midiMessages.addEvent (m2, tickOffsetSamples); playingNotes.set (noteNumber, false); notesToRemove.add (noteOffs[i]); } } for (int i = 0; i < notesToRemove.size(); i++) { // Remove the event from the note-off event list // (We do this in two steps so that the noteOffs array isn't modified inside a loop) noteOffs.removeObject (notesToRemove[i], true); } // If we should play the current column of notes if (playCol) { int panelIndex = pluginAudioProcessor->getPanelIndex(); int tabIndex = pluginAudioProcessor->getTabIndex(); for (int i = 0; i < getTotalRows(); i++) { Cell* cell = getCellAt (panelIndex, tabIndex, i, getPlayheadCol()); if (cell->isOn()) { int noteNumber = cell->getNoteNumber(); // If this note is currently playing if (playingNotes[noteNumber] == true) { Array<NoteOff*> notesToRemove; // Remove any pending note-offs for (int j = 0; j < noteOffs.size(); j++) { if (noteOffs[j]->noteNumber == noteNumber) { notesToRemove.add (noteOffs[j]); } } for (int j = 0; j < notesToRemove.size(); j++) { noteOffs.removeObject (notesToRemove[j], true); } // Send a note-off before retriggering MidiMessage m = MidiMessage::noteOff (1, noteNumber); midiMessages.addEvent (m, tickOffsetSamples); playingNotes.set (noteNumber, false); } // Play this note MidiMessage m = MidiMessage::noteOn (1, noteNumber, velocity); midiMessages.addEvent (m, tickOffsetSamples); playingNotes.set (noteNumber, true); // Add an upcoming note-off event NoteOff* no; noteOffs.add (no = new NoteOff()); no->tick = (tickCount + noteLength) % (ticksPerCol * getTotalCols()); no->noteNumber = noteNumber; } } } } }
//============================================================================== void MidiSequencePlugin::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) { MidiSequencePluginBase::processBlock(buffer, midiMessages); MidiMessageSequence sourceMidi = *midiSequence; std::vector<int> doneTheseControllers; if (transport->isPlaying () && getBoolValue(PROP_SEQENABLED, true)) { const int blockSize = buffer.getNumSamples (); MidiBuffer* midiBuffer = midiBuffers.getUnchecked (0); const int frameCounter = transport->getPositionInFrames (); const int framesPerBeat = transport->getFramesPerBeat (); const int nextBlockFrameNumber = frameCounter + blockSize; const int seqIndex = getLoopRepeatIndex(); const double beatCount = getLoopBeatPosition(); const double frameLenBeatCount = (nextBlockFrameNumber - frameCounter) / (double)framesPerBeat; double frameEndBeatCount = beatCount + frameLenBeatCount; if (frameEndBeatCount > getLengthInBeats()) frameEndBeatCount -= getLengthInBeats(); // loop for each controller we need to interpolate MidiMessage* lastCtrlEvent = NULL; do { lastCtrlEvent = NULL; // hunt for a controller event before now int i; for (i = 0; i < sourceMidi.getNumEvents (); i++) { int timeStampInSeq = roundFloatToInt (sourceMidi.getEventTime (i) * framesPerBeat); int timeStamp = timeStampInSeq + (seqIndex * getLengthInBeats() * framesPerBeat); MidiMessage* midiMessage = &sourceMidi.getEventPointer (i)->message; if (timeStamp >= nextBlockFrameNumber || !midiMessage) break; // event is after now, leave //if (midiMessage->isController() && (std::find(doneTheseControllers.begin(), doneTheseControllers.end(), midiMessage->getControllerNumber()) == doneTheseControllers.end())) // lastCtrlEvent = midiMessage; } // hunt for a matching event after that one if (lastCtrlEvent) { // store the controller number so we know which controllers we've done doneTheseControllers.push_back(lastCtrlEvent->getControllerNumber()); MidiMessage* nextCtrlEvent = NULL; for (; i < sourceMidi.getNumEvents (); i++) { MidiMessage* midiMessage = &sourceMidi.getEventPointer (i)->message; if (midiMessage->isController() && midiMessage->getControllerNumber() == lastCtrlEvent->getControllerNumber()) { nextCtrlEvent = midiMessage; break; } } // render an interpolated event!... if (nextCtrlEvent) { double bt = nextCtrlEvent->getTimeStamp(); double at = lastCtrlEvent->getTimeStamp(); double deltaBeats = bt - at; int a = lastCtrlEvent->getControllerValue(); int b = nextCtrlEvent->getControllerValue(); double now = beatCount + (frameEndBeatCount - beatCount) / 2.0; double interpRemainBeats = deltaBeats - (now - at); if (deltaBeats > 0) { double nextPart = interpRemainBeats / deltaBeats; nextPart = 1 - nextPart; double interpdVal = a + nextPart * (b - a); MidiMessage interpy = MidiMessage::controllerEvent(lastCtrlEvent->getChannel(), lastCtrlEvent->getControllerNumber(), static_cast<int>(interpdVal)); midiBuffer->addEvent (interpy, (nextBlockFrameNumber - frameCounter) / 2); } else { DBG ("Negative delta beats when rendering automation!!"); } } } // now we also need to do that again if there are multiple events per frame AND we are interpolating multiple times per frame // (at the moment only interpolating once per audio frame) } while (lastCtrlEvent != NULL); } }
virtual void menuItemSelected(int menuItemID, int) { if (menuItemID == 200) { WildcardFileFilter wildcardFilter("*.mid", String::empty, "Midi files"); FileBrowserComponent browser(FileBrowserComponent::canSelectFiles | FileBrowserComponent::openMode, lastOpenedFile.exists() ? lastOpenedFile : File(String("C:\\Users\\GeorgeKrueger\\Documents")), &wildcardFilter, nullptr); FileChooserDialogBox dialogBox("Open a midi file", "Please choose a midi file to open...", browser, false, Colours::lightgrey); if (dialogBox.show()) { File selectedFile = browser.getSelectedFile(0); lastOpenedFile = selectedFile; FileInputStream fileStream(selectedFile); juce::MidiFile midiFile; midiFile.readFrom(fileStream); int numTracks = midiFile.getNumTracks(); midiFile.convertTimestampTicksToSeconds(); String msg; msg << "Opened midi file: " << selectedFile.getFileName() << " Tracks: " << numTracks << "\n"; log(msg); for (int i = 0; i < numTracks; ++i) { const MidiMessageSequence* msgSeq = midiFile.getTrack(i); OwnedArray<PluginDescription> results; String plugFile = "C:\\VST\\FMMF.dll"; VSTPluginFormat vstFormat; vstFormat.findAllTypesForFile(results, plugFile); if (results.size() > 0) { msg.clear(); msg << "Found " << results.size() << " plugin(s) matching file " << plugFile << "\n"; log(msg); int secsToRender = 10; double sampleRate = 44100; int totalSizeInSamples = static_cast<int>(44100 * secsToRender); AudioPluginInstance* plugInst = vstFormat.createInstanceFromDescription(*results[0], sampleRate, totalSizeInSamples); if (!plugInst) { msg.clear(); msg << "Failed to load plugin " << plugFile << "\n"; log(msg); continue; } int numInputChannels = plugInst->getTotalNumInputChannels(); int numOutputChannels = plugInst->getTotalNumOutputChannels(); msg.clear(); msg << "Plugin input channels: " << numInputChannels << " output channels: " << numOutputChannels << " Current program: " << plugInst->getCurrentProgram() << "\n"; log(msg); int maxChannels = std::max(numInputChannels, numOutputChannels); AudioBuffer<float> buffer(maxChannels, totalSizeInSamples); MidiBuffer midiMessages; for (int j = 0; j < msgSeq->getNumEvents(); ++j) { MidiMessageSequence::MidiEventHolder* midiEventHolder = msgSeq->getEventPointer(j); MidiMessage midiMsg = midiEventHolder->message; int samplePos = static_cast<int>(midiMsg.getTimeStamp() * sampleRate); midiMessages.addEvent(midiMsg, samplePos); } plugInst->prepareToPlay(sampleRate, totalSizeInSamples); plugInst->processBlock(buffer, midiMessages); /*File txtOutFile("C:\\Users\\GeorgeKrueger\\Documents\\GitHub\\soundserver2\\out.txt"); FileOutputStream* txtOutStream = txtOutFile.createOutputStream(); for (int j = 0; j < 44100; ++j) { float sample = buffer.getSample(0, j); txtOutStream->writeFloat(sample); txtOutStream->writeText(" ", true, false); }*/ File outputFile("C:\\Users\\GeorgeKrueger\\Documents\\GitHub\\soundserver2\\out.wav"); if (outputFile.exists()) { outputFile.deleteFile(); } FileOutputStream* fileOutputStream = outputFile.createOutputStream(); WavAudioFormat wavFormat; StringPairArray metadataValues; juce::AudioFormatWriter* wavFormatWriter = wavFormat.createWriterFor( fileOutputStream, sampleRate, 2, 16, metadataValues, 0); bool writeAudioDataRet = wavFormatWriter->writeFromAudioSampleBuffer(buffer, 0, buffer.getNumSamples()); wavFormatWriter->flush(); msg.clear(); msg << "Done writing to output file " << outputFile.getFileName() << " . Write return value: " << (int)writeAudioDataRet << "\n"; log(msg); delete wavFormatWriter; delete plugInst; } else { msg.clear(); msg << "Could not find plugin from file " << plugFile << "\n"; log(msg); } } } } }
//============================================================================== void MidiTransform::processEvents (MidiBuffer& midiMessages, const int blockSize) { int timeStamp; MidiMessage message (0xf4, 0.0); MidiBuffer::Iterator it (midiMessages); MidiBuffer midiOutput; switch (command) { case MidiTransform::KeepEvents: break; case MidiTransform::DiscardEvents: { midiMessages.clear (); break; } case MidiTransform::RemapChannel: { while (it.getNextEvent (message, timeStamp)) { message.setChannel (channelNumber); midiOutput.addEvent (message, timeStamp); } midiMessages = midiOutput; break; } case MidiTransform::ScaleNotes: { while (it.getNextEvent (message, timeStamp)) { if (message.isNoteOnOrOff ()) { message.setNoteNumber (roundFloatToInt (message.getNoteNumber () * noteScale)); midiOutput.addEvent (message, timeStamp); } } midiMessages = midiOutput; break; } case MidiTransform::InvertNotes: { while (it.getNextEvent (message, timeStamp)) { if (message.isNoteOnOrOff ()) { message.setNoteNumber (127 - message.getNoteNumber ()); midiOutput.addEvent (message, timeStamp); } } midiMessages = midiOutput; } case MidiTransform::TransposeNotes: { while (it.getNextEvent (message, timeStamp)) { if (message.isNoteOnOrOff ()) { message.setNoteNumber (jmax (0, jmin (127, message.getNoteNumber () - noteTranspose))); midiOutput.addEvent (message, timeStamp); } } midiMessages = midiOutput; break; } case MidiTransform::ScaleVelocity: { while (it.getNextEvent (message, timeStamp)) { if (message.isNoteOn ()) { message.setVelocity ((message.getVelocity () / 127.0f) * velocityScale); midiOutput.addEvent (message, timeStamp); } } midiMessages = midiOutput; break; } case MidiTransform::InvertVelocity: { while (it.getNextEvent (message, timeStamp)) { if (message.isNoteOn ()) { message.setVelocity ((uint8) (127 - message.getVelocity ())); midiOutput.addEvent (message, timeStamp); } } midiMessages = midiOutput; break; } case MidiTransform::TransposeVelocity: { while (it.getNextEvent (message, timeStamp)) { if (message.isNoteOn ()) { message.setVelocity (jmax (0, jmin (127, message.getVelocity () - velocityTranspose))); midiOutput.addEvent (message, timeStamp); } } midiMessages = midiOutput; break; } case MidiTransform::TriggerCC: { break; } case MidiTransform::TriggerNote: { break; } } }
bool handlePluginRequest(const PluginRequestParameters ¶ms, OutputStream &ostream, ThreadSafePlugin *plugin = nullptr) { if (!plugin) { // It's very possible that all of this was a premature optimization. // For VSTs at least, code loading and caching is handled by ModuleHandle::findOrCreateModule, // and each instantiation only requires a couple of disc hits for working directory setting. // On the other hand, we want to make sure that each audio request has a "fresh" instance. // The easiest way to do this is by bypassing the instance pool and instantiating on demand. #if PLUGIN_POOL_SIZE // Recurse with a plugin from the pool, locking on it. // Keep trying with a delay until a timeout occurs. const int TIMEOUT = 5000, WAIT = 200; int64 startTime = Time::currentTimeMillis(); while (Time::currentTimeMillis() < startTime + TIMEOUT) { int i = 0; while ((plugin = pluginPool[i++])) { const ScopedTryLock pluginTryLock(plugin->crit); if (pluginTryLock.isLocked()) { DBG << "Handling with plugin " << i << endl; return handlePluginRequest(params, ostream, plugin); } } DBG << "Trying again in " << WAIT << endl; Thread::sleep(WAIT); } // If we were unable to obtain a lock, return failure. DBG << "Timeout" << endl; return false; #else ThreadSafePlugin temporaryPlugin(createSynthInstance()); return handlePluginRequest(params, ostream, &temporaryPlugin); #endif } else { // Re-acquire or acquire the lock. const ScopedLock pluginLock(plugin->crit); AudioPluginInstance *instance = plugin->instance; // unmanaged, for simplicity // Attempt to reset the plugin in all ways possible. instance->reset(); // Setting default parameters here causes miniTERA to become unresponsive to parameter settings. // It's possible that it's effectively pressing some interface buttons that change the editor mode entirely. // It's not necessary anyways if the plugin instance has been freshly created (see above). // pluginParametersOldNewFallback(instance, nullptr, &pluginDefaults); // note that the defaults may be empty instance->setCurrentProgram(0); // Load preset if specified, before listing or modifying parameters! if (params.presetNumber >= 0 && params.presetNumber < instance->getNumPrograms()) { DBG << "Setting program/preset: " << params.presetNumber << endl; instance->setCurrentProgram(params.presetNumber); } int currentProgram = instance->getCurrentProgram(); DBG << "Current program/preset: " << currentProgram << " - " << instance->getProgramName(currentProgram) << endl; // Set parameters, starting with named, then indexed pluginParametersSet(instance, params.parameters); pluginParametersSetIndexed(instance, params.indexedParameters); // If parameters requested, output them and return if (params.listParameters) { DBG << "Rendering parameter list: # parameters " << instance->getNumPrograms() << endl; // Output each parameter setting in two places: // an indexed array and a dictionary by name // All DynamicObjects created will be freed when their var's leave scope. DynamicObject *outer = new DynamicObject(); DynamicObject *innerParams = new DynamicObject(); var indexedParamVar; { for (int i = 0, n = instance->getNumParameters(); i < n; ++i) { String name = instance->getParameterName(i); float val = instance->getParameter(i); innerParams->setProperty(name, val); DynamicObject *indexedInnerObj = new DynamicObject(); indexedInnerObj->setProperty("index", i); indexedInnerObj->setProperty("name", name); indexedInnerObj->setProperty("value", val); indexedParamVar.append(var(indexedInnerObj)); // frees indexedInnerObj when this scope ends } } outer->setProperty(Identifier("parameters"), var(innerParams)); outer->setProperty(Identifier("indexedParameters"), indexedParamVar); // List presets/programs. var progVar; { for (int i = 0, n = instance->getNumPrograms(); i < n; ++i) { progVar.append(var(instance->getProgramName(i))); } } outer->setProperty(Identifier("presets"), progVar); var outerVar(outer); JSON::writeToStream(ostream, outerVar); // DBG << JSON::toString(outerVar, true /* allOnOneLine */) << endl; return true; } // Now attempt to render audio. AudioFormatManager formatManager; formatManager.registerBasicFormats(); OptionalScopedPointer<AudioFormat> outputFormat(formatManager.findFormatForFileExtension(params.getFormatName()), false); if (!outputFormat) return false; instance->setNonRealtime(true); instance->prepareToPlay(params.sampleRate, params.blockSize); instance->setNonRealtime(true); // The writer takes ownership of the output stream; the writer will delete it when the writer leaves scope. // Therefore, we pass a special pointer class that does not allow the writer to delete it. OutputStream *ostreamNonDeleting = new NonDeletingOutputStream(&ostream); ScopedPointer<AudioFormatWriter> writer(outputFormat->createWriterFor(ostreamNonDeleting, params.sampleRate, params.nChannels, params.bitDepth, StringPairArray(), 0)); // Create a MIDI buffer MidiBuffer midiBuffer; midiBuffer.addEvent(MidiMessage::noteOn(params.midiChannel, (uint8)params.midiPitch, (uint8)params.midiVelocity), 0 /* time */); midiBuffer.addEvent(MidiMessage::allNotesOff(params.midiChannel), params.noteSeconds * params.sampleRate); AudioSampleBuffer buffer(params.nChannels, params.blockSize); int numBuffers = (int)(params.renderSeconds * params.sampleRate / params.blockSize); for (int i = 0; i < numBuffers; ++i) { // DBG << "Processing block " << i << "..." << flush; instance->processBlock(buffer, midiBuffer); // DBG << " left RMS level " << buffer.getRMSLevel(0, 0, params.blockSize) << endl; writer->writeFromAudioSampleBuffer(buffer, 0 /* offset into buffer */, params.blockSize); } instance->reset(); return true; } }
void MidiOutFilter::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) { for (int i = 0; i < getNumOutputChannels(); ++i) { buffer.clear (i, 0, buffer.getNumSamples()); } const double SR=getSampleRate(); const double iSR=1.0/SR; AudioPlayHead::CurrentPositionInfo pos; if (getPlayHead() != 0 && getPlayHead()->getCurrentPosition (pos)) { if (memcmp (&pos, &lastPosInfo, sizeof (pos)) != 0) { if(param[kMTC]>=0.5f) { double frameRate=24.0; int mtcFrameRate=0; const double samplesPerPpq=60.0*SR/pos.bpm; const double samplesPerClock = SR/(4.0*frameRate); const long double seconds = (long double)(pos.ppqPosition*60.0f/pos.bpm) /*+ smpteOffset*/; const long double absSecs = fabs (seconds); const bool neg = seconds < 0.0; int hours, mins, secs, frames; if (frameRate==29.97) { int64 frameNumber = int64(absSecs*29.97); frameNumber += 18*(frameNumber/17982) + 2*(((frameNumber%17982) - 2) / 1798); hours = int((((frameNumber / 30) / 60) / 60) % 24); mins = int(((frameNumber / 30) / 60) % 60); secs = int((frameNumber / 30) % 60); frames = int(frameNumber % 30); } else { hours = (int) (absSecs / (60.0 * 60.0)); mins = ((int) (absSecs / 60.0)) % 60; secs = ((int) absSecs) % 60; frames = (int)(int64(absSecs*frameRate) % (int)frameRate); } if (pos.isPlaying) { double i=0.0; const double clockppq = fmod(absSecs*frameRate*4.0,(long double)1.0); samplesToNextMTC = (int)(samplesPerClock * (clockppq+i)); i+=1.0; if (!wasPlaying) { //this is so the song position pointer will be sent before any //other data at the beginning of the song MidiBuffer temp = midiMessages; midiMessages.clear(); if (samplesToNextMTC<buffer.getNumSamples()) { int mtcData; switch (mtcNumber) { case 0: mtcData=frames&0x0f; break; case 1: mtcData=(frames&0xf0)>>4; break; case 2: mtcData=secs&0x0f; break; case 3: mtcData=(secs&0xf0)>>4; break; case 4: mtcData=mins&0x0f; break; case 5: mtcData=(mins&0xf0)>>4; break; case 6: mtcData=hours&0x0f; break; case 7: mtcData=(hours&0x10)>>4 | mtcFrameRate; break; } MidiMessage midiclock(0xf1,(mtcNumber<<4)|(mtcData)); ++mtcNumber; mtcNumber&=0x07; midiMessages.addEvent(midiclock,samplesToNextMTC); samplesToNextMTC = (int)(samplesPerClock * (clockppq+i)); i+=1.0; startMTCAt=-999.0; sendmtc=true; } midiMessages.addEvents(temp,0,buffer.getNumSamples(),0); } if (startMTCAt >-999.0 && (int)(samplesPerPpq*(startMTCAt-pos.ppqPosition))<buffer.getNumSamples()) { samplesToNextMTC = (int)(samplesPerPpq*(startMTCAt-pos.ppqPosition)); int mtcData; switch (mtcNumber) { case 0: mtcData=frames&0x0f; break; case 1: mtcData=(frames&0xf0)>>4; break; case 2: mtcData=secs&0x0f; break; case 3: mtcData=(secs&0xf0)>>4; break; case 4: mtcData=mins&0x0f; break; case 5: mtcData=(mins&0xf0)>>4; break; case 6: mtcData=hours&0x0f; break; case 7: mtcData=(hours&0x10)>>4 | mtcFrameRate; break; } MidiMessage midiclock(0xf1,(mtcNumber<<4)|(mtcData)); ++mtcNumber; mtcNumber&=0x07; midiMessages.addEvent(midiclock,samplesToNextMTC); samplesToNextMTC = (int)(samplesPerClock * (clockppq+i)); i+=1.0; startMTCAt=-999.0; sendmtc=true; }
void CfpluginAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) { RTContext rt; // Debug RT stuff in debug mode auto timeAtStartOfBlock = time::now(); { // This will allocate IFF there is an error. // Can't put the non-rt specifier within the while... NonRTSection nonRt; while (globals_.ErrorBuffer()->dequeue(errorString_)) { juce::NativeMessageBox::showMessageBoxAsync( juce::AlertWindow::WarningIcon, juce::String("Error!"), juce::String(errorString_.c_str())); } } /************************************************************************************/ // Update UI on every run, TODO make this happen every n times? changeBroadcaster.sendChangeMessage(); if (resetting_.load()) { return; } /************************************************************************************/ if (!follower_) { return; } unsigned trackCount = follower_->StartNewBlock(); if (trackCount == 0) { MidiPanic(midiMessages); return; } // Track 0 is supposed to be the tempo track for (unsigned i = 1; i < trackCount; ++i) { follower_->GetTrackEventsForBlock(i, eventBuffer_); auto events = eventBuffer_.AllEvents(); events.ForEach([this, &midiMessages, i](samples_t sample, ScoreEventPtr message) { assert(sample.value() >= 0); assert(sample.value() < samplesPerBlock_); MidiMessage const & msg = midi_event_cast(message)->Message(); // Prohibit all but note on and off for now if (!msg.isNoteOnOrOff()) { return; } // truncating is probably the right thing to do... midiMessages.addEvent(msg, static_cast<unsigned>(sample.value())); }); } /*****************************************************************************************/ // This is the place where you'd normally do the guts of your plugin's // audio processing... /* for (int channel = 0; channel < getNumInputChannels(); ++channel) { float* channelData = buffer.getSampleData (channel); } */ // In case we have more outputs than inputs, we'll clear any output // channels that didn't contain input data, (because these aren't // guaranteed to be empty - they may contain garbage). /* for (int i = getNumInputChannels(); i < getNumOutputChannels(); ++i) { buffer.clear (i, 0, buffer.getNumSamples()); } */ // However, we reset all channels for (int i = 0; i < getNumOutputChannels(); ++i) { buffer.clear (i, 0, buffer.getNumSamples()); } /******************************/ time_quantity elapsedTime = time_cast<time_quantity>(time::now() - timeAtStartOfBlock); time_quantity blockSize((samplesPerBlock_ * score::samples) / (samplerate_ * score::samples_per_second)); if (elapsedTime > (0.3 * blockSize)) { LOG("Possible xrun! Process callback took %1% (max: %2%)", elapsedTime, blockSize); } }
void LumaPlug::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) { // we don't want any midi input events midiMessages.clear(); bool isRunning = false; AudioPlayHead* playHead = getPlayHead(); if (playHead) { //printf("playhead: 0x%x\n", playHead); AudioPlayHead::CurrentPositionInfo posInfo; playHead->getCurrentPosition(posInfo); isRunning = posInfo.isPlaying || posInfo.isRecording; luma_->SetBPM(posInfo.bpm); } if (isRunning && !luma_->IsRunning()) { std::string error; bool setScriptOK = luma_->SetScript(scriptText_.toUTF8(), error); if (!setScriptOK && getActiveEditor()) { ((LumaPlugEditor*)getActiveEditor())->Log(error.c_str()); } // else if (getActiveEditor()) // { // ((LumaPlugEditor*)getActiveEditor())->Log("Play"); // } luma_->Start(); } else if (!isRunning && luma_->IsRunning()) { luma_->Stop(); // if (getActiveEditor()) // { // ((LumaPlugEditor*)getActiveEditor())->Log("Stop"); // } } if (luma_->IsRunning()) { double sampleRate = getSampleRate(); int numSamples = buffer.getNumSamples(); float elapsed = (float(numSamples) / float(sampleRate)) * 1000.0; //printf("Elapsed: %f\n", elapsed); vector<shared_ptr<LumaEvent> > events; vector<float> offsets; luma_->Update(elapsed, events, offsets); if (events.size() > 0) { for (unsigned int i = 0; i < events.size(); i++) { //printf("New Event. Offset = %d, OffsetSamples = %d, Type = %d\n\n", // offsets[i], midiEvent->deltaFrames, events[i]->GetType()); int eventOffset = lroundf( ( float(offsets[i]) / 1000.0 ) * sampleRate ); if (events[i]->GetType() == kLumaEvent_NoteOn) { NoteOnEvent* noteOn = (NoteOnEvent*)events[i].get(); MidiMessage msg = MidiMessage::noteOn(1, noteOn->GetPitch(), (juce::uint8)noteOn->GetVelocity()); midiMessages.addEvent(msg, eventOffset); } else if (events[i]->GetType() == kLumaEvent_NoteOff) { NoteOffEvent* noteOff = (NoteOffEvent*)events[i].get(); MidiMessage msg = MidiMessage::noteOff(1, noteOff->GetPitch()); midiMessages.addEvent(msg, eventOffset); } else { fprintf(stderr, "LumaVST: Unknown event type: %d\n", events[i]->GetType()); } } // clear the used luma events events.clear(); } } /* Simple test of sending midi from the plugin static int count = 0; count += buffer.getNumSamples(); if (count >= 20000) { //MidiMessage msg = MidiMessage::noteOff(0, 60); //midiMessages.addEvent(msg, 0); MidiMessage msg = MidiMessage::noteOn(1, 60, (juce::uint8)100); midiMessages.addEvent(msg, 0); count = 0; } */ }
void MiditoOscAudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) { static float cv[8], shiftcv[8]; static bool _calibMode; MidiBuffer processedMidi; MidiMessage m; int time; char oscBuffer[IP_MTU_SIZE]; osc::OutboundPacketStream p(oscBuffer, IP_MTU_SIZE); if (calibMode) // Calibration Mode A440Hz(MIDI number 69) { p << osc::BeginBundleImmediate << osc::BeginMessage( "/fader1" ) << calibMap[69] << osc::EndMessage << osc::BeginMessage( "/fader2" ) << calibMap[69] << osc::EndMessage << osc::BeginMessage( "/fader3" ) << calibMap[69] << osc::EndMessage << osc::BeginMessage( "/fader4" ) << calibMap[69] << osc::EndMessage << osc::BeginMessage( "/fader5" ) << calibMap[69] << osc::EndMessage << osc::BeginMessage( "/fader6" ) << calibMap[69] << osc::EndMessage << osc::BeginMessage( "/fader7" ) << calibMap[69] << osc::EndMessage << osc::BeginMessage( "/fader8" ) << calibMap[69] << osc::EndMessage << osc::BeginMessage( "/gate1" ) << 1 << osc::EndMessage << osc::BeginMessage( "/gate2" ) << 1 << osc::EndMessage << osc::EndBundle; sendOSCData(p); _calibMode = true; return; } else { if (_calibMode) { p << osc::BeginBundleImmediate << osc::BeginMessage( "/gate1" ) << 0 << osc::EndMessage << osc::BeginMessage( "/gate2" ) << 0 << osc::EndMessage << osc::EndBundle; sendOSCData(p); _calibMode = false; } } for (MidiBuffer::Iterator i (midiMessages); i.getNextEvent (m, time);) { p.Clear(); usleep(30); if (m.isNoteOn()) { if (monoMode) // mono Mode { uint32_t midiCh = m.getChannel(); if (midiCh == 0 || midiCh > 7) { midiCh = 1; } cv[midiCh - 1] = calibMap[m.getNoteNumber()]; switch (midiCh) { case 1: p << osc::BeginMessage("/fader1") << cv[0] << osc::EndMessage; break; case 2: p << osc::BeginMessage("/fader2") << cv[1] << osc::EndMessage; break; case 3: p << osc::BeginMessage("/fader3") << cv[2] << osc::EndMessage; break; case 4: p << osc::BeginMessage("/fader4") << cv[3] << osc::EndMessage; break; case 5: p << osc::BeginMessage("/fader5") << cv[4] << osc::EndMessage; break; case 6: p << osc::BeginMessage("/fader6") << cv[5] << osc::EndMessage; break; case 7: p << osc::BeginMessage("/fader7") << cv[6] << osc::EndMessage; break; case 8: p << osc::BeginMessage("/fader8") << cv[7] << osc::EndMessage; break; default: break; } sendOSCData(p); } else if (shiftMode) { // shift Mode cv[0] = calibMap[m.getNoteNumber()]; for (int i = 7; i > 0; i--) { shiftcv[i] = shiftcv[i-1]; } p << osc::BeginBundleImmediate << osc::BeginMessage( "/fader1" ) << cv[0] << osc::EndMessage << osc::BeginMessage( "/fader2" ) << shiftcv[1] << osc::EndMessage << osc::BeginMessage( "/fader3" ) << shiftcv[2] << osc::EndMessage << osc::BeginMessage( "/fader4" ) << shiftcv[3] << osc::EndMessage << osc::BeginMessage( "/fader5" ) << shiftcv[4] << osc::EndMessage << osc::BeginMessage( "/fader6" ) << shiftcv[5] << osc::EndMessage << osc::BeginMessage( "/fader7" ) << shiftcv[6] << osc::EndMessage << osc::BeginMessage( "/fader8" ) << shiftcv[7] << osc::EndMessage << osc::BeginMessage( "/gate1" ) << 1 << osc::EndMessage << osc::BeginMessage( "/gate2" ) << 1 << osc::EndMessage << osc::EndBundle; sendOSCData(p); shiftcv[0] = cv[0]; } else { // poly Mode cv[ch] = calibMap[m.getNoteNumber()]; if (currentMaxPoly == 1) { cv[1] = cv[0]; } p << osc::BeginBundleImmediate << osc::BeginMessage( "/fader1" ) << cv[0] << osc::EndMessage << osc::BeginMessage( "/fader2" ) << cv[1] << osc::EndMessage << osc::BeginMessage( "/fader3" ) << cv[2] << osc::EndMessage << osc::BeginMessage( "/fader4" ) << cv[3] << osc::EndMessage << osc::BeginMessage( "/fader5" ) << cv[4] << osc::EndMessage << osc::BeginMessage( "/fader6" ) << cv[5] << osc::EndMessage << osc::BeginMessage( "/fader7" ) << m.getFloatVelocity() << osc::EndMessage << osc::BeginMessage( "/gate1" ) << 1 << osc::EndMessage << osc::BeginMessage( "/gate2" ) << 1 << osc::EndMessage << osc::EndBundle; sendOSCData(p); ch++; gateCount++; if (ch >= currentMaxPoly) { ch = 0; } } } else if (m.isNoteOff()) { if (monoMode) { switch (m.getChannel()) { case 1: p << osc::BeginMessage( "/gate1" ) << 0 << osc::EndMessage; break; case 2: p << osc::BeginMessage( "/gate2" ) << 0 << osc::EndMessage; break; case 3: p << osc::BeginMessage( "/gate3" ) << 0 << osc::EndMessage; break; case 4: p << osc::BeginMessage( "/gate4" ) << 0 << osc::EndMessage; break; default: break; } sendOSCData(p); } else if (shiftMode) { p << osc::BeginBundleImmediate << osc::BeginMessage( "/gate1" ) << 0 << osc::EndMessage << osc::BeginMessage( "/gate2" ) << 0 << osc::EndMessage << osc::EndBundle; sendOSCData(p); } else { gateCount --; if (gateCount <= 0) { p << osc::BeginBundleImmediate << osc::BeginMessage( "/gate1" ) << 0 << osc::EndMessage << osc::BeginMessage( "/gate2" ) << 0 << osc::EndMessage << osc::EndBundle; sendOSCData(p); gateCount = 0; } ch--; if (ch == -1) { ch = 0; } } } else if (m.isControllerOfType(1)) { // Modulation Wheel float modulation = m.getControllerValue(); if (!monoMode && !shiftMode) { p << osc::BeginMessage("/fader8") << (modulation / 127) << osc::EndMessage; sendOSCData(p); } } processedMidi.addEvent (m, time); } midiMessages.swapWith (processedMidi); buffer.clear(); for (int channel = 0; channel < getNumInputChannels(); ++channel) { float* channelData = 0; } }