Make portsmf a git submodule (#7755)

Instead of including the files directly in plugins/MidiImport/portsmf,
that path is now a git submodule (https://github.com/portsmf/portsmf).

Several other changes have been made to clean up MidiImport as well.
This commit is contained in:
Fawn
2025-10-24 15:24:46 -07:00
committed by GitHub
parent 0c69da7233
commit b1e6d180c7
22 changed files with 223 additions and 7820 deletions

3
.gitmodules vendored
View File

@@ -47,3 +47,6 @@
[submodule "src/3rdparty/hiir/hiir"]
path = src/3rdparty/hiir/hiir
url = https://github.com/LostRobotMusic/hiir
[submodule "plugins/MidiImport/portsmf"]
path = plugins/MidiImport/portsmf
url = https://github.com/portsmf/portsmf

View File

@@ -1,8 +1,17 @@
INCLUDE(BuildPlugin)
IF (NOT MSVC)
# portsmf raises these warnings, which will prevent compilation with -Werror.
ADD_COMPILE_OPTIONS(-Wno-error=unknown-pragmas -Wno-error=maybe-uninitialized -Wno-error=restrict)
ENDIF()
# For now, portsmf files are included directly in the plugin.
INCLUDE_DIRECTORIES("portsmf/include")
BUILD_PLUGIN(midiimport MidiImport.cpp MidiImport.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 MOCFILES MidiImport.h)
portsmf/include/allegro.h portsmf/src/allegro.cpp
portsmf/include/mfmidi.h portsmf/src/mfmidi.cpp
portsmf/include/strparse.h portsmf/src/strparse.cpp
# portsmf/src/allegrosmfwr.cpp portsmf/src/allegrowr.cpp
portsmf/src/allegrosmfrd.cpp portsmf/src/allegrord.cpp
portsmf/src/algsmfrd_internal.h portsmf/src/algsmfrd_internal.h
MOCFILES MidiImport.h)

View File

@@ -47,128 +47,112 @@
#include "plugin_export.h"
#include "portsmf/allegro.h"
#include "portsmf/include/allegro.h"
namespace
{
constexpr std::int32_t makeID(const char c[4]) { return c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24); }
}
namespace lmms
{
#define makeID(_c0, _c1, _c2, _c3) \
( 0 | \
( ( _c0 ) | ( ( _c1 ) << 8 ) | ( ( _c2 ) << 16 ) | ( ( _c3 ) << 24 ) ) )
extern "C"
{
Plugin::Descriptor PLUGIN_EXPORT midiimport_plugin_descriptor =
{
LMMS_STRINGIFY( PLUGIN_NAME ),
LMMS_STRINGIFY(PLUGIN_NAME),
"MIDI Import",
QT_TRANSLATE_NOOP( "PluginBrowser",
"Filter for importing MIDI-files into LMMS" ),
QT_TRANSLATE_NOOP("PluginBrowser", "Filter for importing MIDI-files into LMMS"),
"Tobias Doerffel <tobydox/at/users/dot/sf/dot/net>",
0x0100,
Plugin::Type::ImportFilter,
nullptr,
nullptr,
nullptr,
} ;
};
PLUGIN_EXPORT Plugin* lmms_plugin_main(Model*, void* data)
{
return new MidiImport(QString::fromUtf8(static_cast<const char*>(data)));
}
}
MidiImport::MidiImport( const QString & _file ) :
ImportFilter( _file, &midiimport_plugin_descriptor ),
m_events(),
m_timingDivision( 0 )
MidiImport::MidiImport(const QString& file) :
ImportFilter(file, &midiimport_plugin_descriptor)
{}
bool MidiImport::tryImport(TrackContainer* tc)
{
}
bool MidiImport::tryImport( TrackContainer* tc )
{
if( openFile() == false )
{
return false;
}
if (!openFile()) { return false; }
#ifdef LMMS_HAVE_FLUIDSYNTH
if (gui::getGUI() != nullptr &&
ConfigManager::inst()->sf2File().isEmpty() )
if (gui::getGUI() != nullptr && ConfigManager::inst()->sf2File().isEmpty())
{
QMessageBox::information(gui::getGUI()->mainWindow(),
tr( "Setup incomplete" ),
tr( "You have not set up a default soundfont in "
tr("Setup incomplete"),
tr("You have not set up a default soundfont in "
"the settings dialog (Edit->Settings). "
"Therefore no sound will be played back after "
"importing this MIDI file. You should download "
"a General MIDI soundfont, specify it in "
"settings dialog and try again." ) );
"settings dialog and try again."));
}
#else
if (gui::getGUI() != nullptr)
{
QMessageBox::information(gui::getGUI()->mainWindow(),
tr( "Setup incomplete" ),
tr( "You did not compile LMMS with support for "
tr("Setup incomplete"),
tr("You did not compile LMMS with support for "
"SoundFont2 player, which is used to add default "
"sound to imported MIDI files. "
"Therefore no sound will be played back after "
"importing this MIDI file." ) );
"importing this MIDI file."));
}
#endif
switch( readID() )
switch (read32LE()) // Read ID
{
case makeID( 'M', 'T', 'h', 'd' ):
printf( "MidiImport::tryImport(): found MThd\n");
return readSMF( tc );
case makeID("MThd"):
// printf("MidiImport::tryImport(): found MThd\n");
return readSMF(tc);
case makeID( 'R', 'I', 'F', 'F' ):
printf( "MidiImport::tryImport(): found RIFF\n");
return readRIFF( tc );
case makeID("RIFF"):
// printf("MidiImport::tryImport(): found RIFF\n");
return readRIFF(tc);
default:
printf( "MidiImport::tryImport(): not a Standard MIDI "
"file\n" );
printf("MidiImport::tryImport(): not a Standard MIDI file\n");
return false;
}
}
class smfMidiCC
{
public:
smfMidiCC() :
at( nullptr ),
ap( nullptr ),
lastPos( 0 )
{ }
AutomationTrack* at = nullptr;
AutomationClip* ap = nullptr;
TimePos lastPos = 0;
AutomationTrack * at;
AutomationClip * ap;
TimePos lastPos;
smfMidiCC & create( TrackContainer* tc, QString tn )
smfMidiCC& create(TrackContainer* tc, QString tn)
{
if( !at )
if (!at)
{
// Keep LMMS responsive, for now the import runs
// in the main thread. This should probably be
// removed if that ever changes.
qApp->processEvents();
at = dynamic_cast<AutomationTrack *>( Track::create( Track::Type::Automation, tc ) );
}
if( tn != "") {
at->setName( tn );
at = dynamic_cast<AutomationTrack*>(Track::create(Track::Type::Automation, tc));
}
if (tn != "") { at->setName(tn); }
return *this;
}
@@ -181,21 +165,19 @@ public:
}
smfMidiCC & putValue( TimePos time, AutomatableModel * objModel, float value )
smfMidiCC& putValue(TimePos time, AutomatableModel* objModel, float value)
{
if( !ap || time > lastPos + DefaultTicksPerBar )
if (!ap || time > lastPos + DefaultTicksPerBar)
{
TimePos pPos = TimePos( time.getBar(), 0 );
ap = dynamic_cast<AutomationClip*>(
at->createClip(pPos));
ap->addObject( objModel );
TimePos pPos = TimePos(time.getBar(), 0);
ap = dynamic_cast<AutomationClip*>(at->createClip(pPos));
ap->addObject(objModel);
}
lastPos = time;
time = time - ap->startPosition();
ap->putValue( time, value, false );
ap->changeLength( TimePos( time.getBar() + 1, 0 ) );
ap->putValue(time, value, false);
ap->changeLength(TimePos(time.getBar() + 1, 0));
return *this;
}
};
@@ -206,52 +188,38 @@ class smfMidiChannel
{
public:
smfMidiChannel() :
it( nullptr ),
p( nullptr ),
it_inst( nullptr ),
isSF2( false ),
hasNotes( false )
{ }
InstrumentTrack * it;
MidiClip* p;
Instrument * it_inst;
bool isSF2;
bool hasNotes;
InstrumentTrack* it = nullptr;
MidiClip* p = nullptr;
Instrument* it_inst = nullptr;
bool isSF2 = false;
bool hasNotes = false;
QString trackName;
smfMidiChannel * create( TrackContainer* tc, QString tn )
smfMidiChannel* create(TrackContainer* tc, QString tn)
{
if( !it ) {
if (!it) {
// Keep LMMS responsive
qApp->processEvents();
it = dynamic_cast<InstrumentTrack *>( Track::create( Track::Type::Instrument, tc ) );
it = dynamic_cast<InstrumentTrack*>(Track::create(Track::Type::Instrument, tc));
#ifdef LMMS_HAVE_FLUIDSYNTH
it_inst = it->loadInstrument( "sf2player" );
it_inst = it->loadInstrument("sf2player");
if( it_inst )
if (it_inst)
{
isSF2 = true;
it_inst->loadFile( ConfigManager::inst()->sf2File() );
it_inst->childModel( "bank" )->setValue( 0 );
it_inst->childModel( "patch" )->setValue( 0 );
}
else
{
it_inst = it->loadInstrument( "patman" );
it_inst->loadFile(ConfigManager::inst()->sf2File());
it_inst->childModel("bank")->setValue(0);
it_inst->childModel("patch")->setValue(0);
}
else { it_inst = it->loadInstrument("patman"); }
#else
it_inst = it->loadInstrument( "patman" );
it_inst = it->loadInstrument("patman");
#endif
trackName = tn;
if( trackName != "") {
it->setName( tn );
}
if (trackName != "") { it->setName(tn); }
// General MIDI default
it->pitchRangeModel()->setInitValue( 2 );
it->pitchRangeModel()->setInitValue(2);
// Create a default pattern
p = dynamic_cast<MidiClip*>(it->createClip(0));
}
@@ -259,19 +227,17 @@ public:
}
void addNote( Note & n )
void addNote(Note& n)
{
if (!p)
{
p = dynamic_cast<MidiClip*>(it->createClip(0));
}
if (!p) { p = dynamic_cast<MidiClip*>(it->createClip(0)); }
p->addNote(n, false);
hasNotes = true;
}
void splitMidiClips()
{
MidiClip * newMidiClip = nullptr;
MidiClip* newMidiClip = nullptr;
TimePos lastEnd(0);
p->rearrangeAllNotes();
@@ -292,28 +258,27 @@ public:
delete p;
p = nullptr;
}
};
bool MidiImport::readSMF( TrackContainer* tc )
bool MidiImport::readSMF(TrackContainer* tc)
{
const int MIDI_CC_COUNT = 128 + 1; // 0-127 (128) + pitch bend
const int preTrackSteps = 2;
QProgressDialog pd( TrackContainer::tr( "Importing MIDI-file..." ),
constexpr int MIDI_CC_COUNT = 128 + 1; // 0-127 (128) + pitch bend
constexpr int preTrackSteps = 2;
QProgressDialog pd(TrackContainer::tr("Importing MIDI-file..."),
TrackContainer::tr("Cancel"), 0, preTrackSteps, gui::getGUI()->mainWindow());
pd.setWindowTitle( TrackContainer::tr( "Please wait..." ) );
pd.setWindowTitle(TrackContainer::tr("Please wait..."));
pd.setWindowModality(Qt::WindowModal);
pd.setMinimumDuration( 0 );
pd.setMinimumDuration(0);
pd.setValue( 0 );
pd.setValue(0);
std::istringstream stream(readAllData().toStdString());
auto seq = new Alg_seq(stream, true);
seq->convert_to_beats();
pd.setMaximum( seq->tracks() + preTrackSteps );
pd.setValue( 1 );
pd.setMaximum(seq->tracks() + preTrackSteps);
pd.setValue(1);
// 128 CC + Pitch Bend
auto ccs = std::array<smfMidiCC, MIDI_CC_COUNT>{};
@@ -343,8 +308,8 @@ bool MidiImport::readSMF( TrackContainer* tc )
double ticksPerBeat = DefaultTicksPerBar / beatsPerBar;
// Time-sig changes
Alg_time_sigs * timeSigs = &seq->time_sig;
for( int s = 0; s < timeSigs->length(); ++s )
Alg_time_sigs* timeSigs = &seq->time_sig;
for (int s = 0; s < timeSigs->length(); ++s)
{
Alg_time_sig timeSig = (*timeSigs)[s];
timeSigNumeratorPat->putValue(timeSig.beat * ticksPerBeat, timeSig.num);
@@ -354,7 +319,7 @@ bool MidiImport::readSMF( TrackContainer* tc )
timeSigNumeratorPat->updateLength();
timeSigDenominatorPat->updateLength();
pd.setValue( 2 );
pd.setValue(2);
// Tempo stuff
auto tt = dynamic_cast<AutomationTrack*>(Track::create(Track::Type::Automation, Engine::getSong()));
@@ -362,22 +327,21 @@ bool MidiImport::readSMF( TrackContainer* tc )
auto tap = new AutomationClip(tt);
tap->setDisplayName(tr("Tempo"));
tap->addObject(&Engine::getSong()->tempoModel());
if( tap )
if (tap)
{
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_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 );
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 )
if (timeMap->last_tempo_flag)
{
Alg_beat_ptr b = &( beats[beats.len - 1] );
tap->putValue( b->beat * ticksPerBeat, timeMap->last_tempo * 60.0 );
Alg_beat_ptr b = &beats[beats.len - 1];
tap->putValue(b->beat * ticksPerBeat, timeMap->last_tempo * 60.0);
}
}
@@ -386,38 +350,35 @@ bool MidiImport::readSMF( TrackContainer* tc )
Engine::updateFramesPerTick();
// Song events
for( int e = 0; e < seq->length(); ++e )
for (int e = 0; e < seq->length(); ++e)
{
Alg_event_ptr evt = (*seq)[e];
if( evt->is_update() )
if (evt->is_update())
{
printf("Unhandled SONG update: %d %f %s\n",
evt->get_type_code(), evt->time, evt->get_attribute() );
evt->get_type_code(), evt->time, evt->get_attribute());
}
}
// Tracks
for( int t = 0; t < seq->tracks(); ++t )
for (int t = 0; t < seq->tracks(); ++t)
{
QString trackName = QString( tr( "Track" ) + " %1" ).arg( t );
Alg_track_ptr trk = seq->track( t );
pd.setValue( t + preTrackSteps );
QString trackName = QString(tr("Track") + " %1").arg(t);
Alg_track_ptr trk = seq->track(t);
pd.setValue(t + preTrackSteps);
for (auto& cc : ccs)
{
cc.clear();
}
for (auto& cc : ccs) { cc.clear(); }
// Now look at events
for( int e = 0; e < trk->length(); ++e )
for (int e = 0; e < trk->length(); ++e)
{
Alg_event_ptr evt = (*trk)[e];
if( evt->chan == -1 )
if (evt->chan == -1)
{
bool handled = false;
if( evt->is_update() )
if (evt->is_update())
{
QString attr = evt->get_attribute();
// seqnames is a track0 identifier (see allegro code)
@@ -428,46 +389,45 @@ bool MidiImport::readSMF( TrackContainer* tc )
handled = true;
}
}
if( !handled ) {
// Write debug output
printf("MISSING GLOBAL HANDLER\n");
printf(" Chn: %d, Type Code: %d, Time: %f", (int) evt->chan,
evt->get_type_code(), evt->time );
if ( evt->is_update() )
{
printf( ", Update Type: %s", evt->get_attribute() );
if ( evt->get_update_type() == 'a' )
{
printf( ", Atom: %s", evt->get_atom_value() );
}
}
printf( "\n" );
if (!handled) {
// Write debug output
printf("MISSING GLOBAL HANDLER\n");
printf("\tChn: %ld, Type Code: %d, Time: %f", evt->chan, evt->get_type_code(), evt->time);
if (evt->is_update())
{
printf(", Update Type: %s", evt->get_attribute());
if (evt->get_update_type() == 'a') {
printf(", Atom: %s", evt->get_atom_value());
}
}
printf("\n");
}
}
else if (evt->is_note())
{
smfMidiChannel * ch = chs[evt->chan].create( tc, trackName );
smfMidiChannel* ch = chs[evt->chan].create(tc, trackName);
auto noteEvt = dynamic_cast<Alg_note_ptr>(evt);
int ticks = noteEvt->get_duration() * ticksPerBeat;
Note n( (ticks < 1 ? 1 : ticks ),
noteEvt->get_start_time() * ticksPerBeat,
noteEvt->get_identifier(),
noteEvt->get_loud() * (200.f / 127.f)); // Map from MIDI velocity to LMMS volume
ch->addNote( n );
tick_t ticks = noteEvt->get_duration() * ticksPerBeat;
Note n(
ticks < 1 ? 1 : ticks,
noteEvt->get_start_time() * ticksPerBeat,
noteEvt->get_identifier(),
// Map from MIDI velocity to LMMS volume
noteEvt->get_loud() * (200.f / 127.f)
);
ch->addNote(n);
}
else if( evt->is_update() )
else if (evt->is_update())
{
smfMidiChannel * ch = chs[evt->chan].create( tc, trackName );
smfMidiChannel* ch = chs[evt->chan].create(tc, trackName);
double time = evt->time*ticksPerBeat;
QString update( evt->get_attribute() );
QString update(evt->get_attribute());
if( update == "programi" )
if (update == "programi")
{
long prog = evt->get_integer_value();
if( ch->isSF2 )
const auto prog = evt->get_integer_value();
if (ch->isSF2)
{
auto& pc = pcs[evt->chan];
AutomatableModel* objModel = ch->it_inst->childModel("patch");
@@ -476,39 +436,36 @@ bool MidiImport::readSMF( TrackContainer* tc )
}
pc.putValue(time, objModel, prog);
}
else {
const QString num = QString::number( prog );
const QString filter = QString().fill( '0', 3 - num.length() ) + num + "*.pat";
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() )
const QStringList files = QDir(dir).
entryList(QStringList(filter));
if (ch->it_inst && !files.empty())
{
ch->it_inst->loadFile( dir+files.front() );
ch->it_inst->loadFile(dir + files.front());
}
}
}
else if( update.startsWith( "control" ) || update == "bendr" )
else if (update.startsWith("control") || update == "bendr")
{
int ccid = update.mid( 7, update.length()-8 ).toInt();
if( update == "bendr" )
{
ccid = 128;
}
if( ccid <= 128 )
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 = nullptr;
AutomatableModel* objModel = nullptr;
switch( ccid )
switch (ccid)
{
case 0:
if( ch->isSF2 && ch->it_inst )
if (ch->isSF2 && ch->it_inst)
{
objModel = ch->it_inst->childModel( "bank" );
printf("BANK SELECT %f %d\n", cc, (int)(cc*127.0));
objModel = ch->it_inst->childModel("bank");
printf("BANK SELECT %f %d\n", cc, static_cast<int>(cc * 127));
cc *= 127.0f;
}
break;
@@ -527,33 +484,35 @@ bool MidiImport::readSMF( TrackContainer* tc )
objModel = ch->it->pitchModel();
cc = cc * 100.0f;
break;
default:
//TODO: something useful for other CCs
break;
}
if( objModel )
if (objModel)
{
if( time == 0 && objModel )
if (time == 0 && objModel)
{
objModel->setInitValue( cc );
objModel->setInitValue(cc);
}
else
{
if( ccs[ccid].at == nullptr ) {
ccs[ccid].create( tc, trackName + " > " + (
objModel != nullptr ?
objModel->displayName() :
QString("CC %1").arg(ccid) ) );
if (ccs[ccid].at == nullptr) {
ccs[ccid].create(tc, trackName + " > " +
// This is inside if (objModel), so objModel should never be nullptr
// (objModel != nullptr ? objModel->displayName() : QString("CC %1").arg(ccid))
objModel->displayName()
);
}
ccs[ccid].putValue( time, objModel, cc );
ccs[ccid].putValue(time, objModel, cc);
}
}
}
}
else {
printf("Unhandled update: %d %d %f %s\n", (int) evt->chan,
evt->get_type_code(), evt->time, evt->get_attribute() );
printf("Unhandled update: %ld %d %f %s\n",
evt->chan, evt->get_type_code(), evt->time, evt->get_attribute());
}
}
}
@@ -561,8 +520,7 @@ bool MidiImport::readSMF( TrackContainer* tc )
delete seq;
for( auto& c: chs )
for (auto& c: chs)
{
if (c.second.hasNotes)
{
@@ -572,7 +530,7 @@ bool MidiImport::readSMF( TrackContainer* tc )
{
printf(" Should remove empty track\n");
// must delete trackView first - but where is it?
//tc->removeTrack( chs[c].it );
//tc->removeTrack(chs[c].it);
//it->deleteLater();
}
// Set channel 10 to drums as per General MIDI's orders
@@ -588,73 +546,45 @@ bool MidiImport::readSMF( TrackContainer* tc )
}
bool MidiImport::readRIFF( TrackContainer* tc )
bool MidiImport::readRIFF(TrackContainer* tc)
{
// skip file length
skip( 4 );
skip(4);
// check file type ("RMID" = RIFF MIDI)
if( readID() != makeID( 'R', 'M', 'I', 'D' ) )
if (read32LE() != makeID("RMID"))
{
invalid_format:
qWarning( "MidiImport::readRIFF(): invalid file format" );
return false;
qWarning("MidiImport::readRIFF(): invalid file format");
return false;
}
// search for "data" chunk
while( true )
while (true)
{
const int id = readID();
const int len = read32LE();
if( file().atEnd() )
const std::int32_t id = read32LE();
const std::int32_t len = read32LE();
if (file().atEnd())
{
data_not_found:
qWarning( "MidiImport::readRIFF(): data chunk not found" );
return false;
qWarning("MidiImport::readRIFF(): data chunk not found");
return false;
}
if( id == makeID( 'd', 'a', 't', 'a' ) )
{
break;
}
if( len < 0 )
{
goto data_not_found;
}
skip( ( len + 1 ) & ~1 );
if (id == makeID("data")) { 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 );
if (read32LE() != makeID("MThd")) { goto invalid_format; }
return readSMF(tc);
}
void MidiImport::error()
{
printf( "MidiImport::readTrack(): invalid MIDI data (offset %#x)\n",
(unsigned int) file().pos() );
}
extern "C"
{
// necessary for getting instance out of shared lib
PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *, void * _data )
{
return new MidiImport( QString::fromUtf8(
static_cast<const char *>( _data ) ) );
}
printf("MidiImport::readTrack(): invalid MIDI data (offset %#x)\n",
static_cast<unsigned int>(file().pos()));
}

View File

@@ -25,9 +25,8 @@
#ifndef _MIDI_IMPORT_H
#define _MIDI_IMPORT_H
#include <cstdint>
#include <QString>
#include <QPair>
#include <QVector>
#include "MidiEvent.h"
#include "ImportFilter.h"
@@ -40,91 +39,53 @@ class MidiImport : public ImportFilter
{
Q_OBJECT
public:
MidiImport( const QString & _file );
MidiImport(const QString& file);
~MidiImport() override = default;
gui::PluginView* instantiateView( QWidget * ) override
{
return( nullptr );
}
gui::PluginView* instantiateView(QWidget*) override { return nullptr; }
private:
bool tryImport( TrackContainer* tc ) override;
bool readSMF( TrackContainer* tc );
bool readRIFF( TrackContainer* tc );
bool readTrack( int _track_end, QString & _track_name );
bool tryImport(TrackContainer* tc) override;
bool readSMF(TrackContainer* tc);
bool readRIFF(TrackContainer* tc);
bool readTrack(int track_end, QString& track_name);
void error();
inline int readInt( int _bytes )
inline std::int32_t read32LE()
{
int value = 0;
do
{
int c = readByte();
if( c == -1 )
{
return( -1 );
}
value = ( value << 8 ) | c;
} while( --_bytes );
return( value );
}
inline int read32LE()
{
int value = readByte();
std::int32_t value = readByte();
value |= readByte() << 8;
value |= readByte() << 16;
value |= readByte() << 24;
return value;
}
inline int readVar()
inline std::int32_t readVar()
{
int c = readByte();
int value = c & 0x7f;
if( c & 0x80 )
std::int32_t c = readByte();
std::int32_t value = c & 0x7f;
if (c & 0x80)
{
c = readByte();
value = ( value << 7 ) | ( c & 0x7f );
if( c & 0x80 )
value = (value << 7) | (c & 0x7f);
if (c & 0x80)
{
c = readByte();
value = ( value << 7 ) | ( c & 0x7f );
if( c & 0x80 )
value = (value << 7) | (c & 0x7f);
if (c & 0x80)
{
c = readByte();
value = ( value << 7 ) | c;
if( c & 0x80 )
{
return -1;
}
value = (value << 7) | c;
if (c & 0x80) { return -1; }
}
}
}
return( !file().atEnd() ? value : -1 );
}
inline int readID()
{
return read32LE();
}
inline void skip( int _bytes )
{
while( _bytes > 0 )
{
readByte();
--_bytes;
}
return !file().atEnd() ? value : -1;
}
using EventVector = QVector<QPair<int, MidiEvent>>;
EventVector m_events;
int m_timingDivision;
inline void skip(unsigned num_bytes) { while (num_bytes--) { readByte(); } }
} ;
int m_timingDivision = 0;
};
} // namespace lmms

