Esempio n. 1
0
void InstrumentTrack::updatePitch()
{
	updateBaseNote();
	processOutEvent( midiEvent( MidiPitchBend,
					midiPort()->realOutputChannel(),
					midiPitch() ), 0 );
}
Esempio n. 2
0
/*! \brief Handle the focus leaving the piano display view
 *
 *  Turn off all notes if we lose focus.
 *
 *  \todo Is there supposed to be a parameter given here?
 */
void PianoView::focusOutEvent( QFocusEvent * )
{
	if( m_piano == NULL )
	{
		return;
	}

	// focus just switched to another control inside the instrument track
	// window we live in?
	if( parentWidget()->parentWidget()->focusWidget() != this &&
		parentWidget()->parentWidget()->focusWidget() != NULL &&
		!parentWidget()->parentWidget()->
				focusWidget()->inherits( "QLineEdit" ) )
	{
		// then reclaim keyboard focus!
		setFocus();
		return;
	}

	// if we loose focus, we HAVE to note off all running notes because
	// we don't receive key-release-events anymore and so the notes would
	// hang otherwise
	for( int i = 0; i < NumKeys; ++i )
	{
		m_piano->m_midiEvProc->processInEvent(
					midiEvent( MidiNoteOff, 0, i, 0 ),
								midiTime() );
		m_piano->m_pressedKeys[i] = false;
	}
	update();
}
Esempio n. 3
0
void SeqDriver::procEvents()
{
    int l1;
    snd_seq_event_t *evIn, evOut;
    bool outOfRange = false;
    bool unmatched = false;
    MidiMap* mm;

    do {
        snd_seq_event_input(seq_handle, &evIn);
        emit midiEvent(evIn);
        unmatched = true;
        for(l1 = 0; l1 < midiMapList->count(); l1++) {
            mm = midiMapList->at(l1);
            if (mm->isMap(evIn)) {
                unmatched = false;
                mm->doMap(evIn, &evOut, &outOfRange);
                if (!outOfRange) {
                    snd_seq_ev_set_subs(&evOut);  
                    snd_seq_ev_set_direct(&evOut);
                    snd_seq_ev_set_source(&evOut, portid_out[mm->portOut]);
                    snd_seq_event_output_direct(seq_handle, &evOut);
                }  
            }
        }
        if (!discardUnmatched && unmatched) {
            snd_seq_ev_set_subs(evIn);  
            snd_seq_ev_set_direct(evIn);
            snd_seq_ev_set_source(evIn, portid_out[portUnmatched]);
            snd_seq_event_output_direct(seq_handle, evIn);
        }
    } while (snd_seq_event_input_pending(seq_handle, 0) > 0);  
}
Esempio n. 4
0
/*! \brief Handle a mouse click on this piano display view
 *
 *  We first determine the key number using the getKeyFromMouse() method.
 *
 *  If we're below the 'root key selection' area,
 *  we set the volume of the note to be proportional to the vertical
 *  position on the keyboard - lower down the key is louder, within the
 *  boundaries of the (white or black) key pressed.  We then tell the
 *  instrument to play that note, scaling for MIDI max loudness = 127.
 *
 *  If we're in the 'root key selection' area, of course, we set the
 *  root key to be that key.
 *
 *  We finally update ourselves to show the key press
 *
 *  \param _me the mouse click to handle.
 */
