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:
26
ChangeLog
26
ChangeLog
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
@@ -130,6 +130,7 @@ public:
|
||||
|
||||
|
||||
bpm_t getTempo( void );
|
||||
virtual automationPattern * tempoAutomationPattern( void );
|
||||
|
||||
|
||||
// every function that replaces current file (e.g. creates new file,
|
||||
|
||||
@@ -82,6 +82,11 @@ public:
|
||||
return( m_currentPosition );
|
||||
}
|
||||
|
||||
virtual automationPattern * tempoAutomationPattern( void )
|
||||
{
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
virtual bool fixedTCOs( void ) const
|
||||
{
|
||||
return( FALSE );
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
@@ -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 )
|
||||
|
||||
Reference in New Issue
Block a user