fixed lot of issues in MIDI-import-filter and added support for tempo-change-events

git-svn-id: https://lmms.svn.sf.net/svnroot/lmms/trunk/lmms@497 0778d3d1-df1d-0410-868b-ea421aaaa00d
This commit is contained in:
Tobias Doerffel
2007-07-23 21:52:02 +00:00
parent 6b89322a7c
commit 41eccc8bf8
9 changed files with 211 additions and 62 deletions

View File

@@ -1,3 +1,29 @@
2007-07-23 Tobias Doerffel <tobydox/at/users/dot/sourceforge/dot/net>
* plugins/midi_import/midi_import.h:
* plugins/midi_import/midi_import.cpp:
- when adding notes to pattern *never* let them be quantized - fixes
bug where notes of imported MIDI-files were not where they should have
been
- added support for tempo-change-events by modifying
automation-pattern of tempo-knob of track-container (if such exists)
- import track-names
- proper handling of time-base, -division etc.
* include/midi.h:
added midiMetaEvents-enum
* include/song_editor.h:
* include/track_container.h:
* src/core/song_editor.cpp:
added virtual tempoAutomationPattern()-method which returns
automation-pattern of tempo-object (i.e. bpmSpinBox) if such exists
for a certain track-container
* include/automation_pattern.h:
- added non-const version of object()-method
- made lot of other methods const
2007-07-21 Tobias Doerffel <tobydox/at/users/dot/sourceforge/dot/net>
* include/mixer.h:

View File

@@ -2,8 +2,8 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.50)
AC_INIT(lmms, 0.2.1-svn20070721, lmms-devel/at/lists/dot/sf/dot/net)
AM_INIT_AUTOMAKE(lmms, 0.2.1-svn20070721)
AC_INIT(lmms, 0.2.1-svn20070723, lmms-devel/at/lists/dot/sf/dot/net)
AM_INIT_AUTOMAKE(lmms, 0.2.1-svn20070723)
AM_CONFIG_HEADER(config.h)

View File

@@ -95,19 +95,24 @@ public:
return( classNodeName() );
}
inline const track * getTrack( void )
inline const track * getTrack( void ) const
{
return( m_track );
}
inline const levelObject * object( void )
inline const levelObject * object( void ) const
{
return( m_object );
}
inline levelObject * object( void )
{
return( m_object );
}
void processMidiTime( const midiTime & _time );
inline bool updateFirst( void )
inline bool updateFirst( void ) const
{
return( m_update_first );
}
@@ -117,7 +122,7 @@ public:
m_update_first = _update;
}
void forgetTrack()
void forgetTrack( void )
{
m_track = NULL;
}

View File

@@ -59,7 +59,23 @@ enum midiEventTypes
MIDI_SYSTEM_RESET = 0xFF,
// meta event - for midi files only
MIDI_META_EVENT = 0xFF,
MIDI_TEMPO = 0xFF51
} ;
enum midiMetaEvents
{
MIDI_COPYRIGHT = 0x02,
MIDI_TRACK_NAME = 0x03,
MIDI_INST_NAME = 0x04,
MIDI_LYRIC = 0x05,
MIDI_MARKER = 0x06,
MIDI_CUE_POINT = 0x07,
MIDI_PORT_NUMBER = 0x21,
MIDI_EOT = 0x2f,
MIDI_SET_TEMPO = 0x51,
MIDI_SMPTE_OFFSET = 0x54,
MIDI_TIME_SIGNATURE = 0x58,
MIDI_KEY_SIGNATURE = 0x59,
MIDI_SEQUENCER_EVENT = 0x7f
} ;

View File

@@ -130,6 +130,7 @@ public:
bpm_t getTempo( void );
virtual automationPattern * tempoAutomationPattern( void );
// every function that replaces current file (e.g. creates new file,

View File