void PianoView::mousePressEvent( QMouseEvent * _me )
{
	if( _me->button() == Qt::LeftButton && m_piano != NULL )
	{
		// get pressed key
		Uint32 key_num = getKeyFromMouse( _me->pos() );
		if( _me->pos().y() > PIANO_BASE )
		{
			int y_diff = _me->pos().y() - PIANO_BASE;
			int velocity = (int)( ( float ) y_diff /
				( ( KEY_ORDER[key_num % KeysPerOctave] ==
							Piano::WhiteKey ) ?
				PW_WHITE_KEY_HEIGHT : PW_BLACK_KEY_HEIGHT ) *
						(float) MidiMaxVelocity );
			if( y_diff < 0 )
			{
				velocity = 0;
			}
			else if( y_diff >
				( ( KEY_ORDER[key_num % KeysPerOctave] ==
							Piano::WhiteKey ) ?
				PW_WHITE_KEY_HEIGHT : PW_BLACK_KEY_HEIGHT ) )
			{
				velocity = MidiMaxVelocity;
			}
			// set note on
			m_piano->m_midiEvProc->processInEvent(
					midiEvent( MidiNoteOn, 0, key_num,
							velocity ),
								midiTime() );
			m_piano->m_pressedKeys[key_num] = true;
			m_lastKey = key_num;

			emit keyPressed( key_num );
		}
		else
		{
			if( _me->modifiers() & Qt::ControlModifier )
			{
				new stringPairDrag( "automatable_model",
					QString::number( m_piano->
						m_midiEvProc->
							baseNoteModel()->id() ),
					QPixmap(), this );
				_me->accept();
			}
			else
			{
				m_piano->m_midiEvProc->
					baseNoteModel()->
						setInitValue( (float) key_num );

				emit baseNoteChanged();
			}
		}

		// and let the user see that he pressed a key... :)
		update();
	}
}
Esempio n. 5
0
void Send::sendToArdour(Jack *pJack) {
    char *data = new char[3];
    data[0] = CC_MASK + id;
    data[1] = (char) trackId;
    data[2] = gain;

    MidiEvent midiEvent(data);
    //Send it to the jack client to handle send to Ardour
    jack_ringbuffer_write(pJack->sceneLoadBuffer, (char *) &midiEvent,sizeof(MidiEvent));

    std::cout << "Send a send on channel " << (int) id << std::endl;
}
Esempio n. 6
0
void ResourcePreviewer::preview( ResourceItem * _item )
{
	// stop any existing preview sounds
	stopPreview();

	// disable journalling of changes in our preview track
	const bool j = engine::projectJournal()->isJournalling();
	engine::projectJournal()->setJournalling( false );
	engine::setSuppressMessages( true );

	// handle individual resource types
	bool handledItem = true;
	switch( _item->type() )
	{
		case ResourceItem::TypePreset:
			// restore default settings, in case we're going to load
			// an incomplete preset
			m_previewTrack->loadTrackSpecificSettings(
				m_defaultSettings.content().
					firstChild().toElement() );
			ResourceAction( _item ).loadPreset( m_previewTrack );
			m_previewTrack->midiPort()->setMode( MidiPort::Disabled );
			break;
		case ResourceItem::TypeSample:
		case ResourceItem::TypePluginSpecificResource:
			// restore default settings we are going to preview a
			// sample (which should be played at a default
			// instrument track)
			m_previewTrack->loadTrackSpecificSettings(
				m_defaultSettings.content().
					firstChild().toElement() );
			ResourceAction( _item ).loadByPlugin( m_previewTrack );
			break;
		default:
			handledItem = false;
			break;
	}

	// re-enable journalling
	engine::setSuppressMessages( false );
	engine::projectJournal()->setJournalling( j );

	if( handledItem )
	{
		// playback default note
		m_previewTrack->processInEvent(
			midiEvent( MidiNoteOn, 0, DefaultKey, MidiMaxVelocity ),
								midiTime() );
	}
}
Esempio n. 7
0
/*! \brief Handle a mouse release event on the piano display view
 *
 *  If a key was pressed by the in the mousePressEvent() function, we
 *  turn the note off.
 *
 *  \param _me the mousePressEvent to handle.
 */
void PianoView::mouseReleaseEvent( QMouseEvent * )
{
	if( m_lastKey != -1 )
	{
		if( m_piano != NULL )
		{
			m_piano->m_midiEvProc->processInEvent(
				midiEvent( MidiNoteOff, 0, m_lastKey, 0 ),
								midiTime() );
			m_piano->m_pressedKeys[m_lastKey] = false;
		}

		// and let the user see that he released a key... :)
		update();

		m_lastKey = -1;
	}
}
Esempio n. 8
0
void InstrumentTrack::processOutEvent( const midiEvent & _me,
							const midiTime & _time )
{
	int k;

	switch( _me.m_type )
	{
		case MidiNoteOn:
			if( !configManager::inst()->value( "ui",
						"manualchannelpiano" ).toInt() )
			{
				m_piano.setKeyState( _me.key(), true );
			}
			if( !configManager::inst()->value( "ui",
				"disablechannelactivityindicators" ).toInt() )
			{
				if( m_notes[_me.key()] == NULL )
				{
					emit newNote();
				}
			}
			k = masterKey( _me.key() );
			if( k >= 0 && k < NumKeys )
			{
				if( m_runningMidiNotes[k] > 0 )
				{
					m_instrument->handleMidiEvent(
	midiEvent( MidiNoteOff, midiPort()->realOutputChannel(), k, 0 ),
									_time );
				}
				++m_runningMidiNotes[k];
				m_instrument->handleMidiEvent(
	midiEvent( MidiNoteOn, midiPort()->realOutputChannel(), k,
						_me.velocity() ), _time );
			}
			break;

		case MidiNoteOff:
			if( !configManager::inst()->value( "ui",
						"manualchannelpiano" ).toInt() )
			{
				m_piano.setKeyState( _me.key(), false );
			}
			k = masterKey( _me.key() );
			if( k >= 0 && k < NumKeys &&
						--m_runningMidiNotes[k] <= 0 )
			{
				m_runningMidiNotes[k] = qMax( 0, m_runningMidiNotes[k] );
				m_instrument->handleMidiEvent(
	midiEvent( MidiNoteOff, midiPort()->realOutputChannel(), k, 0 ),
									_time );
			}
			break;

		default:
			if( m_instrument != NULL )
			{
				m_instrument->handleMidiEvent(
							applyMasterKey( _me ),
									_time );
			}
			break;
	}

	// if appropriate, midi-port does futher routing
	m_midiPort.processOutEvent( _me, _time );
}
Esempio n. 9
0
void InstrumentTrackView::activityIndicatorReleased()
{
	model()->processInEvent( midiEvent( MidiNoteOff, 0, DefaultKey, 0 ),
								midiTime() );
}
Esempio n. 10
0
void InstrumentTrackView::activityIndicatorPressed()
{
	model()->processInEvent(
			midiEvent( MidiNoteOn, 0, DefaultKey, MidiMaxVelocity ),
								midiTime() );
}
Esempio n. 11
0
/*! \brief Handle a mouse move event on the piano display view
 *
 *  This handles the user dragging the mouse across the keys.  It uses
 *  code from mousePressEvent() and mouseReleaseEvent(), also correcting
 *  for if the mouse movement has stayed within one key and if the mouse
 *  has moved outside the vertical area of the keyboard (which is still
 *  allowed but won't make the volume go up to 11).
 *
 *  \param _me the ContextMenuEvent to handle.
 *  \todo Paul Wayper thinks that this code should be refactored to
 *  reduce or remove the duplication between this, the mousePressEvent()
 *  and mouseReleaseEvent() methods.
 */
