Example #1
0
MidiBuffer MPEMessages::setZoneLayout (const MPEZoneLayout& layout)
{
    MidiBuffer buffer;

    buffer.addEvents (clearAllZones(), 0, -1, 0);

    for (int i = 0; i < layout.getNumZones(); ++i)
        buffer.addEvents (addZone (*layout.getZoneByIndex (i)), 0, -1, 0);

    return buffer;
}
Example #2
0
MidiBuffer MPEMessages::addZone (MPEZone zone)
{
    MidiBuffer buffer (MidiRPNGenerator::generate (zone.getFirstNoteChannel(),
                                                   zoneLayoutMessagesRpnNumber,
                                                   zone.getNumNoteChannels(),
                                                   false, false));

    buffer.addEvents (perNotePitchbendRange (zone), 0, -1, 0);
    buffer.addEvents (masterPitchbendRange (zone), 0, -1, 0);

    return buffer;
}
Example #3
0
void DemoJuceFilter::processBlock (AudioSampleBuffer& buffer,
                                   MidiBuffer& midiMessages)
{
	if (isSyncedToHost)
	{
		AudioPlayHead::CurrentPositionInfo pos;

		if (getPlayHead() != 0 && getPlayHead()->getCurrentPosition (pos))
		{
	        if (memcmp (&pos, &lastPosInfo, sizeof (pos)) != 0)
			{
	            lastPosInfo = pos;

				const int ppqPerBar		= (pos.timeSigNumerator * 4 / pos.timeSigDenominator);
				const double beats		= (fmod (pos.ppqPosition, ppqPerBar) / ppqPerBar) * pos.timeSigNumerator;
				const double position	= beats*4;
				const int beat			= (int)position;
				
				currentBpm	= (int)pos.bpm;

				if (_p != beat)
				{
					for (int x=0; x<64; x++)
					{
						if (activePatterns[x])
						{
							patterns[x]->forward(beat+1);
						}
					}

					currentBeat = currentPatternPtr->getCurrentPosition();

					if (currentBeat > 16)
						currentBeat = currentBeat - 16;

					/* process midi events to their devices */
					midiMessages.addEvents (midiManager.getVstMidiEvents(),0,-1,0);

					/* clean the buffers */
					midiManager.clear();

					sendChangeMessage (this);
				}
				
				_p = beat;
			}
		}
		else
		{
	        zeromem (&lastPosInfo, sizeof (lastPosInfo));
			lastPosInfo.timeSigNumerator = 4;
	        lastPosInfo.timeSigDenominator = 4;
			lastPosInfo.bpm = 120;
	   }	
	}
}
    void perform (AudioBuffer<FloatType>& buffer, MidiBuffer& midiMessages, AudioPlayHead* audioPlayHead)
    {
        auto numSamples = buffer.getNumSamples();
        auto maxSamples = renderingBuffer.getNumSamples();

        if (numSamples > maxSamples)
        {
            // being asked to render more samples than our buffers have, so slice things up...
            tempMIDI.clear();
            tempMIDI.addEvents (midiMessages, maxSamples, numSamples, -maxSamples);

            {
                AudioBuffer<FloatType> startAudio (buffer.getArrayOfWritePointers(), buffer.getNumChannels(), maxSamples);
                midiMessages.clear (maxSamples, numSamples);
                perform (startAudio, midiMessages, audioPlayHead);
            }

            AudioBuffer<FloatType> endAudio (buffer.getArrayOfWritePointers(), buffer.getNumChannels(), maxSamples, numSamples - maxSamples);
            perform (endAudio, tempMIDI, audioPlayHead);
            return;
        }

        currentAudioInputBuffer = &buffer;
        currentAudioOutputBuffer.setSize (jmax (1, buffer.getNumChannels()), numSamples);
        currentAudioOutputBuffer.clear();
        currentMidiInputBuffer = &midiMessages;
        currentMidiOutputBuffer.clear();

        {
            const Context context { renderingBuffer.getArrayOfWritePointers(), midiBuffers.begin(), audioPlayHead, numSamples };

            for (auto* op : renderOps)
                op->perform (context);
        }

        for (int i = 0; i < buffer.getNumChannels(); ++i)
            buffer.copyFrom (i, 0, currentAudioOutputBuffer, i, 0, numSamples);

        midiMessages.clear();
        midiMessages.addEvents (currentMidiOutputBuffer, 0, buffer.getNumSamples(), 0);
        currentAudioInputBuffer = nullptr;
    }
    void runTest() override
    {
        beginTest ("initialisation");
        {
            MPEZoneLayout layout;
            expectEquals (layout.getNumZones(), 0);
        }

        beginTest ("adding zones");
        {
            MPEZoneLayout layout;

            expect (layout.addZone (MPEZone (1, 7)));

            expectEquals (layout.getNumZones(), 1);
            expectEquals (layout.getZoneByIndex (0)->getMasterChannel(), 1);
            expectEquals (layout.getZoneByIndex (0)->getNumNoteChannels(), 7);

            expect (layout.addZone (MPEZone (9, 7)));

            expectEquals (layout.getNumZones(), 2);
            expectEquals (layout.getZoneByIndex (0)->getMasterChannel(), 1);
            expectEquals (layout.getZoneByIndex (0)->getNumNoteChannels(), 7);
            expectEquals (layout.getZoneByIndex (1)->getMasterChannel(), 9);
            expectEquals (layout.getZoneByIndex (1)->getNumNoteChannels(), 7);

            expect (! layout.addZone (MPEZone (5, 3)));

            expectEquals (layout.getNumZones(), 3);
            expectEquals (layout.getZoneByIndex (0)->getMasterChannel(), 1);
            expectEquals (layout.getZoneByIndex (0)->getNumNoteChannels(), 3);
            expectEquals (layout.getZoneByIndex (1)->getMasterChannel(), 9);
            expectEquals (layout.getZoneByIndex (1)->getNumNoteChannels(), 7);
            expectEquals (layout.getZoneByIndex (2)->getMasterChannel(), 5);
            expectEquals (layout.getZoneByIndex (2)->getNumNoteChannels(), 3);

            expect (! layout.addZone (MPEZone (5, 4)));

            expectEquals (layout.getNumZones(), 2);
            expectEquals (layout.getZoneByIndex (0)->getMasterChannel(), 1);
            expectEquals (layout.getZoneByIndex (0)->getNumNoteChannels(), 3);
            expectEquals (layout.getZoneByIndex (1)->getMasterChannel(), 5);
            expectEquals (layout.getZoneByIndex (1)->getNumNoteChannels(), 4);

            expect (! layout.addZone (MPEZone (6, 4)));

            expectEquals (layout.getNumZones(), 2);
            expectEquals (layout.getZoneByIndex (0)->getMasterChannel(), 1);
            expectEquals (layout.getZoneByIndex (0)->getNumNoteChannels(), 3);
            expectEquals (layout.getZoneByIndex (1)->getMasterChannel(), 6);
            expectEquals (layout.getZoneByIndex (1)->getNumNoteChannels(), 4);
        }

        beginTest ("querying zones");
        {
            MPEZoneLayout layout;

            layout.addZone (MPEZone (2, 5));
            layout.addZone (MPEZone (9, 4));

            expect (layout.getZoneByMasterChannel (1)  == nullptr);
            expect (layout.getZoneByMasterChannel (2)  != nullptr);
            expect (layout.getZoneByMasterChannel (3)  == nullptr);
            expect (layout.getZoneByMasterChannel (8)  == nullptr);
            expect (layout.getZoneByMasterChannel (9)  != nullptr);
            expect (layout.getZoneByMasterChannel (10) == nullptr);

            expectEquals (layout.getZoneByMasterChannel (2)->getNumNoteChannels(), 5);
            expectEquals (layout.getZoneByMasterChannel (9)->getNumNoteChannels(), 4);

            expect (layout.getZoneByFirstNoteChannel (2)  == nullptr);
            expect (layout.getZoneByFirstNoteChannel (3)  != nullptr);
            expect (layout.getZoneByFirstNoteChannel (4)  == nullptr);
            expect (layout.getZoneByFirstNoteChannel (9)  == nullptr);
            expect (layout.getZoneByFirstNoteChannel (10) != nullptr);
            expect (layout.getZoneByFirstNoteChannel (11) == nullptr);

            expectEquals (layout.getZoneByFirstNoteChannel (3)->getNumNoteChannels(), 5);
            expectEquals (layout.getZoneByFirstNoteChannel (10)->getNumNoteChannels(), 4);

            expect (layout.getZoneByNoteChannel (2)  == nullptr);
            expect (layout.getZoneByNoteChannel (3)  != nullptr);
            expect (layout.getZoneByNoteChannel (4)  != nullptr);
            expect (layout.getZoneByNoteChannel (6)  != nullptr);
            expect (layout.getZoneByNoteChannel (7)  != nullptr);
            expect (layout.getZoneByNoteChannel (8)  == nullptr);
            expect (layout.getZoneByNoteChannel (9)  == nullptr);
            expect (layout.getZoneByNoteChannel (10) != nullptr);
            expect (layout.getZoneByNoteChannel (11) != nullptr);
            expect (layout.getZoneByNoteChannel (12) != nullptr);
            expect (layout.getZoneByNoteChannel (13) != nullptr);
            expect (layout.getZoneByNoteChannel (14) == nullptr);

            expectEquals (layout.getZoneByNoteChannel (5)->getNumNoteChannels(), 5);
            expectEquals (layout.getZoneByNoteChannel (13)->getNumNoteChannels(), 4);
        }

        beginTest ("clear all zones");
        {
            MPEZoneLayout layout;

            expect (layout.addZone (MPEZone (1, 7)));
            expect (layout.addZone (MPEZone (10, 2)));
            layout.clearAllZones();

            expectEquals (layout.getNumZones(), 0);
        }

        beginTest ("process MIDI buffers");
        {
            MPEZoneLayout layout;
            MidiBuffer buffer;

            buffer = MPEMessages::addZone (MPEZone (1, 7));
            layout.processNextMidiBuffer (buffer);

            expectEquals (layout.getNumZones(), 1);
            expectEquals (layout.getZoneByIndex (0)->getMasterChannel(), 1);
            expectEquals (layout.getZoneByIndex (0)->getNumNoteChannels(), 7);

            buffer = MPEMessages::addZone (MPEZone (9, 7));
            layout.processNextMidiBuffer (buffer);

            expectEquals (layout.getNumZones(), 2);
            expectEquals (layout.getZoneByIndex (0)->getMasterChannel(), 1);
            expectEquals (layout.getZoneByIndex (0)->getNumNoteChannels(), 7);
            expectEquals (layout.getZoneByIndex (1)->getMasterChannel(), 9);
            expectEquals (layout.getZoneByIndex (1)->getNumNoteChannels(), 7);

            MPEZone zone (1, 10);

            buffer = MPEMessages::addZone (zone);
            layout.processNextMidiBuffer (buffer);

            expectEquals (layout.getNumZones(), 1);
            expectEquals (layout.getZoneByIndex (0)->getMasterChannel(), 1);
            expectEquals (layout.getZoneByIndex (0)->getNumNoteChannels(), 10);

            zone.setPerNotePitchbendRange (33);
            zone.setMasterPitchbendRange (44);

            buffer = MPEMessages::masterPitchbendRange (zone);
            buffer.addEvents (MPEMessages::perNotePitchbendRange (zone), 0, -1, 0);

            layout.processNextMidiBuffer (buffer);

            expectEquals (layout.getZoneByIndex (0)->getPerNotePitchbendRange(), 33);
            expectEquals (layout.getZoneByIndex (0)->getMasterPitchbendRange(), 44);
        }

        beginTest ("process individual MIDI messages");
        {
            MPEZoneLayout layout;

            layout.processNextMidiEvent (MidiMessage (0x80, 0x59, 0xd0));  // unrelated note-off msg
            layout.processNextMidiEvent (MidiMessage (0xb1, 0x64, 0x06));  // RPN part 1
            layout.processNextMidiEvent (MidiMessage (0xb1, 0x65, 0x00));  // RPN part 2
            layout.processNextMidiEvent (MidiMessage (0xb8, 0x0b, 0x66));  // unrelated CC msg
            layout.processNextMidiEvent (MidiMessage (0xb1, 0x06, 0x03));  // RPN part 3
            layout.processNextMidiEvent (MidiMessage (0x90, 0x60, 0x00));  // unrelated note-on msg

            expectEquals (layout.getNumZones(), 1);
            expectEquals (layout.getZoneByIndex (0)->getMasterChannel(), 1);
            expectEquals (layout.getZoneByIndex (0)->getNumNoteChannels(), 3);
        }
    }