View File

@@ -1,32 +0,0 @@
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.

View File

@@ -1,5 +0,0 @@
/* algread_internal.h -- interface between allegro.cpp and allegrord.cpp */
#include "allegro.h"
Alg_error alg_read(std::istream &file, Alg_seq_ptr new_seq, double *offset_ptr = nullptr);

View File

@@ -1,5 +0,0 @@
/* algsmfrd_internal.h -- interface from allegrosmfrd.cpp to allegro.cpp */
#include "allegro.h"
Alg_error alg_smf_read(std::istream &file, Alg_seq_ptr new_seq);

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,779 +0,0 @@
#include "assert.h"
#include "stdlib.h"
#include "string.h"
#include "ctype.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;
double offset;
bool offset_found;
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);
void parse_error(string &field, long offset, const 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;
offset = 0.0;
offset_found = false;
}
Alg_error alg_read(istream &file, Alg_seq_ptr new_seq, double *offset_ptr)
// read a sequence from allegro file
{
assert(new_seq);
Alg_reader alg_reader(&file, new_seq);
bool err = alg_reader.parse();
if (!err && offset_ptr) {
*offset_ptr = alg_reader.offset;
}
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 = 0;
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 = nullptr;
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 if (streql(field.c_str(), "#offset")) {
if (offset_found) {
parse_error(field, 0, "#offset specified twice");
}
offset_found = true;
line_parser.get_nonspace_quoted(field); // number
field.insert(0, " "); // need char at beginning because
// parse_real ignores first character in the argument
offset = parse_real(field);
}
} 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((unsigned int) 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((unsigned int) 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 = nullptr; // 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 = (float) pitch;
note_ptr->loud = (float) 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 = nullptr; // 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;
const 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;
const 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();
if (n < len && field[n] == '-') n += 1; // parse one minus sign
for (int i = n; i < len; i++) {
char c = field[i];
if (!isdigit(c)) {
if (c == '.' && !decimal) {
decimal = true;
} else {
return i;
}
}
}
return len;
}
double Alg_reader::parse_real(string &field)
{
const 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);
}
void Alg_reader::parse_error(string &field, long offset, const char *message)
{
parse_error(field, offset, const_cast<char*>(message));
}
double duration_lookup[] = { 0.25, 0.5, 1.0, 2.0, 4.0 };
double Alg_reader::parse_dur(string &field, double base)
{
const char *msg = "Duration expected";
const char *durs = "SIQHW";
const 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 {
const char *str;
int val;
} loud_lookup[] = { {"FFF", 127}, {"FF", 120}, {"F", 110}, {"MF", 100},
{"MP", 90}, {"P", 80}, {"PP", 70}, {"PPP", 60},
{nullptr, 0} };
double Alg_reader::parse_loud(string &field)
{
const 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)
{
const char *msg = "Pitch expected";
const char *pitches = "ABCDEFG";
const 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; LMMS unused variable */
if (s[pos] == '-') {
/* sign = -1; LMMS unused variable */
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
*/

View File

@@ -1,2 +0,0 @@
// allegroserial.cpp -- convert track to memory buffer and back to structure

View File

@@ -1,458 +0,0 @@
// midifile reader
#include "stdio.h"
#include "string.h"
#include "assert.h"
#include <fstream>
#include "allegro.h"
#include "algsmfrd_internal.h"
#include "mfmidi.h"
using namespace std;
typedef class Alg_note_list {
public:
Alg_note_ptr note;
class Alg_note_list *next;
Alg_note_list(Alg_note_ptr n, class Alg_note_list *list) {
note = n; next = list; }
} *Alg_note_list_ptr;
class Alg_midifile_reader: public Midifile_reader {
public:
istream *file;
Alg_seq_ptr seq;
int divisions;
Alg_note_list_ptr note_list;
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;
note_list = nullptr;
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) override { return malloc(size); }
void Mf_free(void *obj, size_t size) override { free(obj); }
/* Methods to be called while processing the MIDI file. */
void Mf_starttrack() override;
void Mf_endtrack() override;
int Mf_getc() override;
void Mf_chanprefix(int chan) override;
void Mf_portprefix(int port) override;
void Mf_eot() override;
void Mf_error(char *) override;
void Mf_error(const char *);
void Mf_header(int,int,int) override;
void Mf_on(int,int,int) override;
void Mf_off(int,int,int) override;
void Mf_pressure(int,int,int) override;
void Mf_controller(int,int,int) override;
void Mf_pitchbend(int,int,int) override;
void Mf_program(int,int) override;
void Mf_chanpressure(int,int) override;
void binary_msg(int len, unsigned char *msg, const char *attr_string);
void Mf_sysex(int,unsigned char*) override;
void Mf_arbitrary(int,unsigned char*) override;
void Mf_metamisc(int,int,unsigned char*) override;
void Mf_seqnum(int) override;
void Mf_smpte(int,int,int,int,int) override;
void Mf_timesig(int,int,int,int) override;
void Mf_tempo(int) override;
void Mf_keysig(int,int) override;
void Mf_sqspecific(int,unsigned char*) override;
void Mf_text(int,int,unsigned char*) override;
};
Alg_midifile_reader::~Alg_midifile_reader()
{
while (note_list) {
Alg_note_list_ptr to_be_freed = note_list;
note_list = note_list->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 = nullptr;
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_error(const char *msg)
{
Mf_error(const_cast<char*>(msg));
}
void Alg_midifile_reader::Mf_header(int format, int ntrks, int division)
{
if (format > 1) {
char msg[80];
//#pragma warning(disable: 4996) // msg is long enough
sprintf(msg, "file format %d not implemented", format);
//#pragma warning(default: 4996)
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();
note_list = new Alg_note_list(note, note_list);
/* 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_note_list_ptr *p = &note_list;
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_note_list_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 = nullptr;
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, &parameter);
meta_channel = -1;
}
void Alg_midifile_reader::Mf_controller(int chan, int control, int val)
{
Alg_parameter parameter;
char name[32];
//#pragma warning(disable: 4996) // name is long enough
sprintf(name, "control%dr", control);
//#pragma warning(default: 4996)
parameter.set_attr(symbol_table.insert_string(name));
parameter.r = val / 127.0;
update(chan, -1, &parameter);
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, &parameter);
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, &parameter);
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, &parameter);
meta_channel = -1;
}
void Alg_midifile_reader::binary_msg(int len, unsigned char *msg,
const char *attr_string)
{
Alg_parameter parameter;
char *hexstr = new char[len * 2 + 1];
for (int i = 0; i < len; i++) {
//#pragma warning(disable: 4996) // hexstr is long enough
sprintf(hexstr + 2 * i, "%02x", (0xFF & msg[i]));
//#pragma warning(default: 4996)
}
parameter.s = hexstr;
parameter.set_attr(symbol_table.insert_string(attr_string));
update(meta_channel, -1, &parameter);
}
void Alg_midifile_reader::Mf_sysex(int len, unsigned 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, unsigned char *msg)
{
Mf_error("arbitrary data ignored");
}
void Alg_midifile_reader::Mf_metamisc(int type, int len, unsigned char *msg)
{
char text[128];
//#pragma warning(disable: 4996) // text is long enough
sprintf(text, "metamsic data, type 0x%x, ignored", type);
//#pragma warning(default: 4996)
Mf_error(text);
}
void Alg_midifile_reader::Mf_seqnum(int n)
{
Mf_error("seqnum data ignored");
}
static const 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;
//#pragma warning(disable: 4996) // text is long enough
sprintf(text, "%sfps:%02dh:%02dm:%02ds:%02d.%02df",
fpsstr[fps], hours, mins, secs, frames, subframes);
//#pragma warning(default: 4996)
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(double(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, unsigned 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, unsigned 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, unsigned 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);
}

View File

@@ -1,638 +0,0 @@
// allegrosmfwr.cpp -- Allegro Standard Midi File Write
#include <stdlib.h>
#include <string.h>
#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() = default;
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(ostream &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, const 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 = nullptr;
// 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
}
// 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 == nullptr) {
event->next = nullptr;
return event;
}
event_queue *marker1 = nullptr;
event_queue *marker2 = queue;
while (marker2 != nullptr && marker2->time <= event->time) {
marker1 = marker2;
marker2 = marker2->next;
}
event->next = marker2;
if (marker1 != nullptr) {
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 = char(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('\xFF'); // Meta Event
out_file->put('\x20'); // 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('\xFF');
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('\xFF'); // meta event
out_file->put('\x54'); // 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(const char *s)
{
return (hex_to_nibble(s[0]) << 4) + hex_to_nibble(s[1]);
}
void Alg_smf_write::write_binary(int type_byte, const 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)
{
const 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') {
const 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') {
const char *s = update->parameter.s;
write_delta(update->time);
out_file->put('\xFF');
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
const 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 = 0;
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('\xFF');
out_file->put('\x59');
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 &notes = seq->track_list[i];
event_queue *pending = nullptr;
if (notes.length() > 0) {
pending = new event_queue('n', TICK_TIME(notes[j]->time, 0), 0, nullptr);
}
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, nullptr));
}
if (seq->time_sig.length() > 0) {
pending = push(pending, new event_queue('s',
TICK_TIME(seq->time_sig[0].beat, 0), 0, nullptr));
}
}
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, nullptr));
} 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('\xFF');
out_file->put('\x51');
out_file->put('\x03');
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_delta(ts[i].beat);
// write the time signature
out_file->put('\xFF');
out_file->put('\x58'); // time signature
out_file->put('\x04'); // 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(ostream &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('\xFF');
out_file->put('\x2F');
out_file->put('\x00');
// 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(ostream &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;
}

View File

@@ -1,179 +0,0 @@
// allegrowr.cpp -- write sequence to an Allegro file (text)
#include "assert.h"
#include <fstream>
#include <iomanip>
#include <string>
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 = nullptr; // e is the result, default is 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++) {
Alg_event_ptr ue = events[i];
if (ue->time > 0) break;
if (ue->is_update()) {
Alg_update_ptr u = (Alg_update_ptr) ue;
if (u->parameter.attr == attr) {
file << " " << u->parameter.s;
e = ue; // return the update event we found
break;
}
}
}
file << endl; // end of line containing #track [<name>]
return e; // return parameter event with name if one was found
}
void Alg_seq::write(ostream &file, bool in_secs, double offset)
{
int i, j;
if (in_secs) convert_to_seconds();
else convert_to_beats();
file << "#offset " << offset << endl;
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 &notes = 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, double offset)
{
ofstream file(filename);
if (file.fail()) return false;
write(file, units_are_seconds, offset);
file.close();
return true;
}

View File

@@ -1,40 +0,0 @@
/*
* 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.
*/

View File

@@ -1,491 +0,0 @@
/*
* 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"
#include "assert.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(const char *s, int skip)
/* read through the "MThd" or "MTrk" header string */
/* if skip == 1, we attempt to skip initial garbage. */
{
assert(strlen(s) == 4); // must be "MThd" or "MTrk"
int nread = 0;
char b[4];
char buff[32];
int c;
const 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:
//#pragma warning(disable: 4996) // strcpy is safe since strings have known lengths
(void) strcpy(buff,errmsg);
(void) strcat(buff,s);
//#pragma warning(default: 4996) // turn it back on
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 ) {
unsigned 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];
//#pragma warning(disable: 4996) // safe in this case
(void) sprintf(buff,"unexpected byte: 0x%02x",c);
//#pragma warning(default: 4996)
mferror(buff);
}
void Midifile_reader::metaevent(int type)
{
int leng = msgleng();
// made this unsigned to avoid sign extend
unsigned 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;
}
void Midifile_reader::mferror(const char *s)
{
mferror(const_cast<char *>(s));
}
/* 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 = nullptr;
}
void Midifile_reader::msginit()
{
Msgindex = 0;
}
unsigned 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()
{
unsigned char *newmess;
unsigned char *oldmess = Msgbuff;
int oldleng = Msgsize;
Msgsize += MSGINCREMENT;
newmess = (unsigned char *) Mf_malloc((sizeof(unsigned char) * Msgsize) );
/* copy old message into larger new one */
if ( oldmess != 0 ) {
memcpy(newmess, oldmess, oldleng);
Mf_free(oldmess, oldleng);
}
Msgbuff = newmess;
}

View File

@@ -1,101 +0,0 @@
#include <cstddef>
#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,unsigned char*) = 0;
virtual void Mf_arbitrary(int,unsigned char*) = 0;
virtual void Mf_metamisc(int,int,unsigned 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,unsigned char*) = 0;
virtual void Mf_text(int,int,unsigned char*) = 0;
private:
long Mf_toberead;
long readvarinum();
long read32bit();
int read16bit();
void msgenlarge();
unsigned char *msg();
int readheader();
void readtrack();
void sysex();
void msginit();
int egetc();
int msgleng();
int readmt(const char*,int);
long to32bit(int,int,int,int);
int to16bit(int,int);
void mferror(char *);
void mferror(const char *);
void badbyte(int);
void metaevent(int);
void msgadd(int);
void chanmessage(int,int,int);
unsigned char *Msgbuff;
long Msgsize;
long Msgindex;
};

View File

@@ -1,87 +0,0 @@
#include <string>
#include <cstring>
// #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;
}
}
}
static const char *const escape_chars[] = {"\\n", "\\t", "\\\\", "\\r", "\\\""};
void string_escape(string &result, const char *str, const 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])) {
const char *const chars = "\n\t\\\r\"";
const char *const 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 ((len > 0) && ((*str)[len - 1] == '\n')) { // if str ends in newline,
len--; // reduce length to ignore newline
}
field.insert(0, *str, pos, len);
}

View File

@@ -1,18 +0,0 @@
// 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, const char *s, const char *quote);

View File

@@ -1,25 +0,0 @@
// 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_s(msg, 256, _TRUNCATE, format, args);
va_end(args);
#ifdef _DEBUG
_CrtDbgReport(_CRT_WARN, nullptr, nullptr, nullptr, msg);
#else
printf(msg);
#endif
}

View File

@@ -1,2 +0,0 @@
void trace(char *format, ...);