void PianoView::mouseMoveEvent( QMouseEvent * _me )
{
	if( m_piano == NULL )
	{
		return;
	}

	int key_num = getKeyFromMouse( _me->pos() );
	int y_diff = _me->pos().y() - PIANO_BASE;
	int velocity = (int)( (float) y_diff /
		( ( KEY_ORDER[key_num % KeysPerOctave] == Piano::WhiteKey ) ?
			PW_WHITE_KEY_HEIGHT : PW_BLACK_KEY_HEIGHT ) *
						(float) MidiMaxVelocity );
	// maybe the user moved the mouse-cursor above or under the
	// piano-widget while holding left button so check that and
	// correct volume if necessary
	if( y_diff < 0 )
	{
		velocity = 0;
	}
	else if( y_diff >
		( ( KEY_ORDER[key_num % KeysPerOctave] == Piano::WhiteKey ) ?
				PW_WHITE_KEY_HEIGHT : PW_BLACK_KEY_HEIGHT ) )
	{
		velocity = MidiMaxVelocity;
	}

	// is the calculated key different from current key? (could be the
	// user just moved the cursor one pixel left but on the same key)
	if( key_num != m_lastKey )
	{
		if( m_lastKey != -1 )
		{
			m_piano->m_midiEvProc->processInEvent(
				midiEvent( MidiNoteOff, 0, m_lastKey, 0 ),
								midiTime() );
			m_piano->m_pressedKeys[m_lastKey] = false;
			m_lastKey = -1;
		}
		if( _me->buttons() & Qt::LeftButton )
		{
			if( _me->pos().y() > PIANO_BASE )
			{
				m_piano->m_midiEvProc->processInEvent(
					midiEvent( MidiNoteOn, 0, key_num,
							velocity ),
								midiTime() );
				m_piano->m_pressedKeys[key_num] = true;
				m_lastKey = key_num;
			}
			else
			{
				m_piano->m_midiEvProc->
					baseNoteModel()->
						setInitValue( (float) key_num );
			}
		}
		// and let the user see that he pressed a key... :)
		update();
	}
	else if( m_piano->m_pressedKeys[key_num] == true )
	{
		m_piano->m_midiEvProc->processInEvent(
				midiEvent( MidiKeyPressure, 0, key_num,
							velocity ),
								midiTime() );
	}

}
Esempio n. 12
0
int OSCServer::genericHandler(
        const char *path, const char *types,
        lo_arg **argv, int argc, void *data)
{
    UNUSED(argc); UNUSED(types);

    if(!contacted) {
        firstContact(lo_message_get_source((lo_message)data));
        contacted = true;
    }

    //find out what this message is for (track bus or master)

    std::string pathStr(path);

    pathStr = pathStr.substr(1);
    pathStr = pathStr.substr(pathStr.find("/") + 1);

    std::string object = pathStr.substr(0,pathStr.find("/"));

    pathStr = pathStr.substr(pathStr.find("/") + 1);
    std::string controllable = pathStr.substr(0,pathStr.find("/"));

    if(object == "track") {
        if(controllable == "fader") {
            //get the track number
            pathStr= pathStr.substr(pathStr.find("/") + 1);
            //build a midi event from the fader value and track number
            char data[3];
            if(pthread_mutex_lock(&idMutex) == 0) {
                data[0] = (char) CC_MASK;
                data[1] = trackIds[atoi(pathStr.c_str()) - 1];
                data[2] = (char) ((int) argv[0]->f);

                pthread_mutex_unlock(&idMutex);
            }
            MidiEvent midiEvent(data,true);
            //Send it to the jack client to handle send to Ardour
            if(!((unsigned char)data[1] == 0xFF)){
                jack_ringbuffer_write(controllerBuffer, (char *) &midiEvent,sizeof(MidiEvent));
            }
        }

        if(controllable == "bank") {
            if (argv[0]->f > 0.5){
                pathStr= pathStr.substr(pathStr.find("/") + 1);
                if(pathStr == "up"){
                    int tb = getTrackBank();
                    if((tb+1) < numTrackBanks) {
                        sendTrackBank(tb+1);
                        setTrackBank(tb+1);
                    }
                }
                else if(pathStr == "down"){
                    int tb = getTrackBank();
                    if(tb > 0) {
                        sendTrackBank(tb-1);
                        setTrackBank(tb-1);
                    }
                }

            }
        }


    } else if (object == "bus") {

        if(controllable == "fader") {
            //get the bus number(void *)this
            pathStr= pathStr.substr(pathStr.find("/") + 1);
            char data[3];
            if(pthread_mutex_lock(&idMutex) == 0) {
                data[0] = (char) CC_MASK;
                data[1] = busIds[atoi(pathStr.c_str()) - 1];
                data[2] = (char) ((int) argv[0]->f);

                pthread_mutex_unlock(&idMutex);
            }
            MidiEvent midiEvent(data,true);
            //Send it to the jack client to handle send to Ardour
            //Send it to the jack client to handle send to Ardour
            if(!((unsigned char)data[1] == 0xFF)){
                jack_ringbuffer_write(controllerBuffer, (char *) &midiEvent,sizeof(MidiEvent));
            }
        }

        if(controllable == "bank") {
            if (argv[0]->f > 0.5){
                pathStr= pathStr.substr(pathStr.find("/") + 1);
                if(pathStr == "up"){
                    int bb = getBusBank();
                    if((bb+1) < numBusBanks) {
                        setBusBank(bb+1);
                        sendBusBank(bb+1);
                    }
                }
                else if(pathStr == "down"){
                    int bb = getBusBank();
                    if(bb > 0) {
                        setBusBank(bb-1);
                        sendBusBank(bb-1);
                    }
                }

            }
        }


    }
    else if (object == "master") {
        if(controllable == "fader") {
            //build a midi event from the fader value and track number
            char data[3] = {(char) CC_MASK,MASTER_CC,(char)((int) argv[0]->f)};
            MidiEvent midiEvent(data,true);
            //Send it to the jack client to handle send to Ardour
            jack_ringbuffer_write(controllerBuffer, (char *) &midiEvent,sizeof(MidiEvent));
        }

    }

    else if (object == "scene") {
    }

    return 0;
}
Esempio n. 13
0
/*! \brief Handle a note being pressed on our keyboard display
 *
 *  \param _key the key being pressed
 */