void PatternRecording::recordPattern(MidiBuffer &midiMessages, const int &numSamples)
{
    // if we are recording the pattern, just rip the
    // MIDI messages from the incoming buffer
    if (isPatternRecording)
    {
        // get the iterator for the incoming buffer
        // so we can check through the messages
        MidiBuffer::Iterator i(midiMessages);
        MidiMessage message (0xf4, 0.0);
        int time;

        // if we are still during the precount, do nothing
        if (patternPrecountPosition > numSamples)
            patternPrecountPosition -= numSamples;

        // if the precount finishes during this buffer
        else if (patternPrecountPosition > 0)
        {
            // TODO: overdub could be option here?
            midiPattern.clear();

            const int numSamplesToAdd = numSamples - patternPrecountPosition;

            // DBG("#1 recording " << numSamplesToAdd << " of midi, pos: " << patternPrecountPosition);

            // get messages from the main MIDI message queue
            while(i.getNextEvent(message, time))
            {
                // and add them if they occur after the precount runs out
                if (time > patternPrecountPosition)
                {
                    midiPattern.addEvent(message, time - patternPrecountPosition);

                    // store noteOffs to fire at end
                    if (message.isNoteOn())
                    {
                        MidiMessage tempNoteOff = MidiMessage::noteOff(message.getChannel(), message.getNoteNumber(), message.getVelocity());
                        noteOffs.addEvent(tempNoteOff, 0);
                    }
                }
            }

            // the precount has finished
            patternPrecountPosition = 0;
            // we are now numSamplesToAdd into the buffer
            patternPosition = numSamplesToAdd;
        }
        else
        {
            // if we are during recording (and not near the end), just
            // add the current input into the record buffer
            if (numSamples + patternPosition < patternLengthInSamples)
            {
                // DBG("#2 recording " << numSamples << " of midi, pos: " << patternPosition);

                // get messages from the main MIDI message queue
                while(i.getNextEvent(message, time))
                {
                    midiPattern.addEvent(message, time + patternPosition);

                    // store noteOffs to fire at end
                    if (message.isNoteOn())
                    {
                        MidiMessage tempNoteOff = MidiMessage::noteOff(message.getChannel(), message.getNoteNumber(), message.getVelocity());
                        noteOffs.addEvent(tempNoteOff, 0);
                    }
                }

                //midiPattern.addEvents(midiMessages, 0, numSamples, -patternPosition);
                patternPosition += numSamples;
            }
            // otherwise we are finishing up
            else
            {
                const int numSamplesLeftToRecord = patternLengthInSamples - patternPosition;

                // add remaining messages from the main MIDI message queue
                while(i.getNextEvent(message, time))
                {
                    if (time < numSamplesLeftToRecord)
                        midiPattern.addEvent(message, time + patternPosition);

                    // store noteOffs to fire at end
                    if (message.isNoteOn())
                    {
                        MidiMessage tempNoteOff = MidiMessage::noteOff(message.getChannel(), message.getNoteNumber(), message.getVelocity());
                        noteOffs.addEvent(tempNoteOff, 0);
                    }
                }

                // add the note offs to clear any "phantom notes"
                midiPattern.addEvents(noteOffs, 0, 1, patternLengthInSamples - 1);

                // we are no longer recording
                isPatternRecording = false;

                // if we finish recording, let any listeners know
                // so they can redraw representations of the pattern
                sendChangeMessage();

                DBG("pattern " << patternBank << " finished recording.");


                // start playing back from the start straight away
                isPatternStopping = false;
                isPatternPlaying = true;
                patternPosition = 0;

                // fill the remaining buffer with the newly recorded sequence
                const int samplesRemaining = numSamples - numSamplesLeftToRecord;
                if (patternPosition + samplesRemaining < patternLengthInSamples)
                {
                    midiMessages.addEvents(midiPattern, patternPosition, samplesRemaining, numSamplesLeftToRecord);
                    patternPosition += samplesRemaining;
                }

            }

            // Let the PatternStripControl know to recache pattern
            sendChangeMessage();
        }
    }
}
void PatternRecording::playPattern(MidiBuffer &midiMessages, const int &numSamples)
{
    // if the pattern is playing, add its events to the
    // Midi message queue
    if (isPatternPlaying)
    {
        // if we are stopping
        if (isPatternStopping)
        {
            // add the note off events to the end off the buffer
            // to avoid any hanging notes
            midiMessages.addEvents(noteOffs, 0, 1, numSamples - 1);

            isPatternPlaying = false;
            isPatternStopping = false;

            // we are finished here
            return;
        }

        // calculate the correction to timesteps relative
        // to the current MIDI buffer
        const int correction = -patternPosition;

        if (patternPosition + numSamples < patternLengthInSamples)
        {
            midiMessages.addEvents(midiPattern, patternPosition, numSamples, correction);
            patternPosition += numSamples;
        }
        else
        {
            // see how many samples of the pattern we have left to play back
            const int numToAdd = patternLengthInSamples - patternPosition;
            // and add them to the main MIDI buffer
            midiMessages.addEvents(midiPattern, patternPosition, numToAdd, correction);

            // reset position
            patternPosition = 0;

            // if we are looping the pattern
            if (doesPatternLoop)
            {
                isPatternStopping = false;
                isPatternPlaying = true;

                // go back to the start of the pattern and add these samples
                // to what remains of the main MIDI buffer
                const int samplesRemainingInBuffer = numSamples - numToAdd;
                // remembering that these are offset so as to start where
                // the previously playing pattern finished
                const int offset = numToAdd;

                midiMessages.addEvents(midiPattern, patternPosition, samplesRemainingInBuffer, offset);
                patternPosition += samplesRemainingInBuffer;
            }
            else
            {
                isPatternPlaying = false;
            }

        }
    }
}
Example #8
0
void MidiControllerAutomationHandler::handleParameterData(MidiBuffer &b)
{
	const bool bufferEmpty = b.isEmpty();
	const bool noCCsUsed = !anyUsed && !unlearnedData.used;

	if (bufferEmpty || noCCsUsed) return;

	tempBuffer.clear();

	MidiBuffer::Iterator mb(b);
	MidiMessage m;

	int samplePos;

	while (mb.getNextEvent(m, samplePos))
	{
		bool consumed = false;

		if (m.isController())
		{
			const int number = m.getControllerNumber();

			if (isLearningActive())
			{
				setUnlearndedMidiControlNumber(number, sendNotification);
			}

			for (auto& a : automationData[number])
			{
				if (a.used)
				{
					jassert(a.processor.get() != nullptr);

					auto normalizedValue = (double)m.getControllerValue() / 127.0;

					if (a.inverted) normalizedValue = 1.0 - normalizedValue;

					const double value = a.parameterRange.convertFrom0to1(normalizedValue);

					const float snappedValue = (float)a.parameterRange.snapToLegalValue(value);

					if (a.macroIndex != -1)
					{
						a.processor->getMainController()->getMacroManager().getMacroChain()->setMacroControl(a.macroIndex, (float)m.getControllerValue(), sendNotification);
					}
					else
					{
						if (a.lastValue != snappedValue)
						{
							a.processor->setAttribute(a.attribute, snappedValue, sendNotification);
							a.lastValue = snappedValue;
						}
					}

					consumed = true;
				}
			}
		}

		if (!consumed) tempBuffer.addEvent(m, samplePos);
	}

	b.clear();
	b.addEvents(tempBuffer, 0, -1, 0);
}
Example #9
0
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;
                    }