Reworked MIDI event handling in InstrumentTrack and renamed MIDI classes

The MIDI event handling in InstrumentTrack was complex and buggy. It has
been simplified now such that processInEvent() tries to handle note on,
note off and key pressure events. The actions taken should result in
equivalent calls to processOutEvent() by NotePlayHandle instances. The
processOutEvent() function sends according MIDI events to the attached
instruments. All unhandled MIDI events are directly forwarded to the
instrument in processInEvent().

It's possible that some corner-cases are not handled yet with the new code
and we have regressions now.

Furthermore renamed midiTime/midiEvent to MidiTime/MidiEvent to match
coding style.

Closes #72.
This commit is contained in:
Tobias Doerffel
2014-01-25 23:52:25 +01:00
parent 790266f0c1
commit 3a827f061f
70 changed files with 901 additions and 891 deletions

View File

@@ -1744,8 +1744,7 @@ p->putValue( jt->pos, value, false );
{
continue;
}
trackContentObject * tco =
bb_tracks[it->pattern]->createTCO( midiTime() );
trackContentObject * tco = bb_tracks[it->pattern]->createTCO( MidiTime() );
tco->movePosition( it->position );
if( it->length != DefaultTicksPerTact )
{

View File

@@ -38,6 +38,7 @@
#include "pattern.h"
#include "Instrument.h"
#include "MainWindow.h"
#include "MidiTime.h"
#include "debug.h"
#include "embed.h"
#include "song.h"
@@ -152,7 +153,7 @@ public:
AutomationTrack * at;
AutomationPattern * ap;
midiTime lastPos;
MidiTime lastPos;
smfMidiCC & create( TrackContainer* tc )
{
@@ -172,11 +173,11 @@ public:
}
smfMidiCC & putValue( midiTime time, AutomatableModel * objModel, float value )
smfMidiCC & putValue( MidiTime time, AutomatableModel * objModel, float value )
{
if( !ap || time > lastPos + DefaultTicksPerTact )
{
midiTime pPos = midiTime( time.getTact(), 0 );
MidiTime pPos = MidiTime( time.getTact(), 0 );
ap = dynamic_cast<AutomationPattern*>(
at->createTCO(0) );
ap->movePosition( pPos );
@@ -186,7 +187,7 @@ public:
lastPos = time;
time = time - ap->startPosition();
ap->putValue( time, value, false );
ap->changeLength( midiTime( time.getTact() + 1, 0 ) );
ap->changeLength( MidiTime( time.getTact() + 1, 0 ) );
return *this;
}
@@ -212,7 +213,7 @@ public:
Instrument * it_inst;
bool isSF2;
bool hasNotes;
midiTime lastEnd;
MidiTime lastEnd;
smfMidiChannel * create( TrackContainer* tc )
{
@@ -247,7 +248,7 @@ public:
{
if( !p || n.pos() > lastEnd + DefaultTicksPerTact )
{
midiTime pPos = midiTime(n.pos().getTact(), 0 );
MidiTime pPos = MidiTime( n.pos().getTact(), 0 );
p = dynamic_cast<pattern *>( it->createTCO( 0 ) );
p->movePosition( pPos );
}

View File

@@ -29,7 +29,7 @@
#include <QtCore/QPair>
#include <QtCore/QVector>
#include "midi.h"
#include "MidiEvent.h"
#include "ImportFilter.h"
@@ -117,8 +117,8 @@ private:
}
typedef QVector<QPair<int, midiEvent> > eventVector;
eventVector m_events;
typedef QVector<QPair<int, MidiEvent> > EventVector;
EventVector m_events;
int m_timingDivision;
} ;

View File

@@ -286,17 +286,16 @@ int opl2instrument::pushVoice(int v) {
return i;
}
bool opl2instrument::handleMidiEvent( const midiEvent & _me,
const midiTime & _time )
bool opl2instrument::handleMidiEvent( const MidiEvent& event, const MidiTime& time )
{
emulatorMutex.lock();
int key, vel, voice, tmp_pb;
switch(_me.m_type) {
switch(event.type()) {
case MidiNoteOn:
// to get us in line with MIDI(?)
key = _me.key() +12;
vel = _me.velocity();
key = event.key() +12;
vel = event.velocity();
voice = popVoice();
if( voice != OPL2_NO_VOICE ) {
@@ -311,7 +310,7 @@ bool opl2instrument::handleMidiEvent( const midiEvent & _me,
}
break;
case MidiNoteOff:
key = _me.key() +12;
key = event.key() +12;
for(voice=0; voice<9; ++voice) {
if( voiceNote[voice] == key ) {
theEmulator->write(0xA0+voice, fnums[key] & 0xff);
@@ -323,8 +322,8 @@ bool opl2instrument::handleMidiEvent( const midiEvent & _me,
velocities[key] = 0;
break;
case MidiKeyPressure:
key = _me.key() +12;
vel = _me.velocity();
key = event.key() +12;
vel = event.velocity();
if( velocities[key] != 0) {
velocities[key] = vel;
}
@@ -337,12 +336,12 @@ bool opl2instrument::handleMidiEvent( const midiEvent & _me,
case MidiPitchBend:
// Update fnumber table
// Pitchbend should be in the range 0...16383 but the new range knob gets it wrong.
// tmp_pb = (2*BEND_CENTS)*((float)_me.m_data.m_param[0]/16383)-BEND_CENTS;
// tmp_pb = (2*BEND_CENTS)*((float)event.m_data.m_param[0]/16383)-BEND_CENTS;
// Something like 100 cents = 8192, but offset by 8192 so the +/-100 cents range goes from 0...16383?
tmp_pb = ( _me.m_data.m_param[0]-8192 ) * BEND_CENTS / 8192;
tmp_pb = ( event.pitchBend()-8192 ) * BEND_CENTS / 8192;
printf("Pitch bend: %d -> %d cents\n",_me.m_data.m_param[0],tmp_pb);
printf("Pitch bend: %d -> %d cents\n",event.pitchBend(),tmp_pb);
if( tmp_pb != pitchbend ) {
pitchbend = tmp_pb;
tuneEqual(69, 440.0);
@@ -356,7 +355,7 @@ bool opl2instrument::handleMidiEvent( const midiEvent & _me,
}
break;
default:
printf("Midi event type %d\n",_me.m_type);
printf("Midi event type %d\n",event.type());
}
emulatorMutex.unlock();
return true;

View File

@@ -50,8 +50,7 @@ public:
inline virtual bool isMidiBased() const { return true; }
virtual bool handleMidiEvent( const midiEvent & _me,
const midiTime & _time );
virtual bool handleMidiEvent( const MidiEvent& event, const MidiTime& time );
virtual void play( sampleFrame * _working_buffer );
void saveSettings( QDomDocument & _doc, QDomElement & _this );

View File

@@ -310,13 +310,12 @@ void vestigeInstrument::play( sampleFrame * _buf )
bool vestigeInstrument::handleMidiEvent( const midiEvent & _me,
const midiTime & _time )
bool vestigeInstrument::handleMidiEvent( const MidiEvent& event, const MidiTime& time )
{
m_pluginMutex.lock();
if( m_plugin != NULL )
{
m_plugin->processMidiEvent( _me, _time );
m_plugin->processMidiEvent( event, time );
}
m_pluginMutex.unlock();
@@ -779,8 +778,7 @@ void VestigeInstrumentView::noteOffAll( void )
{
for( int key = 0; key <= MidiMaxNote; ++key )
{
m_vi->m_plugin->processMidiEvent(
midiEvent( MidiNoteOff, 0, key, 0 ), 0 );
m_vi->m_plugin->processMidiEvent( MidiEvent( MidiNoteOff, 0, key, 0 ), 0 );
}
}
m_vi->m_pluginMutex.unlock();

View File

@@ -1,7 +1,7 @@
/*
* vestige.h - instrument VeSTige for hosting VST-plugins
*
* Copyright (c) 2005-2012 Tobias Doerffel <tobydox/at/users.sourceforge.net>
* Copyright (c) 2005-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
*
@@ -34,7 +34,6 @@
#include "Instrument.h"
#include "InstrumentView.h"
#include "midi.h"
#include "note.h"
#include "knob.h"
@@ -69,8 +68,7 @@ public:
return true;
}
virtual bool handleMidiEvent( const midiEvent & _me,
const midiTime & _time );
virtual bool handleMidiEvent( const MidiEvent& event, const MidiTime& time );
virtual PluginView * instantiateView( QWidget * _parent );

View File

@@ -1,7 +1,7 @@
/*
* LocalZynAddSubFx.cpp - local implementation of ZynAddSubFx plugin
*
* Copyright (c) 2009-2013 Tobias Doerffel <tobydox/at/users.sourceforge.net>
* Copyright (c) 2009-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
*
@@ -190,47 +190,43 @@ void LocalZynAddSubFx::setLmmsWorkingDir( const std::string & _dir )
void LocalZynAddSubFx::processMidiEvent( const midiEvent & _e )
void LocalZynAddSubFx::processMidiEvent( const MidiEvent& event )
{
// all functions are called while m_master->mutex is held
static NULLMidiIn midiIn;
switch( _e.m_type )
switch( event.type() )
{
case MidiNoteOn:
if( _e.velocity() > 0 )
if( event.velocity() > 0 )
{
if( _e.key() <= 0 || _e.key() >= 128 )
if( event.key() <= 0 || event.key() >= 128 )
{
break;
}
if( m_runningNotes[_e.key()] > 0 )
if( m_runningNotes[event.key()] > 0 )
{
m_master->NoteOff( _e.channel(), _e.key() );
m_master->NoteOff( event.channel(), event.key() );
}
++m_runningNotes[_e.key()];
m_master->NoteOn( _e.channel(), _e.key(), _e.velocity() );
++m_runningNotes[event.key()];
m_master->NoteOn( event.channel(), event.key(), event.velocity() );
break;
}
case MidiNoteOff:
if( _e.key() <= 0 || _e.key() >= 128 )
if( event.key() <= 0 || event.key() >= 128 )
{
break;
}
if( --m_runningNotes[_e.key()] <= 0 )
if( --m_runningNotes[event.key()] <= 0 )
{
m_master->NoteOff( _e.channel(), _e.key() );
m_master->NoteOff( event.channel(), event.key() );
}
break;
case MidiPitchBend:
m_master->SetController( _e.channel(), C_pitchwheel,
_e.m_data.m_param[0] +
_e.m_data.m_param[1]*128-8192 );
m_master->SetController( event.channel(), C_pitchwheel, event.pitchBend()-8192 );
break;
case MidiControlChange:
m_master->SetController( _e.channel(),
midiIn.getcontroller( _e.m_data.m_param[0] ),
_e.m_data.m_param[1] );
m_master->SetController( event.channel(), midiIn.getcontroller( event.controllerNumber() ), event.controllerValue() );
break;
default:
break;

View File

@@ -1,7 +1,7 @@
/*
* LocalZynAddSubFx.h - local implementation of ZynAddSubFx plugin
*
* Copyright (c) 2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
* Copyright (c) 2009-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
*
@@ -25,6 +25,7 @@
#ifndef _LOCAL_ZYNADDSUBFX_H
#define _LOCAL_ZYNADDSUBFX_H
#include "MidiEvent.h"
#include "note.h"
class Master;
@@ -48,7 +49,7 @@ public:
void setPresetDir( const std::string & _dir );
void setLmmsWorkingDir( const std::string & _dir );
void processMidiEvent( const midiEvent & _e );
void processMidiEvent( const MidiEvent& event );
void processAudio( sampleFrame * _out );

View File

@@ -1,7 +1,7 @@
/*
* RemoteZynAddSubFx.cpp - ZynAddSubFx-embedding plugin
*
* Copyright (c) 2008-2012 Tobias Doerffel <tobydox/at/users.sourceforge.net>
* Copyright (c) 2008-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
*
@@ -126,10 +126,9 @@ public:
}
// all functions are called while m_master->mutex is held
virtual void processMidiEvent( const midiEvent & _e,
const f_cnt_t /* _offset */ )
virtual void processMidiEvent( const MidiEvent& event, const f_cnt_t /* _offset */ )
{
LocalZynAddSubFx::processMidiEvent( _e );
LocalZynAddSubFx::processMidiEvent( event );
}

View File

@@ -340,14 +340,13 @@ void ZynAddSubFxInstrument::play( sampleFrame * _buf )
bool ZynAddSubFxInstrument::handleMidiEvent( const midiEvent & _me,
const midiTime & _time )
bool ZynAddSubFxInstrument::handleMidiEvent( const MidiEvent& event, const MidiTime& time )
{
// do not forward external MIDI Control Change events if the according
// LED is not checked
if( _me.type() == MidiControlChange &&
_me.sourcePort() != this &&
m_forwardMidiCcModel.value() == false )
if( event.type() == MidiControlChange &&
event.sourcePort() != this &&
m_forwardMidiCcModel.value() == false )
{
return true;
}
@@ -355,11 +354,11 @@ bool ZynAddSubFxInstrument::handleMidiEvent( const midiEvent & _me,
m_pluginMutex.lock();
if( m_remotePlugin )
{
m_remotePlugin->processMidiEvent( _me, 0 );
m_remotePlugin->processMidiEvent( event, 0 );
}
else
{
m_plugin->processMidiEvent( _me );
m_plugin->processMidiEvent( event );
}
m_pluginMutex.unlock();
@@ -446,8 +445,7 @@ void ZynAddSubFxInstrument::initPlugin()
void ZynAddSubFxInstrument::sendControlChange( MidiControllers midiCtl, float value )
{
handleMidiEvent( midiEvent( MidiControlChange, instrumentTrack()->midiPort()->realOutputChannel(), midiCtl, (int) value, this ),
midiTime() );
handleMidiEvent( MidiEvent( MidiControlChange, instrumentTrack()->midiPort()->realOutputChannel(), midiCtl, (int) value, this ) );
}

View File

@@ -70,8 +70,7 @@ public:
virtual void play( sampleFrame * _working_buffer );
virtual bool handleMidiEvent( const midiEvent & _me,
const midiTime & _time );
virtual bool handleMidiEvent( const MidiEvent& event, const MidiTime& time = MidiTime() );
virtual void saveSettings( QDomDocument & _doc, QDomElement & _parent );
virtual void loadSettings( const QDomElement & _this );