void Piano::handleKeyPress( int _key )
{
	m_midiEvProc->processInEvent( midiEvent( MidiNoteOn, 0, _key,
						MidiMaxVelocity ), midiTime() );
	m_pressedKeys[_key] = true;
}
Esempio n. 14
0
/*! \brief Handle a note being released on our keyboard display
 *
 *  \param _key the key being releassed
 */
void Piano::handleKeyRelease( int _key )
{
	m_midiEvProc->processInEvent( midiEvent( MidiNoteOff, 0, _key, 0 ),
								midiTime() );
	m_pressedKeys[_key] = false;
}
    void jackProcess(const jack_nframes_t nframes)
    {
#if DISTRHO_PLUGIN_NUM_INPUTS > 0
        const float* audioIns[DISTRHO_PLUGIN_NUM_INPUTS];

        for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i)
            audioIns[i] = (const float*)jack_port_get_buffer(fPortAudioIns[i], nframes);
#else
        static const float** audioIns = nullptr;
#endif

#if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
        float* audioOuts[DISTRHO_PLUGIN_NUM_OUTPUTS];

        for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
            audioOuts[i] = (float*)jack_port_get_buffer(fPortAudioOuts[i], nframes);
#else
        static float** audioOuts = nullptr;
#endif

#if DISTRHO_PLUGIN_WANT_TIMEPOS
        jack_position_t pos;
        fTimePosition.playing = (jack_transport_query(fClient, &pos) == JackTransportRolling);

        if (pos.unique_1 == pos.unique_2)
        {
            fTimePosition.frame = pos.frame;

            if (pos.valid & JackTransportBBT)
            {
                fTimePosition.bbt.valid = true;

                fTimePosition.bbt.bar  = pos.bar;
                fTimePosition.bbt.beat = pos.beat;
                fTimePosition.bbt.tick = pos.tick;
                fTimePosition.bbt.barStartTick = pos.bar_start_tick;

                fTimePosition.bbt.beatsPerBar = pos.beats_per_bar;
                fTimePosition.bbt.beatType    = pos.beat_type;

                fTimePosition.bbt.ticksPerBeat   = pos.ticks_per_beat;
                fTimePosition.bbt.beatsPerMinute = pos.beats_per_minute;
            }
            else
                fTimePosition.bbt.valid = false;
        }
        else
        {
            fTimePosition.bbt.valid = false;
            fTimePosition.frame = 0;
        }

        fPlugin.setTimePosition(fTimePosition);