@@ -82,6 +82,11 @@ public:
return( m_currentPosition );
}
virtual automationPattern * tempoAutomationPattern( void )
{
return( NULL );
}
virtual bool fixedTCOs( void ) const
{
return( FALSE );

View File

@@ -27,9 +27,12 @@
#include "track_container.h"
#include "instrument_track.h"
#include "pattern.h"
#include "automation_pattern.h"
#include "level_object.h"
#include "debug.h"
#ifdef QT4
#include <Qt/QtXml>
@@ -74,8 +77,7 @@ plugin::descriptor midiimport_plugin_descriptor =
midiImport::midiImport( const QString & _file ) :
importFilter( _file, &midiimport_plugin_descriptor ),
m_events(),
m_smpteTiming( FALSE ),
m_tempo( 120 )
m_timingDivision( 0 )
{
}
@@ -114,6 +116,26 @@ bool midiImport::tryImport( trackContainer * _tc )
static inline unsigned long gcd( unsigned long a, unsigned long b )
{
unsigned long r;
if( a < b )
{
r = a;
a = b;
b = r;
}
while( ( r = a % b ) != 0 )
{
a = b;
b = r;
}
return b;
}
bool FASTCALL midiImport::readSMF( trackContainer * _tc )
{
// the curren position is immediately after the "MThd" id
@@ -145,15 +167,14 @@ invalid_format:
printf( "tracks: %d\n", num_tracks );
#endif
int time_division = readInt( 2 );
if( time_division < 0 )
m_timingDivision = readInt( 2 );
if( m_timingDivision < 0 )
{
goto invalid_format;
}
#ifdef LMMS_DEBUG
printf( "time-division: %d\n", time_division );
#endif
m_smpteTiming = !!( time_division & 0x8000 );
#ifdef QT4
QProgressDialog pd( trackContainer::tr( "Importing MIDI-file..." ),
@@ -166,6 +187,22 @@ invalid_format:
pd.setWindowTitle( trackContainer::tr( "Please wait..." ) );
pd.show();
// calculate some timing stuff
int crotchet_time = 16;
int divisor = m_timingDivision ? m_timingDivision : 96;
int multiplier = crotchet_time;
int g = gcd( crotchet_time, divisor );
multiplier /= g;
divisor /= g;
// try to set default tempo
automationPattern * tap = _tc->tempoAutomationPattern();
if( tap != NULL )
{
tap->object()->setLevel( 120 );
tap->putValue( 0, 120 );
}
// read tracks
for( int i = 0; i < num_tracks; ++i )
{
@@ -211,33 +248,64 @@ invalid_format:
continue;
}
if( !readTrack( file().pos() + len ) )
QString track_name = "";
if( !readTrack( file().pos() + len, track_name ) )
{
return( FALSE );
}
if( i == 0 )
{
if( tap == NULL )
{
continue;
}
for( eventVector::const_iterator it = m_events.begin();
it != m_events.end(); ++it )
{
const int tick = it->first;
const midiEvent & ev = it->second;
if( ev.m_type == MIDI_META_EVENT )
{
switch( ev.m_data.m_param[0] )
{
case MIDI_SET_TEMPO:
{
tap->putValue( midiTime( ( tick * multiplier ) / divisor ),
ev.m_data.m_param[1], FALSE );
break;
}
default:
break;
}
}
}
continue;
}
// now create new channel-track for reading track
instrumentTrack * ct = dynamic_cast<instrumentTrack *>(
// now create new instrument-track for reading in track
instrumentTrack * it = dynamic_cast<instrumentTrack *>(
track::create(
track::INSTRUMENT_TRACK,
_tc ) );
#ifdef LMMS_DEBUG
assert( ct != NULL );
assert( it != NULL );
#endif
// TODO: setup program, channel etc.
ct->loadInstrument( "tripleoscillator" );
ct->toggledInstrumentTrackButton( FALSE );
it->loadInstrument( "tripleoscillator" );
it->toggledInstrumentTrackButton( FALSE );
// TODO: track_name.trimmed().isEmpty() (Qt4)
if( !track_name.isEmpty() )
{
it->setName( track_name );
}
// now create pattern to store notes in
pattern * p = dynamic_cast<pattern *>( ct->createTCO( 0 ) );
pattern * p = dynamic_cast<pattern *>( it->createTCO( 0 ) );
#ifdef LMMS_DEBUG
assert( p != NULL );
#endif
ct->addTCO( p );
it->addTCO( p );
// init keys
int keys[NOTES_PER_OCTAVE * OCTAVES][2];
@@ -273,23 +341,33 @@ invalid_format:
NOTES_PER_OCTAVE * OCTAVES &&
keys[ev.key()][0] >= 0 )
{
note n( midiTime( ( tick - keys[ev.key()][0] ) * 16 / m_tempo ),
midiTime( keys[ev.key()][0] * 16 / m_tempo ),
note n( midiTime( ( ( tick - keys[ev.key()][0] ) * multiplier ) / divisor ),
midiTime( ( keys[ev.key()][0] * multiplier ) / divisor ),
(tones)( ev.key() % NOTES_PER_OCTAVE ),
(octaves)( ev.key() / NOTES_PER_OCTAVE ),
keys[ev.key()][1] * 100 / 128 );
p->addNote( n );
p->addNote( n, FALSE );
keys[ev.key()][0] = -1;
}
break;
case MIDI_TEMPO:
/* case MIDI_META_EVENT:
{
switch( ev.m_data.m_param[0] )
{
case MIDI_SET_TEMPO:
{
break;
}
default:
break;
}
break;
}*/
default:
/* printf( "Unhandled event: %#x\n",
ev.m_type );*/
/* printf( "Unhandled event: %#x\n",
(int) ev.m_type );*/
break;
}
}
@@ -345,7 +423,7 @@ data_not_found:
bool FASTCALL midiImport::readTrack( int _track_end )
bool FASTCALL midiImport::readTrack( int _track_end, QString & _track_name )
{
int tick = 0;
unsigned char last_cmd = 0;
@@ -460,7 +538,17 @@ bool FASTCALL midiImport::readTrack( int _track_end )
}*/
switch( c )
{
case 0x21: // port number
case MIDI_TRACK_NAME:
if( len > 0 )
{
char * n = new char[len+1];
readBlock( n, len );
n[len] = 0;
_track_name += n;
delete[] n;
}
break;
case MIDI_PORT_NUMBER:
if( len < 1 )
{
error();
@@ -472,49 +560,50 @@ bool FASTCALL midiImport::readTrack( int _track_end )
skip( len );
break;
case 0x2F: // end of track
case MIDI_EOT:
//track->end_tick = tick;
skip( _track_end -
file().pos() );
return( TRUE );
case 0x51: // tempo
case MIDI_SET_TEMPO: // tempo
{
if( len < 3 )
{
error();
return( FALSE );
}
if( m_smpteTiming )
{
// SMPTE timing
// doesnt change
skip( len );
}
else
{
int tempo = readByte() << 16;
tempo |= readByte() << 8;
tempo |= readByte();
tempo = ( 60*1000*1000 ) / tempo;
m_events.push_back( qMakePair( tick,
midiEvent( MIDI_TEMPO,
0,
tempo,
0 ) ) );
/* event = new_event(track, 0);
event->type = SND_SEQ_EVENT_TEMPO;
event->port = port;
event->tick = tick;
event->data.tempo = read_byte() << 16;
event->data.tempo |= read_byte() << 8;
event->data.tempo |= read_byte();
skip( len -3 );*/
skip( len-3 );
}
int tempo = readByte() << 16;
tempo |= readByte() << 8;
tempo |= readByte();
tempo = ( 60*1000*1000 ) / tempo;
m_events.push_back( qMakePair( tick, midiEvent( MIDI_META_EVENT, 0, MIDI_SET_TEMPO, tempo ) ) );
break;
}
case MIDI_TIME_SIGNATURE:
{
int nominator = readByte();
int denominator = 1 << (int) readByte();
int clocks = readByte();
int notes = readByte();
if( nominator == 0 )
{
nominator = 4;
}
if( denominator == 0 )
{
denominator = 4;
}
#ifdef LMMS_DEBUG
printf("nom:%d denom:%d clocks:%d notes:%d\n",nominator,denominator,clocks,notes);
#endif
break;
}
default:// ignore all other
// meta events
#ifdef LMMS_DEBUG
printf("meta event %d\n", (int) c );
#endif
skip( len );
break;
}

View File

@@ -64,7 +64,7 @@ private:
bool FASTCALL readSMF( trackContainer * _tc );
bool FASTCALL readRIFF( trackContainer * _tc );
bool FASTCALL readTrack( int _track_end );
bool FASTCALL readTrack( int _track_end, QString & _track_name );
void error( void );
@@ -133,8 +133,7 @@ private:
typedef vvector<QPair<int, midiEvent> > eventVector;
eventVector m_events;
bool m_smpteTiming;
int m_tempo;
int m_timingDivision;
} ;

View File

@@ -1355,6 +1355,14 @@ bpm_t songEditor::getTempo( void )
automationPattern * songEditor::tempoAutomationPattern( void )
{
return( m_bpmSpinBox->getAutomationPattern() );
}
bool songEditor::mayChangeProject( void )
{
if( m_modified == FALSE )