Add Portsmf MIDI importing to trunk
git-svn-id: https://lmms.svn.sf.net/svnroot/lmms/trunk/lmms@1622 0778d3d1-df1d-0410-868b-ea421aaaa00d
This commit is contained in:
@@ -128,7 +128,8 @@ IF(WANT_SDL)
|
||||
SET(LMMS_HAVE_SDL TRUE)
|
||||
SET(STATUS_SDL "OK")
|
||||
ELSE(SDL_FOUND)
|
||||
SET(STATUS_SDL "not found, please install libsdl1.2-dev (or similiar)")
|
||||
SET(STATUS_SDL "not found, please install libsdl1.2-dev (or similiar) "
|
||||
"if your require SDL support")
|
||||
ENDIF(SDL_FOUND)
|
||||
ENDIF(WANT_SDL)
|
||||
IF(NOT LMMS_HAVE_SDL)
|
||||
@@ -148,7 +149,8 @@ IF(WANT_STK)
|
||||
SET(STATUS_STK "OK")
|
||||
ELSE(STK_FOUND)
|
||||
SET(STK_INCLUDE_DIR "")
|
||||
SET(STATUS_STK "not found, please install libstk0-dev (or similiar)")
|
||||
SET(STATUS_STK "not found, please install libstk0-dev (or similiar) "
|
||||
"if your require the Vibed Instrument")
|
||||
ENDIF(STK_FOUND)
|
||||
ENDIF(WANT_STK)
|
||||
|
||||
@@ -160,7 +162,8 @@ IF(WANT_PORTAUDIO)
|
||||
SET(LMMS_HAVE_PORTAUDIO TRUE)
|
||||
SET(STATUS_PORTAUDIO "OK")
|
||||
ELSE(PORTAUDIO_FOUND)
|
||||
SET(STATUS_PORTAUDIO "not found, please install libportaudio-dev (or similiar, version >= 1.8)")
|
||||
SET(STATUS_PORTAUDIO "not found, please install libportaudio-dev (or similiar, version >= 1.8) "
|
||||
"if your require Portaudio support")
|
||||
ENDIF(PORTAUDIO_FOUND)
|
||||
ENDIF(WANT_PORTAUDIO)
|
||||
IF(NOT LMMS_HAVE_PORTAUDIO)
|
||||
@@ -176,7 +179,8 @@ IF(WANT_PULSEAUDIO)
|
||||
SET(LMMS_HAVE_PULSEAUDIO TRUE)
|
||||
SET(STATUS_PULSEAUDIO "OK")
|
||||
ELSE(PULSEAUDIO_FOUND)
|
||||
SET(STATUS_PULSEAUDIO "not found, please install libpulse-dev (or similiar)")
|
||||
SET(STATUS_PULSEAUDIO "not found, please install libpulse-dev (or similiar) "
|
||||
"if your require Portaudio support")
|
||||
ENDIF(PULSEAUDIO_FOUND)
|
||||
ENDIF(WANT_PULSEAUDIO)
|
||||
IF(NOT LMMS_HAVE_PULSEAUDIO)
|
||||
@@ -192,7 +196,8 @@ IF(WANT_OGGVORBIS)
|
||||
SET(LMMS_HAVE_OGGVORBIS TRUE)
|
||||
SET(STATUS_OGGVORBIS "OK")
|
||||
ELSE(OGGVORBIS_FOUND)
|
||||
SET(STATUS_OGGVORBIS "not found, please install libogg-dev and libvorbis-dev (or similiar)")
|
||||
SET(STATUS_OGGVORBIS "not found, libogg-dev and libvorbis-dev (or similiar) "
|
||||
"is highly recommended")
|
||||
ENDIF(OGGVORBIS_FOUND)
|
||||
ENDIF(WANT_OGGVORBIS)
|
||||
|
||||
@@ -214,7 +219,8 @@ IF(WANT_ALSA)
|
||||
SET(LMMS_HAVE_ALSA TRUE)
|
||||
SET(STATUS_ALSA "OK")
|
||||
ELSE(ALSA_FOUND)
|
||||
SET(STATUS_ALSA "not found, please install libasound2-dev (or similiar)")
|
||||
SET(STATUS_ALSA "not found, please install libasound2-dev (or similiar) "
|
||||
"if you require ALSA support")
|
||||
ENDIF(ALSA_FOUND)
|
||||
ENDIF(WANT_ALSA)
|
||||
IF(NOT LMMS_HAVE_ALSA)
|
||||
@@ -229,7 +235,8 @@ IF(WANT_JACK)
|
||||
SET(LMMS_HAVE_JACK TRUE)
|
||||
SET(STATUS_JACK "OK")
|
||||
ELSE(JACK_FOUND)
|
||||
SET(STATUS_JACK "not found, please install libjack0.100.0-dev (or similiar)")
|
||||
SET(STATUS_JACK "not found, please install libjack0.100.0-dev (or similiar) "
|
||||
"if you require JACK support")
|
||||
ENDIF(JACK_FOUND)
|
||||
ENDIF(WANT_JACK)
|
||||
|
||||
@@ -241,7 +248,8 @@ IF(WANT_FFTW3F)
|
||||
SET(LMMS_HAVE_FFTW3F TRUE)
|
||||
SET(STATUS_FFTW3F "OK")
|
||||
ELSE(FFTW3F_FOUND)
|
||||
SET(STATUS_FFTW3F "not found, please install libfftw3-dev (or similiar)")
|
||||
SET(STATUS_FFTW3F "not found, libfftw3-dev (or similiar) "
|
||||
"is highly recommended")
|
||||
ENDIF(FFTW3F_FOUND)
|
||||
ENDIF(WANT_FFTW3F)
|
||||
|
||||
@@ -253,7 +261,8 @@ IF(WANT_SF2)
|
||||
SET(LMMS_HAVE_FLUIDSYNTH TRUE)
|
||||
SET(STATUS_FLUIDSYNTH "OK")
|
||||
ELSE(FLUIDSYNTH_FOUND)
|
||||
SET(STATUS_FLUIDSYNTH "not found, please install libfluidsynth-dev (or similiar)")
|
||||
SET(STATUS_FLUIDSYNTH "not found, libfluidsynth-dev (or similiar)"
|
||||
"is highly recommended")
|
||||
ENDIF(FLUIDSYNTH_FOUND)
|
||||
ENDIF(WANT_SF2)
|
||||
|
||||
|
||||
36
ChangeLog
36
ChangeLog
@@ -1,3 +1,39 @@
|
||||
2008-09-08 Paul Giblock <drfaygo/at/gmail/dot/com>
|
||||
|
||||
* plugins/sf2_player/sf2_player.cpp:
|
||||
* plugins/sf2_player/sf2_player.h:
|
||||
* plugins/midi_import/portsmf:
|
||||
* plugins/midi_import/portsmf/allegrowr.cpp:
|
||||
* plugins/midi_import/portsmf/trace.cpp:
|
||||
* plugins/midi_import/portsmf/algsmfrd_internal.h:
|
||||
* plugins/midi_import/portsmf/trace.h:
|
||||
* plugins/midi_import/portsmf/strparse.cpp:
|
||||
* plugins/midi_import/portsmf/allegrosmfwr.cpp:
|
||||
* plugins/midi_import/portsmf/mfmidi.cpp:
|
||||
* plugins/midi_import/portsmf/strparse.h:
|
||||
* plugins/midi_import/portsmf/mfmidi.h:
|
||||
* plugins/midi_import/portsmf/allegrord.cpp:
|
||||
* plugins/midi_import/portsmf/license.txt:
|
||||
* plugins/midi_import/portsmf/allegrosmfrd.cpp:
|
||||
* plugins/midi_import/portsmf/allegro.cpp:
|
||||
* plugins/midi_import/portsmf/allegroserial.cpp:
|
||||
* plugins/midi_import/portsmf/algrd_internal.h:
|
||||
* plugins/midi_import/portsmf/allegro.h:
|
||||
* plugins/midi_import/portsmf/README.txt:
|
||||
* plugins/midi_import/CMakeLists.txt:
|
||||
* plugins/midi_import/midi_import.cpp:
|
||||
* include/meter_model.h:
|
||||
* include/instrument_track.h:
|
||||
* include/config_mgr.h:
|
||||
* include/plugin.h:
|
||||
* include/setup_dialog.h:
|
||||
* src/gui/setup_dialog.cpp:
|
||||
* src/core/plugin.cpp:
|
||||
* src/core/config_mgr.cpp:
|
||||
- Add Portsmf-based MIDI import to trunk
|
||||
- Add default-sf2 to settings dialog, but currently only loads when
|
||||
importing a MIDI
|
||||
|
||||
2008-09-16 Tobias Doerffel <tobydox/at/users/dot/sourceforge/dot/net>
|
||||
|
||||
* src/gui/track_container_view.cpp:
|
||||
|
||||
@@ -146,6 +146,13 @@ public:
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef LMMS_HAVE_FLUIDSYNTH
|
||||
const QString & defaultSoundfont( void ) const
|
||||
{
|
||||
return( m_defaultSoundfont );
|
||||
}
|
||||
#endif
|
||||
|
||||
inline const QStringList & recentlyOpenedProjects( void ) const
|
||||
{
|
||||
return( m_recentlyOpenedProjects );
|
||||
@@ -168,6 +175,7 @@ public:
|
||||
void setFLDir( const QString & _fd );
|
||||
void setLADSPADir( const QString & _fd );
|
||||
void setSTKDir( const QString & _fd );
|
||||
void setDefaultSoundfont( const QString & _sf );
|
||||
|
||||
|
||||
private:
|
||||
@@ -188,6 +196,9 @@ private:
|
||||
QString m_ladDir;
|
||||
#ifdef LMMS_HAVE_STK
|
||||
QString m_stkDir;
|
||||
#endif
|
||||
#ifdef LMMS_HAVE_FLUIDSYNTH
|
||||
QString m_defaultSoundfont;
|
||||
#endif
|
||||
QStringList m_recentlyOpenedProjects;
|
||||
|
||||
|
||||
@@ -164,6 +164,20 @@ public:
|
||||
// simple helper for removing midiport-XML-node when loading presets
|
||||
static void removeMidiPortNode( multimediaProject & _mmp );
|
||||
|
||||
floatModel & pitchModel()
|
||||
{
|
||||
return m_pitchModel;
|
||||
}
|
||||
|
||||
floatModel & volumeModel()
|
||||
{
|
||||
return m_volumeModel;
|
||||
}
|
||||
|
||||
floatModel & panningModel()
|
||||
{
|
||||
return m_panningModel;
|
||||
}
|
||||
|
||||
signals:
|
||||
void instrumentChanged( void );
|
||||
|
||||
@@ -44,6 +44,18 @@ public:
|
||||
|
||||
void reset( void );
|
||||
|
||||
// Must have the sub-models exposed to programatically connect
|
||||
// to automation or controllers
|
||||
intModel & numeratorModel( void )
|
||||
{
|
||||
return m_numeratorModel;
|
||||
}
|
||||
|
||||
intModel & denominatorModel( void )
|
||||
{
|
||||
return m_denominatorModel;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
intModel m_numeratorModel;
|
||||
|
||||
@@ -42,6 +42,7 @@ class QWidget;
|
||||
|
||||
class pixmapLoader;
|
||||
class pluginView;
|
||||
class automatableModel;
|
||||
|
||||
|
||||
class EXPORT plugin : public journallingObject, public model
|
||||
@@ -171,10 +172,9 @@ public:
|
||||
// loaded/processed with the help of this plugin
|
||||
virtual void loadFile( const QString & _file );
|
||||
|
||||
// plugins can overload this for making other classes able to query
|
||||
// settings of the plugin without knowing the actual class
|
||||
virtual QString getParameter( const QString & _param );
|
||||
|
||||
// Called if external source needs to change something but we cannot
|
||||
// reference the class header. Should return null if not key not found.
|
||||
virtual automatableModel * getChildModel( const QString & _modelName );
|
||||
|
||||
// returns an instance of a plugin whose name matches to given one
|
||||
// if specified plugin couldn't be loaded, it creates a dummy-plugin
|
||||
|
||||
@@ -49,7 +49,7 @@ public:
|
||||
enum ConfigTabs
|
||||
{
|
||||
GeneralSettings,
|
||||
DirectorySettings,
|
||||
PathSettings,
|
||||
PerformanceSettings,
|
||||
AudioSettings,
|
||||
MidiSettings
|
||||
@@ -69,13 +69,14 @@ private slots:
|
||||
void resetBufSize( void );
|
||||
void displayBufSizeHelp( void );
|
||||
|
||||
// directory settings widget
|
||||
// path settings widget
|
||||
void setWorkingDir( const QString & _wd );
|
||||
void setVSTDir( const QString & _vd );
|
||||
void setArtworkDir( const QString & _ad );
|
||||
void setFLDir( const QString & _fd );
|
||||
void setLADSPADir( const QString & _fd );
|
||||
void setSTKDir( const QString & _fd );
|
||||
void setDefaultSoundfont( const QString & _fd );
|
||||
|
||||
// audio settings widget
|
||||
void audioInterfaceChanged( const QString & _driver );
|
||||
@@ -98,6 +99,7 @@ private slots:
|
||||
void openFLDir( void );
|
||||
void openLADSPADir( void );
|
||||
void openSTKDir( void );
|
||||
void openDefaultSoundfont( void );
|
||||
|
||||
void toggleDisableChActInd( bool _disabled );
|
||||
void toggleManualChPiano( bool _enabled );
|
||||
@@ -122,6 +124,9 @@ private:
|
||||
QLineEdit * m_adLineEdit;
|
||||
QLineEdit * m_fdLineEdit;
|
||||
QLineEdit * m_ladLineEdit;
|
||||
#ifdef LMMS_HAVE_FLUIDSYNTH
|
||||
QLineEdit * m_sfLineEdit;
|
||||
#endif
|
||||
#ifdef LMMS_HAVE_STK
|
||||
QLineEdit * m_stkLineEdit;
|
||||
#endif
|
||||
@@ -131,6 +136,9 @@ private:
|
||||
QString m_artworkDir;
|
||||
QString m_flDir;
|
||||
QString m_ladDir;
|
||||
#ifdef LMMS_HAVE_FLUIDSYNTH
|
||||
QString m_defaultSoundfont;
|
||||
#endif
|
||||
#ifdef LMMS_HAVE_STK
|
||||
QString m_stkDir;
|
||||
#endif
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#cmakedefine LMMS_HAVE_ALSA
|
||||
#cmakedefine LMMS_HAVE_FFTW3F
|
||||
#cmakedefine LMMS_HAVE_FLUIDSYNTH
|
||||
#cmakedefine LMMS_HAVE_JACK
|
||||
#cmakedefine LMMS_HAVE_OGGVORBIS
|
||||
#cmakedefine LMMS_HAVE_OSS
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
INCLUDE(BuildPlugin)
|
||||
|
||||
BUILD_PLUGIN(midiimport midi_import.cpp midi_import.h)
|
||||
BUILD_PLUGIN(midiimport midi_import.cpp midi_import.h
|
||||
portsmf/allegro.cpp portsmf/allegro.h portsmf/allegrosmfwr.cpp
|
||||
portsmf/allegrord.cpp portsmf/allegrowr.cpp portsmf/allegrosmfrd.cpp
|
||||
portsmf/mfmidi.cpp portsmf/mfmidi.h portsmf/strparse.cpp
|
||||
portsmf/strparse.h portsmf/algrd_internal.h portsmf/algsmfrd_internal.h
|
||||
portsmf/trace.h)
|
||||
|
||||
@@ -31,13 +31,17 @@
|
||||
#include "midi_import.h"
|
||||
#include "track_container.h"
|
||||
#include "instrument_track.h"
|
||||
#include "pattern.h"
|
||||
#include "automation_track.h"
|
||||
#include "automation_pattern.h"
|
||||
#include "config_mgr.h"
|
||||
#include "pattern.h"
|
||||
#include "instrument.h"
|
||||
#include "main_window.h"
|
||||
#include "debug.h"
|
||||
#include "embed.h"
|
||||
#include "song.h"
|
||||
|
||||
#include "portsmf/allegro.h"
|
||||
|
||||
#define makeID(_c0, _c1, _c2, _c3) \
|
||||
( 0 | \
|
||||
@@ -109,28 +113,363 @@ bool midiImport::tryImport( trackContainer * _tc )
|
||||
|
||||
|
||||
|
||||
static inline unsigned long gcd( unsigned long a, unsigned long b )
|
||||
class smfMidiCC
|
||||
{
|
||||
unsigned long r;
|
||||
if( a < b )
|
||||
|
||||
public:
|
||||
smfMidiCC() :
|
||||
at( NULL ),
|
||||
ap( NULL ),
|
||||
lastPos( 0 )
|
||||
{ }
|
||||
|
||||
automationTrack * at;
|
||||
automationPattern * ap;
|
||||
midiTime lastPos;
|
||||
|
||||
smfMidiCC & create( trackContainer * _tc )
|
||||
{
|
||||
r = a;
|
||||
a = b;
|
||||
b = r;
|
||||
if( !at )
|
||||
{
|
||||
at = dynamic_cast<automationTrack *>(
|
||||
track::create( track::AutomationTrack, _tc ) );
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
while( ( r = a % b ) != 0 )
|
||||
{
|
||||
a = b;
|
||||
b = r;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
void clear()
|
||||
{
|
||||
at = NULL;
|
||||
ap = NULL;
|
||||
lastPos = 0;
|
||||
}
|
||||
|
||||
|
||||
smfMidiCC & putValue( midiTime time, automatableModel * objModel, float value )
|
||||
{
|
||||
if( !ap || time > lastPos + DefaultTicksPerTact )
|
||||
{
|
||||
midiTime pPos = midiTime( time.getTact(), 0 );
|
||||
ap = dynamic_cast<automationPattern*>(
|
||||
at->createTCO(0) );
|
||||
ap->movePosition( pPos );
|
||||
}
|
||||
ap->addObject( objModel );
|
||||
|
||||
lastPos = time;
|
||||
time = time - ap->startPosition();
|
||||
ap->putValue( time, value, false );
|
||||
ap->changeLength( midiTime( time.getTact() + 1, 0 ) );
|
||||
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
class smfMidiChannel
|
||||
{
|
||||
|
||||
public:
|
||||
smfMidiChannel() :
|
||||
it( NULL ),
|
||||
p( NULL ),
|
||||
it_inst( NULL ),
|
||||
isSF2( false ),
|
||||
hasNotes( false ),
|
||||
lastEnd( 0 )
|
||||
{ }
|
||||
|
||||
instrumentTrack * it;
|
||||
pattern * p;
|
||||
instrument * it_inst;
|
||||
bool isSF2;
|
||||
bool hasNotes;
|
||||
midiTime lastEnd;
|
||||
|
||||
smfMidiChannel * create( trackContainer * _tc )
|
||||
{
|
||||
if( !it ) {
|
||||
it = dynamic_cast<instrumentTrack *>(
|
||||
track::create( track::InstrumentTrack, _tc ) );
|
||||
|
||||
#ifdef LMMS_HAVE_FLUIDSYNTH
|
||||
it_inst = it->loadInstrument( "sf2player" );
|
||||
|
||||
if( it_inst )
|
||||
{
|
||||
isSF2 = true;
|
||||
it_inst->loadFile( configManager::inst()->defaultSoundfont() );
|
||||
it_inst->getChildModel( "bank" )->setValue( 128 );
|
||||
it_inst->getChildModel( "patch" )->setValue( 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
it_inst = it->loadInstrument( "patman" );
|
||||
}
|
||||
#else
|
||||
it_inst = it->loadInstrument( "patman" );
|
||||
#endif
|
||||
|
||||
lastEnd = 0;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
void addNote( note & n )
|
||||
{
|
||||
if( !p || n.pos() > lastEnd + DefaultTicksPerTact )
|
||||
{
|
||||
midiTime pPos = midiTime(n.pos().getTact(), 0 );
|
||||
p = dynamic_cast<pattern *>( it->createTCO( 0 ) );
|
||||
p->movePosition( pPos );
|
||||
}
|
||||
hasNotes = true;
|
||||
lastEnd = n.pos() + n.length();
|
||||
n.setPos( n.pos( p->startPosition() ) );
|
||||
p->addNote( n, false );
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
bool midiImport::readSMF( trackContainer * _tc )
|
||||
{
|
||||
QString filename = file().fileName();
|
||||
closeFile();
|
||||
|
||||
const int preTrackSteps = 2;
|
||||
QProgressDialog pd( trackContainer::tr( "Importing MIDI-file..." ),
|
||||
trackContainer::tr( "Cancel" ), 0, preTrackSteps, engine::getMainWindow() );
|
||||
pd.setWindowTitle( trackContainer::tr( "Please wait..." ) );
|
||||
pd.setWindowModality(Qt::WindowModal);
|
||||
pd.setMinimumDuration( 0 );
|
||||
|
||||
pd.setValue( 0 );
|
||||
|
||||
Alg_seq_ptr seq = new Alg_seq(filename.toLocal8Bit(), true);
|
||||
seq->convert_to_beats();
|
||||
|
||||
pd.setMaximum( seq->tracks() + preTrackSteps );
|
||||
pd.setValue( 1 );
|
||||
|
||||
// 128 CC + Pitch Bend
|
||||
smfMidiCC ccs[129];
|
||||
smfMidiChannel chs[256];
|
||||
|
||||
meterModel & timeSigMM = engine::getSong()->getTimeSigModel();
|
||||
automationPattern * timeSigNumeratorPat =
|
||||
automationPattern::globalAutomationPattern( &timeSigMM.numeratorModel() );
|
||||
automationPattern * timeSigDenominatorPat =
|
||||
automationPattern::globalAutomationPattern( &timeSigMM.denominatorModel() );
|
||||
|
||||
// TODO: adjust these to Time.Sig changes
|
||||
double beatsPerTact = 4;
|
||||
double ticksPerBeat = DefaultTicksPerTact / beatsPerTact;
|
||||
|
||||
// Time-sig changes
|
||||
Alg_time_sigs * timeSigs = &seq->time_sig;
|
||||
for( int s = 0; s < timeSigs->length(); ++s )
|
||||
{
|
||||
Alg_time_sig timeSig = (*timeSigs)[s];
|
||||
// Initial timeSig, set song-default value
|
||||
if(/* timeSig.beat == 0*/ true )
|
||||
{
|
||||
// TODO set song-global default value
|
||||
printf("Another timesig at %f\n", timeSig.beat);
|
||||
timeSigNumeratorPat->putValue( timeSig.beat*ticksPerBeat, timeSig.num );
|
||||
timeSigDenominatorPat->putValue( timeSig.beat*ticksPerBeat, timeSig.den );
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pd.setValue( 2 );
|
||||
|
||||
// Tempo stuff
|
||||
automationPattern * tap = _tc->tempoAutomationPattern();
|
||||
tap->clear();
|
||||
Alg_time_map * timeMap = seq->get_time_map();
|
||||
Alg_beats & beats = timeMap->beats;
|
||||
for( int i = 0; i < beats.len - 1; i++ )
|
||||
{
|
||||
Alg_beat_ptr b = &(beats[i]);
|
||||
double tempo = ( beats[i + 1].beat - b->beat ) /
|
||||
( beats[i + 1].time - beats[i].time );
|
||||
tap->putValue( b->beat * ticksPerBeat, tempo * 60.0 );
|
||||
}
|
||||
if( timeMap->last_tempo_flag )
|
||||
{
|
||||
Alg_beat_ptr b = &( beats[beats.len - 1] );
|
||||
tap->putValue( b->beat * ticksPerBeat, timeMap->last_tempo * 60.0 );
|
||||
}
|
||||
|
||||
// Song events
|
||||
for( int e = 0; e < seq->length(); ++e )
|
||||
{
|
||||
Alg_event_ptr evt = (*seq)[e];
|
||||
|
||||
if( evt->is_update() )
|
||||
{
|
||||
printf("Unhandled SONG update: %d %f %s\n",
|
||||
evt->get_type_code(), evt->time, evt->get_attribute() );
|
||||
}
|
||||
}
|
||||
|
||||
// Tracks
|
||||
for( int t = 0; t < seq->tracks(); ++t )
|
||||
{
|
||||
Alg_track_ptr trk = seq->track( t );
|
||||
pd.setValue( t + preTrackSteps );
|
||||
|
||||
for( int c = 0; c < 129; c++ )
|
||||
{
|
||||
ccs[c].clear();
|
||||
}
|
||||
|
||||
// Now look at events
|
||||
for( int e = 0; e < trk->length(); ++e )
|
||||
{
|
||||
Alg_event_ptr evt = (*trk)[e];
|
||||
|
||||
if( evt->chan == -1 )
|
||||
{
|
||||
printf("MISSING GLOBAL THINGY\n");
|
||||
printf(" %d %d %f %s\n", evt->chan,
|
||||
evt->get_type_code(), evt->time, evt->get_attribute() );
|
||||
// Global stuff
|
||||
}
|
||||
else if( evt->is_note() && evt->chan < 256 )
|
||||
{
|
||||
smfMidiChannel * ch = chs[evt->chan].create( _tc );
|
||||
Alg_note_ptr noteEvt = dynamic_cast<Alg_note_ptr>( evt );
|
||||
|
||||
note n( noteEvt->get_duration() * ticksPerBeat,
|
||||
noteEvt->get_start_time() * ticksPerBeat,
|
||||
noteEvt->get_identifier() - 3,
|
||||
noteEvt->get_loud());
|
||||
ch->addNote( n );
|
||||
|
||||
}
|
||||
|
||||
else if( evt->is_update() )
|
||||
{
|
||||
smfMidiChannel * ch = chs[evt->chan].create( _tc );
|
||||
|
||||
double time = evt->time*ticksPerBeat;
|
||||
QString update( evt->get_attribute() );
|
||||
|
||||
if( update == "programi" )
|
||||
{
|
||||
long prog = evt->get_integer_value();
|
||||
if( ch->isSF2 )
|
||||
{
|
||||
ch->it_inst->getChildModel( "bank" )->setValue( 0 );
|
||||
ch->it_inst->getChildModel( "patch" )->setValue( prog );
|
||||
}
|
||||
else {
|
||||
const QString num = QString::number( prog );
|
||||
const QString filter = QString().fill( '0', 3 - num.length() ) + num + "*.pat";
|
||||
const QString dir = "/usr/share/midi/"
|
||||
"freepats/Tone_000/";
|
||||
const QStringList files = QDir( dir ).
|
||||
entryList( QStringList( filter ) );
|
||||
if( ch->it_inst && !files.empty() )
|
||||
{
|
||||
ch->it_inst->loadFile( dir+files.front() );
|
||||
}
|
||||
}
|
||||
}
|
||||
else if( update == "tracknames" )
|
||||
{
|
||||
QString trackName( evt->get_string_value() );
|
||||
ch->it->setName( trackName );
|
||||
//ch.p->setName( trackName );
|
||||
}
|
||||
|
||||
else if( update.startsWith( "control" ) || update == "bendr" )
|
||||
{
|
||||
int ccid = update.mid( 7, update.length()-8 ).toInt();
|
||||
if( update == "bendr" )
|
||||
{
|
||||
ccid = 128;
|
||||
}
|
||||
if( ccid <= 128 )
|
||||
{
|
||||
double cc = evt->get_real_value();
|
||||
automatableModel * objModel = NULL;
|
||||
|
||||
switch( ccid )
|
||||
{
|
||||
case 0:
|
||||
if( ch->isSF2 && ch->it_inst )
|
||||
{
|
||||
objModel = ch->it_inst->getChildModel( "bank" );
|
||||
printf("BANK SELECT %f %d\n", cc, (int)(cc*127.0));
|
||||
cc *= 127.0f;
|
||||
}
|
||||
break;
|
||||
|
||||
case 7:
|
||||
objModel = &ch->it->volumeModel();
|
||||
cc *= 100.0f;
|
||||
break;
|
||||
|
||||
case 10:
|
||||
objModel = &ch->it->panningModel();
|
||||
cc = cc * 200.f - 100.0f;
|
||||
break;
|
||||
|
||||
case 128:
|
||||
objModel = &ch->it->pitchModel();
|
||||
cc = cc * 100.0f;
|
||||
break;
|
||||
}
|
||||
|
||||
if( objModel )
|
||||
{
|
||||
if( time == 0 && objModel )
|
||||
{
|
||||
objModel->setInitValue( cc );
|
||||
}
|
||||
else
|
||||
{
|
||||
ccs[ccid].create( _tc );
|
||||
ccs[ccid].putValue( time, objModel, cc );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
printf("Unhandled update: %d %d %f %s\n", evt->chan,
|
||||
evt->get_type_code(), evt->time, evt->get_attribute() );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete seq;
|
||||
|
||||
|
||||
for( int c=0; c < 256; ++c )
|
||||
{
|
||||
if( !chs[c].hasNotes && chs[c].it )
|
||||
{
|
||||
printf(" Should remove empty track\n");
|
||||
// must delete trackView first - but where is it?
|
||||
//_tc->removeTrack( chs[c].it );
|
||||
//it->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
|
||||
/*
|
||||
// the curren position is immediately after the "MThd" id
|
||||
int header_len = readInt( 4 );
|
||||
if( header_len < 6 )
|
||||
@@ -156,6 +495,7 @@ invalid_format:
|
||||
num_tracks = 0;
|
||||
return( FALSE );
|
||||
}
|
||||
|
||||
#ifdef LMMS_DEBUG
|
||||
printf( "tracks: %d\n", num_tracks );
|
||||
#endif
|
||||
@@ -166,193 +506,12 @@ invalid_format:
|
||||
goto invalid_format;
|
||||
}
|
||||
#ifdef LMMS_DEBUG
|
||||
printf( "time-division: %d\n", m_timingDivision );
|
||||
printf( "time-division: %d\n", m_timingDivision );
|
||||
#endif
|
||||
|
||||
QProgressDialog pd( trackContainer::tr( "Importing MIDI-file..." ),
|
||||
trackContainer::tr( "Cancel" ), 0, num_tracks );
|
||||
pd.setWindowTitle( trackContainer::tr( "Please wait..." ) );
|
||||
pd.show();
|
||||
|
||||
// calculate some timing stuff
|
||||
int crotchet_time = 16*3;
|
||||
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();
|
||||
tap->clear();
|
||||
tap->putValue( 0, 120 );
|
||||
|
||||
// read tracks
|
||||
for( int i = 0; i < num_tracks; ++i )
|
||||
{
|
||||
pd.setValue( i );
|
||||
qApp->processEvents( QEventLoop::AllEvents, 100 );
|
||||
|
||||
if( pd.wasCanceled() )
|
||||
{
|
||||
return( FALSE );
|
||||
}
|
||||
|
||||
int len;
|
||||
|
||||
// search for MTrk chunk
|
||||
while( 1 )
|
||||
{
|
||||
Sint32 id = readID();
|
||||
len = readInt( 4 );
|
||||
if( file().atEnd() )
|
||||
{
|
||||
printf( "midiImport::readSMF(): unexpected end "
|
||||
"of file\n" );
|
||||
return( FALSE );
|
||||
}
|
||||
if( len < 0 || len >= 0x10000000 )
|
||||
{
|
||||
printf( "midiImport::readSMF(): invalid chunk "
|
||||
"length %d\n", len );
|
||||
return( FALSE );
|
||||
}
|
||||
if( id == makeID( 'M', 'T', 'r', 'k' ) )
|
||||
{
|
||||
break;
|
||||
}
|
||||
skip( len );
|
||||
}
|
||||
if( len <= 0 )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
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 == MidiMetaEvent )
|
||||
{
|
||||
switch( ev.m_data.m_param[0] )
|
||||
{
|
||||
case MidiSetTempo:
|
||||
{
|
||||
tap->putValue( midiTime( ( tick * multiplier ) / divisor ),
|
||||
ev.m_data.m_param[1], FALSE );
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// now create new instrument-track for reading in track
|
||||
instrumentTrack * it = dynamic_cast<instrumentTrack *>(
|
||||
track::create(
|
||||
track::InstrumentTrack,
|
||||
_tc ) );
|
||||
#ifdef LMMS_DEBUG
|
||||
assert( it != NULL );
|
||||
#endif
|
||||
// TODO: setup program, channel etc.
|
||||
instrument * it_inst = it->loadInstrument( "patman" );
|
||||
bool sample_loaded = 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 *>( it->createTCO( 0 ) );
|
||||
#ifdef LMMS_DEBUG
|
||||
assert( p != NULL );
|
||||
#endif
|
||||
it->addTCO( p );
|
||||
|
||||
// init keys
|
||||
int keys[NumKeys][2];
|
||||
for( int j = 0; j < NumKeys; ++j )
|
||||
{
|
||||
keys[j][0] = -1;
|
||||
}
|
||||
|
||||
// now process every event
|
||||
for( eventVector::const_iterator it = m_events.begin();
|
||||
it != m_events.end(); ++it )
|
||||
{
|
||||
const int tick = it->first;
|
||||
const midiEvent & ev = it->second;
|
||||
switch( ev.m_type )
|
||||
{
|
||||
case MidiNoteOn:
|
||||
if( ev.key() >= NumKeys )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if( ev.velocity() > 0 )
|
||||
{
|
||||
keys[ev.key()][0] = tick;
|
||||
keys[ev.key()][1] =
|
||||
ev.velocity();
|
||||
break;
|
||||
}
|
||||
|
||||
case MidiNoteOff:
|
||||
if( ev.key() < NumKeys &&
|
||||
keys[ev.key()][0] >= 0 )
|
||||
{
|
||||
note n( midiTime( ( ( tick - keys[ev.key()][0] ) * multiplier ) / divisor ),
|
||||
midiTime( ( keys[ev.key()][0] * multiplier ) / divisor ),
|
||||
ev.key(), keys[ev.key()][1] * 100 / 128 );
|
||||
p->addNote( n, FALSE );
|
||||
keys[ev.key()][0] = -1;
|
||||
}
|
||||
break;
|
||||
|
||||
case MidiProgramChange:
|
||||
{
|
||||
const QString num = QString::number( ev.key() );
|
||||
const QString filter = QString().fill( '0', 3 - num.length() ) + num + "*.pat";
|
||||
const QString dir = "/usr/share/midi/"
|
||||
"freepats/Tone_000/";
|
||||
const QStringList files = QDir( dir ).
|
||||
entryList( QStringList(
|
||||
filter ) );
|
||||
if( it_inst && !files.empty() && !sample_loaded )
|
||||
{
|
||||
it_inst->loadFile( dir+files.front() );
|
||||
sample_loaded = TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
/* printf( "Unhandled event: %#x\n",
|
||||
(int) ev.m_type );*/
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return( TRUE );
|
||||
}
|
||||
|
||||
|
||||
@@ -360,252 +519,44 @@ invalid_format:
|
||||
|
||||
bool midiImport::readRIFF( trackContainer * _tc )
|
||||
{
|
||||
// skip file length
|
||||
// skip file length
|
||||
skip( 4 );
|
||||
|
||||
// check file type ("RMID" = RIFF MIDI)
|
||||
if( readID() != makeID( 'R', 'M', 'I', 'D' ) )
|
||||
// check file type ("RMID" = RIFF MIDI)
|
||||
if( readID() != makeID( 'R', 'M', 'I', 'D' ) )
|
||||
{
|
||||
invalid_format:
|
||||
printf( "midiImport::readRIFF(): invalid file format\n" );
|
||||
return( FALSE );
|
||||
}
|
||||
// search for "data" chunk
|
||||
while( 1 )
|
||||
printf( "midiImport::readRIFF(): invalid file format\n" );
|
||||
return( FALSE );
|
||||
}
|
||||
// search for "data" chunk
|
||||
while( 1 )
|
||||
{
|
||||
int id = readID();
|
||||
int len = read32LE();
|
||||
if( file().atEnd() )
|
||||
int id = readID();
|
||||
int len = read32LE();
|
||||
if( file().atEnd() )
|
||||
{
|
||||
data_not_found:
|
||||
printf( "midiImport::readRIFF(): data chunk not "
|
||||
printf( "midiImport::readRIFF(): data chunk not "
|
||||
"found\n" );
|
||||
return( FALSE );
|
||||
}
|
||||
if( id == makeID( 'd', 'a', 't', 'a' ) )
|
||||
{
|
||||
break;
|
||||
}
|
||||
if( len < 0 )
|
||||
{
|
||||
goto data_not_found;
|
||||
}
|
||||
skip( ( len + 1 ) & ~1 );
|
||||
}
|
||||
// the "data" chunk must contain data in SMF format
|
||||
if( readID() != makeID( 'M', 'T', 'h', 'd' ) )
|
||||
{
|
||||
goto invalid_format;
|
||||
}
|
||||
return( readSMF( _tc ) );
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool midiImport::readTrack( int _track_end, QString & _track_name )
|
||||
{
|
||||
int tick = 0;
|
||||
unsigned char last_cmd = 0;
|
||||
// unsigned char port = 0;
|
||||
|
||||
m_events.clear();
|
||||
// the current file position is after the track ID and length
|
||||
while( (int) file().pos() < _track_end )
|
||||
{
|
||||
unsigned char cmd;
|
||||
int len;
|
||||
|
||||
int delta_ticks = readVar();
|
||||
if( delta_ticks < 0 )
|
||||
{
|
||||
break;
|
||||
}
|
||||
tick += delta_ticks;
|
||||
|
||||
int c = readByte();
|
||||
if( c < 0 )
|
||||
{
|
||||
break;
|
||||
}
|
||||
if( c & 0x80 )
|
||||
{
|
||||
// have command
|
||||
cmd = c;
|
||||
if( cmd < 0xf0 )
|
||||
{
|
||||
last_cmd = cmd;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// running status
|
||||
ungetChar( c );
|
||||
cmd = last_cmd;
|
||||
if( !cmd )
|
||||
{
|
||||
error();
|
||||
return( FALSE );
|
||||
}
|
||||
}
|
||||
switch( cmd & 0xF0 )
|
||||
{
|
||||
// channel msg with 2 parameter bytes
|
||||
case MidiNoteOff:
|
||||
case MidiNoteOn:
|
||||
case MidiKeyPressure:
|
||||
case MidiControlChange:
|
||||
case MidiPitchBend:
|
||||
{
|
||||
int data1 = readByte() & 0x7F;
|
||||
int data2 = readByte() & 0x7F;
|
||||
m_events.push_back( qMakePair( tick,
|
||||
midiEvent( static_cast<MidiEventTypes>(
|
||||
cmd & 0xF0 ),
|
||||
cmd & 0x0F,
|
||||
data1,
|
||||
data2 ) ) );
|
||||
break;
|
||||
}
|
||||
// channel msg with 1 parameter byte
|
||||
case MidiProgramChange:
|
||||
case MidiChannelPressure:
|
||||
m_events.push_back( qMakePair( tick,
|
||||
midiEvent( static_cast<MidiEventTypes>(
|
||||
cmd & 0xF0 ),
|
||||
cmd & 0x0F,
|
||||
readByte() & 0x7F ) ) );
|
||||
break;
|
||||
|
||||
case MidiSysEx:
|
||||
switch( cmd )
|
||||
{
|
||||
case MidiSysEx:
|
||||
case MidiEOX:
|
||||
{
|
||||
len = readVar();
|
||||
if( len < 0 )
|
||||
{
|
||||
error();
|
||||
return( FALSE );
|
||||
}
|
||||
if( cmd == MidiSysEx )
|
||||
{
|
||||
++len;
|
||||
}
|
||||
char * data = new char[len];
|
||||
if( cmd == MidiSysEx )
|
||||
{
|
||||
data[0] = MidiSysEx;
|
||||
}
|
||||
for( ; c < len; ++c )
|
||||
{
|
||||
data[c] = readByte();
|
||||
}
|
||||
m_events.push_back(
|
||||
qMakePair( tick,
|
||||
midiEvent( MidiSysEx, data, len ) ) );
|
||||
break;
|
||||
}
|
||||
|
||||
case MidiMetaEvent:
|
||||
c = readByte();
|
||||
len = readVar();
|
||||
/* if( len < 0 )
|
||||
{
|
||||
error();
|
||||
return( FALSE );
|
||||
}*/
|
||||
switch( c )
|
||||
{
|
||||
case MidiTrackName:
|
||||
if( len > 0 )
|
||||
{
|
||||
char * n = new char[len+1];
|
||||
readBlock( n, len );
|
||||
n[len] = 0;
|
||||
_track_name += n;
|
||||
delete[] n;
|
||||
}
|
||||
break;
|
||||
case MidiPortNumber:
|
||||
if( len < 1 )
|
||||
{
|
||||
error();
|
||||
return( FALSE );
|
||||
}
|
||||
/* port = readByte() %
|
||||
port_count;
|
||||
skip( len - 1 );*/
|
||||
skip( len );
|
||||
break;
|
||||
|
||||
case MidiEOT:
|
||||
//track->end_tick = tick;
|
||||
skip( _track_end -
|
||||
file().pos() );
|
||||
return( TRUE );
|
||||
|
||||
case MidiSetTempo: // tempo
|
||||
{
|
||||
if( len < 3 )
|
||||
{
|
||||
error();
|
||||
return( FALSE );
|
||||
}
|
||||
int tempo = readByte() << 16;
|
||||
tempo |= readByte() << 8;
|
||||
tempo |= readByte();
|
||||
tempo = ( 60*1000*1000 ) / tempo;
|
||||
m_events.push_back( qMakePair( tick, midiEvent( MidiMetaEvent, 0, MidiSetTempo, tempo ) ) );
|
||||
break;
|
||||
}
|
||||
case MidiTimeSignature:
|
||||
{
|
||||
int nominator = readByte();
|
||||
int denominator = 1 << (int) readByte();
|
||||
// clocks
|
||||
readByte();
|
||||
// notes
|
||||
readByte();
|
||||
if( nominator == 0 )
|
||||
{
|
||||
nominator = 4;
|
||||
}
|
||||
if( denominator == 0 )
|
||||
{
|
||||
denominator = 4;
|
||||
}
|
||||
engine::getSong()->getTimeSigModel().setNumerator( nominator );
|
||||
engine::getSong()->getTimeSigModel().setDenominator( denominator );
|
||||
#ifdef LMMS_DEBUG
|
||||
printf("nom:%d denom:%d\n",nominator,denominator);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
default:// ignore all other
|
||||
// meta events
|
||||
#ifdef LMMS_DEBUG
|
||||
printf("meta event %d\n", (int) c );
|
||||
#endif
|
||||
skip( len );
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default: // invalid Fx command
|
||||
error();
|
||||
return( FALSE );
|
||||
}
|
||||
break;
|
||||
|
||||
default: // cannot happen
|
||||
error();
|
||||
return( FALSE );
|
||||
}
|
||||
}
|
||||
error();
|
||||
return( FALSE );
|
||||
if( id == makeID( 'd', 'a', 't', 'a' ) )
|
||||
{
|
||||
break;
|
||||
}
|
||||
if( len < 0 )
|
||||
{
|
||||
goto data_not_found;
|
||||
}
|
||||
skip( ( len + 1 ) & ~1 );
|
||||
}
|
||||
// the "data" chunk must contain data in SMF format
|
||||
if( readID() != makeID( 'M', 'T', 'h', 'd' ) )
|
||||
{
|
||||
goto invalid_format;
|
||||
}
|
||||
return( readSMF( _tc ) );
|
||||
}
|
||||
|
||||
|
||||
|
||||
32
plugins/midi_import/portsmf/README.txt
Normal file
32
plugins/midi_import/portsmf/README.txt
Normal file
@@ -0,0 +1,32 @@
|
||||
portsmf README.txt
|
||||
14 Jun 2008
|
||||
Roger B. Dannenberg
|
||||
|
||||
Portsmf is "Port Standard MIDI File", a cross-platform, C++ library
|
||||
for reading and writing Standard MIDI Files.
|
||||
|
||||
License information: free and open source, see license.txt for details
|
||||
|
||||
Features:
|
||||
|
||||
- input and output of Standard MIDI Files
|
||||
- data structures, classes, etc. for representing music data in memory
|
||||
o sequence structure consisting of multiple tracks
|
||||
o track structure consisting of multiple events
|
||||
o events contain note and control data
|
||||
o extensible attribute-value property lists
|
||||
o tempo track and time signature representation
|
||||
- input and output of a text-based representation: Allegro files
|
||||
- extensive editing operations on sequences and tracks
|
||||
- conversion to/from binary buffers for archiving, undo/redo, etc.
|
||||
|
||||
Portsmf is a relatively small number of about 9 files, so there is
|
||||
currently no support for building/maintaining Portsmf as a separate
|
||||
library. (Contributions are welcome.) For now, it is suggested that
|
||||
you simply compile these files along with your application sources.
|
||||
|
||||
There is a test program in portsmf_test and makefiles to build it as
|
||||
an example.
|
||||
|
||||
You might want to browse through portsmf_test/allegro_test.cpp
|
||||
for examples that use and exercise most of the portsmf functions.
|
||||
4
plugins/midi_import/portsmf/algrd_internal.h
Normal file
4
plugins/midi_import/portsmf/algrd_internal.h
Normal file
@@ -0,0 +1,4 @@
|
||||
/* algread_internal.h -- interface between allegro.cpp and allegrord.cpp */
|
||||
|
||||
Alg_error alg_read(std::istream &file, Alg_seq_ptr new_seq);
|
||||
|
||||
3
plugins/midi_import/portsmf/algsmfrd_internal.h
Normal file
3
plugins/midi_import/portsmf/algsmfrd_internal.h
Normal file
@@ -0,0 +1,3 @@
|
||||
/* algsmfrd_internal.h -- interface from allegrosmfrd.cpp to allegro.cpp */
|
||||
|
||||
Alg_error alg_smf_read(std::istream &file, Alg_seq_ptr new_seq);
|
||||
2869
plugins/midi_import/portsmf/allegro.cpp
Normal file
2869
plugins/midi_import/portsmf/allegro.cpp
Normal file
File diff suppressed because it is too large
Load Diff
935
plugins/midi_import/portsmf/allegro.h
Normal file
935
plugins/midi_import/portsmf/allegro.h
Normal file
@@ -0,0 +1,935 @@
|
||||
// Portsmf (also known as Allegro):
|
||||
// music representation system, with
|
||||
// extensible in-memory sequence structure
|
||||
// upward compatible with MIDI
|
||||
// implementations in C++ and Serpent
|
||||
// external, text-based representation
|
||||
// compatible with Aura
|
||||
//
|
||||
// SERIALBUFFER CLASS
|
||||
//
|
||||
// The Serial_buffer class is defined to support serialization and
|
||||
// unserialization. A Serial_buffer is just a block of memory with
|
||||
// a length and a read/write pointer. When writing, it can expand.
|
||||
//
|
||||
// SERIALIZATION
|
||||
//
|
||||
// The Alg_track class has static members:
|
||||
// ser_buf -- a Serial_buffer
|
||||
// When objects are serialized, they are first written to
|
||||
// ser_buf, which is expanded whenever necessary. Then, when
|
||||
// the length is known, new memory is allocated and the data
|
||||
// is copied to a correctly-sized buffer and returned to caller.
|
||||
// The "external" (callable from outside the library)
|
||||
// serialization functions are:
|
||||
// Alg_track::serialize()
|
||||
// Alg_seq::serialize()
|
||||
// The "internal" serialization functions to be called from within
|
||||
// the library are:
|
||||
// Alg_track::serialize_track(bool text)
|
||||
// Alg_seq::serialize_seq(bool text)
|
||||
// Alg_track::serialize_parameter(
|
||||
// Alg_parameter *parm, bool text)
|
||||
// These internal serialize functions append data to ser_buf The text
|
||||
// flag says to write an ascii representation as opposed to binary.
|
||||
//
|
||||
// UNSERIALIZATION:
|
||||
//
|
||||
// The Alg_track class has a static member:
|
||||
// unserialize(char *buffer, long len)
|
||||
// that will unserialize anything -- an Alg_track or an Alg_seq.
|
||||
// No other function should be called from outside the library.
|
||||
// Internal unserialize functions are:
|
||||
// Alg_seq::unserialize_seq()
|
||||
// Alg_track::unserialize_track()
|
||||
// Alg_track::unserialize_parameter(Alg_parameter_ptr parm_ptr)
|
||||
// Just as serialization uses ser_buf for output, unserialization uses
|
||||
// unser_buf for reading. unser_buf is another static member of Alg_track.
|
||||
|
||||
#ifndef __ALLEGRO__
|
||||
#define __ALLEGRO__
|
||||
#include <assert.h>
|
||||
|
||||
#define ALG_EPS 0.000001 // epsilon
|
||||
#define ALG_DEFAULT_BPM 100.0 // default tempo
|
||||
|
||||
// are d1 and d2 within epsilon of each other?
|
||||
bool within(double d1, double d2, double epsilon);
|
||||
|
||||
char *heapify(const char *s); // put a string on the heap
|
||||
|
||||
|
||||
// Alg_attribute is an atom in the symbol table
|
||||
// with the special addition that the last
|
||||
// character is prefixed to the string; thus,
|
||||
// the attribute 'tempor' (a real) is stored
|
||||
// as 'rtempor'. To get the string name, just
|
||||
// use attribute+1.
|
||||
typedef char *Alg_attribute;
|
||||
#define alg_attr_name(a) ((a) + 1)
|
||||
#define alg_attr_type(a) (*(a))
|
||||
|
||||
// Alg_atoms is a symbol table of Alg_attributes and other
|
||||
// unique strings
|
||||
class Alg_atoms {
|
||||
public:
|
||||
Alg_atoms() {
|
||||
maxlen = len = 0;
|
||||
atoms = NULL;
|
||||
}
|
||||
// insert/lookup an atttribute
|
||||
Alg_attribute insert_attribute(Alg_attribute attr);
|
||||
// insert/lookup attribute by name (without prefixed type)
|
||||
Alg_attribute insert_string(const char *name);
|
||||
private:
|
||||
long maxlen;
|
||||
long len;
|
||||
char **atoms;
|
||||
|
||||
// insert an Attriubute not in table after moving attr to heap
|
||||
Alg_attribute insert_new(const char *name, char attr_type);
|
||||
void expand(); // make more space
|
||||
};
|
||||
|
||||
extern Alg_atoms symbol_table;
|
||||
|
||||
|
||||
// an attribute/value pair. Since Alg_attribute names imply type,
|
||||
// we try to keep attributes and values packaged together as
|
||||
// Alg_parameter class
|
||||
typedef class Alg_parameter {
|
||||
public:
|
||||
~Alg_parameter();
|
||||
Alg_attribute attr;
|
||||
union {
|
||||
double r;// real
|
||||
char *s; // string
|
||||
long i; // integer
|
||||
bool l; // logical
|
||||
char *a; // symbol (atom)
|
||||
}; // anonymous union
|
||||
void copy(Alg_parameter *); // copy from another parameter
|
||||
char attr_type() { return alg_attr_type(attr); }
|
||||
char *attr_name() { return alg_attr_name(attr); }
|
||||
void set_attr(Alg_attribute a) { attr = a; }
|
||||
void show();
|
||||
} *Alg_parameter_ptr;
|
||||
|
||||
|
||||
// a list of attribute/value pairs
|
||||
typedef class Alg_parameters {
|
||||
public:
|
||||
class Alg_parameters *next;
|
||||
Alg_parameter parm;
|
||||
|
||||
Alg_parameters(Alg_parameters *list) {
|
||||
next = list;
|
||||
}
|
||||
|
||||
//~Alg_parameters() { }
|
||||
|
||||
// each of these routines takes address of pointer to the list
|
||||
// insertion is performed without checking whether or not a
|
||||
// parameter already exists with this attribute. See find() and
|
||||
// remove_key() to assist in checking for and removing existing
|
||||
// parameters.
|
||||
// Note also that these insert_* methods convert name to an
|
||||
// attribute. If you have already done the symbol table lookup/insert
|
||||
// you can do these operations faster (in which case we should add
|
||||
// another set of functions that take attributes as arguments.)
|
||||
static void insert_real(Alg_parameters **list, char *name, double r);
|
||||
// insert string will copy string to heap
|
||||
static void insert_string(Alg_parameters **list, char *name, char *s);
|
||||
static void insert_integer(Alg_parameters **list, char *name, long i);
|
||||
static void insert_logical(Alg_parameters **list, char *name, bool l);
|
||||
static void insert_atom(Alg_parameters **list, char *name, char *s);
|
||||
static Alg_parameters *remove_key(Alg_parameters **list, char *name);
|
||||
// find an attribute/value pair
|
||||
Alg_parameter_ptr find(Alg_attribute *attr);
|
||||
} *Alg_parameters_ptr;
|
||||
|
||||
|
||||
// these are type codes associated with certain attributes
|
||||
// see Alg_track::find() where these are bit positions in event_type_mask
|
||||
#define ALG_NOTE 0 // This is a note, not an update
|
||||
#define ALG_GATE 1 // "gate"
|
||||
#define ALG_BEND 2 // "bend"
|
||||
#define ALG_CONTROL 3 // "control"
|
||||
#define ALG_PROGRAM 4 // "program"
|
||||
#define ALG_PRESSURE 5 // "pressure"
|
||||
#define ALG_KEYSIG 6 // "keysig"
|
||||
#define ALG_TIMESIG_NUM 7 // "timesig_num"
|
||||
#define ALG_TIMESIG_DEN 8 // "timesig_den"
|
||||
#define ALG_OTHER 9 // any other value
|
||||
|
||||
// abstract superclass of Alg_note and Alg_update:
|
||||
typedef class Alg_event {
|
||||
protected:
|
||||
bool selected;
|
||||
char type; // 'e' event, 'n' note, 'u' update
|
||||
long key; // note identifier
|
||||
static const char* description; // static buffer for debugging (in Alg_event)
|
||||
public:
|
||||
double time;
|
||||
long chan;
|
||||
virtual void show() = 0;
|
||||
// Note: there is no Alg_event() because Alg_event is an abstract class.
|
||||
bool is_note() { return (type == 'n'); } // tell whether an Alg_event is a note
|
||||
bool is_update() { return (type == 'u'); } // tell whether an Alg_event is a parameter update
|
||||
char get_type() { return type; } // return 'n' for note, 'u' for update
|
||||
int get_type_code(); // 1 = volume change, 2 = pitch bend,
|
||||
// 3 = control change, 4 = program change,
|
||||
// 5 = pressure change, 6 = key signature,
|
||||
// 7 = time sig numerator, 8 = time sig denominator
|
||||
bool get_selected() { return selected; }
|
||||
void set_selected(bool b) { selected = b; }
|
||||
// Note: notes are identified by a (channel, identifier) pair.
|
||||
// For midi, the identifier is the key number (pitch). The identifier
|
||||
// does not have to represent pitch; it's main purpose is to identify
|
||||
// notes so that they can be named by subsequent update events.
|
||||
long get_identifier() { return key; } // get MIDI key or note identifier of note or update
|
||||
void set_identifier(long i) { key = i; } // set the identifier
|
||||
// In all of these set_ methods, strings are owned by the caller and
|
||||
// copied as necessary by the callee. For notes, an attribute/value
|
||||
// pair is added to the parameters list. For updates, the single
|
||||
// attribute/value parameter pair is overwritten. In all cases, the
|
||||
// attribute (first argument) must agree in type with the second arg.
|
||||
// The last letter of the attribute implies the type (see below).
|
||||
void set_parameter(Alg_parameter_ptr new_parameter);
|
||||
void set_string_value(char *attr, char *value);
|
||||
void set_real_value(char *attr, double value);
|
||||
void set_logical_value(char *attr, bool value);
|
||||
void set_integer_value(char *attr, long value);
|
||||
void set_atom_value(char *attr, char *atom);
|
||||
|
||||
// Some note methods. These fail (via assert()) if this is not a note:
|
||||
//
|
||||
float get_pitch();// get pitch in steps -- use this even for MIDI
|
||||
float get_loud(); // get loudness (MIDI velocity)
|
||||
// times are in seconds or beats, depending upon the units_are_seconds
|
||||
// flag in the containing sequence
|
||||
double get_start_time(); // get start time in seconds or beats
|
||||
double get_end_time(); // get end time in seconds or beats
|
||||
double get_duration(); // get duration in seconds or beats
|
||||
void set_pitch(float);
|
||||
void set_loud(float);
|
||||
void set_duration(double);
|
||||
|
||||
// Notes have lists of attribute values. Attributes are converted
|
||||
// to/from strings in this API to avoid explicit use of Alg_attribute
|
||||
// types. Attribute names end with a type designation: 's', 'r', 'l',
|
||||
// 'i', or 'a'.
|
||||
//
|
||||
bool has_attribute(char *attr); // test if note has attribute/value pair
|
||||
char get_attribute_type(char *attr); // get the associated type:
|
||||
// 's' = string,
|
||||
// 'r' = real (double), 'l' = logical (bool), 'i' = integer (long),
|
||||
// 'a' = atom (char *), a unique string stored in Alg_seq
|
||||
char *get_string_value(char *attr, char *value = NULL); // get the string value
|
||||
double get_real_value(char *attr, double value = 0.0); // get the real value
|
||||
bool get_logical_value(char *attr, bool value = false); // get the logical value
|
||||
long get_integer_value(char *attr, long value = 0); // get the integer value
|
||||
char *get_atom_value(char *attr, char *value = NULL); // get the atom value
|
||||
void delete_attribute(char *attr); // delete an attribute/value pair
|
||||
// (ignore if no matching attribute/value pair exists)
|
||||
|
||||
// Some attribute/value methods. These fail if this is not an update.
|
||||
// Attributes are converted to/from strings to avoid explicit use
|
||||
// of Alg_attribute types.
|
||||
//
|
||||
const char *get_attribute(); // get the update's attribute (string)
|
||||
char get_update_type(); // get the update's type: 's' = string,
|
||||
// 'r' = real (double), 'l' = logical (bool), 'i' = integer (long),
|
||||
// 'a' = atom (char *), a unique string stored in Alg_seq
|
||||
char *get_string_value(); // get the update's string value
|
||||
// Notes: Caller does not own the return value. Do not modify.
|
||||
// Do not use after underlying Alg_seq is modified.
|
||||
double get_real_value(); // get the update's real value
|
||||
bool get_logical_value(); // get the update's logical value
|
||||
long get_integer_value(); // get the update's integer value
|
||||
char *get_atom_value(); // get the update's atom value
|
||||
// Notes: Caller does not own the return value. Do not modify.
|
||||
// The return value's lifetime is forever.
|
||||
|
||||
// Auxiliary function to aid in editing tracks
|
||||
// Returns true if the event overlaps the given region
|
||||
bool overlap(double t, double len, bool all);
|
||||
|
||||
const char *GetDescription(); // computes a text description of this event
|
||||
// the result is in a static buffer, not thread-safe, just for debugging.
|
||||
Alg_event() { selected = false; }
|
||||
virtual ~Alg_event() {}
|
||||
} *Alg_event_ptr;
|
||||
|
||||
|
||||
typedef class Alg_note : public Alg_event {
|
||||
public:
|
||||
virtual ~Alg_note();
|
||||
Alg_note(Alg_note *); // copy constructor
|
||||
float pitch; // pitch in semitones (69 = A440)
|
||||
float loud; // dynamic corresponding to MIDI velocity
|
||||
double dur; // duration in seconds (normally to release point)
|
||||
Alg_parameters_ptr parameters; // attribute/value pair list
|
||||
Alg_note() { type = 'n'; parameters = NULL; }
|
||||
void show();
|
||||
} *Alg_note_ptr;
|
||||
|
||||
|
||||
typedef class Alg_update : public Alg_event {
|
||||
public:
|
||||
virtual ~Alg_update() {};
|
||||
Alg_update(Alg_update *); // copy constructor
|
||||
Alg_parameter parameter; // an update contains one attr/value pair
|
||||
|
||||
|
||||
Alg_update() { type = 'u'; }
|
||||
void show();
|
||||
} *Alg_update_ptr;
|
||||
|
||||
|
||||
// a sequence of Alg_event objects
|
||||
typedef class Alg_events {
|
||||
private:
|
||||
long maxlen;
|
||||
void expand();
|
||||
protected:
|
||||
long len;
|
||||
Alg_event_ptr *events; // events is array of pointers
|
||||
public:
|
||||
// sometimes, it is nice to have the time of the last note-off.
|
||||
// In the current implementation,
|
||||
// this field is set by append to indicate the time of the
|
||||
// last note-off in the current unit, so it should be correct after
|
||||
// creating a new track and adding notes to it. It is *not*
|
||||
// updated after uninsert(), so use it with care.
|
||||
double last_note_off;
|
||||
virtual int length() { return len; }
|
||||
Alg_event_ptr &operator[](int i) {
|
||||
assert(i >= 0 && i < len);
|
||||
return events[i];
|
||||
}
|
||||
Alg_events() {
|
||||
maxlen = len = 0;
|
||||
events = NULL;
|
||||
last_note_off = 0;
|
||||
}
|
||||
// destructor deletes the events array, but not the
|
||||
// events themselves
|
||||
~Alg_events();
|
||||
void set_events(Alg_event_ptr *e, long l, long m) {
|
||||
if (events) delete [] events;
|
||||
events = e; len = l; maxlen = m; }
|
||||
// for use by Alg_track and Alg_seq
|
||||
void insert(Alg_event_ptr event);
|
||||
void append(Alg_event_ptr event);
|
||||
Alg_event_ptr uninsert(long index);
|
||||
} *Alg_events_ptr;
|
||||
|
||||
class Alg_track;
|
||||
|
||||
typedef class Alg_event_list : public Alg_events {
|
||||
protected:
|
||||
char type; // 'e' Alg_event_list, 't' Alg_track, 's' Alg_seq
|
||||
static const char *last_error_message;
|
||||
Alg_track *events_owner; // if this is an Alg_event_list,
|
||||
// the events are owned by an Alg_track or an Alg_seq
|
||||
static int sequences; // to keep track of sequence numbers
|
||||
int sequence_number; // this sequence number is incremented
|
||||
// whenever an edit is performed on an Alg_track or Alg_seq.
|
||||
// When an Alg_event_list is created to contain pointers to
|
||||
// a subset of an Alg_track or Alg_seq (the events_owner),
|
||||
// the Alg_event_list gets a copy of the events_owner's
|
||||
// sequence_number. If the events_owner is edited, the pointers
|
||||
// in this Alg_event_list will become invalid. This is detected
|
||||
// (for debugging) as differing sequence_numbers.
|
||||
|
||||
// every event list, track, and seq has a duration.
|
||||
// Usually the duration is set when the list is constructed, e.g.
|
||||
// when you extract from 10 to 15 seconds, the duration is 5 secs.
|
||||
// The duration does not tell you when is the last note-off.
|
||||
// duration is recorded in both beats and seconds:
|
||||
double beat_dur;
|
||||
double real_dur;
|
||||
public:
|
||||
// the client should not create one of these, but these are
|
||||
// returned from various track and seq operations. An
|
||||
// Alg_event_list "knows" the Alg_track or Alg_seq that "owns"
|
||||
// the events. All events in an Alg_event_list must belong
|
||||
// to the same Alg_track or Alg_seq structure.
|
||||
// When applied to an Alg_seq, events are enumerated track
|
||||
// by track with increasing indices. This operation is not
|
||||
// particularly fast on an Alg_seq.
|
||||
virtual Alg_event_ptr &operator[](int i);
|
||||
Alg_event_list() { sequence_number = 0;
|
||||
beat_dur = 0.0; real_dur = 0.0; events_owner = NULL; type = 'e'; }
|
||||
Alg_event_list(Alg_track *owner);
|
||||
|
||||
char get_type() { return type; }
|
||||
Alg_track *get_owner() { return events_owner; }
|
||||
|
||||
// The destructor does not free events because they are owned
|
||||
// by a track or seq structure.
|
||||
virtual ~Alg_event_list();
|
||||
|
||||
// Returns the duration of the sequence in beats or seconds
|
||||
double get_beat_dur() { return beat_dur; }
|
||||
void set_beat_dur(double d) { beat_dur = d; }
|
||||
double get_real_dur() { return real_dur; }
|
||||
void set_real_dur(double d) { real_dur = d; }
|
||||
|
||||
// Events are stored in time order, so when you change the time of
|
||||
// an event, you must adjust the position. When you call set_start_time
|
||||
// on an Alg_event_list, the Alg_event_list is not modified, but the
|
||||
// Alg_track that "owns" the event is modified. If the owner is an
|
||||
// Alg_seq, this may require searching the seq for the track containing
|
||||
// the event. This will mean a logN search of every track in the seq
|
||||
// (but if this turns out to be a problem, we can store each event's
|
||||
// track owner in the Alg_event_list.)
|
||||
virtual void set_start_time(Alg_event *event, double);
|
||||
// get text description of run-time errors detected, clear error
|
||||
const char *get_last_error_message() { return last_error_message; }
|
||||
// Implementation hint: keep a sequence number on each Alg_track that is
|
||||
// incremented anytime there is a structural change. (This behavior is
|
||||
// inherited by Alg_seq as well.) Copy the sequence number to any
|
||||
// Alg_event_list object when it is created. Whenever you access an
|
||||
// Alg_event_list, using operator[], assert that the Alg_event_list sequence
|
||||
// number matches the Alg_seq sequence number. This will guarantee that you
|
||||
// do not try to retain pointers to events beyond the point where the events
|
||||
// may no longer exist.
|
||||
} *Alg_event_list_ptr, &Alg_event_list_ref;
|
||||
|
||||
|
||||
// Alg_beat is used to contruct a tempo map
|
||||
typedef class Alg_beat {
|
||||
public:
|
||||
Alg_beat(double t, double b) {
|
||||
time = t; beat = b; }
|
||||
Alg_beat() {};
|
||||
double time;
|
||||
double beat;
|
||||
} *Alg_beat_ptr;
|
||||
|
||||
|
||||
// Alg_beats is a list of Alg_beat objects used in Alg_seq
|
||||
typedef class Alg_beats {
|
||||
private:
|
||||
long maxlen;
|
||||
void expand();
|
||||
public:
|
||||
long len;
|
||||
Alg_beat_ptr beats;
|
||||
Alg_beat &operator[](int i) {
|
||||
assert(i >= 0 && i < len);
|
||||
return beats[i];
|
||||
}
|
||||
Alg_beats() {
|
||||
maxlen = len = 0;
|
||||
beats = NULL;
|
||||
expand();
|
||||
beats[0].time = 0;
|
||||
beats[0].beat = 0;
|
||||
len = 1;
|
||||
}
|
||||
~Alg_beats() {
|
||||
if (beats) delete[] beats;
|
||||
}
|
||||
void insert(long i, Alg_beat_ptr beat);
|
||||
} *Alg_beats_ptr;
|
||||
|
||||
|
||||
typedef class Alg_time_map {
|
||||
private:
|
||||
int refcount;
|
||||
public:
|
||||
Alg_beats beats; // array of Alg_beat
|
||||
double last_tempo;
|
||||
bool last_tempo_flag;
|
||||
Alg_time_map() {
|
||||
last_tempo = ALG_DEFAULT_BPM / 60.0; // note: this value ignored until
|
||||
// last_tempo_flag is set; nevertheless, the default
|
||||
// tempo is 100.
|
||||
last_tempo_flag = true;
|
||||
refcount = 0;
|
||||
}
|
||||
Alg_time_map(Alg_time_map *map); // copy constructor
|
||||
long length() { return beats.len; }
|
||||
void show();
|
||||
long locate_time(double time);
|
||||
long locate_beat(double beat);
|
||||
double beat_to_time(double beat);
|
||||
double time_to_beat(double time);
|
||||
// Time map manipulations: it is prefered to call the corresponding
|
||||
// methods in Alg_seq. If you manipulate an Alg_time_map directly,
|
||||
// you should take care to convert all tracks that use the time map
|
||||
// to beats or seconds as appropriate: Normally if you insert a beat
|
||||
// you want tracks to be in time units and if you insert a tempo change
|
||||
// you want tracks to be in beat units.
|
||||
void insert_beat(double time, double beat); // add a point to the map
|
||||
bool insert_tempo(double tempo, double beat); // insert a tempo change
|
||||
// set the tempo over a region
|
||||
bool set_tempo(double tempo, double start_beat, double end_beat);
|
||||
void cut(double start, double len, bool units_are_seconds);
|
||||
void trim(double start, double end, bool units_are_seconds);
|
||||
void paste(double start, Alg_track *tr);
|
||||
// insert a span of time. If start is at a tempo change, then
|
||||
// the span of time runs at the changed tempo
|
||||
void insert_time(double start, double len);
|
||||
// insert a span of beats. If start is at a tempo change, the
|
||||
// tempo change takes effect before the inserted beats
|
||||
void insert_beats(double start, double len);
|
||||
void dereference() {
|
||||
if (--refcount <= 0) delete this;
|
||||
}
|
||||
void reference() {
|
||||
refcount++;
|
||||
}
|
||||
} *Alg_time_map_ptr;
|
||||
|
||||
|
||||
typedef class Serial_buffer {
|
||||
private:
|
||||
char *buffer;
|
||||
char *ptr;
|
||||
long len;
|
||||
public:
|
||||
Serial_buffer() {
|
||||
buffer = NULL;
|
||||
ptr = NULL;
|
||||
len = 0;
|
||||
}
|
||||
void init_for_write() { ptr = buffer; }
|
||||
long get_posn() { return (long) (ptr - buffer); }
|
||||
long get_len() { return len; }
|
||||
// store_long writes a long at a given offset
|
||||
void store_long(long offset, long value) {
|
||||
assert(offset <= get_posn() - 4);
|
||||
long *loc = (long *) (buffer + offset);
|
||||
*loc = value;
|
||||
}
|
||||
void check_buffer(long needed);
|
||||
void set_string(char *s) {
|
||||
char *fence = buffer + len;
|
||||
assert(ptr < fence);
|
||||
while ((*ptr++ = *s++)) assert(ptr < fence);
|
||||
assert((char *)(((long) (ptr + 7)) & ~7) <= fence);
|
||||
pad(); }
|
||||
void set_int32(long v) { *((long *) ptr) = v; ptr += 4; }
|
||||
void set_double(double v) { *((double *) ptr) = v; ptr += 8; }
|
||||
void set_float(float v) { *((float *) ptr) = v; ptr += 4; }
|
||||
void set_char(char v) { *ptr++ = v; }
|
||||
void pad() { while (((long) ptr) & 7) set_char(0); }
|
||||
void *to_heap(long *len) {
|
||||
*len = get_posn();
|
||||
char *newbuf = new char[*len];
|
||||
memcpy(newbuf, buffer, *len);
|
||||
return newbuf;
|
||||
}
|
||||
void init_for_read(void *buf, long n) {
|
||||
buffer = (char *) buf;
|
||||
ptr = (char *) buf;
|
||||
len = n;
|
||||
}
|
||||
char get_char() { return *ptr++; }
|
||||
long get_int32() { long i = *((long *) ptr); ptr += 4; return i; }
|
||||
float get_float() { float f = *((float *) ptr); ptr += 4; return f; }
|
||||
double get_double() { double d = *((double *) ptr); ptr += sizeof(double);
|
||||
return d; }
|
||||
char *get_string() { char *s = ptr; char *fence = buffer + len;
|
||||
assert(ptr < fence);
|
||||
while (*ptr++) assert(ptr < fence);
|
||||
get_pad();
|
||||
return s; }
|
||||
void get_pad() { while (((long) ptr) & 7) ptr++; }
|
||||
void check_input_buffer(long needed) {
|
||||
assert(get_posn() + needed <= len); }
|
||||
} *Serial_buffer_ptr;
|
||||
|
||||
typedef class Alg_seq *Alg_seq_ptr;
|
||||
|
||||
typedef class Alg_track : public Alg_event_list {
|
||||
protected:
|
||||
Alg_time_map *time_map;
|
||||
bool units_are_seconds;
|
||||
char *get_string(char **p, long *b);
|
||||
long get_int32(char **p, long *b);
|
||||
double get_double(char **p, long *b);
|
||||
float get_float(char **p, long *b);
|
||||
static Serial_buffer ser_buf;
|
||||
void serialize_parameter(Alg_parameter *parm);
|
||||
// *buffer_ptr points to binary data, bytes_ptr points to how many
|
||||
// bytes have been used so far, len is length of binary data
|
||||
void unserialize_parameter(Alg_parameter_ptr parm_ptr);
|
||||
public:
|
||||
void serialize_track();
|
||||
void unserialize_track();
|
||||
virtual Alg_event_ptr &operator[](int i) {
|
||||
assert(i >= 0 && i < len);
|
||||
return events[i];
|
||||
}
|
||||
Alg_track() { units_are_seconds = false; time_map = NULL;
|
||||
set_time_map(NULL); type = 't'; }
|
||||
// initialize empty track with a time map
|
||||
Alg_track(Alg_time_map *map, bool seconds);
|
||||
|
||||
Alg_event_ptr copy_event(Alg_event_ptr event); // make a complete copy
|
||||
|
||||
Alg_track(Alg_track &track); // copy constructor, does not copy time_map
|
||||
// copy constructor: event_list is copied, map is installed and referenced
|
||||
Alg_track(Alg_event_list_ref event_list, Alg_time_map_ptr map,
|
||||
bool units_are_seconds);
|
||||
virtual ~Alg_track() { set_time_map(NULL); }
|
||||
|
||||
// Returns a buffer containing a serialization of the
|
||||
// file. It will be an ASCII representation unless text is true.
|
||||
// *buffer gets a newly allocated buffer pointer. The caller must free it.
|
||||
// *len gets the length of the serialized track
|
||||
virtual void serialize(void **buffer, long *bytes);
|
||||
|
||||
// Try to read from a memory buffer. Automatically guess
|
||||
// whether it's MIDI or text.
|
||||
static Alg_track *unserialize(void *buffer, long len);
|
||||
|
||||
// If the track is really an Alg_seq and you need to access an
|
||||
// Alg_seq method, coerce to an Alg_seq with this function:
|
||||
Alg_seq_ptr to_alg_seq() {
|
||||
return (get_type() == 's' ? (Alg_seq_ptr) this : NULL); }
|
||||
|
||||
// Are we using beats or seconds?
|
||||
bool get_units_are_seconds() { return units_are_seconds; }
|
||||
// Change units
|
||||
virtual void convert_to_beats();
|
||||
virtual void convert_to_seconds();
|
||||
void set_dur(double dur);
|
||||
double get_dur() { return (units_are_seconds ? real_dur : beat_dur); }
|
||||
|
||||
// Every Alg_track may have an associated time_map. If no map is
|
||||
// specified, or if you set_time_map(NULL), then the behavior
|
||||
// should be as if there is a constant tempo of 100 beats/minute
|
||||
// (this constant is determined by ALG_DEFAULT_BPM).
|
||||
// Recommendation: create a static global tempo map object. When
|
||||
// any operation that needs a tempo map gets NULL, use the global
|
||||
// tempo map. (Exception: any operation that would modify the
|
||||
// tempo map should raise an error -- you don't want to change the
|
||||
// default tempo map.)
|
||||
virtual void set_time_map(Alg_time_map *map);
|
||||
Alg_time_map *get_time_map() { return time_map; }
|
||||
|
||||
// Methods to create events. The returned event is owned by the caller.
|
||||
// Use delete to get rid of it unless you call add() -- see below.
|
||||
//
|
||||
Alg_note *create_note(double time, int channel, int identifier,
|
||||
float pitch, float loudness, double duration);
|
||||
// Note: after create_update(), caller should use set_*_value() to
|
||||
// initialize the attribute/value pair:
|
||||
Alg_update *create_update(double time, int channel, int identifier);
|
||||
// Adds a new event - it is automatically inserted into the
|
||||
// correct order in the sequence based on its timestamp.
|
||||
// The ownership passes from the caller to this Alg_seq. The
|
||||
// event is not copied.
|
||||
virtual void add(Alg_event *event) { insert(event); }
|
||||
|
||||
//
|
||||
// Editing regions
|
||||
//
|
||||
|
||||
// Deletes the notes that start within the given region
|
||||
// and returns them in a new sequence. The start times
|
||||
// of the notes in the returned sequence are shifted
|
||||
// by -t. The notes after the region get shifted over
|
||||
// to fill the gap. In an Alg_seq, the tempo track is edited
|
||||
// in a similar way
|
||||
// and the cut tempo information is retained in the new seq.
|
||||
// ONLY NOTES THAT START WITHIN THE REGION ARE CUT unless
|
||||
// "all" is true in which case all notes that intersect
|
||||
// the region are copied. CUT NOTES
|
||||
// MAY EXTEND BEYOND THE DURATION OF THE RESULTING SEQ.
|
||||
// The return type is the same as this (may be Alg_seq).
|
||||
// All times including len are interpreted according to
|
||||
// units_are_seconds in the track.
|
||||
virtual Alg_track *cut(double t, double len, bool all);
|
||||
|
||||
// Like cut() but doesn't remove the notes from the original
|
||||
// sequence. The Alg_events are copied, not shared. ONLY EVENTS
|
||||
// THAT START WITHIN THE REGION ARE COPIED unless "all" is true
|
||||
// in which case all notes that intersect the region are
|
||||
// copied. COPIED NOTES MAY
|
||||
// EXTEND BEYOND THE DURATION OF THE RESULTING SEQ.
|
||||
// The return type is the same as this (may be Alg_seq).
|
||||
virtual Alg_track *copy(double t, double len, bool all);
|
||||
|
||||
// Inserts a sequence in the middle, shifting some notes
|
||||
// over by the duration of the seq, which is first converted
|
||||
// to the same units (seconds or beats) as this. (This makes
|
||||
// a differece because the pasted data may change the tempo,
|
||||
// and notes that overlap the borders will then experience
|
||||
// a tempo change.)
|
||||
// THE SEQ PARAMETER IS NOT MODIFIED, AND Alg_event's ARE
|
||||
// COPIED, NOT SHARED.
|
||||
// The type of seq must be Alg_seq if seq is an Alg_seq, or
|
||||
// Alg_track if seq is an Alg_track or an Alg_event_list.
|
||||
virtual void paste(double t, Alg_event_list *seq); // Shifts notes
|
||||
|
||||
// Merges two sequences with a certain offset. The offset is
|
||||
// interpreted as either beats or seconds according to the
|
||||
// current units of this, and seq is converted to the same
|
||||
// units as this. Except for a possible conversion to beats
|
||||
// or seconds, the tempo track of seq (if any) is ignored.
|
||||
// (There is no way to merge tempo tracks.)
|
||||
// THE SEQ PARAMETER IS NOT MODIFIED, AND Alg_event's ARE
|
||||
// COPIED, NOT SHARED.
|
||||
// The type of seq must be Alg_seq if seq is an Alg_seq, or
|
||||
// Alg_track if seq is an Alg_track or an Alg_event_list.
|
||||
virtual void merge(double t, Alg_event_list_ptr seq);
|
||||
|
||||
// Deletes and shifts notes to fill the gap. The tempo track
|
||||
// is also modified accordingly. ONLY EVENTS THAT START WITHIN
|
||||
// THE REGION ARE DELETED unless "all" is true, in which case
|
||||
// all notes that intersect the region are cleared.
|
||||
// NOTES THAT EXTEND FROM BEFORE THE
|
||||
// REGION INTO THE REGION RETAIN THEIR DURATION IN EITHER
|
||||
// BEATS OR SECONDS ACCORDING TO THE CURRENT UNITS OF this.
|
||||
virtual void clear(double t, double len, bool all);
|
||||
|
||||
// Deletes notes but doesn't shift. If the "all" argument
|
||||
// is true, deletes all notes that intersect the range at all,
|
||||
// not just those that start within it. The tempo track is
|
||||
// not affected.
|
||||
virtual void silence(double t, double len, bool all);
|
||||
|
||||
// Simply shifts notes past time t over by len, which is given
|
||||
// in either beats or seconds according to the units of this.
|
||||
// The resulting interveal (t, t+len) may in fact contain notes
|
||||
// that begin before t. The durations of notes are not changed.
|
||||
// If this is an Alg_seq, the tempo track is expanded at t also.
|
||||
virtual void insert_silence(double t, double len);
|
||||
|
||||
//
|
||||
// Accessing for screen display
|
||||
//
|
||||
|
||||
// A useful generic function to retrieve only certain
|
||||
// types of events. The masks should be bit-masks defined
|
||||
// somewhere else. Part of the mask allows us to search for
|
||||
// selected events. If this is an Alg_seq, search all tracks
|
||||
// (otherwise, call track[i].find())
|
||||
// If channel_mask == 0, accept ALL channels
|
||||
virtual Alg_event_list *find(double t, double len, bool all,
|
||||
long channel_mask, long event_type_mask);
|
||||
|
||||
//
|
||||
// MIDI playback
|
||||
//
|
||||
// See Alg_iterator
|
||||
} *Alg_track_ptr, &Alg_track_ref;
|
||||
|
||||
|
||||
// Alg_time_sig represents a single time signature;
|
||||
// although not recommended, time_signatures may have arbitrary
|
||||
// floating point values, e.g. 4.5 beats per measure
|
||||
typedef class Alg_time_sig {
|
||||
public:
|
||||
double beat; // when does this take effect?
|
||||
double num; // what is the "numerator" (top number?)
|
||||
double den; // what is the "denominator" (bottom number?)
|
||||
Alg_time_sig(double b, double n, double d) {
|
||||
beat = b; num = n; den = d;
|
||||
}
|
||||
Alg_time_sig() {
|
||||
beat = 0; num = 0; den = 0;
|
||||
}
|
||||
void beat_to_measure(double beat, double *measure, double *m_beat,
|
||||
double *num, double *den);
|
||||
|
||||
} *Alg_time_sig_ptr;
|
||||
|
||||
|
||||
// Alg_time_sigs is a dynamic array of time signatures
|
||||
//
|
||||
// The default (empty) time_sigs has 4/4 time at beat 0.
|
||||
// Each time_sig object in time_sigs represents the beginning
|
||||
// of a measure. If there is a beat missing, e.g. in the first
|
||||
// measure, you can represent this by inserting another
|
||||
// time_sig at the next measure beginning. Each time_sig implies
|
||||
// an infinite sequence of full measures until the next time_sig.
|
||||
// If you insert a time_sig and one already exist near the same
|
||||
// beat, the old one is replaced, thus re-barring every measure
|
||||
// until the next time_sig.
|
||||
class Alg_time_sigs {
|
||||
private:
|
||||
long maxlen;
|
||||
void expand(); // make more space
|
||||
long len;
|
||||
Alg_time_sig_ptr time_sigs;
|
||||
public:
|
||||
Alg_time_sigs() {
|
||||
maxlen = len = 0;
|
||||
time_sigs = NULL;
|
||||
}
|
||||
Alg_time_sig &operator[](int i) { // fetch a time signature
|
||||
assert(i >= 0 && i < len);
|
||||
return time_sigs[i];
|
||||
}
|
||||
~Alg_time_sigs() {
|
||||
if (time_sigs) delete[] time_sigs;
|
||||
}
|
||||
void show();
|
||||
long length() { return len; }
|
||||
int find_beat(double beat);
|
||||
void insert(double beat, double num, double den);
|
||||
void cut(double start, double end); // remove from start to end
|
||||
void trim(double start, double end); // retain just start to end
|
||||
void paste(double start, Alg_seq *seq);
|
||||
void insert_beats(double beat, double len); // insert len beats at beat
|
||||
};
|
||||
|
||||
|
||||
// a sequence of Alg_events objects
|
||||
typedef class Alg_tracks {
|
||||
private:
|
||||
long maxlen;
|
||||
void expand();
|
||||
void expand_to(int new_max);
|
||||
long len;
|
||||
public:
|
||||
Alg_track_ptr *tracks; // tracks is array of pointers
|
||||
Alg_track &operator[](int i) {
|
||||
assert(i >= 0 && i < len);
|
||||
return *tracks[i];
|
||||
}
|
||||
long length() { return len; }
|
||||
Alg_tracks() {
|
||||
maxlen = len = 0;
|
||||
tracks = NULL;
|
||||
}
|
||||
~Alg_tracks();
|
||||
// Append a track to tracks. This Alg_tracks becomes the owner of track.
|
||||
void append(Alg_track_ptr track);
|
||||
void add_track(int track_num, Alg_time_map_ptr time_map, bool seconds);
|
||||
void reset();
|
||||
} *Alg_tracks_ptr;
|
||||
|
||||
|
||||
typedef enum {
|
||||
alg_no_error = 0, // no error reading Allegro or MIDI file
|
||||
alg_error_open = -800, // could not open Allegro or MIDI file
|
||||
alg_error_syntax // something found in the file that could not be parsed;
|
||||
// generally you should ignore syntax errors or look at the printed error messages
|
||||
// because there are some things in standard midi files that we do not handle;
|
||||
// (maybe we should only set alg_error_syntax when there is a real problem with
|
||||
// the file as opposed to when there is some warning message for the user)
|
||||
} Alg_error;
|
||||
|
||||
|
||||
// An Alg_seq is an array of Alg_events, each a sequence of Alg_event,
|
||||
// with a tempo map and a sequence of time signatures
|
||||
//
|
||||
typedef class Alg_seq : public Alg_track {
|
||||
protected:
|
||||
long *current; // array of indexes used by iteration methods
|
||||
void serialize_seq();
|
||||
Alg_error error; // error code set by file readers
|
||||
// an internal function used for writing Allegro track names
|
||||
Alg_event_ptr write_track_name(std::ostream &file, int n,
|
||||
Alg_events &events);
|
||||
public:
|
||||
int channel_offset_per_track; // used to encode track_num into channel
|
||||
Alg_tracks track_list; // array of Alg_events
|
||||
Alg_time_sigs time_sig;
|
||||
int beat_x;
|
||||
void basic_initialization() {
|
||||
error = alg_no_error;
|
||||
units_are_seconds = true; type = 's';
|
||||
channel_offset_per_track = 0;
|
||||
add_track(0); // default is one empty track
|
||||
}
|
||||
Alg_seq() {
|
||||
basic_initialization();
|
||||
}
|
||||
// copy constructor -- if track is an Alg_seq, make a copy; if
|
||||
// track is just an Alg_track, the track becomes track 0
|
||||
Alg_seq(Alg_track_ref track) { seq_from_track(track); }
|
||||
Alg_seq(Alg_track_ptr track) { seq_from_track(*track); }
|
||||
void seq_from_track(Alg_track_ref tr);
|
||||
Alg_seq(std::istream &file, bool smf); // create from file
|
||||
Alg_seq(const char *filename, bool smf); // create from filename
|
||||
~Alg_seq();
|
||||
int get_read_error() { return error; }
|
||||
void serialize(void **buffer, long *bytes);
|
||||
void copy_time_sigs_to(Alg_seq *dest); // a utility function
|
||||
void set_time_map(Alg_time_map *map);
|
||||
|
||||
// encode sequence structure into contiguous, moveable memory block
|
||||
// address of newly allocated memory is assigned to *buffer, which must
|
||||
// be freed by caller; the length of data is assigned to *len
|
||||
void unserialize_seq();
|
||||
|
||||
// write an ascii representation to file
|
||||
void write(std::ostream &file, bool in_secs);
|
||||
// returns true on success
|
||||
bool write(const char *filename);
|
||||
void smf_write(std::ofstream &file);
|
||||
bool smf_write(const char *filename);
|
||||
|
||||
// Returns the number of tracks
|
||||
int tracks();
|
||||
|
||||
// create a track
|
||||
void add_track(int track_num) {
|
||||
track_list.add_track(track_num, get_time_map(), units_are_seconds);
|
||||
}
|
||||
|
||||
// Return a particular track. This Alg_seq owns the track, so the
|
||||
// caller must not delete the result.
|
||||
Alg_track_ptr track(int);
|
||||
|
||||
virtual Alg_event_ptr &operator[](int i);
|
||||
|
||||
virtual void convert_to_seconds();
|
||||
virtual void convert_to_beats();
|
||||
|
||||
Alg_track_ptr cut_from_track(int track_num, double start, double dur,
|
||||
bool all);
|
||||
Alg_seq *cut(double t, double len, bool all);
|
||||
void insert_silence_in_track(int track_num, double t, double len);
|
||||
void insert_silence(double t, double len);
|
||||
Alg_track_ptr copy_track(int track_num, double t, double len, bool all);
|
||||
Alg_seq *copy(double start, double len, bool all);
|
||||
void paste(double start, Alg_seq *seq);
|
||||
virtual void clear(double t, double len, bool all);
|
||||
virtual void merge(double t, Alg_event_list_ptr seq);
|
||||
virtual void silence(double t, double len, bool all);
|
||||
void clear_track(int track_num, double start, double len, bool all);
|
||||
void silence_track(int track_num, double start, double len, bool all);
|
||||
Alg_event_list_ptr find_in_track(int track_num, double t, double len,
|
||||
bool all, long channel_mask,
|
||||
long event_type_mask);
|
||||
|
||||
// find index of first score event after time
|
||||
long seek_time(double time, int track_num);
|
||||
bool insert_beat(double time, double beat);
|
||||
// warning: insert_tempo may change representation from seconds to beats
|
||||
bool insert_tempo(double bpm, double beat);
|
||||
|
||||
// add_event takes a pointer to an event on the heap. The event is not
|
||||
// copied, and this Alg_seq becomes the owner and freer of the event.
|
||||
void add_event(Alg_event_ptr event, int track_num);
|
||||
void add(Alg_event_ptr event) { assert(false); } // call add_event instead
|
||||
// warning: set_tempo may change representation from seconds to beats
|
||||
bool set_tempo(double bpm, double start_beat, double end_beat);
|
||||
void set_time_sig(double beat, double num, double den);
|
||||
void beat_to_measure(double beat, long *measure, double *m_beat,
|
||||
double *num, double *den);
|
||||
// void set_events(Alg_event_ptr *events, long len, long max);
|
||||
void merge_tracks(); // move all track data into one track
|
||||
void iteration_begin(); // prepare to enumerate events in order
|
||||
Alg_event_ptr iteration_next(); // return next event (or NULL)
|
||||
void iteration_end(); // clean up after enumerating events
|
||||
} *Alg_seq_ptr, &Alg_seq_ref;
|
||||
|
||||
|
||||
// see Alg_seq::Alg_seq() constructors that read from files
|
||||
// the following are for internal library implementation and are
|
||||
// moved to *_internal.h header files.
|
||||
//Alg_seq_ptr alg_read(std::istream &file, Alg_seq_ptr new_seq);
|
||||
//Alg_seq_ptr alg_smf_read(std::istream &file, Alg_seq_ptr new_seq);
|
||||
#endif
|
||||
755
plugins/midi_import/portsmf/allegrord.cpp
Normal file
755
plugins/midi_import/portsmf/allegrord.cpp
Normal file
@@ -0,0 +1,755 @@
|
||||
#include "assert.h"
|
||||
#include "stdlib.h"
|
||||
#include "string.h"
|
||||
#include "ctype.h"
|
||||
#include "trace.h"
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
#include "strparse.h"
|
||||
#include "allegro.h"
|
||||
#include "algrd_internal.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define streql(s1, s2) (strcmp(s1, s2) == 0)
|
||||
#define field_max 80
|
||||
|
||||
class Alg_reader {
|
||||
public:
|
||||
istream *file;
|
||||
string input_line;
|
||||
int line_no;
|
||||
String_parse line_parser;
|
||||
bool line_parser_flag;
|
||||
string field;
|
||||
bool error_flag;
|
||||
Alg_seq_ptr seq;
|
||||
double tsnum;
|
||||
double tsden;
|
||||
|
||||
Alg_reader(istream *a_file, Alg_seq_ptr new_seq);
|
||||
void readline();
|
||||
Alg_parameters_ptr process_attributes(Alg_parameters_ptr attributes,
|
||||
double time);
|
||||
bool parse();
|
||||
long parse_chan(string &field);
|
||||
long parse_int(string &field);
|
||||
int find_real_in(string &field, int n);
|
||||
double parse_real(string &field);
|
||||
void parse_error(string &field, long offset, char *message);
|
||||
double parse_dur(string &field, double base);
|
||||
double parse_after_dur(double dur, string &field, int n, double base);
|
||||
double parse_loud(string &field);
|
||||
long parse_key(string &field);
|
||||
double parse_pitch(string &field);
|
||||
long parse_after_key(int key, string &field, int n);
|
||||
long find_int_in(string &field, int n);
|
||||
bool parse_attribute(string &field, Alg_parameter_ptr parm);
|
||||
bool parse_val(Alg_parameter_ptr param, string &s, int i);
|
||||
bool check_type(char type_char, Alg_parameter_ptr param);
|
||||
};
|
||||
|
||||
|
||||
double Alg_reader::parse_pitch(string &field)
|
||||
{
|
||||
if (isdigit(field[1])) {
|
||||
int last = find_real_in(field, 1);
|
||||
string real_string = field.substr(1, last - 1);
|
||||
return atof(real_string.c_str());
|
||||
} else {
|
||||
return (double) parse_key(field);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// it is the responsibility of the caller to delete
|
||||
// the seq
|
||||
Alg_reader::Alg_reader(istream *a_file, Alg_seq_ptr new_seq)
|
||||
{
|
||||
file = a_file; // save the file
|
||||
line_parser_flag = false;
|
||||
line_no = 0;
|
||||
tsnum = 4; // default time signature
|
||||
tsden = 4;
|
||||
seq = new_seq;
|
||||
}
|
||||
|
||||
|
||||
Alg_error alg_read(istream &file, Alg_seq_ptr new_seq)
|
||||
// read a sequence from allegro file
|
||||
{
|
||||
assert(new_seq);
|
||||
Alg_reader alg_reader(&file, new_seq);
|
||||
bool err = alg_reader.parse();
|
||||
return (err ? alg_error_syntax : alg_no_error);
|
||||
}
|
||||
|
||||
|
||||
void Alg_reader::readline()
|
||||
{
|
||||
// a word about memory management: this Alg_reader has a
|
||||
// member variable input_line that holds a line of input
|
||||
// it is reused for each line. input_line is parsed by
|
||||
// line_parser, which holds a reference to input_line
|
||||
line_parser_flag = false;
|
||||
if (getline(*file, input_line)) {
|
||||
line_parser.init(&input_line);
|
||||
line_parser_flag = true;
|
||||
error_flag = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Alg_parameters_ptr Alg_reader::process_attributes(
|
||||
Alg_parameters_ptr attributes, double time)
|
||||
{
|
||||
// print "process_attributes:", attributes
|
||||
bool ts_flag = false;
|
||||
if (attributes) {
|
||||
Alg_parameters_ptr a;
|
||||
bool in_seconds = seq->get_units_are_seconds();
|
||||
if (a = Alg_parameters::remove_key(&attributes, "tempor")) {
|
||||
double tempo = a->parm.r;
|
||||
seq->insert_tempo(tempo, seq->get_time_map()->time_to_beat(time));
|
||||
}
|
||||
if (a = Alg_parameters::remove_key(&attributes, "beatr")) {
|
||||
double beat = a->parm.r;
|
||||
seq->insert_beat(time, beat);
|
||||
}
|
||||
if (a = Alg_parameters::remove_key(&attributes, "timesig_numr")) {
|
||||
tsnum = a->parm.r;
|
||||
ts_flag = true;
|
||||
}
|
||||
if (a = Alg_parameters::remove_key(&attributes, "timesig_denr")) {
|
||||
tsden = a->parm.r;
|
||||
ts_flag = true;
|
||||
}
|
||||
if (ts_flag) {
|
||||
seq->set_time_sig(seq->get_time_map()->time_to_beat(time),
|
||||
tsnum, tsden);
|
||||
}
|
||||
if (in_seconds) seq->convert_to_seconds();
|
||||
}
|
||||
return attributes; // in case it was modified
|
||||
}
|
||||
|
||||
|
||||
bool Alg_reader::parse()
|
||||
{
|
||||
int voice = 0;
|
||||
int key = 60;
|
||||
double loud = 100.0;
|
||||
double pitch = 60.0;
|
||||
double dur = 1.0;
|
||||
double time = 0.0;
|
||||
int track_num = 0;
|
||||
seq->convert_to_seconds();
|
||||
//seq->set_real_dur(0.0); // just in case it's not initialized already
|
||||
readline();
|
||||
bool valid = false; // ignore blank lines
|
||||
while (line_parser_flag) {
|
||||
bool time_flag = false;
|
||||
bool next_flag = false;
|
||||
double next;
|
||||
bool voice_flag = false;
|
||||
bool loud_flag = false;
|
||||
bool dur_flag = false;
|
||||
bool new_pitch_flag = false; // "P" syntax or "A"-"G" syntax
|
||||
double new_pitch = 0.0;
|
||||
bool new_key_flag = false; // "K" syntax
|
||||
int new_key = 0;
|
||||
Alg_parameters_ptr attributes = NULL;
|
||||
if (line_parser.peek() == '#') {
|
||||
// look for #track
|
||||
line_parser.get_nonspace_quoted(field);
|
||||
if (streql(field.c_str(), "#track")) {
|
||||
line_parser.get_nonspace_quoted(field); // number
|
||||
field.insert(0, " "); // need char at beginning because
|
||||
// parse_int ignores the first character of the argument
|
||||
track_num = parse_int(field);
|
||||
seq->add_track(track_num);
|
||||
}
|
||||
// maybe we have a sequence or track name
|
||||
line_parser.get_remainder(field);
|
||||
// if there is a non-space character after #track n then
|
||||
// use it as sequence or track name. Note that because we
|
||||
// skip over spaces, a sequence or track name cannot begin
|
||||
// with leading blanks. Another decision is that the name
|
||||
// must be at time zero
|
||||
if (field.length() > 0) {
|
||||
// insert the field as sequence name or track name
|
||||
Alg_update_ptr update = new Alg_update;
|
||||
update->chan = -1;
|
||||
update->time = 0;
|
||||
update->set_identifier(-1);
|
||||
// sequence name is whatever is on track 0
|
||||
// other tracks have track names
|
||||
const char *attr =
|
||||
(track_num == 0 ? "seqnames" : "tracknames");
|
||||
update->parameter.set_attr(symbol_table.insert_string(attr));
|
||||
update->parameter.s = heapify(field.c_str());
|
||||
seq->add_event(update, track_num);
|
||||
}
|
||||
} else {
|
||||
// we must have a track to insert into
|
||||
if (seq->tracks() == 0) seq->add_track(0);
|
||||
line_parser.get_nonspace_quoted(field);
|
||||
char pk = line_parser.peek();
|
||||
// attributes are parsed as two adjacent nonspace_quoted tokens
|
||||
// so we have to conditionally call get_nonspace_quoted() again
|
||||
if (pk && !isspace(pk)) {
|
||||
string field2;
|
||||
line_parser.get_nonspace_quoted(field2);
|
||||
field.append(field2);
|
||||
}
|
||||
while (field[0]) {
|
||||
char first = toupper(field[0]);
|
||||
if (strchr("ABCDEFGKLPUSIQHW-", first)) {
|
||||
valid = true; // it's a note or event
|
||||
}
|
||||
if (first == 'V') {
|
||||
if (voice_flag) {
|
||||
parse_error(field, 0, "Voice specified twice");
|
||||
} else {
|
||||
voice = parse_chan(field);
|
||||
}
|
||||
voice_flag = true;
|
||||
} else if (first == 'T') {
|
||||
if (time_flag) {
|
||||
parse_error(field, 0, "Time specified twice");
|
||||
} else {
|
||||
time = parse_dur(field, 0.0);
|
||||
}
|
||||
time_flag = true;
|
||||
} else if (first == 'N') {
|
||||
if (next_flag) {
|
||||
parse_error(field, 0, "Next specified twice");
|
||||
} else {
|
||||
next = parse_dur(field, time);
|
||||
}
|
||||
next_flag = true;
|
||||
} else if (first == 'K') {
|
||||
if (new_key_flag) {
|
||||
parse_error(field, 0, "Key specified twice");
|
||||
} else {
|
||||
new_key = parse_key(field);
|
||||
new_key_flag = true;
|
||||
}
|
||||
} else if (first == 'L') {
|
||||
if (loud_flag) {
|
||||
parse_error(field, 0, "Loudness specified twice");
|
||||
} else {
|
||||
loud = parse_loud(field);
|
||||
}
|
||||
loud_flag = true;
|
||||
} else if (first == 'P') {
|
||||
if (new_pitch_flag) {
|
||||
parse_error(field, 0, "Pitch specified twice");
|
||||
} else {
|
||||
new_pitch = parse_pitch(field);
|
||||
new_pitch_flag = true;
|
||||
}
|
||||
} else if (first == 'U') {
|
||||
if (dur_flag) {
|
||||
parse_error(field, 0, "Dur specified twice");
|
||||
} else {
|
||||
dur = parse_dur(field, time);
|
||||
dur_flag = true;
|
||||
}
|
||||
} else if (strchr("SIQHW", first)) {
|
||||
if (dur_flag) {
|
||||
parse_error(field, 0, "Dur specified twice");
|
||||
} else {
|
||||
// prepend 'U' to field, copy EOS too
|
||||
field.insert(0, 1, 'U');
|
||||
dur = parse_dur(field, time);
|
||||
dur_flag = true;
|
||||
}
|
||||
} else if (strchr("ABCDEFG", first)) {
|
||||
if (new_pitch_flag) {
|
||||
parse_error(field, 0, "Pitch specified twice");
|
||||
} else {
|
||||
// prepend 'P' to field
|
||||
field.insert(0, 1, 'P');
|
||||
new_pitch = parse_pitch(field);
|
||||
new_pitch_flag = true;
|
||||
}
|
||||
} else if (first == '-') {
|
||||
Alg_parameter parm;
|
||||
if (parse_attribute(field, &parm)) { // enter attribute-value pair
|
||||
attributes = new Alg_parameters(attributes);
|
||||
attributes->parm = parm;
|
||||
parm.s = NULL; // protect string from deletion by destructor
|
||||
}
|
||||
} else {
|
||||
parse_error(field, 0, "Unknown field");
|
||||
}
|
||||
|
||||
if (error_flag) {
|
||||
field[0] = 0; // exit the loop
|
||||
} else {
|
||||
line_parser.get_nonspace_quoted(field);
|
||||
pk = line_parser.peek();
|
||||
// attributes are parsed as two adjacent nonspace_quoted
|
||||
// tokens so we have to conditionally call
|
||||
// get_nonspace_quoted() again
|
||||
if (pk && !isspace(pk)) {
|
||||
string field2;
|
||||
line_parser.get_nonspace_quoted(field2);
|
||||
field.append(field2);
|
||||
}
|
||||
}
|
||||
}
|
||||
// a case analysis:
|
||||
// Key < 128 implies pitch unless pitch is explicitly given
|
||||
// Pitch implies Key unless key is explicitly given,
|
||||
// Pitch is rounded to nearest integer to determine the Key
|
||||
// if necessary, so MIDI files will lose the pitch fraction
|
||||
// A-G is a Pitch specification (therefore it implies Key)
|
||||
// K60 P60 -- both are specified, use 'em
|
||||
// K60 P60 C4 -- overconstrained, an error
|
||||
// K60 C4 -- OK, but K60 is already implied by C4
|
||||
// K60 -- OK, pitch is 60
|
||||
// C4 P60 -- over constrained
|
||||
// P60 -- OK, key is 60
|
||||
// P60.1 -- OK, key is 60
|
||||
// C4 -- OK, key is 60, pitch is 60
|
||||
// <nothing> -- OK, key and pitch from before
|
||||
// K200 P60 -- ok, pitch is 60
|
||||
// K200 with neither P60 nor C4 uses
|
||||
// pitch from before
|
||||
|
||||
// figure out what the key/instance is:
|
||||
if (new_key_flag) { // it was directly specified
|
||||
key = new_key;
|
||||
} else if (new_pitch_flag) {
|
||||
// pitch was specified, but key was not; get key from pitch
|
||||
key = (int) (new_pitch + 0.5); // round to integer key number
|
||||
}
|
||||
if (new_pitch_flag) {
|
||||
pitch = new_pitch;
|
||||
} else if (key < 128 && new_key_flag) {
|
||||
// no explicit pitch, but key < 128, so it implies pitch
|
||||
pitch = key;
|
||||
new_pitch_flag = true;
|
||||
}
|
||||
// now we've acquired new parameters
|
||||
// if (it is a note, then enter the note
|
||||
if (valid) {
|
||||
// change tempo or beat
|
||||
attributes = process_attributes(attributes, time);
|
||||
// if there's a duration or pitch, make a note:
|
||||
if (new_pitch_flag || dur_flag) {
|
||||
Alg_note_ptr note_ptr = new Alg_note;
|
||||
note_ptr->chan = voice;
|
||||
note_ptr->time = time;
|
||||
note_ptr->dur = dur;
|
||||
note_ptr->set_identifier(key);
|
||||
note_ptr->pitch = pitch;
|
||||
note_ptr->loud = loud;
|
||||
note_ptr->parameters = attributes;
|
||||
seq->add_event(note_ptr, track_num); // sort later
|
||||
if (seq->get_real_dur() < (time + dur)) seq->set_real_dur(time + dur);
|
||||
} else {
|
||||
int update_key = -1;
|
||||
// key must appear explicitly; otherwise
|
||||
// update applies to channel
|
||||
if (new_key_flag) {
|
||||
update_key = key;
|
||||
}
|
||||
if (loud_flag) {
|
||||
Alg_update_ptr new_upd = new Alg_update;
|
||||
new_upd->chan = voice;
|
||||
new_upd->time = time;
|
||||
new_upd->set_identifier(update_key);
|
||||
new_upd->parameter.set_attr(symbol_table.insert_string("loudr"));
|
||||
new_upd->parameter.r = pitch;
|
||||
seq->add_event(new_upd, track_num);
|
||||
if (seq->get_real_dur() < time) seq->set_real_dur(time);
|
||||
}
|
||||
if (attributes) {
|
||||
while (attributes) {
|
||||
Alg_update_ptr new_upd = new Alg_update;
|
||||
new_upd->chan = voice;
|
||||
new_upd->time = time;
|
||||
new_upd->set_identifier(update_key);
|
||||
new_upd->parameter = attributes->parm;
|
||||
seq->add_event(new_upd, track_num);
|
||||
Alg_parameters_ptr p = attributes;
|
||||
attributes = attributes->next;
|
||||
p->parm.s = NULL; // so we don't delete the string
|
||||
delete p;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (next_flag) {
|
||||
time = time + next;
|
||||
} else if (dur_flag || new_pitch_flag) { // a note: incr by dur
|
||||
time = time + dur;
|
||||
}
|
||||
}
|
||||
}
|
||||
readline();
|
||||
}
|
||||
if (!error_flag) { // why not convert even if there was an error? -RBD
|
||||
seq->convert_to_seconds(); // make sure format is correct
|
||||
}
|
||||
// real_dur is valid, translate to beat_dur
|
||||
seq->set_beat_dur((seq->get_time_map())->time_to_beat(seq->get_real_dur()));
|
||||
return error_flag;
|
||||
}
|
||||
|
||||
|
||||
long Alg_reader::parse_chan(string &field)
|
||||
{
|
||||
const char *int_string = field.c_str() + 1;
|
||||
char *msg = "Integer or - expected";
|
||||
const char *p = int_string;
|
||||
char c;
|
||||
// check that all chars in int_string are digits or '-':
|
||||
while (c = *p++) {
|
||||
if (!isdigit(c) && c != '-') {
|
||||
parse_error(field, p - field.c_str() - 1, msg);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
p--; // p now points to end-of-string character
|
||||
if (p - int_string == 0) {
|
||||
// bad: string length is zero
|
||||
parse_error(field, 1, msg);
|
||||
return 0;
|
||||
}
|
||||
if (p - int_string == 1 && int_string[0] == '-') {
|
||||
// special case: entire string is "-", interpret as -1
|
||||
return -1;
|
||||
}
|
||||
return atoi(int_string);
|
||||
}
|
||||
|
||||
|
||||
long Alg_reader::parse_int(string &field)
|
||||
{
|
||||
const char *int_string = field.c_str() + 1;
|
||||
char *msg = "Integer expected";
|
||||
const char *p = int_string;
|
||||
char c;
|
||||
// check that all chars in int_string are digits:
|
||||
while (c = *p++) {
|
||||
if (!isdigit(c)) {
|
||||
parse_error(field, p - field.c_str() - 1, msg);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
p--; // p now points to end-of-string character
|
||||
if (p - int_string == 0) {
|
||||
// bad: string length is zero
|
||||
parse_error(field, 1, msg);
|
||||
return 0;
|
||||
}
|
||||
return atoi(int_string);
|
||||
}
|
||||
|
||||
|
||||
int Alg_reader::find_real_in(string &field, int n)
|
||||
{
|
||||
// scans from offset n to the end of a real constant
|
||||
bool decimal = false;
|
||||
int len = field.length();
|
||||
for (int i = n; i < len; i++) {
|
||||
char c = field[i];
|
||||
if (!isdigit(c)) {
|
||||
if (c == '.' && !decimal) {
|
||||
decimal = true;
|
||||
} else {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return field.length();
|
||||
}
|
||||
|
||||
|
||||
double Alg_reader::parse_real(string &field)
|
||||
{
|
||||
char *msg = "Real expected";
|
||||
int last = find_real_in(field, 1);
|
||||
string real_string = field.substr(1, last - 1);
|
||||
if (last <= 1 || last < (int) field.length()) {
|
||||
parse_error(field, 1, msg);
|
||||
return 0;
|
||||
}
|
||||
return atof(real_string.c_str());
|
||||
}
|
||||
|
||||
|
||||
void Alg_reader::parse_error(string &field, long offset, char *message)
|
||||
{
|
||||
int position = line_parser.pos - field.length() + offset;
|
||||
error_flag = true;
|
||||
puts(line_parser.str->c_str());
|
||||
for (int i = 0; i < position; i++) {
|
||||
putc(' ', stdout);
|
||||
}
|
||||
putc('^', stdout);
|
||||
printf(" %s\n", message);
|
||||
}
|
||||
|
||||
|
||||
double duration_lookup[] = { 0.25, 0.5, 1.0, 2.0, 4.0 };
|
||||
|
||||
|
||||
double Alg_reader::parse_dur(string &field, double base)
|
||||
{
|
||||
char *msg = "Duration expected";
|
||||
char *durs = "SIQHW";
|
||||
char *p;
|
||||
int last;
|
||||
double dur;
|
||||
if (field.length() < 2) {
|
||||
// fall through to error message
|
||||
return -1;
|
||||
} else if (isdigit(field[1])) {
|
||||
last = find_real_in(field, 1);
|
||||
string real_string = field.substr(1, last - 1);
|
||||
dur = atof(real_string.c_str());
|
||||
// convert dur from seconds to beats
|
||||
dur = seq->get_time_map()->time_to_beat(base + dur) -
|
||||
seq->get_time_map()->time_to_beat(base);
|
||||
} else if (p = strchr(durs, toupper(field[1]))) {
|
||||
dur = duration_lookup[p - durs];
|
||||
last = 2;
|
||||
} else {
|
||||
parse_error(field, 1, msg);
|
||||
return 0;
|
||||
}
|
||||
dur = parse_after_dur(dur, field, last, base);
|
||||
dur = seq->get_time_map()->beat_to_time(
|
||||
seq->get_time_map()->time_to_beat(base) + dur) - base;
|
||||
return dur;
|
||||
}
|
||||
|
||||
|
||||
double Alg_reader::parse_after_dur(double dur, string &field,
|
||||
int n, double base)
|
||||
{
|
||||
if ((int) field.length() == n) {
|
||||
return dur;
|
||||
}
|
||||
if (toupper(field[n]) == 'T') {
|
||||
return parse_after_dur(dur * 2/3, field, n + 1, base);
|
||||
}
|
||||
if (field[n] == '.') {
|
||||
return parse_after_dur(dur * 1.5, field, n + 1, base);
|
||||
}
|
||||
if (isdigit(field[n])) {
|
||||
int last = find_real_in(field, n);
|
||||
string a_string = field.substr(n, last - n);
|
||||
double f = atof(a_string.c_str());
|
||||
return parse_after_dur(dur * f, field, last, base);
|
||||
}
|
||||
if (field[n] == '+') {
|
||||
string a_string = field.substr(n + 1);
|
||||
return dur + parse_dur(
|
||||
a_string, seq->get_time_map()->beat_to_time(
|
||||
seq->get_time_map()->time_to_beat(base) + dur));
|
||||
}
|
||||
parse_error(field, n, "Unexpected character in duration");
|
||||
return dur;
|
||||
}
|
||||
|
||||
struct loud_lookup_struct {
|
||||
char *str;
|
||||
int val;
|
||||
} loud_lookup[] = { {"FFF", 127}, {"FF", 120}, {"F", 110}, {"MF", 100},
|
||||
{"MP", 90}, {"P", 80}, {"PP", 70}, {"PPP", 60},
|
||||
{NULL, 0} };
|
||||
|
||||
|
||||
double Alg_reader::parse_loud(string &field)
|
||||
{
|
||||
char *msg = "Loudness expected";
|
||||
if (isdigit(field[1])) {
|
||||
return parse_int(field);
|
||||
} else {
|
||||
string dyn = field.substr(1);
|
||||
transform(dyn.begin(), dyn.end(), dyn.begin(), ::toupper);
|
||||
for (int i = 0; loud_lookup[i].str; i++) {
|
||||
if (streql(loud_lookup[i].str, dyn.c_str())) {
|
||||
return (double) loud_lookup[i].val;
|
||||
}
|
||||
}
|
||||
}
|
||||
parse_error(field, 1, msg);
|
||||
return 100.0;
|
||||
}
|
||||
|
||||
|
||||
int key_lookup[] = {21, 23, 12, 14, 16, 17, 19};
|
||||
|
||||
|
||||
// the field can be K<number> or K[A-G]<number> or P[A-G]<number>
|
||||
// (this can be called from parse_pitch() to handle [A-G])
|
||||
// Notice that the routine ignores the first character: K or P
|
||||
//
|
||||
long Alg_reader::parse_key(string &field)
|
||||
{
|
||||
char *msg = "Pitch expected";
|
||||
char *pitches = "ABCDEFG";
|
||||
char *p;
|
||||
if (isdigit(field[1])) {
|
||||
// This routine would not have been called if field = "P<number>"
|
||||
// so it must be "K<number>" so <number> must be an integer.
|
||||
return parse_int(field);
|
||||
} else if (p = strchr(pitches, toupper(field[1]))) {
|
||||
long key = key_lookup[p - pitches];
|
||||
key = parse_after_key(key, field, 2);
|
||||
return key;
|
||||
}
|
||||
parse_error(field, 1, msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
long Alg_reader::parse_after_key(int key, string &field, int n)
|
||||
{
|
||||
if ((int) field.length() == n) {
|
||||
return key;
|
||||
}
|
||||
char c = toupper(field[n]);
|
||||
if (c == 'S') {
|
||||
return parse_after_key(key + 1, field, n + 1);
|
||||
}
|
||||
if (c == 'F') {
|
||||
return parse_after_key(key - 1, field, n + 1);
|
||||
}
|
||||
if (isdigit(field[n])) {
|
||||
int last = find_int_in(field, n);
|
||||
string octave = field.substr(n, last - n);
|
||||
int oct = atoi(octave.c_str());
|
||||
return parse_after_key(key + oct * 12, field, last);
|
||||
}
|
||||
parse_error(field, n, "Unexpected character in pitch");
|
||||
return key;
|
||||
}
|
||||
|
||||
|
||||
long Alg_reader::find_int_in(string &field, int n)
|
||||
{
|
||||
while ((int) field.length() > n && isdigit(field[n])) {
|
||||
n = n + 1;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
bool Alg_reader::parse_attribute(string &field, Alg_parameter_ptr param)
|
||||
{
|
||||
int i = 1;
|
||||
while (i < (int) field.length()) {
|
||||
if (field[i] == ':') {
|
||||
string attr = field.substr(1, i - 1);
|
||||
char type_char = field[i - 1];
|
||||
if (strchr("iarsl", type_char)) {
|
||||
param->set_attr(symbol_table.insert_string(attr.c_str()));
|
||||
parse_val(param, field, i + 1);
|
||||
} else {
|
||||
parse_error(field, 0, "attribute needs to end with typecode: i,a,r,s, or l");
|
||||
}
|
||||
return !error_flag;
|
||||
}
|
||||
i = i + 1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool Alg_reader::parse_val(Alg_parameter_ptr param, string &s, int i)
|
||||
{
|
||||
int len = (int) s.length();
|
||||
if (i >= len) {
|
||||
return false;
|
||||
}
|
||||
if (s[i] == '"') {
|
||||
if (!check_type('s', param)) {
|
||||
return false;
|
||||
}
|
||||
// note: (len - i) includes 2 quote characters but no EOS character
|
||||
// so total memory to allocate is (len - i) - 1
|
||||
char *r = new char[(len - i) - 1];
|
||||
strncpy(r, s.c_str() + i + 1, (len - i) - 2);
|
||||
r[(len - i) - 2] = 0; // terminate the string
|
||||
param->s = r;
|
||||
} else if (s[i] == '\'') {
|
||||
if (!check_type('a', param)) {
|
||||
return false;
|
||||
}
|
||||
string r = s.substr(i + 1, len - i - 2);
|
||||
param->a = symbol_table.insert_string(r.c_str());
|
||||
} else if (param->attr_type() == 'l') {
|
||||
if (streql(s.c_str() + i, "true") ||
|
||||
streql(s.c_str() + i, "t")) {
|
||||
param->l = true;
|
||||
} else if (streql(s.c_str() + i, "false") ||
|
||||
streql(s.c_str() + i, "nil")) {
|
||||
param->l = false;
|
||||
} else return false;
|
||||
} else if (isdigit(s[i]) || s[i] == '-' || s[i] == '.') {
|
||||
int pos = i;
|
||||
bool period = false;
|
||||
int sign = 1;
|
||||
if (s[pos] == '-') {
|
||||
sign = -1;
|
||||
pos++;
|
||||
}
|
||||
while (pos < len) {
|
||||
if (isdigit(s[pos])) {
|
||||
;
|
||||
} else if (!period && s[pos] == '.') {
|
||||
period = true;
|
||||
} else {
|
||||
parse_error(s, pos, "Unexpected char in number");
|
||||
return false;
|
||||
}
|
||||
pos = pos + 1;
|
||||
}
|
||||
string r = s.substr(i, len - i);
|
||||
if (period) {
|
||||
if (!check_type('r', param)) {
|
||||
return false;
|
||||
}
|
||||
param->r = atof(r.c_str());
|
||||
} else {
|
||||
if (param->attr_type() == 'r') {
|
||||
param->r = atoi(r.c_str());
|
||||
} else if (!check_type('i', param)) {
|
||||
return false;
|
||||
} else {
|
||||
param->i = atoi(r.c_str());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
parse_error(s, i, "invalid value");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool Alg_reader::check_type(char type_char, Alg_parameter_ptr param)
|
||||
{
|
||||
return param->attr_type() == type_char;
|
||||
}
|
||||
|
||||
|
||||
//duration_lookup = {"S": 0.5, "I": 0.5, "Q": 1, "H": 2, "W": 4}
|
||||
//key_lookup = {"C": 12, "D": 14, "E": 16, "F": 17, "G": 19, "A": 21, "B": 23}
|
||||
|
||||
/*
|
||||
def test():
|
||||
reader = Alg_reader(open("data\\test.gro", "r"))
|
||||
reader.parse()
|
||||
score = reader->seq.notes
|
||||
print "score:", score
|
||||
reader = nil
|
||||
*/
|
||||
2
plugins/midi_import/portsmf/allegroserial.cpp
Normal file
2
plugins/midi_import/portsmf/allegroserial.cpp
Normal file
@@ -0,0 +1,2 @@
|
||||
// allegroserial.cpp -- convert track to memory buffer and back to structure
|
||||
|
||||
445
plugins/midi_import/portsmf/allegrosmfrd.cpp
Normal file
445
plugins/midi_import/portsmf/allegrosmfrd.cpp
Normal file
@@ -0,0 +1,445 @@
|
||||
// midifile reader
|
||||
|
||||
#include "stdlib.h"
|
||||
#include "stdio.h"
|
||||
#include "string.h"
|
||||
#include "assert.h"
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include "allegro.h"
|
||||
#include "algsmfrd_internal.h"
|
||||
#include "mfmidi.h"
|
||||
#include "trace.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
typedef class Alg_pending {
|
||||
public:
|
||||
Alg_note_ptr note;
|
||||
class Alg_pending *next;
|
||||
Alg_pending(Alg_note_ptr n, class Alg_pending *list) {
|
||||
note = n; next = list; }
|
||||
} *Alg_pending_ptr;
|
||||
|
||||
|
||||
class Alg_midifile_reader: public Midifile_reader {
|
||||
public:
|
||||
istream *file;
|
||||
Alg_seq_ptr seq;
|
||||
int divisions;
|
||||
Alg_pending_ptr pending;
|
||||
Alg_track_ptr track;
|
||||
int track_number; // the number of the (current) track
|
||||
// chan is actual_channel + channel_offset_per_track * track_num +
|
||||
// channel_offset_per_track * port
|
||||
long channel_offset_per_track; // used to encode track number into channel
|
||||
// default is 0, set this to 0 to merge all tracks to 16 channels
|
||||
long channel_offset_per_port; // used to encode port number into channel
|
||||
// default is 16, set to 0 to ignore port prefix meta events
|
||||
// while reading, this is channel_offset_per_track * track_num
|
||||
int channel_offset;
|
||||
|
||||
Alg_midifile_reader(istream &f, Alg_seq_ptr new_seq) {
|
||||
file = &f;
|
||||
pending = NULL;
|
||||
seq = new_seq;
|
||||
channel_offset_per_track = 0;
|
||||
channel_offset_per_port = 16;
|
||||
track_number = -1; // no tracks started yet, 1st will be #0
|
||||
meta_channel = -1;
|
||||
port = 0;
|
||||
}
|
||||
// delete destroys the seq member as well, so set it to NULL if you
|
||||
// copied the pointer elsewhere
|
||||
~Alg_midifile_reader();
|
||||
// the following is used to load the Alg_seq from the file:
|
||||
bool parse();
|
||||
|
||||
void set_nomerge(bool flag) { Mf_nomerge = flag; }
|
||||
void set_skipinit(bool flag) { Mf_skipinit = flag; }
|
||||
long get_currtime() { return Mf_currtime; }
|
||||
|
||||
protected:
|
||||
int meta_channel; // the channel for meta events, set by MIDI chan prefix
|
||||
int port; // value from the portprefix meta event
|
||||
|
||||
double get_time();
|
||||
void update(int chan, int key, Alg_parameter_ptr param);
|
||||
void *Mf_malloc(size_t size) { return malloc(size); }
|
||||
void Mf_free(void *obj, size_t size) { free(obj); }
|
||||
/* Methods to be called while processing the MIDI file. */
|
||||
void Mf_starttrack();
|
||||
void Mf_endtrack();
|
||||
int Mf_getc();
|
||||
void Mf_chanprefix(int chan);
|
||||
void Mf_portprefix(int port);
|
||||
void Mf_eot();
|
||||
void Mf_error(char *);
|
||||
void Mf_header(int,int,int);
|
||||
void Mf_on(int,int,int);
|
||||
void Mf_off(int,int,int);
|
||||
void Mf_pressure(int,int,int);
|
||||
void Mf_controller(int,int,int);
|
||||
void Mf_pitchbend(int,int,int);
|
||||
void Mf_program(int,int);
|
||||
void Mf_chanpressure(int,int);
|
||||
void binary_msg(int len, char *msg, const char *attr_string);
|
||||
void Mf_sysex(int,char*);
|
||||
void Mf_arbitrary(int,char*);
|
||||
void Mf_metamisc(int,int,char*);
|
||||
void Mf_seqnum(int);
|
||||
void Mf_smpte(int,int,int,int,int);
|
||||
void Mf_timesig(int,int,int,int);
|
||||
void Mf_tempo(int);
|
||||
void Mf_keysig(int,int);
|
||||
void Mf_sqspecific(int,char*);
|
||||
void Mf_text(int,int,char*);
|
||||
};
|
||||
|
||||
|
||||
Alg_midifile_reader::~Alg_midifile_reader()
|
||||
{
|
||||
while (pending) {
|
||||
Alg_pending_ptr to_be_freed = pending;
|
||||
pending = pending->next;
|
||||
delete to_be_freed;
|
||||
}
|
||||
finalize(); // free Mf reader memory
|
||||
}
|
||||
|
||||
|
||||
bool Alg_midifile_reader::parse()
|
||||
{
|
||||
channel_offset = 0;
|
||||
seq->convert_to_beats();
|
||||
midifile();
|
||||
seq->set_real_dur(seq->get_time_map()->beat_to_time(seq->get_beat_dur()));
|
||||
return midifile_error != 0;
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_starttrack()
|
||||
{
|
||||
// printf("starting new track\n");
|
||||
// create a new track that will share the sequence time map
|
||||
// since time is in beats, the seconds parameter is false
|
||||
track_number++;
|
||||
seq->add_track(track_number); // make sure track exists
|
||||
track = seq->track(track_number); // keep pointer to current track
|
||||
meta_channel = -1;
|
||||
port = 0;
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_endtrack()
|
||||
{
|
||||
// note: track is already part of seq, so do not add it here
|
||||
// printf("finished track, length %d number %d\n", track->len, track_num / 100);
|
||||
channel_offset += seq->channel_offset_per_track;
|
||||
track = NULL;
|
||||
double now = get_time();
|
||||
if (seq->get_beat_dur() < now) seq->set_beat_dur(now);
|
||||
meta_channel = -1;
|
||||
port = 0;
|
||||
}
|
||||
|
||||
|
||||
int Alg_midifile_reader::Mf_getc()
|
||||
{
|
||||
return file->get();
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_chanprefix(int chan)
|
||||
{
|
||||
meta_channel = chan;
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_portprefix(int p)
|
||||
{
|
||||
port = p;
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_eot()
|
||||
{
|
||||
meta_channel = -1;
|
||||
port = 0;
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_error(char *msg)
|
||||
{
|
||||
fprintf(stdout, "Midifile reader error: %s\n", msg);
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_header(int format, int ntrks, int division)
|
||||
{
|
||||
if (format > 1) {
|
||||
char msg[80];
|
||||
sprintf(msg, "file format %d not implemented", format);
|
||||
Mf_error(msg);
|
||||
}
|
||||
divisions = division;
|
||||
}
|
||||
|
||||
|
||||
double Alg_midifile_reader::get_time()
|
||||
{
|
||||
double beat = ((double) get_currtime()) / divisions;
|
||||
return beat;
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_on(int chan, int key, int vel)
|
||||
{
|
||||
assert(!seq->get_units_are_seconds());
|
||||
if (vel == 0) {
|
||||
Mf_off(chan, key, vel);
|
||||
return;
|
||||
}
|
||||
Alg_note_ptr note = new Alg_note();
|
||||
pending = new Alg_pending(note, pending);
|
||||
/* trace("on: %d at %g\n", key, get_time()); */
|
||||
note->time = get_time();
|
||||
note->chan = chan + channel_offset + port * channel_offset_per_port;
|
||||
note->dur = 0;
|
||||
note->set_identifier(key);
|
||||
note->pitch = (float) key;
|
||||
note->loud = (float) vel;
|
||||
track->append(note);
|
||||
meta_channel = -1;
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_off(int chan, int key, int vel)
|
||||
{
|
||||
double time = get_time();
|
||||
Alg_pending_ptr *p = &pending;
|
||||
while (*p) {
|
||||
if ((*p)->note->get_identifier() == key &&
|
||||
(*p)->note->chan ==
|
||||
chan + channel_offset + port * channel_offset_per_port) {
|
||||
(*p)->note->dur = time - (*p)->note->time;
|
||||
// trace("updated %d dur %g\n", (*p)->note->key, (*p)->note->dur);
|
||||
Alg_pending_ptr to_be_freed = *p;
|
||||
*p = to_be_freed->next;
|
||||
delete to_be_freed;
|
||||
} else {
|
||||
p = &((*p)->next);
|
||||
}
|
||||
}
|
||||
meta_channel = -1;
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::update(int chan, int key, Alg_parameter_ptr param)
|
||||
{
|
||||
Alg_update_ptr update = new Alg_update;
|
||||
update->time = get_time();
|
||||
update->chan = chan;
|
||||
if (chan != -1) {
|
||||
update->chan = chan + channel_offset + port * channel_offset_per_port;
|
||||
}
|
||||
update->set_identifier(key);
|
||||
update->parameter = *param;
|
||||
// prevent the destructor from destroying the string twice!
|
||||
// the new Update takes the string from param
|
||||
if (param->attr_type() == 's') param->s = NULL;
|
||||
track->append(update);
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_pressure(int chan, int key, int val)
|
||||
{
|
||||
Alg_parameter parameter;
|
||||
parameter.set_attr(symbol_table.insert_string("pressurer"));
|
||||
parameter.r = val / 127.0;
|
||||
update(chan, key, ¶meter);
|
||||
meta_channel = -1;
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_controller(int chan, int control, int val)
|
||||
{
|
||||
Alg_parameter parameter;
|
||||
char name[32];
|
||||
sprintf(name, "control%dr", control);
|
||||
parameter.set_attr(symbol_table.insert_string(name));
|
||||
parameter.r = val / 127.0;
|
||||
update(chan, -1, ¶meter);
|
||||
meta_channel = -1;
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_pitchbend(int chan, int c1, int c2)
|
||||
{
|
||||
Alg_parameter parameter;
|
||||
parameter.set_attr(symbol_table.insert_string("bendr"));
|
||||
parameter.r = ((c2 << 7) + c1) / 8192.0 - 1.0;
|
||||
update(chan, -1, ¶meter);
|
||||
meta_channel = -1;
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_program(int chan, int program)
|
||||
{
|
||||
Alg_parameter parameter;
|
||||
parameter.set_attr(symbol_table.insert_string("programi"));
|
||||
parameter.i = program;
|
||||
update(chan, -1, ¶meter);
|
||||
meta_channel = -1;
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_chanpressure(int chan, int val)
|
||||
{
|
||||
Alg_parameter parameter;
|
||||
parameter.set_attr(symbol_table.insert_string("pressurer"));
|
||||
parameter.r = val / 127.0;
|
||||
update(chan, -1, ¶meter);
|
||||
meta_channel = -1;
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::binary_msg(int len, char *msg,
|
||||
const char *attr_string)
|
||||
{
|
||||
Alg_parameter parameter;
|
||||
char *hexstr = new char[len * 2 + 1];
|
||||
for (int i = 0; i < len; i++) {
|
||||
sprintf(hexstr + 2 * i, "%02x", (0xFF & msg[i]));
|
||||
}
|
||||
parameter.s = hexstr;
|
||||
parameter.set_attr(symbol_table.insert_string(attr_string));
|
||||
update(meta_channel, -1, ¶meter);
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_sysex(int len, char *msg)
|
||||
{
|
||||
// sysex messages become updates with attribute sysexs and a hex string
|
||||
binary_msg(len, msg, "sysexs");
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_arbitrary(int len, char *msg)
|
||||
{
|
||||
Mf_error("arbitrary data ignored");
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_metamisc(int type, int len, char *msg)
|
||||
{
|
||||
char text[128];
|
||||
sprintf(text, "metamsic data, type 0x%x, ignored", type);
|
||||
Mf_error(text);
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_seqnum(int n)
|
||||
{
|
||||
Mf_error("seqnum data ignored");
|
||||
}
|
||||
|
||||
|
||||
static char *fpsstr[4] = {"24", "25", "29.97", "30"};
|
||||
|
||||
void Alg_midifile_reader::Mf_smpte(int hours, int mins, int secs,
|
||||
int frames, int subframes)
|
||||
{
|
||||
// string will look like "24fps:01h:27m:07s:19.00f"
|
||||
// 30fps (drop frame) is notated as "29.97fps"
|
||||
char text[32];
|
||||
int fps = (hours >> 6) & 3;
|
||||
hours &= 0x1F;
|
||||
sprintf(text, "%sfps:%02dh:%02dm:%02ds:%02d.%02df",
|
||||
fpsstr[fps], hours, mins, secs, frames, subframes);
|
||||
Alg_parameter smpteoffset;
|
||||
smpteoffset.s = heapify(text);
|
||||
smpteoffset.set_attr(symbol_table.insert_string("smpteoffsets"));
|
||||
update(meta_channel, -1, &smpteoffset);
|
||||
// Mf_error("SMPTE data ignored");
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_timesig(int i1, int i2, int i3, int i4)
|
||||
{
|
||||
seq->set_time_sig(get_currtime() / divisions, i1, 1 << i2);
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_tempo(int tempo)
|
||||
{
|
||||
double beat = get_currtime();
|
||||
beat = beat / divisions; // convert to quarters
|
||||
// 6000000 us/min / n us/beat => beat / min
|
||||
double bpm = 60000000.0 / tempo;
|
||||
seq->insert_tempo(bpm, beat);
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_keysig(int key, int mode)
|
||||
{
|
||||
Alg_parameter key_parm;
|
||||
key_parm.set_attr(symbol_table.insert_string("keysigi"));
|
||||
// use 0 for C major, 1 for G, -1 for F, etc., that is,
|
||||
// the number of sharps, where flats are negative sharps
|
||||
key_parm.i = key; //<<<---- fix this
|
||||
// use -1 to mean "all channels"
|
||||
update(meta_channel, -1, &key_parm);
|
||||
Alg_parameter mode_parm;
|
||||
mode_parm.set_attr(symbol_table.insert_string("modea"));
|
||||
mode_parm.a = (mode == 0 ? symbol_table.insert_string("major") :
|
||||
symbol_table.insert_string("minor"));
|
||||
update(meta_channel, -1, &mode_parm);
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_sqspecific(int len, char *msg)
|
||||
{
|
||||
// sequencer specific messages become updates with attribute sqspecifics
|
||||
// and a hex string for the value
|
||||
binary_msg(len, msg, "sqspecifics");
|
||||
}
|
||||
|
||||
|
||||
char *heapify2(int len, char *s)
|
||||
{
|
||||
char *h = new char[len + 1];
|
||||
memcpy(h, s, len);
|
||||
h[len] = 0;
|
||||
return h;
|
||||
}
|
||||
|
||||
|
||||
void Alg_midifile_reader::Mf_text(int type, int len, char *msg)
|
||||
{
|
||||
Alg_parameter text;
|
||||
text.s = heapify2(len, msg);
|
||||
const char *attr = "miscs";
|
||||
if (type == 1) attr = "texts";
|
||||
else if (type == 2) attr = "copyrights";
|
||||
else if (type == 3)
|
||||
attr = (track_number == 0 ? "seqnames" : "tracknames");
|
||||
else if (type == 4) attr = "instruments";
|
||||
else if (type == 5) attr = "lyrics";
|
||||
else if (type == 6) attr = "markers";
|
||||
else if (type == 7) attr = "cues";
|
||||
text.set_attr(symbol_table.insert_string(attr));
|
||||
update(meta_channel, -1, &text);
|
||||
}
|
||||
|
||||
|
||||
// parse file into a seq.
|
||||
Alg_error alg_smf_read(istream &file, Alg_seq_ptr new_seq)
|
||||
{
|
||||
assert(new_seq);
|
||||
Alg_midifile_reader ar(file, new_seq);
|
||||
bool err = ar.parse();
|
||||
ar.seq->set_real_dur(ar.seq->get_time_map()->
|
||||
beat_to_time(ar.seq->get_beat_dur()));
|
||||
return (err ? alg_error_syntax : alg_no_error);
|
||||
}
|
||||
649
plugins/midi_import/portsmf/allegrosmfwr.cpp
Normal file
649
plugins/midi_import/portsmf/allegrosmfwr.cpp
Normal file
@@ -0,0 +1,649 @@
|
||||
// allegrosmfwr.cpp -- Allegro Standard Midi File Write
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <errno.h>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
using namespace std;
|
||||
#include "allegro.h"
|
||||
|
||||
// event_queue is a list element that keeps track of pending
|
||||
// things to write to a track, including note-ons, note-offs,
|
||||
// updates, tempo changes, and time signatures
|
||||
//
|
||||
class event_queue{
|
||||
public:
|
||||
char type;//'n' for note, 'o' for off, 's' for time signature,
|
||||
// 'c' for tempo changes
|
||||
double time;
|
||||
long index; //of the event in mSeq->notes
|
||||
class event_queue *next;
|
||||
event_queue(char t, double when, long x, class event_queue *n) {
|
||||
type = t; time = when; index = x; next = n; }
|
||||
};
|
||||
|
||||
|
||||
class Alg_smf_write {
|
||||
public:
|
||||
Alg_smf_write(Alg_seq_ptr seq);
|
||||
~Alg_smf_write();
|
||||
long channels_per_track; // used to encode track number into chan field
|
||||
// chan is actual_channel + channels_per_track * track_number
|
||||
// default is 100, set this to 0 to merge all tracks to 16 channels
|
||||
|
||||
void write(ofstream &file /* , midiFileFormat = 1 */);
|
||||
|
||||
private:
|
||||
long previous_divs; // time in ticks of most recently written event
|
||||
|
||||
void write_track(int i);
|
||||
void write_tempo(int divs, int tempo);
|
||||
void write_tempo_change(int i);
|
||||
void write_time_signature(int i);
|
||||
void write_note(Alg_note_ptr note, bool on);
|
||||
void write_update(Alg_update_ptr update);
|
||||
void write_text(Alg_update_ptr update, char type);
|
||||
void write_binary(int type_byte, char *msg);
|
||||
void write_midi_channel_prefix(Alg_update_ptr update);
|
||||
void write_smpteoffset(Alg_update_ptr update, char *s);
|
||||
void write_data(int data);
|
||||
int to_midi_channel(int channel);
|
||||
int to_track(int channel);
|
||||
|
||||
ostream *out_file;
|
||||
|
||||
Alg_seq_ptr seq;
|
||||
|
||||
int num_tracks; // number of tracks not counting tempo track
|
||||
int division; // divisions per quarter note, default = 120
|
||||
int initial_tempo;
|
||||
|
||||
int timesig_num; // numerator of time signature
|
||||
int timesig_den; // denominator of time signature
|
||||
double timesig_when; // time of time signature
|
||||
|
||||
int keysig; // number of sharps (+) or flats (-), -99 for undefined
|
||||
char keysig_mode; // 'M' or 'm' for major/minor
|
||||
double keysig_when; // time of key signature
|
||||
|
||||
void write_delta(double event_time);
|
||||
void write_varinum(int num);
|
||||
void write_16bit(int num);
|
||||
void write_24bit(int num);
|
||||
void write_32bit(int num);
|
||||
};
|
||||
|
||||
#define ROUND(x) (int) ((x)+0.5)
|
||||
|
||||
Alg_smf_write::Alg_smf_write(Alg_seq_ptr a_seq)
|
||||
{
|
||||
out_file = NULL;
|
||||
|
||||
// at 100bpm (a nominal tempo value), we would like a division
|
||||
// to represent 1ms of time. So
|
||||
// d ticks/beat * 100 beats/min = 60,000 ms/min * 1 tick/ms
|
||||
// solving for d, d = 600
|
||||
division = 600; // divisions per quarter note
|
||||
timesig_num = timesig_den = 0; // initially undefined
|
||||
keysig = -99;
|
||||
keysig_mode = 0;
|
||||
initial_tempo = 500000;
|
||||
|
||||
seq = a_seq;
|
||||
|
||||
previous_divs = 0; // used to compute deltas for midifile
|
||||
}
|
||||
|
||||
|
||||
Alg_smf_write::~Alg_smf_write()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// sorting is quite subtle due to rounding
|
||||
// For example, suppose times from a MIDI file are exact, but in
|
||||
// decimal round to TW0.4167 Q0.3333. Since the time in whole notes
|
||||
// rounded up, this note will start late. Even though the duration
|
||||
// rounded down, the amount is 1/4 as much because units are quarter
|
||||
// notes. Therefore, the total roundup is 0.0001 beats. This is
|
||||
// enough to cause the note to sort later in the queue, perhaps
|
||||
// coming after a new note-on on the same pitch, and resulting in
|
||||
// a turning on-off, on-off into on, on, off, off if data is moved
|
||||
// to Allegro (ascii) format with rounding and then back to SMF.
|
||||
//
|
||||
// The solution here is to consider things that round to the same
|
||||
// tick to be simultaneous. Then, be sure to deal with note-offs
|
||||
// before note-ons. We're going to do that by using event_queue
|
||||
// times that are rounded to the nearest tick time. Except note-offs
|
||||
// are going to go in with times that are 1/4 tick earlier so they
|
||||
// get scheduled first, but still end up on the same tick.
|
||||
//
|
||||
event_queue* push(event_queue *queue, event_queue *event)
|
||||
{
|
||||
// printf("push: %.6g, %c, %d\n", event->time, event->type, event->index);
|
||||
if (queue == NULL) {
|
||||
event->next = NULL;
|
||||
return event;
|
||||
}
|
||||
|
||||
event_queue *marker1 = NULL;
|
||||
event_queue *marker2 = queue;
|
||||
while (marker2 != NULL && marker2->time <= event->time) {
|
||||
marker1 = marker2;
|
||||
marker2 = marker2->next;
|
||||
}
|
||||
event->next = marker2;
|
||||
if (marker1 != NULL) {
|
||||
marker1->next=event;
|
||||
return queue;
|
||||
} else return event;
|
||||
}
|
||||
|
||||
|
||||
void print_queue(event_queue *q)
|
||||
{
|
||||
printf("Printing queue. . .\n");
|
||||
event_queue *q2=q;
|
||||
while (q2) {
|
||||
printf("%c at %f ;", q2->type, q2->time);
|
||||
q2 = q2->next;
|
||||
}
|
||||
printf("\nDone printing.\n");
|
||||
}
|
||||
|
||||
|
||||
void Alg_smf_write::write_note(Alg_note_ptr note, bool on)
|
||||
{
|
||||
double event_time = (on ? note->time : note->time + note->dur);
|
||||
write_delta(event_time);
|
||||
|
||||
//printf("deltaDivisions: %d, beats elapsed: %g, on? %c\n", deltaDivisions, note->time, on);
|
||||
|
||||
char chan = (note->chan & 15);
|
||||
int pitch = int(note->pitch + 0.5);
|
||||
if (pitch < 0) {
|
||||
pitch = pitch % 12;
|
||||
} else if (pitch > 127) {
|
||||
pitch = (pitch % 12) + 120; // put pitch in 10th octave
|
||||
if (pitch > 127) pitch -= 12; // or 9th octave
|
||||
}
|
||||
out_file->put(0x90 + chan);
|
||||
out_file->put(pitch);
|
||||
if (on) {
|
||||
int vel = (int) note->loud;
|
||||
if (vel <= 0) vel = 1;
|
||||
write_data(vel);
|
||||
} else out_file->put(0); // note-off indicated by velocty zero
|
||||
}
|
||||
|
||||
|
||||
void Alg_smf_write::write_midi_channel_prefix(Alg_update_ptr update)
|
||||
{
|
||||
if (update->chan >= 0) { // write MIDI Channel Prefix
|
||||
write_delta(update->time);
|
||||
out_file->put(0xFF); // Meta Event
|
||||
out_file->put(0x20); // Type code for MIDI Channel Prefix
|
||||
out_file->put(1); // length
|
||||
out_file->put(to_midi_channel(update->chan));
|
||||
// one thing odd about the Std MIDI File spec is that once
|
||||
// you turn on MIDI Channel Prefix, there seems to be no
|
||||
// way to cancel it unless a non-Meta event shows up. We
|
||||
// don't do any analysis to avoid assigning channels to
|
||||
// meta events.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Alg_smf_write::write_text(Alg_update_ptr update, char type)
|
||||
{
|
||||
write_midi_channel_prefix(update);
|
||||
write_delta(update->time);
|
||||
out_file->put(0xFF);
|
||||
out_file->put(type);
|
||||
out_file->put((char) strlen(update->parameter.s));
|
||||
*out_file << update->parameter.s;
|
||||
}
|
||||
|
||||
|
||||
void Alg_smf_write::write_smpteoffset(Alg_update_ptr update, char *s)
|
||||
{
|
||||
write_midi_channel_prefix(update);
|
||||
write_delta(update->time);
|
||||
out_file->put(0xFF); // meta event
|
||||
out_file->put(0x54); // smpte offset type code
|
||||
out_file->put(5); // length
|
||||
for (int i = 0; i < 5; i++) *out_file << s[i];
|
||||
}
|
||||
|
||||
|
||||
// write_data - limit data to the range of [0...127] and write it
|
||||
void Alg_smf_write::write_data(int data)
|
||||
{
|
||||
if (data < 0) data = 0;
|
||||
else if (data > 0x7F) data = 0x7F;
|
||||
|
||||
out_file->put(data);
|
||||
}
|
||||
|
||||
|
||||
int Alg_smf_write::to_midi_channel(int channel)
|
||||
{
|
||||
// allegro track number is stored as multiple of 100
|
||||
// also mask off all but 4 channel bits just in case
|
||||
if (channels_per_track > 0) channel %= channels_per_track;
|
||||
return channel & 0xF;
|
||||
}
|
||||
|
||||
|
||||
int Alg_smf_write::to_track(int channel)
|
||||
{
|
||||
if (channel == -1) return 0;
|
||||
return channel / channels_per_track;
|
||||
}
|
||||
|
||||
|
||||
static char hex_to_nibble(char c)
|
||||
{
|
||||
if (isalpha(c)) {
|
||||
return 10 + (toupper(c) - 'A');
|
||||
} else {
|
||||
return c - '0';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static char hex_to_char(char *s)
|
||||
{
|
||||
return (hex_to_nibble(s[0]) << 4) + hex_to_nibble(s[1]);
|
||||
}
|
||||
|
||||
|
||||
void Alg_smf_write::write_binary(int type_byte, char *msg)
|
||||
{
|
||||
int len = strlen(msg) / 2;
|
||||
out_file->put(type_byte);
|
||||
write_varinum(len);
|
||||
for (int i = 0; i < len; i++) {
|
||||
out_file->put(hex_to_char(msg));
|
||||
msg += 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Alg_smf_write::write_update(Alg_update_ptr update)
|
||||
{
|
||||
char *name = update->parameter.attr_name();
|
||||
|
||||
/****Non-Meta Events****/
|
||||
if (!strcmp(name, "pressurer")) {
|
||||
write_delta(update->time);
|
||||
if (update->get_identifier() < 0) { // channel pressure message
|
||||
out_file->put(0xD0 + to_midi_channel(update->chan));
|
||||
write_data((int)(update->parameter.r * 127));
|
||||
} else { // just 1 key -- poly pressure
|
||||
out_file->put(0xA0 + to_midi_channel(update->chan));
|
||||
write_data(update->get_identifier());
|
||||
write_data((int)(update->parameter.r * 127));
|
||||
}
|
||||
} else if (!strcmp(name, "programi")) {
|
||||
write_delta(update->time);
|
||||
out_file->put(0xC0 + to_midi_channel(update->chan));
|
||||
write_data(update->parameter.i);
|
||||
} else if (!strcmp(name, "bendr")) {
|
||||
int temp = ROUND(0x2000 * (update->parameter.r + 1));
|
||||
if (temp > 0x3fff) temp = 0x3fff; // 14 bits maximum
|
||||
if (temp < 0) temp = 0;
|
||||
int c1 = temp & 0x7F; // low 7 bits
|
||||
int c2 = temp >> 7; // high 7 bits
|
||||
write_delta(update->time);
|
||||
out_file->put(0xE0 + to_midi_channel(update->chan));
|
||||
write_data(c1);
|
||||
write_data(c2);
|
||||
} else if (!strncmp(name, "control", 7) &&
|
||||
update->parameter.attr_type() == 'r') {
|
||||
int ctrlnum = atoi(name + 7);
|
||||
int val = ROUND(update->parameter.r * 127);
|
||||
write_delta(update->time);
|
||||
out_file->put(0xB0 + to_midi_channel(update->chan));
|
||||
write_data(ctrlnum);
|
||||
write_data(val);
|
||||
} else if (!strcmp(name, "sysexs") &&
|
||||
update->parameter.attr_type() == 's') {
|
||||
char *s = update->parameter.s;
|
||||
if (s[0] && s[1] && toupper(s[0]) == 'F' && s[1] == '0') {
|
||||
s += 2; // skip the initial "F0" byte in message: it is implied
|
||||
}
|
||||
write_delta(update->time);
|
||||
write_binary(0xF0, s);
|
||||
} else if (!strcmp(name, "sqspecifics") &&
|
||||
update->parameter.attr_type() == 's') {
|
||||
char *s = update->parameter.s;
|
||||
write_delta(update->time);
|
||||
out_file->put(0xFF);
|
||||
write_binary(0x7F, s);
|
||||
|
||||
/****Text Events****/
|
||||
} else if (!strcmp(name, "texts")) {
|
||||
write_text(update, 0x01);
|
||||
} else if (!strcmp(name, "copyrights")) {
|
||||
write_text(update, 0x02);
|
||||
} else if (!strcmp(name, "seqnames") || !strcmp(name, "tracknames")) {
|
||||
write_text(update, 0x03);
|
||||
} else if (!strcmp(name, "instruments")) {
|
||||
write_text(update, 0x04);
|
||||
} else if (!strcmp(name, "lyrics")) {
|
||||
write_text(update, 0x05);
|
||||
} else if (!strcmp(name, "markers")) {
|
||||
write_text(update, 0x06);
|
||||
} else if (!strcmp(name, "cues")) {
|
||||
write_text(update, 0x07);
|
||||
} else if (!strcmp(name, "miscs")) {
|
||||
write_text(update, 0x08);
|
||||
|
||||
/****Other Events****/
|
||||
} else if (!strcmp(name, "smpteoffsets")) {
|
||||
#define decimal(p) (((p)[0] - '0') * 10 + ((p)[1] - '0'))
|
||||
// smpteoffset is specified as "24fps:00h:10m:00s:11.00f"
|
||||
// the following simple parser does not reject all badly
|
||||
// formatted strings, but it should parse good strings ok
|
||||
char *s = update->parameter.s;
|
||||
int len = strlen(s);
|
||||
char smpteoffset[5];
|
||||
if (len < 24) return; // not long enough, must be bad format
|
||||
int fps;
|
||||
if (s[0] == '2') {
|
||||
if (s[1] == '4') fps = 0;
|
||||
else if (s[1] == '5') fps = 1;
|
||||
else if (s[1] == '9') {
|
||||
fps = 2;
|
||||
if (len != 27) return; // not right length
|
||||
s += 3; // cancel effect of longer string
|
||||
}
|
||||
} else fps = 3;
|
||||
s += 6; int hours = decimal(s);
|
||||
s += 4; int mins = decimal(s);
|
||||
s += 4; int secs = decimal(s);
|
||||
s += 4; int frames = decimal(s);
|
||||
s += 3; int subframes = decimal(s);
|
||||
smpteoffset[0] = (fps << 6) + hours;
|
||||
smpteoffset[1] = mins;
|
||||
smpteoffset[2] = secs;
|
||||
smpteoffset[3] = frames;
|
||||
smpteoffset[4] = subframes;
|
||||
write_smpteoffset(update, smpteoffset);
|
||||
|
||||
// key signature is special because it takes two events in the Alg_seq
|
||||
// structure to make one midi file event. When we encounter one or
|
||||
// the other event, we'll just record it in the Alg_smf_write object.
|
||||
// After both events are seen, we write the data. (See below.)
|
||||
} else if (!strcmp(name, "keysigi")) {
|
||||
keysig = update->parameter.i;
|
||||
keysig_when = update->time;
|
||||
} else if (!strcmp(name, "modea")) {
|
||||
if (!strcmp(alg_attr_name(update->parameter.a), "major"))
|
||||
keysig_mode = 'M';
|
||||
else keysig_mode = 'm';
|
||||
keysig_when = update->time;
|
||||
}
|
||||
if (keysig != -99 && keysig_mode) { // write when both are defined
|
||||
write_delta(keysig_when);
|
||||
out_file->put(0xFF);
|
||||
out_file->put(0x59);
|
||||
out_file->put(2);
|
||||
// mask off high bits so that this value appears to be positive
|
||||
// i.e. -1 -> 0xFF (otherwise, write_data will clip -1 to 0)
|
||||
out_file->put(keysig & 0xFF);
|
||||
out_file->put(keysig_mode == 'm');
|
||||
keysig = -99;
|
||||
keysig_mode = false;
|
||||
}
|
||||
//printf("Update: %s, key: %g\n", update->parameter.attr_name(), update->key);
|
||||
}
|
||||
|
||||
|
||||
// see notes on event_queue::push, TICK_TIME converts from beat to
|
||||
// the number of the nearest tick. The second parameter is an offset in
|
||||
// quarter ticks. By scheduling with -1, note-offs should get dispatched
|
||||
// first. Note that TICK_TIME only determines the order of events, so
|
||||
// it is ok to change units from beats to ticks, saving a divide.
|
||||
#define TICK_TIME(t, o) (ROUND((t) * division) + 0.25 * (o))
|
||||
|
||||
void Alg_smf_write::write_track(int i)
|
||||
{
|
||||
int j = 0; // note index
|
||||
Alg_events ¬es = seq->track_list[i];
|
||||
event_queue *pending = NULL;
|
||||
if (notes.length() > 0) {
|
||||
pending = new event_queue('n', TICK_TIME(notes[j]->time, 0), 0, NULL);
|
||||
}
|
||||
if (i == 0) { // track 0 may have tempo and timesig info
|
||||
if (seq->get_time_map()->last_tempo_flag || seq->get_time_map()->beats.len > 0) {
|
||||
pending = push(pending, new event_queue('c', 0.0, 0, NULL));
|
||||
}
|
||||
if (seq->time_sig.length() > 0) {
|
||||
pending = push(pending, new event_queue('s',
|
||||
TICK_TIME(seq->time_sig[0].beat, 0), 0, NULL));
|
||||
}
|
||||
}
|
||||
while (pending) {
|
||||
event_queue *current = pending;
|
||||
pending = pending->next;
|
||||
if (current->type == 'n') {
|
||||
Alg_note_ptr n = (Alg_note_ptr) notes[current->index];
|
||||
if (n->is_note()) {
|
||||
write_note(n, true);
|
||||
pending = push(pending, new event_queue('o',
|
||||
TICK_TIME(n->time + n->dur, -1), current->index, NULL));
|
||||
} else if (n->is_update()) {
|
||||
Alg_update_ptr u = (Alg_update_ptr) n;
|
||||
write_update(u);
|
||||
}
|
||||
int next = current->index + 1;
|
||||
if (next < notes.length()) {
|
||||
current->time = TICK_TIME(notes[next]->time, 0);
|
||||
current->index = next;
|
||||
pending = push(pending, current);
|
||||
}
|
||||
} else if (current->type == 'o') { //note-off
|
||||
Alg_note_ptr n = (Alg_note_ptr) notes[current->index];
|
||||
write_note(n, false);
|
||||
delete current;
|
||||
} else if (current->type == 'c') { // tempo change
|
||||
write_tempo_change(current->index);
|
||||
current->index++; // -R
|
||||
if (current->index < seq->get_time_map()->beats.len) {
|
||||
current->time =
|
||||
TICK_TIME(seq->get_time_map()->
|
||||
beats[current->index].beat, 0);
|
||||
pending = push(pending, current);
|
||||
} else {
|
||||
delete current;
|
||||
}
|
||||
} else if (current->type == 's') { // time sig
|
||||
write_time_signature(current->index);
|
||||
current->index++;
|
||||
if (current->index < seq->time_sig.length()) {
|
||||
current->time =
|
||||
TICK_TIME(seq->time_sig[current->index].beat, 0);
|
||||
pending = push(pending, current);
|
||||
} else {
|
||||
delete current;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Alg_smf_write::write_tempo(int divs, int tempo)
|
||||
{
|
||||
// printf("Inserting tempo %f after %f clocks.\n", tempo, delta);
|
||||
write_varinum(divs - previous_divs);
|
||||
previous_divs = divs;
|
||||
out_file->put(0xFF);
|
||||
out_file->put(0x51);
|
||||
out_file->put(0x03);
|
||||
write_24bit((int)tempo);
|
||||
}
|
||||
|
||||
|
||||
void Alg_smf_write::write_tempo_change(int i)
|
||||
// i is index of tempo map
|
||||
{
|
||||
// extract tempo map
|
||||
Alg_beats &b = seq->get_time_map()->beats;
|
||||
double tempo;
|
||||
long divs;
|
||||
if (i < seq->get_time_map()->beats.len - 1) {
|
||||
tempo = 1000000 * ((b[i+1].time - b[i].time) /
|
||||
(b[i+1].beat - b[i].beat));
|
||||
divs = ROUND(b[i].beat * division);
|
||||
write_tempo(divs, ROUND(tempo));
|
||||
} else if (seq->get_time_map()->last_tempo_flag) { // write the final tempo
|
||||
divs = ROUND(division * b[i].beat);
|
||||
tempo = (1000000.0 / seq->get_time_map()->last_tempo);
|
||||
write_tempo(divs, ROUND(tempo));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Alg_smf_write::write_time_signature(int i)
|
||||
{
|
||||
Alg_time_sigs &ts = seq->time_sig;
|
||||
// write the time signature
|
||||
long divs = ROUND(ts[i].beat * division);
|
||||
write_varinum(divs - previous_divs);
|
||||
out_file->put(0xFF);
|
||||
out_file->put(0x58); // time signature
|
||||
out_file->put(4); // length of message
|
||||
out_file->put(ROUND(ts[i].num));
|
||||
int den = ROUND(ts[i].den);
|
||||
int den_byte = 0;
|
||||
while (den > 1) { // compute the log2 of denominator
|
||||
den_byte++;
|
||||
den >>= 1;
|
||||
}
|
||||
out_file->put(den_byte);
|
||||
out_file->put(24); // clocks per quarter
|
||||
out_file->put(8); // 32nd notes per 24 clocks
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Alg_smf_write::write(ofstream &file)
|
||||
{
|
||||
int track_len_offset;
|
||||
int track_end_offset;
|
||||
int track_len;
|
||||
|
||||
out_file = &file;
|
||||
|
||||
// Header
|
||||
file << "MThd";
|
||||
|
||||
write_32bit(6); // chunk length
|
||||
|
||||
write_16bit(1); // format 1 MIDI file
|
||||
|
||||
write_16bit(seq->tracks()); // number of tracks
|
||||
write_16bit(division); // divisions per quarter note
|
||||
|
||||
|
||||
// write_ all tracks
|
||||
seq->convert_to_beats();
|
||||
int i;
|
||||
for (i = 0; i < seq->tracks(); i++) {
|
||||
previous_divs = 0;
|
||||
*out_file << "MTrk";
|
||||
track_len_offset = out_file->tellp();
|
||||
write_32bit(0); // track len placeholder
|
||||
|
||||
write_track(i);
|
||||
|
||||
// End of track event
|
||||
write_varinum(0); // delta time
|
||||
out_file->put(0xFF);
|
||||
out_file->put(0x2F);
|
||||
out_file->put(0x00);
|
||||
|
||||
// Go back and write in the length of the track
|
||||
track_end_offset = out_file->tellp();
|
||||
track_len = track_end_offset - track_len_offset - 4;
|
||||
out_file->seekp(track_len_offset);
|
||||
write_32bit(track_len);
|
||||
out_file->seekp(track_end_offset);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Alg_smf_write::write_16bit(int num)
|
||||
{
|
||||
out_file->put((num & 0xFF00) >> 8);
|
||||
out_file->put(num & 0xFF);
|
||||
}
|
||||
|
||||
void Alg_smf_write::write_24bit(int num)
|
||||
{
|
||||
out_file->put((num & 0xFF0000) >> 16);
|
||||
out_file->put((num & 0xFF00) >> 8);
|
||||
out_file->put((num & 0xFF));
|
||||
}
|
||||
|
||||
void Alg_smf_write::write_32bit(int num)
|
||||
{
|
||||
out_file->put((num & 0xFF000000) >> 24);
|
||||
out_file->put((num & 0xFF0000) >> 16);
|
||||
out_file->put((num & 0xFF00) >> 8);
|
||||
out_file->put((num & 0xFF));
|
||||
}
|
||||
|
||||
|
||||
void Alg_smf_write::write_delta(double event_time)
|
||||
{
|
||||
// divisions is ideal absolute time in divisions
|
||||
long divisions = ROUND(division * event_time);
|
||||
long delta_divs = divisions - previous_divs;
|
||||
write_varinum(delta_divs);
|
||||
previous_divs = divisions;
|
||||
}
|
||||
|
||||
|
||||
void Alg_smf_write::write_varinum(int value)
|
||||
{
|
||||
if(value<0) value=0;//this line should not have to be here!
|
||||
int buffer;
|
||||
|
||||
buffer = value & 0x7f;
|
||||
while ((value >>= 7) > 0) {
|
||||
buffer <<= 8;
|
||||
buffer |= 0x80;
|
||||
buffer += (value & 0x7f);
|
||||
}
|
||||
|
||||
for(;;) {
|
||||
out_file->put(buffer);
|
||||
if (buffer & 0x80)
|
||||
buffer >>= 8;
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Alg_seq::smf_write(ofstream &file)
|
||||
{
|
||||
Alg_smf_write writer(this);
|
||||
writer.write(file);
|
||||
}
|
||||
|
||||
bool Alg_seq::smf_write(const char *filename)
|
||||
{
|
||||
ofstream outf(filename, ios::binary | ios::out);
|
||||
if (outf.fail()) return false;
|
||||
smf_write(outf);
|
||||
outf.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
181
plugins/midi_import/portsmf/allegrowr.cpp
Normal file
181
plugins/midi_import/portsmf/allegrowr.cpp
Normal file
@@ -0,0 +1,181 @@
|
||||
// allegrowr.cpp -- write sequence to an Allegro file (text)
|
||||
|
||||
#include "assert.h"
|
||||
#include "stdlib.h"
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <errno.h>
|
||||
#include <string>
|
||||
#include "memory.h"
|
||||
using namespace std;
|
||||
#include "strparse.h"
|
||||
#include "allegro.h"
|
||||
|
||||
// Note about precision: %g prints 6 significant digits. For 1ms precision,
|
||||
// the maximum magnitude is 999.999, i.e. 1000s < 17minutes. For anything
|
||||
// over 1000s, time in seconds will be printed with 10ms precision, which
|
||||
// is not good. Therefore, times and durations are printed as %.4d, which
|
||||
// gives 100us precision.
|
||||
// The following define allows you to change this decision:
|
||||
/* #define TIMFMT "%.4d" */
|
||||
#define TIMPREC 4
|
||||
#define TIMFMT fixed << setprecision(TIMPREC)
|
||||
#define GFMT resetiosflags(ios::floatfield) << setprecision(6)
|
||||
|
||||
void parameter_print(ostream &file, Alg_parameter_ptr p)
|
||||
{
|
||||
file << " -" << p->attr_name() << ":";
|
||||
switch (p->attr_type()) {
|
||||
case 'a':
|
||||
file << "'" << alg_attr_name(p->a) << "'";
|
||||
break;
|
||||
case 'i':
|
||||
file << p->i;
|
||||
break;
|
||||
case 'l':
|
||||
file << (p->l ? "true" : "false");
|
||||
break;
|
||||
case 'r':
|
||||
file << p->r;
|
||||
break;
|
||||
case 's': {
|
||||
string str;
|
||||
string_escape(str, p->s, "\"");
|
||||
file << str;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Alg_event_ptr Alg_seq::write_track_name(ostream &file, int n,
|
||||
Alg_events &events)
|
||||
// write #track <n> <trackname-or-sequencename>
|
||||
// if we write the name on the "#track" line, then we do *not* want
|
||||
// to write again as an update: "-seqnames:"Jordu", so if we do
|
||||
// find a name and write it, return a pointer to it so the track
|
||||
// writer knows what update (if any) to skip
|
||||
{
|
||||
Alg_event_ptr e = NULL;
|
||||
file << "#track " << n;
|
||||
const char *attr = symbol_table.insert_string(
|
||||
n == 0 ? "seqnames" : "tracknames");
|
||||
// search for name in events with timestamp of 0
|
||||
for (int i = 0; i < events.length(); i++) {
|
||||
e = events[i];
|
||||
if (e->time > 0) break;
|
||||
if (e->is_update()) {
|
||||
Alg_update_ptr u = (Alg_update_ptr) e;
|
||||
if (u->parameter.attr == attr) {
|
||||
file << " " << u->parameter.s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
file << endl;
|
||||
return e;
|
||||
}
|
||||
|
||||
|
||||
void Alg_seq::write(ostream &file, bool in_secs)
|
||||
{
|
||||
int i, j;
|
||||
if (in_secs) convert_to_seconds();
|
||||
else convert_to_beats();
|
||||
Alg_event_ptr update_to_skip = write_track_name(file, 0, track_list[0]);
|
||||
Alg_beats &beats = time_map->beats;
|
||||
for (i = 0; i < beats.len - 1; i++) {
|
||||
Alg_beat_ptr b = &(beats[i]);
|
||||
if (in_secs) {
|
||||
file << "T" << TIMFMT << b->time;
|
||||
} else {
|
||||
file << "TW" << TIMFMT << b->beat / 4;
|
||||
}
|
||||
double tempo = (beats[i + 1].beat - b->beat) /
|
||||
(beats[i + 1].time - beats[i].time);
|
||||
file << " -tempor:" << GFMT << tempo * 60 << "\n";
|
||||
}
|
||||
if (time_map->last_tempo_flag) { // we have final tempo:
|
||||
Alg_beat_ptr b = &(beats[beats.len - 1]);
|
||||
if (in_secs) {
|
||||
file << "T" << TIMFMT << b->time;
|
||||
} else {
|
||||
file << "TW" << TIMFMT << b->beat / 4;
|
||||
}
|
||||
file << " -tempor:" << GFMT << time_map->last_tempo * 60.0 << "\n";
|
||||
}
|
||||
|
||||
// write the time signatures
|
||||
for (i = 0; i < time_sig.length(); i++) {
|
||||
Alg_time_sig &ts = time_sig[i];
|
||||
double time = ts.beat;
|
||||
if (in_secs) {
|
||||
file << "T" << TIMFMT << time << " V- -timesig_numr:" <<
|
||||
GFMT << ts.num << "\n";
|
||||
file << "T" << TIMFMT << time << " V- -timesig_denr:" <<
|
||||
GFMT << ts.den << "\n";
|
||||
} else {
|
||||
double wholes = ts.beat / 4;
|
||||
file << "TW" << TIMFMT << wholes << " V- -timesig_numr:" <<
|
||||
GFMT << ts.num << "\n";
|
||||
file << "TW" << TIMFMT << wholes << " V- -timesig_denr:" <<
|
||||
GFMT << ts.den << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j < track_list.length(); j++) {
|
||||
Alg_events ¬es = track_list[j];
|
||||
if (j != 0) update_to_skip = write_track_name(file, j, notes);
|
||||
// now write the notes at beat positions
|
||||
for (i = 0; i < notes.length(); i++) {
|
||||
Alg_event_ptr e = notes[i];
|
||||
// if we already wrote this event as a track or sequence name,
|
||||
// do not write it again
|
||||
if (e == update_to_skip) continue;
|
||||
double start = e->time;
|
||||
if (in_secs) {
|
||||
file << "T" << TIMFMT << start;
|
||||
} else {
|
||||
file << "TW" << TIMFMT << start / 4;
|
||||
}
|
||||
// write the channel as Vn or V-
|
||||
if (e->chan == -1) file << " V-";
|
||||
else file << " V" << e->chan;
|
||||
// write the note or update data
|
||||
if (e->is_note()) {
|
||||
Alg_note_ptr n = (Alg_note_ptr) e;
|
||||
double dur = n->dur;
|
||||
file << " K" << n->get_identifier() <<
|
||||
" P" << GFMT << n->pitch;
|
||||
if (in_secs) {
|
||||
file << " U" << TIMFMT << dur;
|
||||
} else {
|
||||
file << " Q" << TIMFMT << dur;
|
||||
}
|
||||
file << " L" << GFMT << n->loud;
|
||||
Alg_parameters_ptr p = n->parameters;
|
||||
while (p) {
|
||||
parameter_print(file, &(p->parm));
|
||||
p = p->next;
|
||||
}
|
||||
} else { // an update
|
||||
assert(e->is_update());
|
||||
Alg_update_ptr u = (Alg_update_ptr) e;
|
||||
if (u->get_identifier() != -1) {
|
||||
file << " K" << u->get_identifier();
|
||||
}
|
||||
parameter_print(file, &(u->parameter));
|
||||
}
|
||||
file << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Alg_seq::write(const char *filename)
|
||||
{
|
||||
ofstream file(filename);
|
||||
if (file.fail()) return false;
|
||||
write(file, units_are_seconds);
|
||||
file.close();
|
||||
return true;
|
||||
}
|
||||
40
plugins/midi_import/portsmf/license.txt
Normal file
40
plugins/midi_import/portsmf/license.txt
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Portsmf: Portable Standard MIDI File Library
|
||||
*
|
||||
* license.txt -- a copy of the Portsmf copyright notice and license information
|
||||
*
|
||||
* Latest version available at: http://sourceforge.net/projects/portmedia
|
||||
*
|
||||
* Copyright (c) 1999-2000 Ross Bencina and Phil Burk
|
||||
* Copyright (c) 2001-2006 Roger B. Dannenberg
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The text above constitutes the entire Portsmf license; however,
|
||||
* the PortMusic community also makes the following non-binding requests:
|
||||
*
|
||||
* Any person wishing to distribute modifications to the Software is
|
||||
* requested to send the modifications to the original developer so that
|
||||
* they can be incorporated into the canonical version. It is also
|
||||
* requested that these non-binding requests be included along with the
|
||||
* license above.
|
||||
*/
|
||||
485
plugins/midi_import/portsmf/mfmidi.cpp
Normal file
485
plugins/midi_import/portsmf/mfmidi.cpp
Normal file
@@ -0,0 +1,485 @@
|
||||
/*
|
||||
* Read a Standard MIDI File. Externally-assigned function pointers are
|
||||
* called upon recognizing things in the file. See midifile(3).
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
* Change Log
|
||||
* Date | who : Change
|
||||
*-----------+-----------------------------------------------------------------
|
||||
* 2-Mar-92 | GWL : created changelog; MIDIFILE_ERROR to satisfy compiler
|
||||
*****************************************************************************/
|
||||
|
||||
#include "stdio.h"
|
||||
#include "mfmidi.h"
|
||||
#include "string.h"
|
||||
|
||||
#define MIDIFILE_ERROR -1
|
||||
|
||||
/* public stuff */
|
||||
extern int abort_flag;
|
||||
|
||||
|
||||
void Midifile_reader::midifile()
|
||||
{
|
||||
int ntrks;
|
||||
midifile_error = 0;
|
||||
|
||||
ntrks = readheader();
|
||||
if (midifile_error) return;
|
||||
if (ntrks <= 0) {
|
||||
mferror("No tracks!");
|
||||
/* no need to return since midifile_error is set */
|
||||
}
|
||||
while (ntrks-- > 0 && !midifile_error) readtrack();
|
||||
}
|
||||
|
||||
int Midifile_reader::readmt(char *s, int skip)
|
||||
/* read through the "MThd" or "MTrk" header string */
|
||||
/* if skip == 1, we attempt to skip initial garbage. */
|
||||
{
|
||||
int nread = 0;
|
||||
char b[4];
|
||||
char buff[32];
|
||||
int c;
|
||||
char *errmsg = "expecting ";
|
||||
|
||||
retry:
|
||||
while ( nread<4 ) {
|
||||
c = Mf_getc();
|
||||
if ( c == EOF ) {
|
||||
errmsg = "EOF while expecting ";
|
||||
goto err;
|
||||
}
|
||||
b[nread++] = c;
|
||||
}
|
||||
/* See if we found the 4 characters we're looking for */
|
||||
if ( s[0]==b[0] && s[1]==b[1] && s[2]==b[2] && s[3]==b[3] )
|
||||
return(0);
|
||||
if ( skip ) {
|
||||
/* If we are supposed to skip initial garbage, */
|
||||
/* try again with the next character. */
|
||||
b[0]=b[1];
|
||||
b[1]=b[2];
|
||||
b[2]=b[3];
|
||||
nread = 3;
|
||||
goto retry;
|
||||
}
|
||||
err:
|
||||
(void) strcpy(buff,errmsg);
|
||||
(void) strcat(buff,s);
|
||||
mferror(buff);
|
||||
return(0);
|
||||
}
|
||||
|
||||
int Midifile_reader::egetc()
|
||||
/* read a single character and abort on EOF */
|
||||
{
|
||||
int c = Mf_getc();
|
||||
|
||||
if ( c == EOF ) {
|
||||
mferror("premature EOF");
|
||||
return EOF;
|
||||
}
|
||||
Mf_toberead--;
|
||||
return(c);
|
||||
}
|
||||
|
||||
int Midifile_reader::readheader()
|
||||
/* read a header chunk */
|
||||
{
|
||||
int format, ntrks, division;
|
||||
|
||||
if ( readmt("MThd",Mf_skipinit) == EOF )
|
||||
return(0);
|
||||
|
||||
Mf_toberead = read32bit();
|
||||
if (midifile_error) return MIDIFILE_ERROR;
|
||||
format = read16bit();
|
||||
if (midifile_error) return MIDIFILE_ERROR;
|
||||
ntrks = read16bit();
|
||||
if (midifile_error) return MIDIFILE_ERROR;
|
||||
division = read16bit();
|
||||
if (midifile_error) return MIDIFILE_ERROR;
|
||||
|
||||
Mf_header(format,ntrks,division);
|
||||
|
||||
/* flush any extra stuff, in case the length of header is not 6 */
|
||||
while ( Mf_toberead > 0 && !midifile_error)
|
||||
(void) egetc();
|
||||
return(ntrks);
|
||||
}
|
||||
|
||||
void Midifile_reader::readtrack()
|
||||
/* read a track chunk */
|
||||
{
|
||||
/* This array is indexed by the high half of a status byte. It's */
|
||||
/* value is either the number of bytes needed (1 or 2) for a channel */
|
||||
/* message, or 0 (meaning it's not a channel message). */
|
||||
static int chantype[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 through 0x70 */
|
||||
2, 2, 2, 2, 1, 1, 2, 0 /* 0x80 through 0xf0 */
|
||||
};
|
||||
long lookfor, lng;
|
||||
int c, c1, type;
|
||||
int sysexcontinue = 0; /* 1 if last message was an unfinished sysex */
|
||||
int running = 0; /* 1 when running status used */
|
||||
int status = 0; /* (possibly running) status byte */
|
||||
int needed;
|
||||
|
||||
if ( readmt("MTrk",0) == EOF )
|
||||
return;
|
||||
|
||||
Mf_toberead = read32bit();
|
||||
|
||||
if (midifile_error) return;
|
||||
|
||||
Mf_currtime = 0L;
|
||||
|
||||
Mf_starttrack();
|
||||
|
||||
while ( Mf_toberead > 0 ) {
|
||||
|
||||
Mf_currtime += readvarinum(); /* delta time */
|
||||
if (midifile_error) return;
|
||||
|
||||
c = egetc();
|
||||
if (midifile_error) return;
|
||||
|
||||
if ( sysexcontinue && c != 0xf7 ) {
|
||||
mferror("didn't find expected continuation of a sysex");
|
||||
return;
|
||||
}
|
||||
if ( (c & 0x80) == 0 ) { /* running status? */
|
||||
if ( status == 0 ) {
|
||||
mferror("unexpected running status");
|
||||
return;
|
||||
}
|
||||
running = 1;
|
||||
} else {
|
||||
status = c;
|
||||
running = 0;
|
||||
}
|
||||
|
||||
needed = chantype[ (status>>4) & 0xf ];
|
||||
|
||||
if ( needed ) { /* ie. is it a channel message? */
|
||||
|
||||
if ( running )
|
||||
c1 = c;
|
||||
else {
|
||||
c1 = egetc();
|
||||
if (midifile_error) return;
|
||||
}
|
||||
chanmessage( status, c1, (needed>1) ? egetc() : 0 );
|
||||
if (midifile_error) return;
|
||||
continue;;
|
||||
}
|
||||
|
||||
switch ( c ) {
|
||||
|
||||
case 0xff: /* meta event */
|
||||
|
||||
type = egetc();
|
||||
if (midifile_error) return;
|
||||
/* watch out - Don't combine the next 2 statements */
|
||||
lng = readvarinum();
|
||||
if (midifile_error) return;
|
||||
lookfor = Mf_toberead - lng;
|
||||
msginit();
|
||||
|
||||
while ( Mf_toberead > lookfor ) {
|
||||
char c = egetc();
|
||||
if (midifile_error) return;
|
||||
msgadd(c);
|
||||
}
|
||||
metaevent(type);
|
||||
break;
|
||||
|
||||
case 0xf0: /* start of system exclusive */
|
||||
|
||||
/* watch out - Don't combine the next 2 statements */
|
||||
lng = readvarinum();
|
||||
if (midifile_error) return;
|
||||
lookfor = Mf_toberead - lng;
|
||||
msginit();
|
||||
msgadd(0xf0);
|
||||
|
||||
while ( Mf_toberead > lookfor ) {
|
||||
c = egetc();
|
||||
if (midifile_error) return;
|
||||
msgadd(c);
|
||||
}
|
||||
if ( c==0xf7 || Mf_nomerge==0 )
|
||||
sysex();
|
||||
else
|
||||
sysexcontinue = 1; /* merge into next msg */
|
||||
break;
|
||||
|
||||
case 0xf7: /* sysex continuation or arbitrary stuff */
|
||||
|
||||
/* watch out - Don't combine the next 2 statements */
|
||||
lng = readvarinum();
|
||||
if (midifile_error) return;
|
||||
lookfor = Mf_toberead - lng;
|
||||
|
||||
if ( ! sysexcontinue )
|
||||
msginit();
|
||||
|
||||
while ( Mf_toberead > lookfor ) {
|
||||
c = egetc();
|
||||
if (midifile_error) return;
|
||||
msgadd(c);
|
||||
}
|
||||
if ( ! sysexcontinue ) {
|
||||
Mf_arbitrary(msgleng(), msg());
|
||||
}
|
||||
else if ( c == 0xf7 ) {
|
||||
sysex();
|
||||
sysexcontinue = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
||||
badbyte(c);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
Mf_endtrack();
|
||||
return;
|
||||
}
|
||||
|
||||
void Midifile_reader::badbyte(int c)
|
||||
{
|
||||
char buff[32];
|
||||
|
||||
(void) sprintf(buff,"unexpected byte: 0x%02x",c);
|
||||
mferror(buff);
|
||||
}
|
||||
|
||||
void Midifile_reader::metaevent(int type)
|
||||
{
|
||||
int leng = msgleng();
|
||||
char *m = msg();
|
||||
|
||||
switch ( type ) {
|
||||
case 0x00:
|
||||
Mf_seqnum(to16bit(m[0],m[1]));
|
||||
break;
|
||||
case 0x01: /* Text event */
|
||||
case 0x02: /* Copyright notice */
|
||||
case 0x03: /* Sequence/Track name */
|
||||
case 0x04: /* Instrument name */
|
||||
case 0x05: /* Lyric */
|
||||
case 0x06: /* Marker */
|
||||
case 0x07: /* Cue point */
|
||||
case 0x08:
|
||||
case 0x09:
|
||||
case 0x0a:
|
||||
case 0x0b:
|
||||
case 0x0c:
|
||||
case 0x0d:
|
||||
case 0x0e:
|
||||
case 0x0f:
|
||||
/* These are all text events */
|
||||
Mf_text(type,leng,m);
|
||||
break;
|
||||
case 0x20:
|
||||
Mf_chanprefix(m[0]);
|
||||
break;
|
||||
case 0x21:
|
||||
Mf_portprefix(m[0]);
|
||||
break;
|
||||
case 0x2f: /* End of Track */
|
||||
Mf_eot();
|
||||
break;
|
||||
case 0x51: /* Set tempo */
|
||||
Mf_tempo(to32bit(0,m[0],m[1],m[2]));
|
||||
break;
|
||||
case 0x54:
|
||||
Mf_smpte(m[0],m[1],m[2],m[3],m[4]);
|
||||
break;
|
||||
case 0x58:
|
||||
Mf_timesig(m[0],m[1],m[2],m[3]);
|
||||
break;
|
||||
case 0x59:
|
||||
Mf_keysig(m[0],m[1]);
|
||||
break;
|
||||
case 0x7f:
|
||||
Mf_sqspecific(leng,m);
|
||||
break;
|
||||
default:
|
||||
Mf_metamisc(type,leng,m);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Midifile_reader::sysex()
|
||||
{
|
||||
Mf_sysex(msgleng(), msg());
|
||||
}
|
||||
|
||||
|
||||
void Midifile_reader::chanmessage(int status, int c1, int c2)
|
||||
{
|
||||
int chan = status & 0xf;
|
||||
|
||||
switch ( status & 0xf0 ) {
|
||||
case NOTEOFF:
|
||||
Mf_off(chan,c1,c2);
|
||||
break;
|
||||
case NOTEON:
|
||||
Mf_on(chan,c1,c2);
|
||||
break;
|
||||
case PRESSURE:
|
||||
Mf_pressure(chan,c1,c2);
|
||||
break;
|
||||
case CONTROLLER:
|
||||
Mf_controller(chan,c1,c2);
|
||||
break;
|
||||
case PITCHBEND:
|
||||
Mf_pitchbend(chan,c1,c2);
|
||||
break;
|
||||
case PROGRAM:
|
||||
Mf_program(chan,c1);
|
||||
break;
|
||||
case CHANPRESSURE:
|
||||
Mf_chanpressure(chan,c1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* readvarinum - read a varying-length number, and return the */
|
||||
/* number of characters it took. */
|
||||
|
||||
long Midifile_reader::readvarinum()
|
||||
{
|
||||
long value;
|
||||
int c;
|
||||
|
||||
c = egetc();
|
||||
if (midifile_error) return 0;
|
||||
|
||||
value = (long) c;
|
||||
if ( c & 0x80 ) {
|
||||
value &= 0x7f;
|
||||
do {
|
||||
c = egetc();
|
||||
if (midifile_error) return 0;
|
||||
value = (value << 7) + (c & 0x7f);
|
||||
} while (c & 0x80);
|
||||
}
|
||||
return (value);
|
||||
}
|
||||
|
||||
long Midifile_reader::to32bit(int c1, int c2, int c3, int c4)
|
||||
{
|
||||
long value = 0L;
|
||||
|
||||
value = (c1 & 0xff);
|
||||
value = (value<<8) + (c2 & 0xff);
|
||||
value = (value<<8) + (c3 & 0xff);
|
||||
value = (value<<8) + (c4 & 0xff);
|
||||
return (value);
|
||||
}
|
||||
|
||||
int Midifile_reader::to16bit(int c1, int c2)
|
||||
{
|
||||
return ((c1 & 0xff ) << 8) + (c2 & 0xff);
|
||||
}
|
||||
|
||||
long Midifile_reader::read32bit()
|
||||
{
|
||||
int c1, c2, c3, c4;
|
||||
|
||||
c1 = egetc(); if (midifile_error) return 0;
|
||||
c2 = egetc(); if (midifile_error) return 0;
|
||||
c3 = egetc(); if (midifile_error) return 0;
|
||||
c4 = egetc(); if (midifile_error) return 0;
|
||||
return to32bit(c1,c2,c3,c4);
|
||||
}
|
||||
|
||||
int Midifile_reader::read16bit()
|
||||
{
|
||||
int c1, c2;
|
||||
c1 = egetc(); if (midifile_error) return 0;
|
||||
c2 = egetc(); if (midifile_error) return 0;
|
||||
return to16bit(c1,c2);
|
||||
}
|
||||
|
||||
void Midifile_reader::mferror(char *s)
|
||||
{
|
||||
Mf_error(s);
|
||||
midifile_error = 1;
|
||||
}
|
||||
|
||||
/* The code below allows collection of a system exclusive message of */
|
||||
/* arbitrary length. The Msgbuff is expanded as necessary. The only */
|
||||
/* visible data/routines are msginit(), msgadd(), msg(), msgleng(). */
|
||||
|
||||
#define MSGINCREMENT 128
|
||||
|
||||
Midifile_reader::Midifile_reader()
|
||||
{
|
||||
Mf_nomerge = 0;
|
||||
Mf_currtime = 0L;
|
||||
Mf_skipinit = 0;
|
||||
Mf_toberead = 0;
|
||||
|
||||
Msgbuff = 0; /* message buffer */
|
||||
Msgsize = 0; /* Size of currently allocated Msg */
|
||||
Msgindex = 0; /* index of next available location in Msg */
|
||||
}
|
||||
|
||||
void Midifile_reader::finalize()
|
||||
{
|
||||
if (Msgbuff) Mf_free(Msgbuff, Msgsize);
|
||||
Msgbuff = NULL;
|
||||
}
|
||||
|
||||
|
||||
void Midifile_reader::msginit()
|
||||
{
|
||||
Msgindex = 0;
|
||||
}
|
||||
|
||||
char *Midifile_reader::msg()
|
||||
{
|
||||
return(Msgbuff);
|
||||
}
|
||||
|
||||
int Midifile_reader::msgleng()
|
||||
{
|
||||
return(Msgindex);
|
||||
}
|
||||
|
||||
void Midifile_reader::msgadd(int c)
|
||||
{
|
||||
/* If necessary, allocate larger message buffer. */
|
||||
if ( Msgindex >= Msgsize )
|
||||
msgenlarge();
|
||||
Msgbuff[Msgindex++] = c;
|
||||
}
|
||||
|
||||
void Midifile_reader::msgenlarge()
|
||||
{
|
||||
char *newmess;
|
||||
char *oldmess = Msgbuff;
|
||||
int oldleng = Msgsize;
|
||||
|
||||
Msgsize += MSGINCREMENT;
|
||||
newmess = (char *) Mf_malloc((sizeof(char) * Msgsize) );
|
||||
|
||||
/* copy old message into larger new one */
|
||||
if ( oldmess != 0 ) {
|
||||
register char *p = newmess;
|
||||
register char *q = oldmess;
|
||||
register char *endq = &oldmess[oldleng];
|
||||
|
||||
for ( ; q!=endq ; p++,q++ )
|
||||
*p = *q;
|
||||
Mf_free(oldmess, oldleng);
|
||||
}
|
||||
Msgbuff = newmess;
|
||||
}
|
||||
98
plugins/midi_import/portsmf/mfmidi.h
Normal file
98
plugins/midi_import/portsmf/mfmidi.h
Normal file
@@ -0,0 +1,98 @@
|
||||
#define NOTEOFF 0x80
|
||||
#define NOTEON 0x90
|
||||
#define PRESSURE 0xa0
|
||||
#define CONTROLLER 0xb0
|
||||
#define PITCHBEND 0xe0
|
||||
#define PROGRAM 0xc0
|
||||
#define CHANPRESSURE 0xd0
|
||||
|
||||
/* These are the strings used in keynote to identify Standard MIDI File */
|
||||
/* meta text messages. */
|
||||
|
||||
#define METATEXT "Text Event"
|
||||
#define METACOPYRIGHT "Copyright Notice"
|
||||
#define METASEQUENCE "Sequence/Track Name"
|
||||
#define METAINSTRUMENT "Instrument Name"
|
||||
#define METALYRIC "Lyric"
|
||||
#define METAMARKER "Marker"
|
||||
#define METACUE "Cue Point"
|
||||
#define METAUNRECOGNIZED "Unrecognized"
|
||||
|
||||
|
||||
class Midifile_reader {
|
||||
public:
|
||||
void midifile();
|
||||
int Mf_nomerge; /* 1 => continue'ed system exclusives are */
|
||||
/* not collapsed. */
|
||||
long Mf_currtime; /* current time in delta-time units */
|
||||
int Mf_skipinit; /* 1 if initial garbage should be skipped */
|
||||
Midifile_reader();
|
||||
// call finalize() when done or you may leak memory.
|
||||
void finalize(); /* clean up before deletion */
|
||||
// Note: rather than finalize, we should have ~Midifile_reader(),
|
||||
// but at least VC++ complains that there is no Mf_free(), even
|
||||
// though Mf_free is declared as virtual and this is an abstract
|
||||
// class. I don't understand this, so finalize() is a workaround. -RBD
|
||||
|
||||
protected:
|
||||
int midifile_error;
|
||||
|
||||
virtual void *Mf_malloc(size_t size) = 0; /* malloc() */
|
||||
virtual void Mf_free(void *obj, size_t size) = 0; /* free() */
|
||||
/* Methods to be called while processing the MIDI file. */
|
||||
virtual void Mf_starttrack() = 0;
|
||||
virtual void Mf_endtrack() = 0;
|
||||
virtual int Mf_getc() = 0;
|
||||
virtual void Mf_chanprefix(int) = 0;
|
||||
virtual void Mf_portprefix(int) = 0;
|
||||
virtual void Mf_eot() = 0;
|
||||
virtual void Mf_error(char *) = 0;
|
||||
virtual void Mf_header(int,int,int) = 0;
|
||||
virtual void Mf_on(int,int,int) = 0;
|
||||
virtual void Mf_off(int,int,int) = 0;
|
||||
virtual void Mf_pressure(int,int,int) = 0;
|
||||
virtual void Mf_controller(int,int,int) = 0;
|
||||
virtual void Mf_pitchbend(int,int,int) = 0;
|
||||
virtual void Mf_program(int,int) = 0;
|
||||
virtual void Mf_chanpressure(int,int) = 0;
|
||||
virtual void Mf_sysex(int,char*) = 0;
|
||||
virtual void Mf_arbitrary(int,char*) = 0;
|
||||
virtual void Mf_metamisc(int,int,char*) = 0;
|
||||
virtual void Mf_seqnum(int) = 0;
|
||||
virtual void Mf_smpte(int,int,int,int,int) = 0;
|
||||
virtual void Mf_timesig(int,int,int,int) = 0;
|
||||
virtual void Mf_tempo(int) = 0;
|
||||
virtual void Mf_keysig(int,int) = 0;
|
||||
virtual void Mf_sqspecific(int,char*) = 0;
|
||||
virtual void Mf_text(int,int,char*) = 0;
|
||||
|
||||
private:
|
||||
long Mf_toberead;
|
||||
|
||||
long readvarinum();
|
||||
long read32bit();
|
||||
int read16bit();
|
||||
void msgenlarge();
|
||||
char *msg();
|
||||
int readheader();
|
||||
void readtrack();
|
||||
void sysex();
|
||||
void msginit();
|
||||
int egetc();
|
||||
int msgleng();
|
||||
|
||||
int readmt(char*,int);
|
||||
long to32bit(int,int,int,int);
|
||||
int to16bit(int,int);
|
||||
void mferror(char *);
|
||||
void badbyte(int);
|
||||
void metaevent(int);
|
||||
void msgadd(int);
|
||||
void chanmessage(int,int,int);
|
||||
|
||||
char *Msgbuff;
|
||||
long Msgsize;
|
||||
long Msgindex;
|
||||
};
|
||||
|
||||
|
||||
86
plugins/midi_import/portsmf/strparse.cpp
Normal file
86
plugins/midi_import/portsmf/strparse.cpp
Normal file
@@ -0,0 +1,86 @@
|
||||
#include <string>
|
||||
// #include <iostream> -- for debugging (cout)
|
||||
#include "ctype.h"
|
||||
using namespace std;
|
||||
#include "strparse.h"
|
||||
|
||||
void String_parse::skip_space()
|
||||
{
|
||||
while ((*str)[pos] && isspace((*str)[pos])) {
|
||||
pos = pos + 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
char String_parse::peek()
|
||||
{
|
||||
return (*str)[pos];
|
||||
}
|
||||
|
||||
|
||||
void String_parse::get_nonspace_quoted(string &field)
|
||||
{
|
||||
field.clear();
|
||||
skip_space();
|
||||
bool quoted = false;
|
||||
if ((*str)[pos] == '"') {
|
||||
quoted = true;
|
||||
field.append(1, '"');
|
||||
pos = pos + 1;
|
||||
}
|
||||
while ((*str)[pos] && (quoted || !isspace((*str)[pos]))) {
|
||||
if ((*str)[pos] == '"') {
|
||||
if (quoted) {
|
||||
field.append(1, '"');
|
||||
pos = pos + 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if ((*str)[pos] == '\\') {
|
||||
pos = pos + 1;
|
||||
}
|
||||
if ((*str)[pos]) {
|
||||
field.append(1, (*str)[pos]);
|
||||
pos = pos + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
char *escape_chars[] = {"\\n", "\\t", "\\\\", "\\r", "\\\""};
|
||||
|
||||
|
||||
void string_escape(string &result, char *str, char *quote)
|
||||
{
|
||||
int length = (int) strlen(str);
|
||||
if (quote[0]) {
|
||||
result.append(1, quote[0]);
|
||||
}
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (!isalnum((unsigned char) str[i])) {
|
||||
char *chars = "\n\t\\\r\"";
|
||||
char *special = strchr(chars, str[i]);
|
||||
if (special) {
|
||||
result.append(escape_chars[special - chars]);
|
||||
} else {
|
||||
result.append(1, str[i]);
|
||||
}
|
||||
} else {
|
||||
result.append(1, str[i]);
|
||||
}
|
||||
}
|
||||
result.append(1, quote[0]);
|
||||
}
|
||||
|
||||
void String_parse::get_remainder(std::string &field)
|
||||
{
|
||||
field.clear();
|
||||
skip_space();
|
||||
int len = str->length() - pos;
|
||||
if ((*str)[len - 1] == '\n') { // if str ends in newline,
|
||||
len--; // reduce length to ignore newline
|
||||
}
|
||||
field.insert(0, *str, pos, len);
|
||||
}
|
||||
|
||||
|
||||
18
plugins/midi_import/portsmf/strparse.h
Normal file
18
plugins/midi_import/portsmf/strparse.h
Normal file
@@ -0,0 +1,18 @@
|
||||
// strparse.h -- header for String_parse class
|
||||
|
||||
class String_parse {
|
||||
public:
|
||||
int pos;
|
||||
std::string *str;
|
||||
void init(std::string *s) {
|
||||
str = s;
|
||||
pos = 0;
|
||||
}
|
||||
void skip_space();
|
||||
char peek();
|
||||
void get_nonspace_quoted(std::string &field);
|
||||
// get the remaining characters, skipping initial spaces and final return
|
||||
void get_remainder(std::string &field);
|
||||
};
|
||||
|
||||
void string_escape(std::string &result, char *s, char *quote);
|
||||
25
plugins/midi_import/portsmf/trace.cpp
Normal file
25
plugins/midi_import/portsmf/trace.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
// trace.cpp -- debugging print function
|
||||
//
|
||||
// (I think this was created to provide a generic print function
|
||||
// for use in non-command-line Windows applications where printf
|
||||
// does not work. Currently, it is not used, but kept around for
|
||||
// possible debugging needs. -RBD)
|
||||
|
||||
#include "stdarg.h"
|
||||
#include "stdio.h"
|
||||
#include "crtdbg.h"
|
||||
|
||||
|
||||
void trace(char *format, ...)
|
||||
{
|
||||
char msg[256];
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
_vsnprintf(msg, 256, format, args);
|
||||
va_end(args);
|
||||
#ifdef _DEBUG
|
||||
_CrtDbgReport(_CRT_WARN, NULL, NULL, NULL, msg);
|
||||
#else
|
||||
printf(msg);
|
||||
#endif
|
||||
}
|
||||
2
plugins/midi_import/portsmf/trace.h
Normal file
2
plugins/midi_import/portsmf/trace.h
Normal file
@@ -0,0 +1,2 @@
|
||||
void trace(char *format, ...);
|
||||
|
||||
@@ -67,6 +67,7 @@ plugin::descriptor sf2player_plugin_descriptor =
|
||||
|
||||
// Static map of current sfonts
|
||||
QMap<QString, sf2Font*> sf2Instrument::s_fonts;
|
||||
QMutex sf2Instrument::s_fontsMutex;
|
||||
|
||||
|
||||
|
||||
@@ -116,6 +117,8 @@ sf2Instrument::sf2Instrument( instrumentTrack * _instrument_track ) :
|
||||
instrumentPlayHandle * iph = new instrumentPlayHandle( this );
|
||||
engine::getMixer()->addPlayHandle( iph );
|
||||
|
||||
//loadFile( configManager::inst()->defaultSoundfont() );
|
||||
|
||||
updateSampleRate();
|
||||
updateReverbOn();
|
||||
updateReverb();
|
||||
@@ -240,17 +243,34 @@ void sf2Instrument::loadSettings( const QDomElement & _this )
|
||||
|
||||
void sf2Instrument::loadFile( const QString & _file )
|
||||
{
|
||||
openFile( _file );
|
||||
updatePatch();
|
||||
if( !_file.isEmpty() )
|
||||
{
|
||||
openFile( _file );
|
||||
updatePatch();
|
||||
|
||||
// for some reason we've to call that, otherwise preview of a
|
||||
// soundfont for the first time fails
|
||||
updateSampleRate();
|
||||
// for some reason we've to call that, otherwise preview of a
|
||||
// soundfont for the first time fails
|
||||
updateSampleRate();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
automatableModel * sf2Instrument::getChildModel( const QString & _modelName )
|
||||
{
|
||||
if( _modelName == "bank" )
|
||||
{
|
||||
return &m_bankNum;
|
||||
}
|
||||
else if( _modelName == "patch" )
|
||||
{
|
||||
return &m_patchNum;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
QString sf2Instrument::nodeName( void ) const
|
||||
{
|
||||
return( sf2player_plugin_descriptor.name );
|
||||
@@ -267,6 +287,7 @@ void sf2Instrument::freeFont( void )
|
||||
|
||||
if ( m_font != NULL )
|
||||
{
|
||||
s_fontsMutex.lock();
|
||||
--(m_font->refCount);
|
||||
|
||||
// No more references
|
||||
@@ -286,6 +307,7 @@ void sf2Instrument::freeFont( void )
|
||||
|
||||
fluid_synth_remove_sfont( m_synth, m_font->fluidFont );
|
||||
}
|
||||
s_fontsMutex.unlock();
|
||||
|
||||
m_font = NULL;
|
||||
}
|
||||
@@ -298,12 +320,15 @@ void sf2Instrument::openFile( const QString & _sf2File )
|
||||
{
|
||||
emit fileLoading();
|
||||
|
||||
char * sf2Ascii = qstrdup( qPrintable( _sf2File ) );
|
||||
// Used for loading file
|
||||
char * sf2Ascii = qstrdup( qPrintable(
|
||||
sampleBuffer::tryToMakeAbsolute( _sf2File ) ) );
|
||||
|
||||
// free reference to soundfont if one is selected
|
||||
freeFont();
|
||||
|
||||
m_synthMutex.lock();
|
||||
s_fontsMutex.lock();
|
||||
|
||||
// Increment Reference
|
||||
if( s_fonts.contains( _sf2File ) )
|
||||
@@ -335,13 +360,16 @@ void sf2Instrument::openFile( const QString & _sf2File )
|
||||
}
|
||||
}
|
||||
|
||||
s_fontsMutex.unlock();
|
||||
m_synthMutex.unlock();
|
||||
|
||||
if( m_fontId >= 0 )
|
||||
{
|
||||
m_patchNum.setValue( 0 );
|
||||
m_bankNum.setValue( 0 );
|
||||
m_filename = _sf2File;
|
||||
// Don't reset patch/bank, so that it isn't cleared when
|
||||
// someone resolves a missing file
|
||||
//m_patchNum.setValue( 0 );
|
||||
//m_bankNum.setValue( 0 );
|
||||
m_filename = sampleBuffer::tryToMakeRelative( _sf2File );
|
||||
|
||||
emit fileChanged();
|
||||
}
|
||||
@@ -922,7 +950,7 @@ void sf2InstrumentView::showFileDialog( void )
|
||||
ofd.setFileMode( QFileDialog::ExistingFiles );
|
||||
|
||||
QStringList types;
|
||||
types << tr( "SoundFont2-Files (*.sf2)" );
|
||||
types << tr( "SoundFont2 Files (*.sf2)" );
|
||||
ofd.setFilters( types );
|
||||
|
||||
QString dir;
|
||||
|
||||
@@ -47,6 +47,9 @@ class QLabel;
|
||||
class sf2Instrument : public instrument
|
||||
{
|
||||
Q_OBJECT
|
||||
mapPropertyFromModel(int,getBank,setBank,m_bankNum);
|
||||
mapPropertyFromModel(int,getPatch,setPatch,m_patchNum);
|
||||
|
||||
public:
|
||||
sf2Instrument( instrumentTrack * _instrument_track );
|
||||
virtual ~sf2Instrument();
|
||||
@@ -64,6 +67,8 @@ public:
|
||||
|
||||
virtual void loadFile( const QString & _file );
|
||||
|
||||
virtual automatableModel * getChildModel( const QString & _modelName );
|
||||
|
||||
virtual QString nodeName( void ) const;
|
||||
|
||||
virtual f_cnt_t desiredReleaseFrames( void ) const
|
||||
@@ -81,6 +86,8 @@ public:
|
||||
QString getCurrentPatchName();
|
||||
|
||||
|
||||
void setParameter( const QString & _param, const QString & _value );
|
||||
|
||||
public slots:
|
||||
void openFile( const QString & _sf2File );
|
||||
void updatePatch( void );
|
||||
@@ -96,6 +103,7 @@ public slots:
|
||||
|
||||
|
||||
private:
|
||||
static QMutex s_fontsMutex;
|
||||
static QMap<QString, sf2Font*> s_fonts;
|
||||
static int (* s_origFree)( fluid_sfont_t * );
|
||||
|
||||
|
||||
@@ -134,6 +134,16 @@ void configManager::setSTKDir( const QString & _fd )
|
||||
|
||||
|
||||
|
||||
void configManager::setDefaultSoundfont( const QString & _sf )
|
||||
{
|
||||
#ifdef LMMS_HAVE_FLUIDSYNTH
|
||||
m_defaultSoundfont = _sf;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void configManager::addRecentlyOpenedProject( const QString & _file )
|
||||
{
|
||||
m_recentlyOpenedProjects.removeAll( _file );
|
||||
@@ -270,6 +280,9 @@ bool configManager::loadConfigFile( void )
|
||||
#ifdef LMMS_HAVE_STK
|
||||
setSTKDir( value( "paths", "stkdir" ) );
|
||||
#endif
|
||||
#ifdef LMMS_HAVE_FLUIDSYNTH
|
||||
setDefaultSoundfont( value( "paths", "defaultsf2" ) );
|
||||
#endif
|
||||
|
||||
if( m_vstDir == "" )
|
||||
{
|
||||
@@ -297,6 +310,7 @@ bool configManager::loadConfigFile( void )
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
QDir::setSearchPaths( "resources", QStringList() << artworkDir()
|
||||
<< defaultArtworkDir() );
|
||||
|
||||
@@ -335,6 +349,9 @@ void configManager::saveConfigFile( void )
|
||||
#ifdef LMMS_HAVE_STK
|
||||
setValue( "paths", "stkdir", m_stkDir );
|
||||
#endif
|
||||
#ifdef LMMS_HAVE_FLUIDSYNTH
|
||||
setValue( "paths", "defaultsf2", m_defaultSoundfont );
|
||||
#endif
|
||||
|
||||
QDomDocument doc( "lmms-config-file" );
|
||||
|
||||
|
||||
@@ -83,9 +83,9 @@ void plugin::loadFile( const QString & )
|
||||
|
||||
|
||||
|
||||
QString plugin::getParameter( const QString & )
|
||||
automatableModel * plugin::getChildModel( const QString & )
|
||||
{
|
||||
return( "" );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -103,6 +103,9 @@ setupDialog::setupDialog( ConfigTabs _tab_to_open ) :
|
||||
m_artworkDir( configManager::inst()->artworkDir() ),
|
||||
m_flDir( configManager::inst()->flDir() ),
|
||||
m_ladDir( configManager::inst()->ladspaDir() ),
|
||||
#ifdef LMMS_HAVE_FLUIDSYNTH
|
||||
m_defaultSoundfont( configManager::inst()->defaultSoundfont() ),
|
||||
#endif
|
||||
#ifdef LMMS_HAVE_STK
|
||||
m_stkDir( configManager::inst()->stkDir() ),
|
||||
#endif
|
||||
@@ -130,11 +133,14 @@ setupDialog::setupDialog( ConfigTabs _tab_to_open ) :
|
||||
m_tabBar->setFixedWidth( 72 );
|
||||
|
||||
QWidget * ws = new QWidget( settings );
|
||||
int wsHeight = 336;
|
||||
#ifdef LMMS_HAVE_STK
|
||||
ws->setFixedSize( 360, 422 );
|
||||
#else
|
||||
ws->setFixedSize( 360, 366 );
|
||||
wsHeight += 50;
|
||||
#endif
|
||||
#ifdef LMMS_HAVE_FLUIDSYNTH
|
||||
wsHeight += 50;
|
||||
#endif
|
||||
ws->setFixedSize( 360, wsHeight );
|
||||
QWidget * general = new QWidget( ws );
|
||||
general->setFixedSize( 360, 240 );
|
||||
QVBoxLayout * gen_layout = new QVBoxLayout( general );
|
||||
@@ -228,22 +234,25 @@ setupDialog::setupDialog( ConfigTabs _tab_to_open ) :
|
||||
|
||||
|
||||
|
||||
QWidget * directories = new QWidget( ws );
|
||||
QWidget * paths = new QWidget( ws );
|
||||
int pathsHeight = 296;
|
||||
#ifdef LMMS_HAVE_STK
|
||||
directories->setFixedSize( 360, 372 );
|
||||
#else
|
||||
directories->setFixedSize( 360, 326 );
|
||||
pathsHeight += 50;
|
||||
#endif
|
||||
QVBoxLayout * dir_layout = new QVBoxLayout( directories );
|
||||
#ifdef LMMS_HAVE_FLUIDSYNTH
|
||||
pathsHeight += 50;
|
||||
#endif
|
||||
paths->setFixedSize( 360, pathsHeight );
|
||||
QVBoxLayout * dir_layout = new QVBoxLayout( paths );
|
||||
dir_layout->setSpacing( 0 );
|
||||
dir_layout->setMargin( 0 );
|
||||
labelWidget( directories, tr( "Directories" ) );
|
||||
labelWidget( paths, tr( "Paths" ) );
|
||||
|
||||
// working-dir
|
||||
tabWidget * lmms_wd_tw = new tabWidget( tr(
|
||||
"LMMS working directory" ).toUpper(),
|
||||
directories );
|
||||
lmms_wd_tw->setFixedHeight( 56 );
|
||||
paths );
|
||||
lmms_wd_tw->setFixedHeight( 48 );
|
||||
|
||||
m_wdLineEdit = new QLineEdit( m_workingDir, lmms_wd_tw );
|
||||
m_wdLineEdit->setGeometry( 10, 20, 300, 16 );
|
||||
@@ -254,15 +263,15 @@ setupDialog::setupDialog( ConfigTabs _tab_to_open ) :
|
||||
embed::getIconPixmap( "project_open", 16, 16 ),
|
||||
"", lmms_wd_tw );
|
||||
workingdir_select_btn->setFixedSize( 24, 24 );
|
||||
workingdir_select_btn->move( 320, 20 );
|
||||
workingdir_select_btn->move( 320, 16 );
|
||||
connect( workingdir_select_btn, SIGNAL( clicked() ), this,
|
||||
SLOT( openWorkingDir() ) );
|
||||
|
||||
// vst-dir
|
||||
tabWidget * vst_tw = new tabWidget( tr(
|
||||
"VST-plugin directory" ).toUpper(),
|
||||
directories );
|
||||
vst_tw->setFixedHeight( 56 );
|
||||
paths );
|
||||
vst_tw->setFixedHeight( 48 );
|
||||
|
||||
m_vdLineEdit = new QLineEdit( m_vstDir, vst_tw );
|
||||
m_vdLineEdit->setGeometry( 10, 20, 300, 16 );
|
||||
@@ -273,15 +282,15 @@ setupDialog::setupDialog( ConfigTabs _tab_to_open ) :
|
||||
embed::getIconPixmap( "project_open", 16, 16 ),
|
||||
"", vst_tw );
|
||||
vstdir_select_btn->setFixedSize( 24, 24 );
|
||||
vstdir_select_btn->move( 320, 20 );
|
||||
vstdir_select_btn->move( 320, 16 );
|
||||
connect( vstdir_select_btn, SIGNAL( clicked() ), this,
|
||||
SLOT( openVSTDir() ) );
|
||||
|
||||
// artwork-dir
|
||||
tabWidget * artwork_tw = new tabWidget( tr(
|
||||
"Artwork directory" ).toUpper(),
|
||||
directories );
|
||||
artwork_tw->setFixedHeight( 56 );
|
||||
paths );
|
||||
artwork_tw->setFixedHeight( 48 );
|
||||
|
||||
m_adLineEdit = new QLineEdit( m_artworkDir, artwork_tw );
|
||||
m_adLineEdit->setGeometry( 10, 20, 300, 16 );
|
||||
@@ -292,15 +301,18 @@ setupDialog::setupDialog( ConfigTabs _tab_to_open ) :
|
||||
embed::getIconPixmap( "project_open", 16, 16 ),
|
||||
"", artwork_tw );
|
||||
artworkdir_select_btn->setFixedSize( 24, 24 );
|
||||
artworkdir_select_btn->move( 320, 20 );
|
||||
artworkdir_select_btn->move( 320, 16 );
|
||||
connect( artworkdir_select_btn, SIGNAL( clicked() ), this,
|
||||
SLOT( openArtworkDir() ) );
|
||||
|
||||
|
||||
|
||||
|
||||
// FL Studio-dir
|
||||
tabWidget * fl_tw = new tabWidget( tr(
|
||||
"FL Studio installation directory" ).toUpper(),
|
||||
directories );
|
||||
fl_tw->setFixedHeight( 56 );
|
||||
paths );
|
||||
fl_tw->setFixedHeight( 48 );
|
||||
|
||||
m_fdLineEdit = new QLineEdit( m_flDir, fl_tw );
|
||||
m_fdLineEdit->setGeometry( 10, 20, 300, 16 );
|
||||
@@ -311,14 +323,14 @@ setupDialog::setupDialog( ConfigTabs _tab_to_open ) :
|
||||
embed::getIconPixmap( "project_open", 16, 16 ),
|
||||
"", fl_tw );
|
||||
fldir_select_btn->setFixedSize( 24, 24 );
|
||||
fldir_select_btn->move( 320, 20 );
|
||||
fldir_select_btn->move( 320, 16 );
|
||||
connect( fldir_select_btn, SIGNAL( clicked() ), this,
|
||||
SLOT( openFLDir() ) );
|
||||
// LADSPA-dir
|
||||
tabWidget * lad_tw = new tabWidget( tr(
|
||||
"LADSPA plugin directories" ).toUpper(),
|
||||
directories );
|
||||
lad_tw->setFixedHeight( 56 );
|
||||
"LADSPA plugin paths" ).toUpper(),
|
||||
paths );
|
||||
lad_tw->setFixedHeight( 48 );
|
||||
|
||||
m_ladLineEdit = new QLineEdit( m_ladDir, lad_tw );
|
||||
m_ladLineEdit->setGeometry( 10, 20, 300, 16 );
|
||||
@@ -329,7 +341,7 @@ setupDialog::setupDialog( ConfigTabs _tab_to_open ) :
|
||||
embed::getIconPixmap( "project_open", 16, 16 ),
|
||||
"", lad_tw );
|
||||
laddir_select_btn->setFixedSize( 24, 24 );
|
||||
laddir_select_btn->move( 320, 20 );
|
||||
laddir_select_btn->move( 320, 16 );
|
||||
connect( laddir_select_btn, SIGNAL( clicked() ), this,
|
||||
SLOT( openLADSPADir() ) );
|
||||
|
||||
@@ -337,8 +349,8 @@ setupDialog::setupDialog( ConfigTabs _tab_to_open ) :
|
||||
// STK-dir
|
||||
tabWidget * stk_tw = new tabWidget( tr(
|
||||
"STK rawwave directory" ).toUpper(),
|
||||
directories );
|
||||
stk_tw->setFixedHeight( 56 );
|
||||
paths );
|
||||
stk_tw->setFixedHeight( 48 );
|
||||
|
||||
m_stkLineEdit = new QLineEdit( m_stkDir, stk_tw );
|
||||
m_stkLineEdit->setGeometry( 10, 20, 300, 16 );
|
||||
@@ -349,11 +361,32 @@ setupDialog::setupDialog( ConfigTabs _tab_to_open ) :
|
||||
embed::getIconPixmap( "project_open", 16, 16 ),
|
||||
"", stk_tw );
|
||||
stkdir_select_btn->setFixedSize( 24, 24 );
|
||||
stkdir_select_btn->move( 320, 20 );
|
||||
stkdir_select_btn->move( 320, 16 );
|
||||
connect( stkdir_select_btn, SIGNAL( clicked() ), this,
|
||||
SLOT( openSTKDir() ) );
|
||||
#endif
|
||||
|
||||
#ifdef LMMS_HAVE_FLUIDSYNTH
|
||||
// Soundfont
|
||||
tabWidget * sf_tw = new tabWidget( tr(
|
||||
"Default Soundfont File" ).toUpper(), paths );
|
||||
sf_tw->setFixedHeight( 48 );
|
||||
|
||||
m_sfLineEdit = new QLineEdit( m_defaultSoundfont, sf_tw );
|
||||
m_sfLineEdit->setGeometry( 10, 20, 300, 16 );
|
||||
connect( m_sfLineEdit, SIGNAL( textChanged( const QString & ) ), this,
|
||||
SLOT( setDefaultSoundfont( const QString & ) ) );
|
||||
|
||||
QPushButton * sf_select_btn = new QPushButton(
|
||||
embed::getIconPixmap( "project_open", 16, 16 ),
|
||||
"", sf_tw );
|
||||
sf_select_btn->setFixedSize( 24, 24 );
|
||||
sf_select_btn->move( 320, 16 );
|
||||
connect( sf_select_btn, SIGNAL( clicked() ), this,
|
||||
SLOT( openDefaultSoundfont() ) );
|
||||
#endif
|
||||
|
||||
|
||||
dir_layout->addWidget( lmms_wd_tw );
|
||||
dir_layout->addSpacing( 10 );
|
||||
dir_layout->addWidget( vst_tw );
|
||||
@@ -366,6 +399,10 @@ setupDialog::setupDialog( ConfigTabs _tab_to_open ) :
|
||||
#ifdef LMMS_HAVE_STK
|
||||
dir_layout->addSpacing( 10 );
|
||||
dir_layout->addWidget( stk_tw );
|
||||
#endif
|
||||
#ifdef LMMS_HAVE_FLUIDSYNTH
|
||||
dir_layout->addSpacing( 10 );
|
||||
dir_layout->addWidget( sf_tw );
|
||||
#endif
|
||||
dir_layout->addStretch();
|
||||
|
||||
@@ -373,7 +410,6 @@ setupDialog::setupDialog( ConfigTabs _tab_to_open ) :
|
||||
|
||||
|
||||
|
||||
|
||||
QWidget * performance = new QWidget( ws );
|
||||
performance->setFixedSize( 360, 240 );
|
||||
QVBoxLayout * perf_layout = new QVBoxLayout( performance );
|
||||
@@ -584,7 +620,7 @@ setupDialog::setupDialog( ConfigTabs _tab_to_open ) :
|
||||
|
||||
m_tabBar->addTab( general, tr( "General settings" ), 0, FALSE, TRUE
|
||||
)->setIcon( embed::getIconPixmap( "setup_general" ) );
|
||||
m_tabBar->addTab( directories, tr( "Directories" ), 1, FALSE, TRUE
|
||||
m_tabBar->addTab( paths, tr( "Paths" ), 1, FALSE, TRUE
|
||||
)->setIcon( embed::getIconPixmap(
|
||||
"setup_directories" ) );
|
||||
m_tabBar->addTab( performance, tr( "Performance settings" ), 2, FALSE,
|
||||
@@ -676,6 +712,9 @@ void setupDialog::accept( void )
|
||||
configManager::inst()->setArtworkDir( m_artworkDir );
|
||||
configManager::inst()->setFLDir( m_flDir );
|
||||
configManager::inst()->setLADSPADir( m_ladDir );
|
||||
#ifdef LMMS_HAVE_FLUIDSYNTH
|
||||
configManager::inst()->setDefaultSoundfont( m_defaultSoundfont );
|
||||
#endif
|
||||
#ifdef LMMS_HAVE_STK
|
||||
configManager::inst()->setSTKDir( m_stkDir );
|
||||
#endif
|
||||
@@ -940,6 +979,23 @@ void setupDialog::openSTKDir( void )
|
||||
|
||||
|
||||
|
||||
void setupDialog::openDefaultSoundfont( void )
|
||||
{
|
||||
#ifdef LMMS_HAVE_FLUIDSYNTH
|
||||
QString new_file = QFileDialog::getOpenFileName( this,
|
||||
tr( "Choose defeault SoundFont" ), m_defaultSoundfont,
|
||||
"SoundFont2 Files (*.sf2)" );
|
||||
|
||||
if( new_file != QString::null )
|
||||
{
|
||||
m_sfLineEdit->setText( new_file );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void setupDialog::setFLDir( const QString & _fd )
|
||||
{
|
||||
m_flDir = _fd;
|
||||
@@ -966,6 +1022,16 @@ void setupDialog::setSTKDir( const QString & _fd )
|
||||
|
||||
|
||||
|
||||
void setupDialog::setDefaultSoundfont( const QString & _sf )
|
||||
{
|
||||
#ifdef LMMS_HAVE_FLUIDSYNTH
|
||||
m_defaultSoundfont = _sf;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void setupDialog::audioInterfaceChanged( const QString & _iface )
|
||||
{
|
||||
for( aswMap::iterator it = m_audioIfaceSetupWidgets.begin();
|
||||
|
||||
Reference in New Issue
Block a user