#endif

#if DISTRHO_PLUGIN_IS_SYNTH
        void* const midiBuf = jack_port_get_buffer(fPortMidiIn, nframes);

        if (const uint32_t eventCount = jack_midi_get_event_count(midiBuf))
        {
            uint32_t  midiEventCount = 0;
            MidiEvent midiEvents[eventCount];

            jack_midi_event_t jevent;

            for (uint32_t i=0; i < eventCount; ++i)
            {
                if (jack_midi_event_get(&jevent, midiBuf, i) != 0)
                    break;

                MidiEvent& midiEvent(midiEvents[midiEventCount++]);

                midiEvent.frame = jevent.time;
                midiEvent.size  = jevent.size;

                if (midiEvent.size > MidiEvent::kDataSize)
                    midiEvent.dataExt = jevent.buffer;
                else
                    std::memcpy(midiEvent.data, jevent.buffer, midiEvent.size);
            }

            fPlugin.run(audioIns, audioOuts, nframes, midiEvents, midiEventCount);
        }
        else
        {
            fPlugin.run(audioIns, audioOuts, nframes, nullptr, 0);
        }
#else
        fPlugin.run(audioIns, audioOuts, nframes);
#endif
    }
Esempio n. 16
0
    void lv2_run(const uint32_t sampleCount)
    {
        // cache midi input and time position first
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
        uint32_t midiEventCount = 0;
#endif

#if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_TIMEPOS
        LV2_ATOM_SEQUENCE_FOREACH(fPortEventsIn, event)
        {
            if (event == nullptr)
                break;

# if DISTRHO_PLUGIN_WANT_MIDI_INPUT
            if (event->body.type == fURIDs.midiEvent)
            {
                if (midiEventCount >= kMaxMidiEvents)
                    continue;

                const uint8_t* const data((const uint8_t*)(event + 1));

                MidiEvent& midiEvent(fMidiEvents[midiEventCount++]);

                midiEvent.frame = event->time.frames;
                midiEvent.size  = event->body.size;

                if (midiEvent.size > MidiEvent::kDataSize)
                {
                    midiEvent.dataExt = data;
                    std::memset(midiEvent.data, 0, MidiEvent::kDataSize);
                }
                else
                {
                    midiEvent.dataExt = nullptr;
                    std::memcpy(midiEvent.data, data, midiEvent.size);
                }

                continue;
            }
# endif
# if DISTRHO_PLUGIN_WANT_TIMEPOS
            if (event->body.type == fURIDs.atomBlank || event->body.type == fURIDs.atomObject)
            {
                const LV2_Atom_Object* const obj((const LV2_Atom_Object*)&event->body);

                if (obj->body.otype != fURIDs.timePosition)
                    continue;

                LV2_Atom* bar     = nullptr;
                LV2_Atom* barBeat = nullptr;
                LV2_Atom* beatUnit = nullptr;
                LV2_Atom* beatsPerBar = nullptr;
                LV2_Atom* beatsPerMinute = nullptr;
                LV2_Atom* frame = nullptr;
                LV2_Atom* speed = nullptr;
                LV2_Atom* ticksPerBeat = nullptr;

                lv2_atom_object_get(obj,
                                    fURIDs.timeBar, &bar,
                                    fURIDs.timeBarBeat, &barBeat,
                                    fURIDs.timeBeatUnit, &beatUnit,
                                    fURIDs.timeBeatsPerBar, &beatsPerBar,
                                    fURIDs.timeBeatsPerMinute, &beatsPerMinute,
                                    fURIDs.timeFrame, &frame,
                                    fURIDs.timeSpeed, &speed,
                                    fURIDs.timeTicksPerBeat, &ticksPerBeat,
                                    nullptr);

                // need to handle this first as other values depend on it
                if (ticksPerBeat != nullptr)
                {
                    /**/ if (ticksPerBeat->type == fURIDs.atomDouble)
                        fLastPositionData.ticksPerBeat = ((LV2_Atom_Double*)ticksPerBeat)->body;
                    else if (ticksPerBeat->type == fURIDs.atomFloat)
                        fLastPositionData.ticksPerBeat = ((LV2_Atom_Float*)ticksPerBeat)->body;
                    else if (ticksPerBeat->type == fURIDs.atomInt)
                        fLastPositionData.ticksPerBeat = ((LV2_Atom_Int*)ticksPerBeat)->body;
                    else if (ticksPerBeat->type == fURIDs.atomLong)
                        fLastPositionData.ticksPerBeat = ((LV2_Atom_Long*)ticksPerBeat)->body;
                    else
                        d_stderr("Unknown lv2 ticksPerBeat value type");

                    if (fLastPositionData.ticksPerBeat > 0)
                        fTimePosition.bbt.ticksPerBeat = fLastPositionData.ticksPerBeat;
                }

                // same
                if (speed != nullptr)
                {
                    /**/ if (speed->type == fURIDs.atomDouble)
                        fLastPositionData.speed = ((LV2_Atom_Double*)speed)->body;
                    else if (speed->type == fURIDs.atomFloat)
                        fLastPositionData.speed = ((LV2_Atom_Float*)speed)->body;
                    else if (speed->type == fURIDs.atomInt)
                        fLastPositionData.speed = ((LV2_Atom_Int*)speed)->body;
                    else if (speed->type == fURIDs.atomLong)
                        fLastPositionData.speed = ((LV2_Atom_Long*)speed)->body;
                    else
                        d_stderr("Unknown lv2 speed value type");

                    fTimePosition.playing = d_isNotZero(fLastPositionData.speed);
                }

                if (bar != nullptr)
                {
                    /**/ if (bar->type == fURIDs.atomDouble)
                        fLastPositionData.bar = ((LV2_Atom_Double*)bar)->body;
                    else if (bar->type == fURIDs.atomFloat)
                        fLastPositionData.bar = ((LV2_Atom_Float*)bar)->body;
                    else if (bar->type == fURIDs.atomInt)
                        fLastPositionData.bar = ((LV2_Atom_Int*)bar)->body;
                    else if (bar->type == fURIDs.atomLong)
                        fLastPositionData.bar = ((LV2_Atom_Long*)bar)->body;
                    else
                        d_stderr("Unknown lv2 bar value type");

                    if (fLastPositionData.bar >= 0)
                        fTimePosition.bbt.bar = fLastPositionData.bar + 1;
                }

                if (barBeat != nullptr)
                {
                    /**/ if (barBeat->type == fURIDs.atomDouble)
                        fLastPositionData.barBeat = ((LV2_Atom_Double*)barBeat)->body;
                    else if (barBeat->type == fURIDs.atomFloat)
                        fLastPositionData.barBeat = ((LV2_Atom_Float*)barBeat)->body;
                    else if (barBeat->type == fURIDs.atomInt)
                        fLastPositionData.barBeat = ((LV2_Atom_Int*)barBeat)->body;
                    else if (barBeat->type == fURIDs.atomLong)
                        fLastPositionData.barBeat = ((LV2_Atom_Long*)barBeat)->body;
                    else
                        d_stderr("Unknown lv2 barBeat value type");

                    if (fLastPositionData.barBeat >= 0.0f)
                    {
                        const double rest = std::fmod(fLastPositionData.barBeat, 1.0);
                        fTimePosition.bbt.beat = fLastPositionData.barBeat-rest+1.0;
                        fTimePosition.bbt.tick = rest*fTimePosition.bbt.ticksPerBeat+0.5;
                    }
                }

                if (beatUnit != nullptr)
                {
                    /**/ if (beatUnit->type == fURIDs.atomDouble)
                        fLastPositionData.beatUnit = ((LV2_Atom_Double*)beatUnit)->body;
                    else if (beatUnit->type == fURIDs.atomFloat)
                        fLastPositionData.beatUnit = ((LV2_Atom_Float*)beatUnit)->body;
                    else if (beatUnit->type == fURIDs.atomInt)
                        fLastPositionData.beatUnit = ((LV2_Atom_Int*)beatUnit)->body;
                    else if (beatUnit->type == fURIDs.atomLong)
                        fLastPositionData.beatUnit = ((LV2_Atom_Long*)beatUnit)->body;
                    else
                        d_stderr("Unknown lv2 beatUnit value type");

                    if (fLastPositionData.beatUnit > 0)
                        fTimePosition.bbt.beatType = fLastPositionData.beatUnit;
                }

                if (beatsPerBar != nullptr)
                {
                    /**/ if (beatsPerBar->type == fURIDs.atomDouble)
                        fLastPositionData.beatsPerBar = ((LV2_Atom_Double*)beatsPerBar)->body;
                    else if (beatsPerBar->type == fURIDs.atomFloat)
                        fLastPositionData.beatsPerBar = ((LV2_Atom_Float*)beatsPerBar)->body;
                    else if (beatsPerBar->type == fURIDs.atomInt)
                        fLastPositionData.beatsPerBar = ((LV2_Atom_Int*)beatsPerBar)->body;
                    else if (beatsPerBar->type == fURIDs.atomLong)
                        fLastPositionData.beatsPerBar = ((LV2_Atom_Long*)beatsPerBar)->body;
                    else
                        d_stderr("Unknown lv2 beatsPerBar value type");

                    if (fLastPositionData.beatsPerBar > 0.0f)
                        fTimePosition.bbt.beatsPerBar = fLastPositionData.beatsPerBar;
                }

                if (beatsPerMinute != nullptr)
                {
                    /**/ if (beatsPerMinute->type == fURIDs.atomDouble)
                        fLastPositionData.beatsPerMinute = ((LV2_Atom_Double*)beatsPerMinute)->body;
                    else if (beatsPerMinute->type == fURIDs.atomFloat)
                        fLastPositionData.beatsPerMinute = ((LV2_Atom_Float*)beatsPerMinute)->body;
                    else if (beatsPerMinute->type == fURIDs.atomInt)
                        fLastPositionData.beatsPerMinute = ((LV2_Atom_Int*)beatsPerMinute)->body;
                    else if (beatsPerMinute->type == fURIDs.atomLong)
                        fLastPositionData.beatsPerMinute = ((LV2_Atom_Long*)beatsPerMinute)->body;
                    else
                        d_stderr("Unknown lv2 beatsPerMinute value type");

                    if (fLastPositionData.beatsPerMinute > 0.0f)
                    {
                        fTimePosition.bbt.beatsPerMinute = fLastPositionData.beatsPerMinute;

                        if (d_isNotZero(fLastPositionData.speed))
                            fTimePosition.bbt.beatsPerMinute *= std::abs(fLastPositionData.speed);
                    }
                }

                if (frame != nullptr)
                {
                    /**/ if (frame->type == fURIDs.atomDouble)
                        fLastPositionData.frame = ((LV2_Atom_Double*)frame)->body;
                    else if (frame->type == fURIDs.atomFloat)
                        fLastPositionData.frame = ((LV2_Atom_Float*)frame)->body;
                    else if (frame->type == fURIDs.atomInt)
                        fLastPositionData.frame = ((LV2_Atom_Int*)frame)->body;
                    else if (frame->type == fURIDs.atomLong)
                        fLastPositionData.frame = ((LV2_Atom_Long*)frame)->body;
                    else
                        d_stderr("Unknown lv2 frame value type");

                    if (fLastPositionData.frame >= 0)
                        fTimePosition.frame = fLastPositionData.frame;
                }

                fTimePosition.bbt.barStartTick = fTimePosition.bbt.ticksPerBeat*
                                                 fTimePosition.bbt.beatsPerBar*
                                                 (fTimePosition.bbt.bar-1);

                fTimePosition.bbt.valid = (fLastPositionData.beatsPerMinute > 0.0 &&
                                           fLastPositionData.beatUnit > 0 &&
                                           fLastPositionData.beatsPerBar > 0.0f);

                fPlugin.setTimePosition(fTimePosition);

                continue;
            }
# endif
        }
#endif

        // check for messages from UI
#if DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI
        LV2_ATOM_SEQUENCE_FOREACH(fPortEventsIn, event)
        {
            if (event == nullptr)
                break;

            if (event->body.type == fURIDs.distrhoState && fWorker != nullptr)
            {
                const void* const data((const void*)(event + 1));

                // check if this is our special message
                if (std::strcmp((const char*)data, "__dpf_ui_data__") == 0)
                {
                    for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i)
                        fNeededUiSends[i] = true;
                }
                else
                // no, send to DSP as usual
                {
                    fWorker->schedule_work(fWorker->handle, event->body.size, data);
                }
            }
        }
