static void findKeySigEvents (MidiFile& midiFile, StringPairArray& midiMetadata) { MidiMessageSequence keySigEvents; midiFile.findAllKeySigEvents (keySigEvents); const int numKeySigEvents = keySigEvents.getNumEvents(); MemoryOutputStream keySigSequence; for (int i = 0; i < numKeySigEvents; ++i) { const MidiMessage& message (keySigEvents.getEventPointer (i)->message); const int key = jlimit (0, 14, message.getKeySignatureNumberOfSharpsOrFlats() + 7); const bool isMajor = message.isKeySignatureMajorKey(); static const char* majorKeys[] = { "Cb", "Gb", "Db", "Ab", "Eb", "Bb", "F", "C", "G", "D", "A", "E", "B", "F#", "C#" }; static const char* minorKeys[] = { "Ab", "Eb", "Bb", "F", "C", "G", "D", "A", "E", "B", "F#", "C#", "G#", "D#", "A#" }; String keySigString (isMajor ? majorKeys[key] : minorKeys[key]); if (! isMajor) keySigString << 'm'; if (i == 0) midiMetadata.set (CoreAudioFormat::keySig, keySigString); if (numKeySigEvents > 1) keySigSequence << keySigString << ',' << keySigEvents.getEventTime (i) << ';'; } if (keySigSequence.getDataSize() > 0) midiMetadata.set ("key signature sequence", keySigSequence.toUTF8()); }
static void findTimeSigEvents (MidiFile& midiFile, StringPairArray& midiMetadata) { MidiMessageSequence timeSigEvents; midiFile.findAllTimeSigEvents (timeSigEvents); const int numTimeSigEvents = timeSigEvents.getNumEvents(); MemoryOutputStream timeSigSequence; for (int i = 0; i < numTimeSigEvents; ++i) { int numerator, denominator; timeSigEvents.getEventPointer(i)->message.getTimeSignatureInfo (numerator, denominator); String timeSigString; timeSigString << numerator << '/' << denominator; if (i == 0) midiMetadata.set (CoreAudioFormat::timeSig, timeSigString); if (numTimeSigEvents > 1) timeSigSequence << timeSigString << ',' << timeSigEvents.getEventTime (i) << ';'; } if (timeSigSequence.getDataSize() > 0) midiMetadata.set ("time signature sequence", timeSigSequence.toUTF8()); }
static void findTempoEvents (MidiFile& midiFile, StringPairArray& midiMetadata) { MidiMessageSequence tempoEvents; midiFile.findAllTempoEvents (tempoEvents); const int numTempoEvents = tempoEvents.getNumEvents(); MemoryOutputStream tempoSequence; for (int i = 0; i < numTempoEvents; ++i) { const double tempo = getTempoFromTempoMetaEvent (tempoEvents.getEventPointer (i)); if (tempo > 0.0) { if (i == 0) midiMetadata.set (CoreAudioFormat::tempo, String (tempo)); if (numTempoEvents > 1) tempoSequence << String (tempo) << ',' << tempoEvents.getEventTime (i) << ';'; } } if (tempoSequence.getDataSize() > 0) midiMetadata.set ("tempo sequence", tempoSequence.toUTF8()); }
void MidiMessageSequence::extractSysExMessages (MidiMessageSequence& destSequence) const { for (int i = 0; i < list.size(); ++i) { const MidiMessage& mm = list.getUnchecked(i)->message; if (mm.isSysEx()) destSequence.addEvent (mm); } }
//============================================================================== void MidiMessageSequence::extractMidiChannelMessages (const int channelNumberToExtract, MidiMessageSequence& destSequence, const bool alsoIncludeMetaEvents) const { for (int i = 0; i < list.size(); ++i) { const MidiMessage& mm = list.getUnchecked(i)->message; if (mm.isForChannel (channelNumberToExtract) || (alsoIncludeMetaEvents && mm.isMetaEvent())) destSequence.addEvent (mm); } }
//============================================================================== 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); } }
MidiReader::TrackReaderImpl::TrackReaderImpl(MidiMessageSequence const & sequence) : sequence_(sequence) , count_(sequence.getNumEvents()) , current_(0) {}
MidiMessageSequence* MidiStochaster::createMelody(int minOctaveOffset, int maxOctaveOffset, double speedFactor) { int currentOctaveOffset; if (minOctaveOffset <= 0) { currentOctaveOffset = 0; } else { currentOctaveOffset = minOctaveOffset; } int randInt1 = randomInt(100, 200); double stopProbability = (double) randInt1 / (randInt1 + 1); int randInt2 = randomInt(4, 10); double octaveSwitchProbability = (double) randInt2 / (randInt2 + 1); double currentTime = 0.0; MidiMessageSequence* melody = new MidiMessageSequence(starter); if (!goodSource) { return melody; } while (randomDouble(0,1) < stopProbability) { int notePitch = pitches->at(randomInt(0, pitches->size() - 1)) + 12 * currentOctaveOffset; while (notePitch < 0) { notePitch+=12; currentOctaveOffset++; minOctaveOffset++; } while (notePitch > 127) { notePitch-=12; currentOctaveOffset--; maxOctaveOffset--; } int noteVelocity = randomInt(lowVelocity, highVelocity); int noteLength = randomDouble(lowNoteLength, highNoteLength) * (1.0 / speedFactor); int nextRestLength; if (randomDouble(0,1) < negRestProbability) { nextRestLength = randomDouble(lowNegRestLength, highNegRestLength) * 1.0 / speedFactor; } else { nextRestLength = randomDouble(lowRestLength, highRestLength) * 1.0 / speedFactor; } MidiMessage noteDown = MidiMessage::noteOn(1, notePitch, (uint8_t) noteVelocity); noteDown.setTimeStamp(currentTime); MidiMessage noteUp = MidiMessage::noteOff(1, notePitch); noteUp.setTimeStamp(currentTime + noteLength); melody->addEvent(noteDown); melody->addEvent(noteUp); melody->updateMatchedPairs(); currentTime += noteLength + nextRestLength; if (randomDouble(0,1) > octaveSwitchProbability) { currentOctaveOffset = randomInt(minOctaveOffset, maxOctaveOffset); } } return melody; }
void MidiThread::run() { midiPort = MidiOutput::openDevice(0); map<double, double>::iterator tempoIterator = tempos.begin(); double tickLength = tempoIterator->second; double initTempo = 60000 / (tickLength * ppq); tempoIterator++; map<double, std::pair<int, int>>::iterator timeSigIterator = timeSigs.begin(); int timeSigNumerator = timeSigIterator->second.first; int timeSigDenominator = timeSigIterator->second.second; timeSigIterator++; MidiMessage message; // MIDI click intro File clickFile("Click120.mid"); ScopedPointer<FileInputStream> clickStream = clickFile.createInputStream(); double eventTick; double prevTick = 0; double prevDelta = 0; int now = Time::getMillisecondCounter(); if (clickStream) { click.readFrom(*clickStream); short clickPpq = click.getTimeFormat(); MidiMessageSequence clickSequence = MidiMessageSequence(); clickSequence = *click.getTrack(0); int i = 0; while (i < clickSequence.getNumEvents()) { message = clickSequence.getEventPointer(i)->message; eventTick = message.getTimeStamp(); double origBpm = 60000 / (tickLength * clickPpq); double delta = prevDelta + ((eventTick - prevTick) * (origBpm / initTempo)) * tickLength; Time::waitForMillisecondCounter(now + delta); midiPort->sendMessageNow(message); i++; prevTick = eventTick; prevDelta = delta; } } now = Time::getMillisecondCounter(); prevTick = 0; prevDelta = 0; int currentBar = 1; int currentBeat = 1; int currentTick = 0; int quartersPerBar = timeSigNumerator * (timeSigDenominator / 4); int tickStartOfBar = 0; int tickStartNextBar = quartersPerBar * ppq; currentBpm = initTempo; int i = 0; while (i < midiSequence.getNumEvents() && !threadShouldExit()) { std::stringstream ss; ss << currentBpm; content->updateBpm(ss.str()); message = midiSequence.getEventPointer(i)->message; eventTick = message.getTimeStamp(); // number of midi ticks until note if (tempoIterator != tempos.end() && eventTick >= tempoIterator->first) { tickLength = tempoIterator->second; tempoIterator++; } if (timeSigIterator != timeSigs.end() && eventTick >= timeSigIterator->first) { timeSigNumerator = timeSigIterator->second.first; timeSigDenominator = timeSigIterator->second.second; quartersPerBar = timeSigNumerator * (4 / timeSigDenominator); timeSigIterator++; } if (eventTick >= tickStartNextBar) { tickStartOfBar = tickStartNextBar; tickStartNextBar = tickStartOfBar + quartersPerBar * ppq; currentBar = currentBar + static_cast<int> (std::floor((eventTick - tickStartOfBar) / (ppq * quartersPerBar))) + 1; } currentBeat = static_cast<int> (std::floor((eventTick - tickStartOfBar)/ ppq)) % timeSigNumerator + 1; currentTick = static_cast<int> (eventTick) % ppq; if (!message.isMetaEvent() && (currentTick == 0 || currentTick == 120 || currentTick == 240 || currentTick == 360)) { std::stringstream ss; ss << currentBar << ":" << currentBeat << ":" << setfill('0') << setw(3) << currentTick; content->updateBbt(ss.str()); } double origBpm = 60000 / (tickLength * ppq); double delta = prevDelta + ((eventTick - prevTick) * (origBpm / currentBpm)) * tickLength; Time::waitForMillisecondCounter(now + delta); midiPort->sendMessageNow(message); i++; prevTick = eventTick; prevDelta = delta; } delete midiPort; return; }