#endif

        // Check for updated parameters
        float curValue;

        for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i)
        {
            if (fPortControls[i] == nullptr)
                continue;

            curValue = *fPortControls[i];

            if (fLastControlValues[i] != curValue && ! fPlugin.isParameterOutput(i))
            {
                fLastControlValues[i] = curValue;
                fPlugin.setParameterValue(i, curValue);
            }
        }

        // Run plugin
        if (sampleCount != 0)
        {
#if DISTRHO_PLUGIN_WANT_MIDI_INPUT
            fPlugin.run(fPortAudioIns, fPortAudioOuts, sampleCount, fMidiEvents, midiEventCount);
#else
            fPlugin.run(fPortAudioIns, fPortAudioOuts, sampleCount);
#endif

#if DISTRHO_PLUGIN_WANT_TIMEPOS
            // update timePos for next callback
            if (d_isNotZero(fLastPositionData.speed))
            {
                if (fLastPositionData.speed > 0.0)
                {
                    // playing forwards
                    fLastPositionData.frame += sampleCount;
                }
                else
                {
                    // playing backwards
                    fLastPositionData.frame -= sampleCount;

                    if (fLastPositionData.frame < 0)
                        fLastPositionData.frame = 0;
                }

                fTimePosition.frame = fLastPositionData.frame;

                if (fTimePosition.bbt.valid)
                {
                    const double beatsPerMinute = fLastPositionData.beatsPerMinute * fLastPositionData.speed;
                    const double framesPerBeat  = 60.0 * fSampleRate / beatsPerMinute;
                    const double addedBarBeats  = double(sampleCount) / framesPerBeat;

                    if (fLastPositionData.barBeat >= 0.0f)
                    {
                        fLastPositionData.barBeat = std::fmod(fLastPositionData.barBeat+addedBarBeats,
                                                              fLastPositionData.beatsPerBar);

                        const double rest = std::fmod(fLastPositionData.barBeat, 1.0);
                        fTimePosition.bbt.beat = fLastPositionData.barBeat-rest+1.0;
                        fTimePosition.bbt.tick = rest*fTimePosition.bbt.ticksPerBeat+0.5;

                        if (fLastPositionData.bar >= 0)
                        {
                            fLastPositionData.bar += std::floor((fLastPositionData.barBeat+addedBarBeats)/
                                                             fLastPositionData.beatsPerBar);

                            if (fLastPositionData.bar < 0)
                                fLastPositionData.bar = 0;

                            fTimePosition.bbt.bar = fLastPositionData.bar + 1;

                            fTimePosition.bbt.barStartTick = fTimePosition.bbt.ticksPerBeat*
                                                             fTimePosition.bbt.beatsPerBar*
                                                            (fTimePosition.bbt.bar-1);
                        }
                    }

                    fTimePosition.bbt.beatsPerMinute = std::abs(beatsPerMinute);
                }
            }
#endif
        }

        updateParameterOutputs();

#if DISTRHO_PLUGIN_WANT_STATE && DISTRHO_PLUGIN_HAS_UI
        const uint32_t capacity = fPortEventsOut->atom.size;

        bool needsInit = true;
        uint32_t size, offset = 0;
        LV2_Atom_Event* aev;

        // TODO - MIDI Output

        for (uint32_t i=0, count=fPlugin.getStateCount(); i < count; ++i)
        {
            if (! fNeededUiSends[i])
                continue;

            const String& key = fPlugin.getStateKey(i);

            for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
            {
                const String& curKey = cit->first;

                if (curKey != key)
                    continue;

                const String& value = cit->second;

                // set msg size (key + value + separator + 2x null terminator)
                const size_t msgSize(key.length()+value.length()+3);

                if (sizeof(LV2_Atom_Event) + msgSize > capacity - offset)
                    break;

                if (needsInit)
                {
                    fPortEventsOut->atom.size = 0;
                    fPortEventsOut->atom.type = fURIDs.atomSequence;
                    fPortEventsOut->body.unit = 0;
                    fPortEventsOut->body.pad  = 0;
                    needsInit = false;
                }

                // reserve msg space
                char msgBuf[msgSize];
                std::memset(msgBuf, 0, msgSize);

                // write key and value in atom bufer
                std::memcpy(msgBuf, key.buffer(), key.length());
                std::memcpy(msgBuf+(key.length()+1), value.buffer(), value.length());

                // put data
                aev = (LV2_Atom_Event*)(LV2_ATOM_CONTENTS(LV2_Atom_Sequence, fPortEventsOut) + offset);
                aev->time.frames = 0;
                aev->body.type   = fURIDs.distrhoState;
                aev->body.size   = msgSize;
                std::memcpy(LV2_ATOM_BODY(&aev->body), msgBuf, msgSize-1);

                size    = lv2_atom_pad_size(sizeof(LV2_Atom_Event) + msgSize);
                offset += size;
                fPortEventsOut->atom.size += size;

                fNeededUiSends[i] = false;
                break;
            }
        }
#endif
    }