From b1e6d180c77b50a635fb9765c1027a8433150b2e Mon Sep 17 00:00:00 2001 From: Fawn Date: Fri, 24 Oct 2025 15:24:46 -0700 Subject: [PATCH] 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. --- .gitmodules | 3 + plugins/MidiImport/CMakeLists.txt | 19 +- plugins/MidiImport/MidiImport.cpp | 432 +- plugins/MidiImport/MidiImport.h | 87 +- plugins/MidiImport/portsmf | 1 + plugins/MidiImport/portsmf/README.txt | 32 - plugins/MidiImport/portsmf/algrd_internal.h | 5 - .../MidiImport/portsmf/algsmfrd_internal.h | 5 - plugins/MidiImport/portsmf/allegro.cpp | 3515 ----------------- plugins/MidiImport/portsmf/allegro.h | 1124 ------ plugins/MidiImport/portsmf/allegrord.cpp | 779 ---- plugins/MidiImport/portsmf/allegroserial.cpp | 2 - plugins/MidiImport/portsmf/allegrosmfrd.cpp | 458 --- plugins/MidiImport/portsmf/allegrosmfwr.cpp | 638 --- plugins/MidiImport/portsmf/allegrowr.cpp | 179 - plugins/MidiImport/portsmf/license.txt | 40 - plugins/MidiImport/portsmf/mfmidi.cpp | 491 --- plugins/MidiImport/portsmf/mfmidi.h | 101 - plugins/MidiImport/portsmf/strparse.cpp | 87 - plugins/MidiImport/portsmf/strparse.h | 18 - plugins/MidiImport/portsmf/trace.cpp | 25 - plugins/MidiImport/portsmf/trace.h | 2 - 22 files changed, 223 insertions(+), 7820 deletions(-) create mode 160000 plugins/MidiImport/portsmf delete mode 100644 plugins/MidiImport/portsmf/README.txt delete mode 100644 plugins/MidiImport/portsmf/algrd_internal.h delete mode 100644 plugins/MidiImport/portsmf/algsmfrd_internal.h delete mode 100644 plugins/MidiImport/portsmf/allegro.cpp delete mode 100644 plugins/MidiImport/portsmf/allegro.h delete mode 100644 plugins/MidiImport/portsmf/allegrord.cpp delete mode 100644 plugins/MidiImport/portsmf/allegroserial.cpp delete mode 100644 plugins/MidiImport/portsmf/allegrosmfrd.cpp delete mode 100644 plugins/MidiImport/portsmf/allegrosmfwr.cpp delete mode 100644 plugins/MidiImport/portsmf/allegrowr.cpp delete mode 100644 plugins/MidiImport/portsmf/license.txt delete mode 100644 plugins/MidiImport/portsmf/mfmidi.cpp delete mode 100644 plugins/MidiImport/portsmf/mfmidi.h delete mode 100644 plugins/MidiImport/portsmf/strparse.cpp delete mode 100644 plugins/MidiImport/portsmf/strparse.h delete mode 100644 plugins/MidiImport/portsmf/trace.cpp delete mode 100644 plugins/MidiImport/portsmf/trace.h diff --git a/.gitmodules b/.gitmodules index 87d9a04f1..734769e21 100644 --- a/.gitmodules +++ b/.gitmodules @@ -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 diff --git a/plugins/MidiImport/CMakeLists.txt b/plugins/MidiImport/CMakeLists.txt index 9fd6b6876..914ef3144 100644 --- a/plugins/MidiImport/CMakeLists.txt +++ b/plugins/MidiImport/CMakeLists.txt @@ -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) diff --git a/plugins/MidiImport/MidiImport.cpp b/plugins/MidiImport/MidiImport.cpp index 24fed85df..071ba6570 100644 --- a/plugins/MidiImport/MidiImport.cpp +++ b/plugins/MidiImport/MidiImport.cpp @@ -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 ", 0x0100, Plugin::Type::ImportFilter, nullptr, nullptr, nullptr, -} ; +}; + +PLUGIN_EXPORT Plugin* lmms_plugin_main(Model*, void* data) +{ + return new MidiImport(QString::fromUtf8(static_cast(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( Track::create( Track::Type::Automation, tc ) ); - } - if( tn != "") { - at->setName( tn ); + at = dynamic_cast(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( - at->createClip(pPos)); - ap->addObject( objModel ); + TimePos pPos = TimePos(time.getBar(), 0); + ap = dynamic_cast(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( Track::create( Track::Type::Instrument, tc ) ); + it = dynamic_cast(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(it->createClip(0)); } @@ -259,19 +227,17 @@ public: } - void addNote( Note & n ) + void addNote(Note& n) { - if (!p) - { - p = dynamic_cast(it->createClip(0)); - } + if (!p) { p = dynamic_cast(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{}; @@ -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(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(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(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( _data ) ) ); -} - - + printf("MidiImport::readTrack(): invalid MIDI data (offset %#x)\n", + static_cast(file().pos())); } diff --git a/plugins/MidiImport/MidiImport.h b/plugins/MidiImport/MidiImport.h index 0870511b5..430325df2 100644 --- a/plugins/MidiImport/MidiImport.h +++ b/plugins/MidiImport/MidiImport.h @@ -25,9 +25,8 @@ #ifndef _MIDI_IMPORT_H #define _MIDI_IMPORT_H +#include #include -#include -#include #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>; - EventVector m_events; - int m_timingDivision; + inline void skip(unsigned num_bytes) { while (num_bytes--) { readByte(); } } -} ; + int m_timingDivision = 0; +}; } // namespace lmms diff --git a/plugins/MidiImport/portsmf b/plugins/MidiImport/portsmf new file mode 160000 index 000000000..081261bcf --- /dev/null +++ b/plugins/MidiImport/portsmf @@ -0,0 +1 @@ +Subproject commit 081261bcfd8faca4cee8560010de34831c0dea97 diff --git a/plugins/MidiImport/portsmf/README.txt b/plugins/MidiImport/portsmf/README.txt deleted file mode 100644 index 0f36c9e4e..000000000 --- a/plugins/MidiImport/portsmf/README.txt +++ /dev/null @@ -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. diff --git a/plugins/MidiImport/portsmf/algrd_internal.h b/plugins/MidiImport/portsmf/algrd_internal.h deleted file mode 100644 index 56a9cdc46..000000000 --- a/plugins/MidiImport/portsmf/algrd_internal.h +++ /dev/null @@ -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); diff --git a/plugins/MidiImport/portsmf/algsmfrd_internal.h b/plugins/MidiImport/portsmf/algsmfrd_internal.h deleted file mode 100644 index 65e7ac874..000000000 --- a/plugins/MidiImport/portsmf/algsmfrd_internal.h +++ /dev/null @@ -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); diff --git a/plugins/MidiImport/portsmf/allegro.cpp b/plugins/MidiImport/portsmf/allegro.cpp deleted file mode 100644 index 80313db46..000000000 --- a/plugins/MidiImport/portsmf/allegro.cpp +++ /dev/null @@ -1,3515 +0,0 @@ -// Allegro: music representation system, with -// extensible in-memory sequence structure -// upward compatible with MIDI -// implementations in C++ and Serpent -// external, text-based representation -// compatible with Aura -// -/* CHANGE LOG: -04 apr 03 -- fixed bug in add_track that caused infinite loop -*/ - -#include -#include -#include -#include -using namespace std; -#include "allegro.h" -#include "algrd_internal.h" -#include "algsmfrd_internal.h" -// #include "trace.h" -- only needed for debugging - -#define STREQL(x, y) (strcmp(x, y) == 0) -#define MAX(x, y) ((x) > (y) ? (x) : (y)) -#define ROUND(x) ((int) ((x) + 0.5)) - -// 4311 is type cast ponter to long warning -// 4996 is warning against strcpy -// 4267 is size_t to long warning -//#pragma warning(disable: 4311 4996 4267) -Alg_atoms symbol_table; -Serial_read_buffer Alg_track::ser_read_buf; // declare the static variables -Serial_write_buffer Alg_track::ser_write_buf; - -bool within(double d1, double d2, double epsilon) -{ - d1 -= d2; - return d1 < epsilon && d1 > -epsilon; -} - - -char *heapify(const char *s) -{ - char *h = new char[strlen(s) + 1]; - strcpy(h, s); - return h; -} - - -void Alg_atoms::expand() -{ - maxlen = (maxlen + 5); // extra growth for small sizes - maxlen += (maxlen >> 2); // add 25% - Alg_attribute *new_atoms = new Alg_attribute[maxlen]; - // now do copy - memcpy(new_atoms, atoms, len * sizeof(Alg_attribute)); - if (atoms) delete[] atoms; - atoms = new_atoms; -} - - -// insert_new -- insert an attribute name and type -// -// attributes are stored as a string consisting of the type -// (a char) followed by the attribute name. This makes it -// easy to retrieve the type or the name or both. -// -Alg_attribute Alg_atoms::insert_new(const char *name, char attr_type) -{ - if (len == maxlen) expand(); - char *h = new char[strlen(name) + 2]; - strcpy(h + 1, name); - *h = attr_type; - atoms[len++] = h; - return h; -} - - -Alg_attribute Alg_atoms::insert_attribute(Alg_attribute attr) -{ - // should use hash algorithm - for (int i = 0; i < len; i++) { - if (STREQL(attr, atoms[i])) { - return atoms[i]; - } - } - return insert_new(attr + 1, attr[0]); -} - - -Alg_attribute Alg_atoms::insert_string(const char *name) -{ - char attr_type = name[strlen(name) - 1]; - for (int i = 0; i < len; i++) { - if (attr_type == atoms[i][0] && - STREQL(name, atoms[i] + 1)) { - return atoms[i]; - } - } - return insert_new(name, attr_type); -} - - -void Alg_parameter::copy(Alg_parameter_ptr parm) -{ - *this = *parm; // copy all fields - // if the value is a string, copy the string - if (attr_type() == 's') { - s = heapify(s); - } -} - - -void Alg_parameter::show() -{ - switch (attr[0]) { - case 'r': - printf("%s:%g", attr_name(), r); - break; - case 's': - printf("%s:%s", attr_name(), s); - break; - case 'i': - printf("%s:%ld", attr_name(), i); - break; - case 'l': - printf("%s:%s", attr_name(), (l ? "t" : "f")); - break; - case 'a': - printf("%s:%s", attr_name(), a); - break; - } -} - - -Alg_parameter::~Alg_parameter() -{ - if (attr_type() == 's' && s) { - delete[] s; - } -} - - -void Alg_parameters::insert_real(Alg_parameters **list, const char *name, - double r) -{ - Alg_parameters_ptr a = new Alg_parameters(*list); - *list = a; - a->parm.set_attr(symbol_table.insert_string(name)); - a->parm.r = r; - assert(a->parm.attr_type() == 'r'); -} - - -void Alg_parameters::insert_string(Alg_parameters **list, const char *name, - const char *s) -{ - Alg_parameters_ptr a = new Alg_parameters(*list); - *list = a; - a->parm.set_attr(symbol_table.insert_string(name)); - // string is deleted when parameter is deleted - a->parm.s = heapify(s); - assert(a->parm.attr_type() == 's'); -} - - -void Alg_parameters::insert_integer(Alg_parameters **list, const char *name, - long i) -{ - Alg_parameters_ptr a = new Alg_parameters(*list); - *list = a; - a->parm.set_attr(symbol_table.insert_string(name)); - a->parm.i = i; - assert(a->parm.attr_type() == 'i'); -} - - -void Alg_parameters::insert_logical(Alg_parameters **list, const char *name, - bool l) -{ - Alg_parameters_ptr a = new Alg_parameters(*list); - *list = a; - a->parm.set_attr(symbol_table.insert_string(name)); - a->parm.l = l; - assert(a->parm.attr_type() == 'l'); -} - - -void Alg_parameters::insert_atom(Alg_parameters **list, const char *name, - const char *s) -{ - Alg_parameters_ptr a = new Alg_parameters(*list); - *list = a; - a->parm.set_attr(symbol_table.insert_string(name)); - a->parm.a = symbol_table.insert_string(s); - assert(a->parm.attr_type() == 'a'); -} - - -Alg_parameters *Alg_parameters::remove_key(Alg_parameters **list, - const char *name) -{ - while (*list) { - if (STREQL((*list)->parm.attr_name(), name)) { - Alg_parameters_ptr p = *list; - *list = p->next; - p->next = nullptr; - return p; // caller should free this pointer - } - list = &((*list)->next); - } - return nullptr; -} - - -Alg_parameter_ptr Alg_parameters::find(Alg_attribute attr) -{ - assert(attr); - Alg_parameters_ptr temp = this; - while (temp) { - if (temp->parm.attr == attr) { - return &(temp->parm); - } - } - return nullptr; -} - - -int Alg_event::get_type_code() -{ - if (!is_note()) { - const char* attr = get_attribute(); - if (STREQL(attr, "gater")) // volume change - return ALG_GATE; - if (STREQL(attr, "bendr")) // pitch bend - return ALG_BEND; - if (strncmp(attr, "control", 7) == 0) // control change - // note that midi control changes have attributes of the form - // "control" where n is the decimal number (as a character string) - // of the midi controller, e.g. control2 is the breath controller. - // We don't check for decimal numbers in the range 0-127, so any - // attribute that begins with "control" is an ALG_CONTROL: - return ALG_CONTROL; - if (STREQL(attr, "programi")) // program change - return ALG_PROGRAM; - if (STREQL(attr, "pressurer")) // pressure change - return ALG_PRESSURE; - if (STREQL(attr, "keysigi")) // key signature - return ALG_KEYSIG; - if (STREQL(attr, "timesig_numi")) // time signature numerator - return ALG_TIMESIG_NUM; - if (STREQL(attr, "timesig_deni")) // time signature denominator - return ALG_TIMESIG_DEN; - return ALG_OTHER; - } - return ALG_NOTE; // it is a note -} - - -void Alg_event::set_parameter(Alg_parameter_ptr new_parameter) -{ - Alg_parameter_ptr parm; - if (is_note()) { - Alg_note_ptr note = (Alg_note_ptr) this; - parm = note->parameters->find(new_parameter->attr); - if (!parm) { - note->parameters = new Alg_parameters(note->parameters); - parm = &(note->parameters->parm); - } - } else { // update - Alg_update_ptr update = (Alg_update_ptr) this; - parm = &(update->parameter); - } - parm->copy(new_parameter); // copy entire parameter -} - - -void Alg_event::set_string_value(const char *a, const char *value) -{ - assert(a); // must be non-null - Alg_attribute attr = symbol_table.insert_string(a); - assert(attr[0] == 's'); - Alg_parameter parm; - parm.set_attr(attr); - parm.s = value; - set_parameter(&parm); - parm.s = nullptr; // do this to prevent string from being freed -} - - -void Alg_event::set_real_value(const char *a, double value) -{ - assert(a); // must be non-null - // attr is like a, but it has the type code prefixed for - // fast lookup, and it is a unique string in symbol_table - // e.g. a="attackr" -> attr="rattackr" - Alg_attribute attr = symbol_table.insert_string(a); - assert(attr[0] == 'r'); - Alg_parameter parm; - parm.set_attr(attr); - parm.r = value; - set_parameter(&parm); - // since type is 'r' we don't have to NULL the string -} - - -void Alg_event::set_logical_value(const char *a, bool value) -{ - assert(a); // must be non-null - Alg_attribute attr = symbol_table.insert_string(a); - assert(attr[0] == 'l'); - Alg_parameter parm; - parm.set_attr(attr); - parm.l = value; - set_parameter(&parm); - // since type is 'l' we don't have to NULL the string -} - - -void Alg_event::set_integer_value(const char *a, long value) -{ - assert(a); // must be non-null - Alg_attribute attr = symbol_table.insert_string(a); - assert(attr[0] == 'i'); - Alg_parameter parm; - parm.set_attr(attr); - parm.i = value; - set_parameter(&parm); - // since tpye is 'i' we don't have to NULL the string -} - - -void Alg_event::set_atom_value(const char *a, const char *value) -{ - assert(a); // must be non-null - Alg_attribute attr = symbol_table.insert_string(a); - assert(attr[0] == 'a'); - Alg_parameter parm; - parm.set_attr(attr); - parm.a = value; - set_parameter(&parm); - /* since type is 'a' we don't have to null the string */ -} - - -float Alg_event::get_pitch() -{ - assert(is_note()); - Alg_note* note = (Alg_note *) this; - return note->pitch; -} - - -float Alg_event::get_loud() -{ - assert(is_note()); - Alg_note* note = (Alg_note *) this; - return note->loud; -} - - -double Alg_event::get_start_time() -{ - assert(is_note()); - Alg_note* note = (Alg_note *) this; - return note->time; -} - - -double Alg_event::get_end_time() -{ - assert(is_note()); - Alg_note* note = (Alg_note *) this; - return note->time + note->dur; -} - - -double Alg_event::get_duration() -{ - assert(is_note()); - Alg_note* note = (Alg_note *) this; - return note->dur; -} - - -void Alg_event::set_pitch(float p) -{ - assert(is_note()); - Alg_note* note = (Alg_note *) this; - note->pitch = p; -} - -void Alg_event::set_loud(float l) -{ - assert(is_note()); - Alg_note *note = (Alg_note *) this; - note->loud = l; -} - - -void Alg_event::set_duration(double d) -{ - assert(is_note()); - Alg_note* note = (Alg_note *) this; - note->dur = d; -} - - -bool Alg_event::has_attribute(const char *a) -{ - assert(is_note()); - assert(a); // must be non-null - Alg_note* note = (Alg_note *) this; - Alg_attribute attr = symbol_table.insert_string(a); - Alg_parameter_ptr parm = note->parameters->find(attr); - return parm != nullptr; -} - - -char Alg_event::get_attribute_type(const char *a) -{ - assert(is_note()); - assert(a); - return a[strlen(a) - 1]; -} - - -const char *Alg_event::get_string_value(const char *a, const char *value) -{ - assert(is_note()); - assert(a); // must be non-null - Alg_note* note = (Alg_note *) this; - Alg_attribute attr = symbol_table.insert_string(a); - assert(a[0] == 's'); // must be of type string - Alg_parameter_ptr parm = note->parameters->find(attr); - if (parm) return parm->s; - return value; -} - - -double Alg_event::get_real_value(const char *a, double value) -{ - assert(is_note()); - assert(a); - Alg_note* note = (Alg_note *) this; - Alg_attribute attr = symbol_table.insert_string(a); - assert(a[0] == 'r'); // must be of type real - Alg_parameter_ptr parm = note->parameters->find(attr); - if (parm) return parm->r; - return value; -} - - -bool Alg_event::get_logical_value(const char *a, bool value) -{ - assert(is_note()); - assert(a); - Alg_note* note = (Alg_note *) this; - Alg_attribute attr = symbol_table.insert_string(a); - assert(a[0] == 'l'); // must be of type logical - Alg_parameter_ptr parm = note->parameters->find(attr); - if (parm) return parm->l; - return value; -} - - -long Alg_event::get_integer_value(const char *a, long value) -{ - assert(is_note()); - assert(a); - Alg_note* note = (Alg_note *) this; - Alg_attribute attr = symbol_table.insert_string(a); - assert(a[0] == 'i'); // must be of type integer - Alg_parameter_ptr parm = note->parameters->find(attr); - if (parm) return parm->i; - return value; -} - - -const char *Alg_event::get_atom_value(const char *a, const char *value) -{ - assert(is_note()); - assert(a); - Alg_note* note = (Alg_note *) this; - Alg_attribute attr = symbol_table.insert_string(a); - assert(a[0] == 'a'); // must be of type atom - Alg_parameter_ptr parm = note->parameters->find(attr); - if (parm) return parm->a; - // if default is a string, convert to an atom (unique - // string in symbol table) and return it - return (value == nullptr ? nullptr : - symbol_table.insert_string(value)); -} - - -void Alg_event::delete_attribute(const char *a) -{ - assert(is_note()); - Alg_note* note = (Alg_note *) this; - Alg_parameters::remove_key(&(note->parameters), a); -} - - -const char *Alg_event::get_attribute() -// Note: this returns a string, not an Alg_attribute -{ - assert(is_update()); - Alg_update* update = (Alg_update *) this; - return update->parameter.attr_name(); -} - - -char Alg_event::get_update_type() -{ - assert(is_update()); - Alg_update* update = (Alg_update *) this; - return update->parameter.attr_type(); -} - - -const char *Alg_event::get_string_value() -{ - assert(is_update()); - Alg_update* update = (Alg_update *) this; - assert(get_update_type() == 's'); - return update->parameter.s; -} - - -double Alg_event::get_real_value() -{ - assert(is_update()); - Alg_update* update = (Alg_update *) this; - assert(get_update_type() == 'r'); - return update->parameter.r; -} - - -bool Alg_event::get_logical_value() -{ - assert(is_update()); - Alg_update* update = (Alg_update *) this; - assert(get_update_type() == 'l'); - return update->parameter.l; -} - - -long Alg_event::get_integer_value() -{ - assert(is_update()); - Alg_update* update = (Alg_update *) this; - assert(get_update_type() == 'i'); - return update->parameter.i; -} - - -const char *Alg_event::get_atom_value() -{ - assert(is_update()); - Alg_update* update = (Alg_update *) this; - assert(get_update_type() == 'a'); - return update->parameter.a; -} - - -bool Alg_event::overlap(double t, double len, bool all) -{ - // event starts within region - if (time >= t && time <= t + len - ALG_EPS) - return true; - if (all && is_note()) { - double dur = ((Alg_note_ptr) this)->dur; - // note overlaps with region - if (time < t && time + dur - ALG_EPS > t) - return true; - } - // does not overlap - return false; -} - - -Alg_note::Alg_note(Alg_note_ptr note) -{ - *this = *note; // copy all fields - // parameters is now a shared pointer. We need to copy the - // parameters - Alg_parameters_ptr next_param_ptr = parameters; - while (next_param_ptr) { - Alg_parameters_ptr new_params = new Alg_parameters(next_param_ptr->next); - new_params->parm.copy(&(next_param_ptr->parm)); // copy the attribute and value - next_param_ptr = new_params->next; - } -} - - -Alg_note::~Alg_note() -{ - while (parameters) { - Alg_parameters_ptr to_delete = parameters; - parameters = parameters->next; - delete to_delete; - } -} - - -void Alg_note::show() -{ - printf("Alg_note: time %g, chan %ld, dur %g, key %ld, " - "pitch %g, loud %g, attributes ", - time, chan, dur, key, pitch, loud); - Alg_parameters_ptr parms = parameters; - while (parms) { - parms->parm.show(); - printf(" "); - parms = parms->next; - } - printf("\n"); -} - - -Alg_update::Alg_update(Alg_update_ptr update) -{ - *this = *update; // copy all fields - // parameter requires careful copy to possibly duplicate string value: - this->parameter.copy(&(update->parameter)); -} - - -void Alg_update::show() -{ - printf("Alg_update: "); - parameter.show(); - printf("\n"); -} - - -void Alg_events::expand() -{ - maxlen = (maxlen + 5); // extra growth for small sizes - maxlen += (maxlen >> 2); // add 25% - Alg_event_ptr *new_events = new Alg_event_ptr[maxlen]; - // now do copy - memcpy(new_events, events, len * sizeof(Alg_event_ptr)); - if (events) delete[] events; - events = new_events; -} - - -void Alg_events::insert(Alg_event_ptr event) -{ - if (maxlen <= len) { - expand(); - } - // Note: if the new event is the last one, the assignment - // events[i] = event; (below) will never execute, so just - // in case, we do the assignment here. events[len] will - // be replaced during the memmove() operation below if - // this is not the last event. - events[len] = event; - len++; - // find insertion point: (this could be a binary search) - for (int i = 0; i < len; i++) { - if (events[i]->time > event->time) { - // insert event at i - memmove(&events[i + 1], &events[i], - sizeof(Alg_event_ptr) * (len - i - 1)); - events[i] = event; - return; - } - } -} - -Alg_event_ptr Alg_events::uninsert(long index) -{ - assert(0 <= index && index < len); - Alg_event_ptr event = events[index]; - //printf("memmove: %x from %x (%d)\n", events + index, events + index + 1, - // sizeof(Alg_event_ptr) * (len - index - 1)); - memmove(events + index, events + index + 1, - sizeof(Alg_event_ptr) * (len - index - 1)); - len--; - return event; -} - - -void Alg_events::append(Alg_event_ptr event) -{ - if (maxlen <= len) { - expand(); - } - events[len++] = event; - // keep track of last note_off time - if (event->is_note()) { - Alg_note_ptr note = (Alg_note_ptr) event; - double note_off = note->time + note->dur; - if (note_off > last_note_off) - last_note_off = note_off; - } -} - - -Alg_events::~Alg_events() -{ - assert(!in_use); - // individual events are not deleted, only the array - if (events) { - delete[] events; - } -} - - -Alg_event_list::Alg_event_list(Alg_track *owner) -{ - events_owner = owner; - sequence_number = owner->sequence_number; - beat_dur = 0.0; real_dur = 0.0; type = 'e'; -} - - -Alg_event_ptr const &Alg_event_list::operator [](int i) -{ - assert(i >= 0 && i < len); - return events[i]; -} - - -Alg_event_list::~Alg_event_list() -{ - // note that the events contained in the list are not destroyed -} - - -void Alg_event_list::set_start_time(Alg_event *event, double t) -{ - // For Alg_event_list, find the owner and do the update there - // For Alg_track, change the time and move the event to the right place - // For Alg_seq, find the track and do the update there - - long index = 0, i; - Alg_track_ptr track_ptr = nullptr; - if (type == 'e') { // this is an Alg_event_list - // make sure the owner has not changed its event set - assert(events_owner && - sequence_number == events_owner->sequence_number); - // do the update on the owner - events_owner->set_start_time(event, t); - return; - } else if (type == 't') { // this is an Alg_track - // find the event in the track - track_ptr = (Alg_track_ptr) this; - // this should be a binary search since events are in time order - // probably there should be member function to do the search - for (index = 0; index < length(); index++) { - if ((*track_ptr)[index] == event) goto found_event; - } - } else { // type == 's', an Alg_seq - Alg_seq_ptr seq = (Alg_seq_ptr) this; - for (i = 0; i < seq->tracks(); i++) { - track_ptr = seq->track(i); - // if you implemented binary search, you could call it - // instead of this loop too. - for (index = 0; index < track_ptr->length(); index++) { - if ((*track_ptr)[index] == event) goto found_event; - } - } - } - assert(false); // event not found seq or track! - found_event: - // at this point, track[index] == event - // we could be clever and figure out exactly what notes to move - // but it is simpler to just remove the event and reinsert it: - track_ptr->uninsert(index); - event->time = t; - track_ptr->insert(event); -} - - -void Alg_beats::expand() -{ - maxlen = (maxlen + 5); // extra growth for small sizes - maxlen += (maxlen >> 2); // add 25% - Alg_beat_ptr new_beats = new Alg_beat[maxlen]; - // now do copy - memcpy(new_beats, beats, len * sizeof(Alg_beat)); - if (beats) delete[] beats; - beats = new_beats; -} - - -void Alg_beats::insert(long i, Alg_beat_ptr beat) -{ - assert(i >= 0 && i <= len); - if (maxlen <= len) { - expand(); - } - memmove(&beats[i + 1], &beats[i], sizeof(Alg_beat) * (len - i)); - memcpy(&beats[i], beat, sizeof(Alg_beat)); - len++; -} - - -Alg_time_map::Alg_time_map(Alg_time_map *map) -{ - refcount = 0; - assert(map->beats[0].beat == 0 && map->beats[0].time == 0); - assert(map->beats.len > 0); - // new_beats[0] = map->beats[0]; - // this is commented because - // both new_beats[0] and map->beats[0] should be (0, 0) - for (int i = 1; i < map->beats.len; i++) { - beats.insert(i, &map->beats[i]); - } - last_tempo = map->last_tempo; - last_tempo_flag = map->last_tempo_flag; -} - - -void Alg_time_map::show() -{ - printf("Alg_time_map: "); - for (int i = 0; i < beats.len; i++) { - Alg_beat &b = beats[i]; - printf("(%g, %g) ", b.time, b.beat); - } - printf("last tempo: %g\n", last_tempo); -} - - -long Alg_time_map::locate_time(double time) -{ - int i = 0; - while ((i < beats.len) && (time > beats[i].time)) { - i++; - } - return i; -} - - -long Alg_time_map::locate_beat(double beat) -{ - int i = 0; - while ((i < beats.len) && (beat > beats[i].beat)) { - i++; - } - return i; -} - - -double Alg_time_map::beat_to_time(double beat) -{ - Alg_beat_ptr mbi; - Alg_beat_ptr mbi1; - if (beat <= 0) { - return beat; - } - int i = locate_beat(beat); - // case 1: beat is between two time/beat pairs - if (0 < i && i < beats.len) { - mbi = &beats[i - 1]; - mbi1 = &beats[i]; - // case 2: beat is beyond last time/beat pair - } else if (i == beats.len) { - if (last_tempo_flag) { - return beats[i - 1].time + - (beat - beats[i - 1].beat) / last_tempo; - } else if (i == 1) { - return beat * 60.0 / ALG_DEFAULT_BPM; - // so we use that as default allegro tempo too - } else { - mbi = &beats[i - 2]; - mbi1 = &beats[i - 1]; - } - // case 3: beat is at time 0 - } else /* if (i == 0) */ { - return beats[0].time; - } - // whether we extrapolate or interpolate, the math is the same - double time_dif = mbi1->time - mbi->time; - double beat_dif = mbi1->beat - mbi->beat; - return mbi->time + (beat - mbi->beat) * time_dif / beat_dif; -} - - -double Alg_time_map::time_to_beat(double time) -{ - Alg_beat_ptr mbi; - Alg_beat_ptr mbi1; - if (time <= 0.0) return time; - int i = locate_time(time); - if (i == beats.len) { - if (last_tempo_flag) { - return beats[i - 1].beat + - (time - beats[i - 1].time) * last_tempo; - } else if (i == 1) { - return time * (ALG_DEFAULT_BPM / 60.0); - } else { - mbi = &beats[i - 2]; - mbi1 = &beats[i - 1]; - } - } else { - mbi = &beats[i - 1]; - mbi1 = & beats[i]; - } - double time_dif = mbi1->time - mbi->time; - double beat_dif = mbi1->beat - mbi->beat; - return mbi->beat + (time - mbi->time) * beat_dif / time_dif; -} - - -void Alg_time_map::insert_beat(double time, double beat) -{ - int i = locate_time(time); // i is insertion point - if (i < beats.len && within(beats[i].time, time, 0.000001)) { - // replace beat if time is already in the map - beats[i].beat = beat; - } else { - Alg_beat point; - point.beat = beat; - point.time = time; - beats.insert(i, &point); - } - // beats[i] contains new beat - // make sure we didn't generate a zero tempo. - // if so, space beats by one microbeat as necessary - long j = i; - if (j == 0) j = 1; // do not adjust beats[0] - while (j < beats.len && - beats[j - 1].beat + 0.000001 >= beats[j].beat) { - beats[j].beat = beats[j - 1].beat + 0.000001; - j++; - } -} - - -bool Alg_time_map::insert_tempo(double tempo, double beat) -{ - tempo = tempo / 60.0; // convert to beats per second - // change the tempo at the given beat until the next beat event - if (beat < 0) return false; - double time = beat_to_time(beat); - long i = locate_time(time); - if (i >= beats.len || !within(beats[i].time, time, 0.000001)) { - insert_beat(time, beat); - } - // now i is index of beat where tempo will change - if (i == beats.len - 1) { - last_tempo = tempo; - // printf("last_tempo to %g\n", last_tempo); - last_tempo_flag = true; - } else { // adjust all future beats - // compute the difference in beats - double diff = beats[i + 1].beat - beats[i].beat; - // convert beat difference to seconds at new tempo - diff = diff / tempo; - // figure out old time difference: - double old_diff = beats[i + 1].time - time; - // compute difference too - diff = diff - old_diff; - // apply new_diff to score and beats - i++; - while (i < beats.len) { - beats[i].time = beats[i].time + diff; - i++; - } - } - return true; -} - - -double Alg_time_map::get_tempo(double beat) -{ - Alg_beat_ptr mbi; - Alg_beat_ptr mbi1; - // if beat < 0, there is probably an error; return something nice anyway - if (beat < 0) return ALG_DEFAULT_BPM / 60.0; - long i = locate_beat(beat); - // this code is similar to beat_to_time() so far, but we want to get - // beyond beat if possible because we want the tempo FOLLOWING beat - // (Consider the case beat == 0.0) - if (i < beats.len && beat >= beats[i].beat) i++; - // case 1: beat is between two time/beat pairs - if (i < beats.len) { - mbi = &beats[i - 1]; - mbi1 = &beats[i]; - // case 2: beat is beyond last time/beat pair - } else /* if (i == beats.len) */ { - if (last_tempo_flag) { - return last_tempo; - } else if (i == 1) { - return ALG_DEFAULT_BPM / 60.0; - } else { - mbi = &beats[i - 2]; - mbi1 = &beats[i - 1]; - } - } - double time_dif = mbi1->time - mbi->time; - double beat_dif = mbi1->beat - mbi->beat; - return beat_dif / time_dif; -} - - -bool Alg_time_map::set_tempo(double tempo, double start_beat, double end_beat) -{ - if (start_beat >= end_beat) return false; - // algorithm: insert a beat event if necessary at start_beat - // and at end_beat - // delete intervening map elements - // change the tempo - insert_beat(beat_to_time(start_beat), start_beat); - insert_beat(beat_to_time(end_beat), end_beat); - long start_x = locate_beat(start_beat) + 1; - long stop_x = locate_beat(end_beat); - while (stop_x < beats.len) { - beats[start_x] = beats[stop_x]; - start_x++; - stop_x++; - } - beats.len = start_x; // truncate the map to new length - return insert_tempo(tempo, start_beat); -} - - -bool Alg_time_map::stretch_region(double b0, double b1, double dur) -{ - // find current duration - double t0 = beat_to_time(b0); - double t1 = beat_to_time(b1); - double old_dur = t1 - t0; - if (old_dur <= 0 || dur <= 0) return false; - double scale = dur / old_dur; // larger scale => slower - // insert a beat if necessary at b0 and b1 - insert_beat(t0, b0); - insert_beat(t1, b1); - long start_x = locate_beat(b0); - long stop_x = locate_beat(b1); - double orig_time = beats[start_x].time; - double prev_time = orig_time; - for (int i = start_x + 1; i < beats.len; i++) { - double delta = beats[i].time - orig_time; - if (i <= stop_x) { // change tempo to next Alg_beat - delta *= scale; - } - orig_time = beats[i].time; - prev_time += delta; - beats[i].time = prev_time; - } - return true; -} - - -void Alg_time_map::trim(double start, double end, bool units_are_seconds) -{ - // extract the time map from start to end and shift to time zero - // start and end are time in seconds if units_are_seconds is true - int i = 0; // index into beats - int start_index; // index of first breakpoint after start - int count = 1; - double initial_beat = start; - double final_beat = end; - if (units_are_seconds) { - initial_beat = time_to_beat(start); - final_beat = time_to_beat(end); - } else { - start = beat_to_time(initial_beat); - end = beat_to_time(final_beat); - } - while (i < length() && beats[i].time < start) i++; - // now i is index into beats of the first breakpoint after start - // beats[0] is (0,0) and remains that way - // copy beats[start_index] to beats[1], etc. - // skip any beats at or near (start,initial_beat), using count - // to keep track of how many entries there are - start_index = i; - while (i < length() && beats[i].time < end) { - if (beats[i].time - start > ALG_EPS && - beats[i].beat - initial_beat > ALG_EPS) { - beats[i].time = beats[i].time - start; - beats[i].beat = beats[i].beat - initial_beat; - beats[i - start_index + 1] = beats[i]; - count = count + 1; - } else { - start_index = start_index + 1; - } - i = i + 1; - } - // set last tempo data - // we last examined beats[i-1] and copied it to - // beats[i - start_index]. Next tempo should come - // from beats[i] and store in beats[i - start_index + 1] - // case 1: there is at least one breakpoint beyond end - // => interpolate to put a breakpoint at end - // case 2: no more breakpoints => set last tempo data - if (i < length()) { - // we know beats[i].time >= end, so case 1 applies - beats[i - start_index + 1].time = end - start; - beats[i - start_index + 1].beat = final_beat - initial_beat; - count = count + 1; - } - // else we'll just use stored last tempo (if any) - beats.len = count; -} - - -void Alg_time_map::cut(double start, double len, bool units_are_seconds) -{ - // remove portion of time map from start to start + len, - // shifting the tail left by len. start and len are in whatever - // units the score is in. If you cut the time_map as well as cut - // the tracks of the sequence, then sequences will preserve the - // association between tempo changes and events - double end = start + len; - double initial_beat = start; - double final_beat = end; - int i = 0; - - if (units_are_seconds) { - initial_beat = time_to_beat(start); - final_beat = time_to_beat(end); - } else { - start = beat_to_time(initial_beat); - end = beat_to_time(final_beat); - len = end - start; - } - double beat_len = final_beat - initial_beat; - - while (i < length() && beats[i].time < start - ALG_EPS) { - i = i + 1; - } - - // if no beats exist at or after start, just return; nothing to cut - if (i == length()) return; - - // now i is index into beats of the first breakpoint on or - // after start, insert (start, initial_beat) in map - if (i < length() && within(beats[i].time, start, ALG_EPS)) { - // perterb time map slightly (within alg_eps) to place - // break point exactly at the start time - beats[i].time = start; - beats[i].beat = initial_beat; - } else { - Alg_beat point(start, initial_beat); - beats.insert(i, &point); - } - // now, we're correct up to beats[i] and beats[i] happens at start. - // find first beat after end so we can start shifting from there - i = i + 1; - int start_index = i; - while (i < length() && beats[i].time < end + ALG_EPS) i++; - // now beats[i] is the next point to be included in beats - // but from i onward, we must shift by (-len, -beat_len) - while (i < length()) { - Alg_beat &b = beats[i]; - b.time = b.time - len; - b.beat = b.beat - beat_len; - beats[start_index] = b; - i = i + 1; - start_index = start_index + 1; - } - beats.len = start_index; -} - - -void Alg_time_map::paste(double beat, Alg_track *tr) -{ - // insert a given time map at a given time and dur (in beats) - Alg_time_map_ptr from_map = tr->get_time_map(); - // printf("time map paste\nfrom map\n"); - // from_map->show(); - // printf("to map\n"); - // show(); - Alg_beats &from = from_map->beats; - double time = beat_to_time(beat); - // Locate the point at which dur occurs - double dur = tr->get_beat_dur(); - double tr_end_time = from_map->beat_to_time(dur); - // add offset to make room for insert - int i = locate_beat(beat); - while (i < length()) { - beats[i].beat += dur; - beats[i].time += tr_end_time; - i++; - } - // printf("after opening up\n"); - // show(); - // insert point at beginning and end of paste - insert_beat(time, beat); - // printf("after beginning point insert\n"); - // show(); - // insert_beat(time + tr_end_time, beat + dur); - // printf("after ending point insert\n"); - // show(); - int j = from_map->locate_beat(dur); - for (i = 0; i < j; i++) { - insert_beat(from[i].time + time, // shift by time - from[i].beat + beat); // and beat - } - // printf("after inserts\n"); - show(); -} - - -void Alg_time_map::insert_time(double start, double len) -{ - // find time,beat pair that determines tempo at start - // compute beat offset = (delta beat / delta time) * len - // add len,beat offset to each following Alg_beat - // show(); - int i = locate_time(start); // start <= beats[i].time - if (beats[i].time == start) i++; // start < beats[i].time - // case 1: between beats - if (i > 0 && i < length()) { - double beat_offset = len * (beats[i].beat - beats[i-1].beat) / - (beats[i].time - beats[i-1].time); - while (i < length()) { - beats[i].beat += beat_offset; - beats[i].time += len; - i++; - } - } // otherwise, last tempo is in effect; nothing to do - // printf("time_map AFTER INSERT\n"); - // show(); -} - - -void Alg_time_map::insert_beats(double start, double len) -{ - int i = locate_beat(start); // start <= beats[i].beat - if (beats[i].beat == start) i++; - if (i > 0 && i < length()) { - double time_offset = len * (beats[i].time - beats[i-1].time) / - (beats[i].beat - beats[i-1].beat); - while (i < length()) { - beats[i].time += time_offset; - beats[i].beat += len; - i++; - } - } // otherwise, last tempo is in effect; nothing to do - // printf("time_map AFTER INSERT\n"); - // show(); -} - - -Alg_track::Alg_track(Alg_time_map *map, bool seconds) -{ - type = 't'; - time_map = nullptr; - units_are_seconds = seconds; - set_time_map(map); -} - - -Alg_event_ptr Alg_track::copy_event(Alg_event_ptr event) -{ - Alg_event *new_event; - if (event->is_note()) { - new_event = new Alg_note((Alg_note_ptr) event); - } else { // update - new_event = new Alg_update((Alg_update_ptr) event); - } - return new_event; -} - - -Alg_track::Alg_track(Alg_track &track) -{ - type = 't'; - time_map = nullptr; - for (int i = 0; i < track.length(); i++) { - append(copy_event(track.events[i])); - } - set_time_map(track.time_map); - units_are_seconds = track.units_are_seconds; -} - - -Alg_track::Alg_track(Alg_event_list_ref event_list, Alg_time_map_ptr map, - bool units_are_seconds) -{ - type = 't'; - time_map = nullptr; - for (int i = 0; i < event_list.length(); i++) { - append(copy_event(event_list[i])); - } - set_time_map(map); - this->units_are_seconds = units_are_seconds; -} - - -void Alg_track::serialize(void **buffer, long *bytes) -{ - // first determine whether this is a seq or a track. - // if it is a seq, then we will write the time map and a set of tracks - // if it is a track, we just write the track data and not the time map - // - // The code will align doubles on ALIGN boundaries, and longs and - // floats are aligned to multiples of 4 bytes. - // - // The format for a seq is: - // 'ALGS' -- indicates that this is a sequence - // long length of all seq data in bytes starting with 'ALGS' - // long channel_offset_per_track - // long units_are_seconds - // time_map: - // double last_tempo - // long last_tempo_flag - // long len -- number of tempo changes - // for each tempo change (Alg_beat): - // double time - // double beat - // time_sigs: - // long len -- number of time_sigs - // long pad - // for each time signature: - // double beat - // double num - // double den - // tracks: - // long len -- number of tracks - // long pad - // for each track: - // 'ALGT' -- indicates this is a track - // long length of all track data in bytes starting with 'ALGT' - // long units_are_seconds - // double beat_dur - // double real_dur - // long len -- number of events - // for each event: - // long selected - // long type - // long key - // long channel - // double time - // if this is a note: - // double pitch - // double dur - // double loud - // long len -- number of parameters - // for each parameter: - // char attribute[] with zero pad to ALIGN - // one of the following, depending on type: - // double r - // char s[] terminated by zero - // long i - // long l - // char a[] terminated by zero - // zero pad to ALIGN - // else if this is an update - // (same representation as parameter above) - // zero pad to ALIGN - // - // The format for a track is given within the Seq format above - assert(get_type() == 't'); - ser_write_buf.init_for_write(); - serialize_track(); - *buffer = ser_write_buf.to_heap(bytes); -} - - -void Alg_seq::serialize(void **buffer, long *bytes) -{ - assert(get_type() == 's'); - ser_write_buf.init_for_write(); - serialize_seq(); - *buffer = ser_write_buf.to_heap(bytes); -} - - -void Serial_write_buffer::check_buffer(long needed) -{ - if (len < (ptr - buffer) + needed) { // do we need more space? - long new_len = len * 2; // exponential growth is important - // initially, length is zero, so bump new_len to a starting value - if (new_len == 0) new_len = 1024; - // make sure new_len is as big as needed - if (needed > new_len) new_len = needed; - char *new_buffer = new char[new_len]; // allocate space - ptr = new_buffer + (ptr - buffer); // relocate ptr to new buffer - if (len > 0) { // we had a buffer already - memcpy(new_buffer, buffer, len); // copy from old buffer - delete buffer; // free old buffer - } - buffer = new_buffer; // update buffer information - len = new_len; - } -} - - -void Alg_seq::serialize_seq() -{ - int i; // loop counters - // we can easily compute how much buffer space we need until we - // get to tracks, so expand at least that much - long needed = 64 + 16 * time_map->beats.len + 24 * time_sig.length(); - ser_write_buf.check_buffer(needed); - ser_write_buf.set_char('A'); - ser_write_buf.set_char('L'); - ser_write_buf.set_char('G'); - ser_write_buf.set_char('S'); - long length_offset = ser_write_buf.get_posn(); - ser_write_buf.set_int32(0); // leave room to come back and write length - ser_write_buf.set_int32(channel_offset_per_track); - ser_write_buf.set_int32(units_are_seconds); - ser_write_buf.set_double(beat_dur); - ser_write_buf.set_double(real_dur); - ser_write_buf.set_double(time_map->last_tempo); - ser_write_buf.set_int32(time_map->last_tempo_flag); - ser_write_buf.set_int32(time_map->beats.len); - for (i = 0; i < time_map->beats.len; i++) { - ser_write_buf.set_double(time_map->beats[i].time); - ser_write_buf.set_double(time_map->beats[i].beat); - } - ser_write_buf.set_int32(time_sig.length()); - ser_write_buf.pad(); - for (i = 0; i < time_sig.length(); i++) { - ser_write_buf.set_double(time_sig[i].beat); - ser_write_buf.set_double(time_sig[i].num); - ser_write_buf.set_double(time_sig[i].den); - } - ser_write_buf.set_int32(tracks()); - ser_write_buf.pad(); - for (i = 0; i < tracks(); i++) { - track(i)->serialize_track(); - } - // do not include ALGS, include padding at end - ser_write_buf.store_long(length_offset, ser_write_buf.get_posn() - length_offset); -} - - -void Alg_track::serialize_track() -{ - // to simplify the code, copy from parameter addresses to locals - int j; - ser_write_buf.check_buffer(32); - ser_write_buf.set_char('A'); - ser_write_buf.set_char('L'); - ser_write_buf.set_char('G'); - ser_write_buf.set_char('T'); - long length_offset = ser_write_buf.get_posn(); // save location for track length - ser_write_buf.set_int32(0); // room to write track length - ser_write_buf.set_int32(units_are_seconds); - ser_write_buf.set_double(beat_dur); - ser_write_buf.set_double(real_dur); - ser_write_buf.set_int32(len); - for (j = 0; j < len; j++) { - ser_write_buf.check_buffer(24); - Alg_event *event = (*this)[j]; - ser_write_buf.set_int32(event->get_selected()); - ser_write_buf.set_int32(event->get_type()); - ser_write_buf.set_int32(event->get_identifier()); - ser_write_buf.set_int32(event->chan); - ser_write_buf.set_double(event->time); - if (event->is_note()) { - ser_write_buf.check_buffer(20); - Alg_note *note = (Alg_note *) event; - ser_write_buf.set_float(note->pitch); - ser_write_buf.set_float(note->loud); - ser_write_buf.set_double(note->dur); - long parm_num_offset = ser_write_buf.get_posn(); - long parm_num = 0; - ser_write_buf.set_int32(0); // placeholder for no. parameters - Alg_parameters_ptr parms = note->parameters; - while (parms) { - serialize_parameter(&(parms->parm)); - parms = parms->next; - parm_num++; - } - ser_write_buf.store_long(parm_num_offset, parm_num); - } else { - assert(event->is_update()); - Alg_update *update = (Alg_update *) event; - serialize_parameter(&(update->parameter)); - } - ser_write_buf.check_buffer(7); // maximum padding possible - ser_write_buf.pad(); - } - // write length, not including ALGT, including padding at end - ser_write_buf.store_long(length_offset, ser_write_buf.get_posn() - length_offset); -} - - -void Alg_track::serialize_parameter(Alg_parameter *parm) -{ - // add eight to account for name + zero end-of-string and the - // possibility of adding 7 padding bytes - long len = strlen(parm->attr_name()) + 8; - ser_write_buf.check_buffer(len); - ser_write_buf.set_string(parm->attr_name()); - ser_write_buf.pad(); - switch (parm->attr_type()) { - case 'r': - ser_write_buf.check_buffer(8); - ser_write_buf.set_double(parm->r); - break; - case 's': - ser_write_buf.check_buffer(strlen(parm->s) + 1); - ser_write_buf.set_string(parm->s); - break; - case 'i': - ser_write_buf.check_buffer(4); - ser_write_buf.set_int32(parm->i); - break; - case 'l': - ser_write_buf.check_buffer(4); - ser_write_buf.set_int32(parm->l); - break; - case 'a': - ser_write_buf.check_buffer(strlen(parm->a) + 1); - ser_write_buf.set_string(parm->a); - break; - } -} - - - -Alg_track *Alg_track::unserialize(void *buffer, long len) -{ - assert(len > 8); - ser_read_buf.init_for_read(buffer, len); - bool alg = ser_read_buf.get_char() == 'A' && - ser_read_buf.get_char() == 'L' && - ser_read_buf.get_char() == 'G'; - assert(alg); (void)alg; // unused variable - char c = ser_read_buf.get_char(); - if (c == 'S') { - Alg_seq *seq = new Alg_seq; - ser_read_buf.unget_chars(4); // undo get_char() of A,L,G,S - seq->unserialize_seq(); - return seq; - } else { - assert(c == 'T'); - Alg_track *track = new Alg_track; - ser_read_buf.unget_chars(4); // undo get_char() of A,L,G,T - track->unserialize_track(); - return track; - } -} - - -//#pragma warning(disable: 4800) // long to bool performance warning - -/* Note: this Alg_seq must have a default initialized Alg_time_map. - * It will be filled in with data from the ser_read_buf buffer. - */ -void Alg_seq::unserialize_seq() -{ - ser_read_buf.check_input_buffer(48); - bool algs = (ser_read_buf.get_char() == 'A') && - (ser_read_buf.get_char() == 'L') && - (ser_read_buf.get_char() == 'G') && - (ser_read_buf.get_char() == 'S'); - assert(algs); (void)algs; // unused variable - long len = ser_read_buf.get_int32(); - assert(ser_read_buf.get_len() >= len); (void)len; // unused variable - channel_offset_per_track = ser_read_buf.get_int32(); - units_are_seconds = ser_read_buf.get_int32() != 0; - beat_dur = ser_read_buf.get_double(); - real_dur = ser_read_buf.get_double(); - // no need to allocate an Alg_time_map since it's done during initialization - time_map->last_tempo = ser_read_buf.get_double(); - time_map->last_tempo_flag = ser_read_buf.get_int32() != 0; - long beats = ser_read_buf.get_int32(); - ser_read_buf.check_input_buffer(beats * 16 + 4); - int i; - for (i = 0; i < beats; i++) { - double time = ser_read_buf.get_double(); - double beat = ser_read_buf.get_double(); - time_map->insert_beat(time, beat); - // printf("time_map: %g, %g\n", time, beat); - } - long time_sig_len = ser_read_buf.get_int32(); - ser_read_buf.get_pad(); - ser_read_buf.check_input_buffer(time_sig_len * 24 + 8); - for (i = 0; i < time_sig_len; i++) { - double beat = ser_read_buf.get_double(); - double num = ser_read_buf.get_double(); - double den = ser_read_buf.get_double(); - time_sig.insert(beat, num, den); - } - long tracks_num = ser_read_buf.get_int32(); - ser_read_buf.get_pad(); - add_track(tracks_num - 1); // create tracks_num tracks - for (i = 0; i < tracks_num; i++) { - track(i)->unserialize_track(); - } - // assume seq started at beginning of buffer. len measures - // bytes after 'ALGS' header, so add 4 bytes and compare to - // current buffer position -- they should agree - assert(ser_read_buf.get_posn() == len + 4); -} - - -void Alg_track::unserialize_track() -{ - ser_read_buf.check_input_buffer(32); - bool algt = (ser_read_buf.get_char() == 'A') && - (ser_read_buf.get_char() == 'L') && - (ser_read_buf.get_char() == 'G') && - (ser_read_buf.get_char() == 'T'); - assert(algt); (void)algt; // unused variable - long offset = ser_read_buf.get_posn(); // stored length does not include 'ALGT' - long bytes = ser_read_buf.get_int32(); - assert(bytes <= ser_read_buf.get_len() - offset); (void)offset; (void)bytes; // unused variable - units_are_seconds = (bool) ser_read_buf.get_int32(); - beat_dur = ser_read_buf.get_double(); - real_dur = ser_read_buf.get_double(); - int event_count = ser_read_buf.get_int32(); - for (int i = 0; i < event_count; i++) { - ser_read_buf.check_input_buffer(24); - long selected = ser_read_buf.get_int32(); - char type = (char) ser_read_buf.get_int32(); - long key = ser_read_buf.get_int32(); - long channel = ser_read_buf.get_int32(); - double time = ser_read_buf.get_double(); - if (type == 'n') { - ser_read_buf.check_input_buffer(20); - float pitch = ser_read_buf.get_float(); - float loud = ser_read_buf.get_float(); - double dur = ser_read_buf.get_double(); - Alg_note *note = - create_note(time, channel, key, pitch, loud, dur); - note->set_selected(selected != 0); - long param_num = ser_read_buf.get_int32(); - int j; - // this builds a list of parameters in the correct order - // (although order shouldn't matter) - Alg_parameters_ptr *list = ¬e->parameters; - for (j = 0; j < param_num; j++) { - *list = new Alg_parameters(nullptr); - unserialize_parameter(&((*list)->parm)); - list = &((*list)->next); - } - append(note); - } else { - assert(type == 'u'); - Alg_update *update = create_update(time, channel, key); - update->set_selected(selected != 0); - unserialize_parameter(&(update->parameter)); - append(update); - } - ser_read_buf.get_pad(); - } - assert(offset + bytes == ser_read_buf.get_posn()); -} - - -void Alg_track::unserialize_parameter(Alg_parameter_ptr parm_ptr) -{ - Alg_attribute attr = ser_read_buf.get_string(); - parm_ptr->attr = symbol_table.insert_string(attr); - switch (parm_ptr->attr_type()) { - case 'r': - ser_read_buf.check_input_buffer(8); - parm_ptr->r = ser_read_buf.get_double(); - break; - case 's': - parm_ptr->s = heapify(ser_read_buf.get_string()); - break; - case 'i': - ser_read_buf.check_input_buffer(4); - parm_ptr->i = ser_read_buf.get_int32(); - break; - case 'l': - ser_read_buf.check_input_buffer(4); - parm_ptr->l = ser_read_buf.get_int32() != 0; - break; - case 'a': - parm_ptr->a = symbol_table.insert_attribute(ser_read_buf.get_string()); - break; - } -} - -//#pragma warning(default: 4800) - -void Alg_track::set_time_map(Alg_time_map *map) -{ - if (time_map) time_map->dereference(); - if (map == nullptr) { - time_map = new Alg_time_map(); // new default map - time_map->reference(); - } else { - time_map = map; - time_map->reference(); - } -} - - -void Alg_track::convert_to_beats() -// modify all times and durations in notes to beats -{ - if (units_are_seconds) { - units_are_seconds = false; - long i; - - for (i = 0; i < length(); i++) { - Alg_event_ptr e = events[i]; - double beat = time_map->time_to_beat(e->time); - if (e->is_note()) { - Alg_note_ptr n = (Alg_note_ptr) e; - n->dur = time_map->time_to_beat(n->time + n->dur) - beat; - } - e->time = beat; - } - } -} - - -void Alg_track::convert_to_seconds() -// modify all times and durations in notes to seconds -{ - if (!units_are_seconds) { - last_note_off = time_map->beat_to_time(last_note_off); - units_are_seconds = true; - long i; - for (i = 0; i < length(); i++) { - Alg_event_ptr e = events[i]; - double time = time_map->beat_to_time(e->time); - if (e->is_note()) { - Alg_note_ptr n = (Alg_note_ptr) e; - n->dur = time_map->beat_to_time(n->time + n->dur) - time; - } - e->time = time; - } - } -} - - -void Alg_track::set_dur(double duration) -{ - // set beat_dur and real_dur - if (units_are_seconds) { - set_real_dur(duration); - set_beat_dur(time_map->time_to_beat(duration)); - } else { - set_beat_dur(duration); - set_real_dur(time_map->beat_to_time(duration)); - } -} - - -Alg_note *Alg_track::create_note(double time, int channel, int identifier, - float pitch, float loudness, double duration) -{ - Alg_note *note = new Alg_note(); - note->time = time; - note->chan = channel; - note->set_identifier(identifier); - note->pitch = pitch; - note->loud = loudness; - note->dur = duration; - return note; -} - - -Alg_update *Alg_track::create_update(double time, int channel, int identifier) -{ - Alg_update *update = new Alg_update(); - update->time = time; - update->chan = channel; - update->set_identifier(identifier); - return update; -} - - -Alg_track_ptr Alg_track::cut(double t, double len, bool all) -{ - // since we are translating notes in time, do not copy or use old timemap - Alg_track_ptr track = new Alg_track(); - track->units_are_seconds = units_are_seconds; - if (units_are_seconds) { - track->set_real_dur(len); - track->set_beat_dur(time_map->time_to_beat(t + len) - - time_map->time_to_beat(t)); - } else { - track->set_beat_dur(len); - track->set_real_dur(time_map->beat_to_time(t + len) - - time_map->beat_to_time(t)); - } - int i; - int new_len = 0; - int change = 0; - for (i = 0; i < length(); i++) { - Alg_event_ptr event = events[i]; - if (event->overlap(t, len, all)) { - event->time -= t; - track->append(event); - change = 1; - } else { // if we're not cutting this event, move it to - // eliminate the gaps in events left by cut events - events[new_len] = event; - // adjust times of events after t + len - if (event->time > t + len - ALG_EPS) { - event->time -= len; - change = 1; - } - new_len++; - } - } - // Alg_event_lists based on this track become invalid - sequence_number += change; - this->len = new_len; // adjust length since we removed events - return track; -} - - -Alg_track_ptr Alg_track::copy(double t, double len, bool all) -{ - // since we are translating notes in time, do not copy or use old timemap - Alg_track_ptr track = new Alg_track(); - track->units_are_seconds = units_are_seconds; - if (units_are_seconds) { - track->set_real_dur(len); - track->set_beat_dur(time_map->time_to_beat(t + len) - - time_map->time_to_beat(t)); - } else { - track->set_beat_dur(len); - track->set_real_dur(time_map->beat_to_time(t + len) - - time_map->beat_to_time(t)); - } - int i; - for (i = 0; i < length(); i++) { - Alg_event_ptr event = events[i]; - if (event->overlap(t, len, all)) { - Alg_event_ptr new_event = copy_event(event); - new_event->time -= t; - track->append(new_event); - } - } - return track; -} - - -void Alg_track::paste(double t, Alg_event_list *seq) -{ - assert(get_type() == 't'); - // seq can be an Alg_event_list, an Alg_track, or an Alg_seq - // if it is an Alg_event_list, units_are_seconds must match - bool prev_units_are_seconds; - if (seq->get_type() == 'e') { - assert(seq->get_owner()->get_units_are_seconds() == units_are_seconds); - prev_units_are_seconds = seq->get_owner()->get_units_are_seconds(); - } else { // make it match - Alg_track_ptr tr = (Alg_track_ptr) seq; - prev_units_are_seconds = tr->get_units_are_seconds(); - if (units_are_seconds) tr->convert_to_seconds(); - else tr->convert_to_beats(); - } - double dur = (units_are_seconds ? seq->get_real_dur() : - seq->get_beat_dur()); - - // Note: in the worst case, seq may contain notes - // that start almost anytime up to it's duration, - // so the simplest algorithm is simply a sequence - // of inserts. If this turns out to be too slow, - // we can do a merge sort in the case that seq - // is an Alg_track (if it's an Alg_event_list, we - // are not guaranteed that the events are in time - // order, but currently, only a true seq is allowed) - - int i; - for (i = 0; i < length(); i++) { - if (events[i]->time > t - ALG_EPS) { - events[i]->time += dur; - } - } - for (i = 0; i < seq->length(); i++) { - Alg_event *new_event = copy_event((*seq)[i]); - new_event->time += t; - insert(new_event); - } - // restore track units to what they were before - if (seq->get_type() != 'e') { - Alg_track_ptr tr = (Alg_track_ptr) seq; - if (prev_units_are_seconds) tr->convert_to_seconds(); - else tr->convert_to_beats(); - } - -} - - -void Alg_track::merge(double t, Alg_event_list_ptr seq) -{ - Alg_event_list_ref s = *seq; - for (int i = 0; i < s.length(); i++) { - Alg_event *new_event; - if (s[i]->is_note()) { - new_event = new Alg_note((Alg_note_ptr) s[i]); - } else { - new_event = new Alg_update((Alg_update_ptr) s[i]); - } - new_event->time += t; - insert(new_event); - } -} - - -void Alg_track::clear(double t, double len, bool all) -{ - int i; - int move_to = 0; - for (i = 0; i < length(); i++) { - Alg_event_ptr event = events[i]; - if (event->overlap(t, len, all)) { - delete events[i]; - } else { // if we're not clearing this event, move it to - // eliminate the gaps in events left by cleared events - events[move_to] = event; - // adjust times of events after t + len. This test is based - // on the one in Alg_event::overlap() for consistency. - if (event->time > t + len - ALG_EPS && event->time > t) - event->time -= len; - move_to++; - } - } - if (move_to != this->len) { // we cleared at least one note - sequence_number++; // Alg_event_lists based on this track become invalid - } - this->len = move_to; // adjust length since we removed events -} - - -void Alg_track::silence(double t, double len, bool all) -{ - int i; - int move_to = 0; - for (i = 0; i < length(); i++) { - Alg_event_ptr event = events[i]; - if (event->overlap(t, len, all)) { - delete events[i]; - } else { // if we're not clearing this event, move it to - // eliminate the gaps in events left by cleared events - events[move_to] = event; - move_to++; - } - } - if (move_to != this->len) { // we cleared at least one note - sequence_number++; // Alg_event_lists based on this track become invalid - } - this->len = move_to; // adjust length since we removed events -} - - -void Alg_track::insert_silence(double t, double len) -{ - int i; - for (i = 0; i < length(); i++) { - Alg_event_ptr event = events[i]; - if (event->time > t - ALG_EPS) event->time += len; - } -} - - -Alg_event_list *Alg_track::find(double t, double len, bool all, - long channel_mask, long event_type_mask) -{ - int i; - Alg_event_list *list = new Alg_event_list(this); - if (units_are_seconds) { // t and len are seconds - list->set_real_dur(len); - list->set_beat_dur(get_time_map()->time_to_beat(t + len) - - get_time_map()->time_to_beat(t)); - } else { // t and len are beats - list->set_real_dur(get_time_map()->beat_to_time(t + len) - - get_time_map()->beat_to_time(t)); - list->set_beat_dur(len); - } - for (i = 0; i < length(); i++) { - Alg_event_ptr event = events[i]; - if (event->overlap(t, len, all)) { - if ((channel_mask == 0 || - (event->chan < 32 && - (channel_mask & (1 << event->chan)))) && - ((event_type_mask == 0 || - (event_type_mask & (1 << event->get_type_code()))))) { - list->append(event); - } - } - } - return list; -} - - -void Alg_time_sigs::expand() -{ - assert(maxlen >= len); - maxlen = (maxlen + 5); // extra growth for small sizes - maxlen += (maxlen >> 2); // add 25% - Alg_time_sig_ptr new_time_sigs = new Alg_time_sig[maxlen]; - // now do copy - memcpy(new_time_sigs, time_sigs, len * sizeof(Alg_time_sig)); - if (time_sigs) - delete[] time_sigs; - time_sigs = new_time_sigs; -} - - -void Alg_time_sigs::insert(double beat, double num, double den, bool force) -{ - // find insertion point: - for (int i = 0; i < len; i++) { - if (within(time_sigs[i].beat, beat, ALG_EPS)) { - // overwrite location i with new info - time_sigs[i].beat = beat; - time_sigs[i].num = num; - time_sigs[i].den = den; - return; - } else if (time_sigs[i].beat > beat) { - if ((i > 0 && // check if redundant with prev. time sig - time_sigs[i - 1].num == num && - time_sigs[i - 1].den == den && - within(fmod(beat - time_sigs[i - 1].beat, - 4 * time_sigs[i-1].num / time_sigs[i-1].den), - 0, ALG_EPS)) || - // check if redundant with implied initial 4/4 time sig: - (i == 0 && num == 4 && den == 4 && - within(fmod(beat, 4), 0, ALG_EPS))) { - if (!force) return; // redundant inserts can be ignored here - } - // make room for new event - if (maxlen <= len) expand(); - // insert new event at i - memmove(&time_sigs[i + 1], &time_sigs[i], - sizeof(Alg_time_sig) * (len - i)); - time_sigs[i].beat = beat; - time_sigs[i].num = num; - time_sigs[i].den = den; - len++; - return; - } - } - // if we fall out of loop, then this goes at end - if (maxlen <= len) expand(); - time_sigs[len].beat = beat; - time_sigs[len].num = num; - time_sigs[len].den = den; - len++; -} - - -void Alg_time_sigs::show() -{ - printf("Alg_time_sig: "); - for (int i = 0; i < len; i++) { - printf("(%g: %g/%g) ", time_sigs[i].beat, time_sigs[i].num, time_sigs[i].den); - } - printf("\n"); -} - - -int Alg_time_sigs::find_beat(double beat) -{ - // index where you would insert a new time signature at beat - int i = 0; - while (i < len && time_sigs[i].beat < beat - ALG_EPS) i++; - return i; -} - - -double Alg_time_sigs::get_bar_len(double beat) -{ - int i = find_beat(beat); - double num = 4.0; - double den = 4.0; - if (i != 0) { - num = time_sigs[i - 1].num; - den = time_sigs[i - 1].den; - } - return 4 * num / den; -} - -void Alg_time_sigs::cut(double start, double end, double dur) -{ - // remove time_sig's from start to end -- these must be - // in beats (not seconds). - // The duration of the whole sequence is dur (beats). - - // If the first bar line after end comes before a time signature - // and does not fall on a bar line, insert a time signature at - // the time of the bar line to retain relative bar line positions - - int i = find_beat(end); - // i is where you would insert a new time sig at beat, - // Case 1: beat coincides with a time sig at i. Time signature - // at beat means that there is a barline at beat, so when beat - // is shifted to start, the relative barline positions are preserved - if (len > 0 && - within(end, time_sigs[i].beat, ALG_EPS)) { - // beat coincides with time signature change, so end is on a barline - /* do nothing */ ; - // Case 2: there is no time signature before end - } else if (i == 0 && (len == 0 || - time_sigs[0].beat > end)) { - // If the next time signature does not fall on a barline, - // then end must not be on a barline, so there is a partial - // measure from end to the next barline. We need - // a time signature there to preserve relative barline - // locations. It may be that the next bar after start is - // due to another time signature, in which case we do not - // need to insert anything. - double measures = end / 4.0; - double imeasures = ROUND(measures); - if (!within(measures, imeasures, ALG_EPS)) { - // start is not on a barline, maybe add one here: - double bar_loc = (int(measures) + 1) * 4.0; - if (bar_loc < dur - ALG_EPS && - (len == 0 || time_sigs[0].beat > bar_loc + ALG_EPS)) { - insert(bar_loc, 4, 4, true); // forced insert - } - } - // This case should never be true because if i == 0, either there - // are no time signatures before beat (Case 2), - // or there is one time signature at beat (Case 1) - } else if (i == 0) { - /* do nothing (might be good to assert(false)) */ ; - // Case 3: i-1 must be the effective time sig position - } else { - // get the time signature in effect at end - Alg_time_sig &tsp = time_sigs[i - 1]; - double beats_per_measure = (tsp.num * 4) / tsp.den; - double measures = (end - tsp.beat) / beats_per_measure; - int imeasures = ROUND(measures); - if (!within(measures, imeasures, ALG_EPS)) { - // end is not on a measure, so we need to insert a time sig - // to force a bar line at the first measure location after - // beat, if any - double bar_loc = tsp.beat + beats_per_measure * (int(measures) + 1); - // insert new time signature at bar_loc - // It will have the same time signature, but the position will - // force a barline to match the barline before the shift - // However, we should not insert a barline if there is a - // time signature earlier than the barline time - if (i < len /* time_sigs[i] is the last one */ && - time_sigs[i].beat < bar_loc - ALG_EPS) { - /* do not insert because there's already a time signature */; - } else if (bar_loc < dur - ALG_EPS) { - insert(bar_loc, tsp.num, tsp.den, true); // forced insert - } - } - // else beat coincides with a barline, so no need for an extra - // time signature to force barline alignment - } - - // Figure out if time signature at start matches - // the time signature at end. If not, we need to insert a - // time signature at end to force the correct time signature - // there. - // Find time signature at start: - double start_num = 4.0; // default if no time signature specified - double start_den = 4.0; - i = find_beat(start); - // A time signature at start would go at index i, so the effective - // time signature prior to start is at i - 1. If i == 0, the default - // time signature is in effect prior to start. - if (i != 0) { - start_num = time_sigs[i - 1].num; - start_den = time_sigs[i - 1].den; - } - // Find the time signature at end: - double end_num = 4.0; // default if no time signature specified - double end_den = 4.0; - int j = find_beat(end); - if (j != 0) { - end_num = time_sigs[j - 1].num; - end_den = time_sigs[j - 1].den; - } - // compare: If meter changes and there is no time signature at end, - // insert a time signature at end - if (end < dur - ALG_EPS && - (start_num != end_num || start_den != end_den) && - (j >= len || !within(time_sigs[j].beat, end, ALG_EPS))) { - insert(end, end_num, end_den, true); - } - - // Remove time signatures from start to end (not including one AT - // end, if there is one there. Be careful with ALG_EPS on that one.) - - // since we may have inserted a time signature, find position again: - int i0 = find_beat(start); - int i1 = i0; - // scan to end of cut region - while (i1 < len && time_sigs[i1].beat < end - ALG_EPS) { - i1++; - } - // scan from end to len(time_sig) - while (i1 < len) { - Alg_time_sig &ts = time_sigs[i1]; - ts.beat -= (end - start); - time_sigs[i0] = ts; - i0++; - i1++; - } - len = i1; -} - - -void Alg_time_sigs::trim(double start, double end) -{ - // remove time_sig's not in [start, end), but retain - // barline positions relative to the notes. This means that - // if the meter (time signature) changes between start and - // end that we need to insert a time signature at start. - // Also, if trim() would cause barlines to move, we need to - // insert a time signature on a barline (timesignatures - // imply the beginning of a bar even if the previous bar - // does not have enough beats. Note that bars do not need - // to have an integer number of beats). - // - // units must be in beats (not seconds) - // - // Uses Alg_time_sigs::cut() to avoid writing a special case - double dur = end + 1000; - if (len > 0) { - dur = time_sigs[len - 1].beat + 1000; - } - cut(end, dur, dur); - cut(0, start, dur); - -#ifdef IGNORE_THIS_OLD_CODE - // first, skip time signatures up to start - int i = find_beat(start); - // i is where you would insert a new time sig at beat, - // Case 1: beat coincides with a time sig at i. Time signature - // at beat means that there is a barline at beat, so when beat - // is shifted to 0, the relative barline positions are preserved - if (len > 0 && - within(start, time_sigs[i].beat, ALG_EPS)) { - // beat coincides with time signature change, so offset must - // be a multiple of beats - /* do nothing */ ; - // Case 2: there is no time signature before start - } else if (i == 0 && (len == 0 || - time_sigs[0].beat > start)) { - // If the next time signature does not fall on a barline, - // then start must not be on a barline, so there is a partial - // measure from start to the next barline. We need - // a time signature there to preserve relative barline - // locations. It may be that the next bar after start is - // due to another time signature, in which case we do not - // need to insert anything. - double measures = start / 4.0; - double imeasures = ROUND(measures); - if (!within(measures, imeasures, ALG_EPS)) { - // start is not on a barline, maybe add one here: - double bar_loc = (int(measures) + 1) * 4.0; - if (len == 0 || time_sigs[1].beat > bar_loc + ALG_EPS) { - insert(bar_loc, 4, 4, true); - } - } - // This case should never be true because if i == 0, either there - // are no time signatures before beat (Case 2), - // or there is one time signature at beat (Case 1) - } else if (i == 0) { - /* do nothing (might be good to assert(false)) */ ; - // Case 3: i-1 must be the effective time sig position - } else { - i -= 1; // index the time signature in effect at start - Alg_time_sig &tsp = time_sigs[i]; - double beats_per_measure = (tsp.num * 4) / tsp.den; - double measures = (start - tsp.beat) / beats_per_measure; - int imeasures = ROUND(measures); - if (!within(measures, imeasures, ALG_EPS)) { - // beat is not on a measure, so we need to insert a time sig - // to force a bar line at the first measure location after - // beat, if any - double bar_loc = tsp.beat + beats_per_measure * (int(measures) + 1); - // insert new time signature at bar_loc - // It will have the same time signature, but the position will - // force a barline to match the barline before the shift - insert(bar_loc, tsp.num, tsp.den, true); - } - // else beat coincides with a barline, so no need for an extra - // time signature to force barline alignment - } - // since we may have inserted a time signature, find position again: - int i_in = find_beat(start); - int i_out = 0; - - // put time_sig at start if necessary - // if 0 < i_in < len, then the time sig at i_in is either - // at start or after start. - // If after start, then insert time sig at i_in-1 at 0. - // Otherwise, we'll pick up time sig at i_in below. - // If 0 == i_in < len, then the time sig at i_in is either - // at start or after start. - // If after start, then time sig at 0 is 4/4, but that's the - // default, so do nothing. - // Otherwise, we'll pick up time sig at i_in below. - // If 0 < i_in == len, then insert time_sig at i_in-1 at start - // If 0 == i_in == len, then 4/4 default applies and we're done. - // - // So the conditions for inserting time_sig[in_i-1] at 0 are: - // (0 < i_in < len and time_sig[i] > start+ALG_EPS) OR - // (0 < i_in == len) - // We can rewrite this to - // (0 < i_in) && ((i_in < len && time_sig[i_in].beat > start + ALG_EPS) || - // (i_in == len))) - // - if (0 < i_in && ((i_in < len && time_sigs[i_in].beat > start + ALG_EPS) || - (i_in == len))) { - time_sigs[0] = time_sigs[i_in - 1]; - time_sigs[0].beat = 0.0; - i_out = 1; - } - // copy from i_in to i_out as we scan time_sig array to end of cut region - while (i_in < len && time_sigs[i_in].beat < end - ALG_EPS) { - Alg_time_sig &ts = time_sigs[i_in]; - ts.beat = ts.beat - start; - time_sigs[i_out] = ts; - i_in++; - i_out++; - } - len = i_out; -#endif -} - - -void Alg_time_sigs::paste(double start, Alg_seq *seq) -{ - // printf("time_sig::insert before paste\n"); - // show(); - Alg_time_sigs &from = seq->time_sig; - // printf("time_sig::insert from\n"); - // from.show(); - // insert time signatures from seq into this time_sigs at start - if (len == 0 && from.len == 0) { - return; // default applies - } - int i = find_beat(start); - // remember the time signature at the splice point - double num_after_splice = 4; - double den_after_splice = 4; // default - double num_before_splice = 4; - double den_before_splice = 4; // default - // this is computed for use in aligning beats after the inserted - // time signatures and duration. It is the position of time signature - // in effect immediately after start (the time signature will be - // before start or at start) - double beat_after_splice = 0.0; - // three cases: - // 1) time sig at splice is at i-1 - // for this, we must have len>0 & i>0 - // two sub-cases: - // A) i < len && time_sig[i].beat > start - // B) i == len - // 2) time_sig at splice is at i - // for this, i < len && time_sig[i].beat ~= start - // 3) time_sig at splice is default 4/4 - if (len > 0 && i > 0 && - ((i < len && time_sigs[i].beat > start + ALG_EPS) || - (i == len))) { - // no time_signature at i - num_after_splice = time_sigs[i-1].num; - den_after_splice = time_sigs[i-1].den; - beat_after_splice = time_sigs[i - 1].beat; - num_before_splice = num_after_splice; - den_before_splice = den_after_splice; - } else if (i < len && time_sigs[i].beat <= start + ALG_EPS) { - // time_signature at i is at "start" beats - num_after_splice = time_sigs[i].num; - den_after_splice = time_sigs[i].den; - beat_after_splice = start; - if (i > 0) { // time signature before start is at i - 1 - num_before_splice = time_sigs[i-1].num; - den_before_splice = time_sigs[i-1].den; - } - } - // i is where insert will go, time_sig[i].beat >= start - // begin by adding duration to time_sig's at i and above - // move time signatures forward by duration of seq - double dur = seq->get_beat_dur(); - while (i < len) { - time_sigs[i].beat += dur; - i++; - } - //printf("time_sig::insert after making space\n"); - //show(); - // If time signature of "from" is not the effective time signature - // at start, insert a time_signature at start. This may create - // an extra measure if seq does not begin on a measure boundary - double num_of_insert = 4.0; - double den_of_insert = 4.0; - double beat_of_insert = 0.0; - /* int first_from_index = 0; // where to start copying from TODO: LMMS commented out unused variable */ - if (from.length() > 0 && from[0].beat < ALG_EPS) { - // there is an initial time signature in "from" - num_of_insert = from[0].num; - den_of_insert = from[0].den; - // since we are handling the first time signature in from, - // we can start copying at index == 1: - /* first_from_index = 1; TODO: LMMS commented out unused variable */ - } - // compare time signatures to see if we need a change at start: - if (num_before_splice != num_of_insert || - den_before_splice != den_of_insert) { - // note that this will overwrite an existing time signature if - // it is within ALG_EPS of start -- this is correct because the - // existing time signature will already be recorded as - // num_after_splice and den_after_splice - insert(start, num_of_insert, den_of_insert); - } - //printf("time_sig::insert after 4/4 at start\n"); - //show(); - // insert time signatures from seq offset by start - for (i = 0; i < from.length() && from[i].beat < dur - ALG_EPS; i++) { - num_of_insert = from[i].num; // keep latest time signature info - den_of_insert = from[i].den; - beat_of_insert = from[i].beat; - insert(start + beat_of_insert, num_of_insert, den_of_insert); - } - //printf("time_sig::insert after pasting in sigs\n"); - //show(); - // now insert time signature at end of splice if necessary - // if the time signature changes, we need to insert a time signature - // immediately: - if (num_of_insert != num_after_splice && - den_of_insert != den_after_splice) { - insert(start + dur, num_after_splice, den_after_splice); - num_of_insert = num_after_splice; - den_of_insert = den_after_splice; - beat_of_insert = start + dur; - } - // if the insert had a partial number of measures, we might need an - // additional time signature to realign the barlines after the insert - // To decide, we compare the beat of the first barline on or after - // start before the splice to the beat of the first barline on or - // after start + dur after the splice. In a sense, this is the "same" - // barline, so it should be shifted exactly by dur. - // First, compute the beat of the first barline on or after start: - double beats_per_measure = (num_after_splice * 4) / den_after_splice; - double measures = (start - beat_after_splice) / beats_per_measure; - // Measures might be slightly negative due to rounding. Use max() - // to eliminate any negative rounding error: - int imeasures = int(MAX(measures, 0.0)); - double old_bar_loc = beat_after_splice + (imeasures * beats_per_measure); - if (old_bar_loc < start) old_bar_loc += beats_per_measure; - // now old_bar_loc is the original first bar position after start - // Do similar calculation for position after end after the insertion: - // beats_per_measure already calculated because signatures match - measures = (start + dur - beat_of_insert) / beats_per_measure; - imeasures = int(MAX(measures, 0.0)); - double new_bar_loc = beat_of_insert + (imeasures * beats_per_measure); - if (new_bar_loc < start + dur) new_bar_loc += beats_per_measure; - // old_bar_loc should be shifted by dur: - old_bar_loc += dur; - // now the two bar locations should be equal, but due to rounding, - // they could be off by one measure - double diff = (new_bar_loc - old_bar_loc) + beats_per_measure; - double diff_in_measures = diff / beats_per_measure; - // if diff_in_measures is not (approximately) integer, we need to - // force a barline (time signature) after start + dur to maintain - // the relationship between barliness and notes - if (!within(diff_in_measures, ROUND(diff_in_measures), ALG_EPS)) { - // recall that old_bar_loc is shifted by dur - insert(old_bar_loc, num_after_splice, den_after_splice); - } - //printf("time_sig::insert after sig at end of splice\n"); - //show(); -} - - -void Alg_time_sigs::insert_beats(double start, double dur) -{ - int i = find_beat(start); - - // time_sigs[i] is after beat and needs to shift - // Compute the time of the first bar at or after beat so that - // a bar can be placed at bar_loc + dur - double tsnum = 4.0; - double tsden = 4.0; - double tsbeat = 0.0; // defaults - - // three cases: - // 1) time sig at splice is at i-1 - // for this, we must have len>0 & i>0 - // two sub-cases: - // A) i < len && time_sig[i].beat > start - // B) i == len - // 2) time_sig at splice is at i - // for this, i < len && time_sig[i].beat ~= start - // 3) time_sig at splice is default 4/4 - if (len > 0 && i > 0 && - ((i < len && time_sigs[i].beat > start + ALG_EPS) || - (i == len))) { - // no time_signature at i - tsnum = time_sigs[i-1].num; - tsden = time_sigs[i-1].den; - tsbeat = time_sigs[i-1].beat; - } else if (i < len && time_sigs[i].beat <= start + ALG_EPS) { - // time_signature at i is at "start" beats - tsnum = time_sigs[i].num; - tsden = time_sigs[i].den; - tsbeat = start; - i++; // we want i to be index of next time signature after start - } - // invariant: i is index of next time signature after start - - // increase beat times from i to len - 1 by dur - for (int j = i; j < len; j++) { - time_sigs[j].beat += dur; - } - - // insert a time signature to maintain bar positions if necessary - double beats_per_measure = (tsnum * 4) / tsden; - double measures = dur / beats_per_measure; // shift distance - int imeasures = ROUND(measures); - if (!within(measures, imeasures, ALG_EPS)) { - // shift is not a whole number of measures, so we may need to insert - // time signature after silence - // compute measures from time signature to next bar after time - measures = (start - tsbeat) / beats_per_measure; - // round up and add to tsbeat to get time of next bar - double bar_loc = tsbeat + beats_per_measure * (int(measures) + 1); - // translate bar_loc by len: - bar_loc += dur; // this is where we want a bar to be, but maybe - // there is a time signature change before bar, in which case we - // should not insert a new time signature - // The next time signature after start is at i if i < len - if (i < len && time_sigs[i].beat < bar_loc) { - /* do not insert */; - } else { - insert(bar_loc, tsnum, tsden); - } - } -} - - -double Alg_time_sigs::nearest_beat(double beat) -{ - int i = find_beat(beat); - // i is where we would insert time signature at beat - // case 1: there is no time signature - if (i == 0 && len == 0) { - return ROUND(beat); - // case 2: beat falls approximately on time signature - } else if (i < len && within(time_sigs[i].beat, beat, ALG_EPS)) { - return time_sigs[i].beat; - // case 3: beat is after no time signature and before one - } else if (i == 0) { - double trial_beat = ROUND(beat); - // it is possible that we rounded up past a time signature - if (trial_beat > time_sigs[0].beat - ALG_EPS) { - return time_sigs[0].beat; - } - return trial_beat; - } - // case 4: beat is after some time signature - double trial_beat = time_sigs[i - 1].beat + - ROUND(beat - time_sigs[i - 1].beat); - // rounding may advance trial_beat past next time signature: - if (i < len && trial_beat > time_sigs[i].beat - ALG_EPS) { - return time_sigs[i].beat; - } - return trial_beat; -} - - -Alg_tracks::~Alg_tracks() -{ - reset(); -} - - -void Alg_tracks::expand_to(int new_max) -{ - maxlen = new_max; - Alg_track_ptr *new_tracks = new Alg_track_ptr[maxlen]; - // now do copy - memcpy(new_tracks, tracks, len * sizeof(Alg_track_ptr)); - if (tracks) { - delete[] tracks; - } - tracks = new_tracks; -} - - -void Alg_tracks::expand() -{ - maxlen = (maxlen + 5); // extra growth for small sizes - maxlen += (maxlen >> 2); // add 25% - expand_to(maxlen); -} - - -void Alg_tracks::append(Alg_track_ptr track) -{ - if (maxlen <= len) { - expand(); - } - tracks[len] = track; - len++; -} - - -void Alg_tracks::add_track(int track_num, Alg_time_map_ptr time_map, - bool seconds) - // Create a new track at index track_num. - // If track already exists, this call does nothing. - // If highest previous track is not at track_num-1, then - // create tracks at len, len+1, ..., track_num. -{ - assert(track_num >= 0); - if (track_num == maxlen) { - // use eponential growth to insert tracks sequentially - expand(); - } else if (track_num > maxlen) { - // grow to exact size for random inserts - expand_to(track_num + 1); - } - if (track_num < len) return; // don't add if already there - while (len <= track_num) { - tracks[len] = new Alg_track(time_map, seconds); - //printf("allocated track at %d (%x, this %x) = %x\n", len, - // &(tracks[len]), this, tracks[len]); - len++; - } -} - - -void Alg_tracks::reset() -{ - // all track events are incorporated into the seq, - // so all we need to delete are the arrays of pointers - for (int i = 0; i < len; i++) { - // printf("deleting track at %d (%x, this %x) = %x\n", i, &(tracks[i]), - // this, tracks[i]); - delete tracks[i]; - } - if (tracks) delete [] tracks; - tracks = nullptr; - len = 0; - maxlen = 0; -} - - -void Alg_tracks::set_in_use(bool flag) -{ - for (int i = 0; i < len; i++) { - tracks[i]->in_use = flag; - } -} - - -void Alg_iterator::expand_to(int new_max) -{ - maxlen = new_max; - Alg_pending_event_ptr new_pending_events = new Alg_pending_event[maxlen]; - // now do copy - memcpy(new_pending_events, pending_events, - len * sizeof(Alg_pending_event)); - if (pending_events) { - delete[] pending_events; - } - pending_events = new_pending_events; -} - - -void Alg_iterator::expand() -{ - maxlen = (maxlen + 5); // extra growth for small sizes - maxlen += (maxlen >> 2); // add 25% - expand_to(maxlen); -} - - -Alg_iterator::~Alg_iterator() -{ - if (pending_events) { - delete[] pending_events; - } -} - - -/* in the heap, the children of N are (N+1)*2 and (N+1)*2-1, so - * the parent of N is (N+1)/2-1. This would be easier if arrays - * were 1-based instead of 0-based - */ -#define HEAP_PARENT(loc) ((((loc) + 1) / 2) - 1) -#define FIRST_CHILD(loc) (((loc) * 2) + 1) - -void Alg_iterator::show() -{ - for (int i = 0; i < len; i++) { - Alg_pending_event_ptr p = &(pending_events[i]); - printf(" %d: %p[%ld]@%g on %d\n", i, p->events, p->index, - p->offset, p->note_on); - } -} - - -bool Alg_iterator::earlier(int i, int j) -// see if event i is earlier than event j -{ - // note-offs are scheduled ALG_EPS early so that if a note-off is - // followed immediately with the same timestamp by a note-on (common - // in MIDI files), the note-off will be scheduled first - - double t_i = pending_events[i].time; - double t_j = pending_events[j].time; - - if (t_i < t_j) return true; - // not sure if this case really exists or this is the best rule, but - // we want to give precedence to note-off events - else if (t_i == t_j && pending_events[j].note_on) return true; - return false; -} - - -void Alg_iterator::insert(Alg_events_ptr events, long index, - bool note_on, void *cookie, double offset) -{ - if (len == maxlen) expand(); - pending_events[len].events = events; - pending_events[len].index = index; - pending_events[len].note_on = note_on; - pending_events[len].cookie = cookie; - pending_events[len].offset = offset; - Alg_event_ptr event = (*events)[index]; - pending_events[len].time = (note_on ? event->time : - event->get_end_time() - ALG_EPS) + offset; - /* BEGIN DEBUG * - printf("insert %p=%p[%d] @ %g\n", event, events, index, - pending_events[len].time); - printf(" is_note %d note_on %d time %g dur %g end_time %g offset %g\n", - event->is_note(), note_on, event->time, event->get_duration(), - event->get_end_time(), offset); - } - * END DEBUG */ - int loc = len; - int loc_parent = HEAP_PARENT(loc); - len++; - // sift up: - while (loc > 0 && - earlier(loc, loc_parent)) { - // swap loc with loc_parent - Alg_pending_event temp = pending_events[loc]; - pending_events[loc] = pending_events[loc_parent]; - pending_events[loc_parent] = temp; - loc = loc_parent; - loc_parent = HEAP_PARENT(loc); - } -} - - -bool Alg_iterator::remove_next(Alg_events_ptr &events, long &index, - bool ¬e_on, void *&cookie, - double &offset, double &time) -{ - if (len == 0) return false; // empty! - events = pending_events[0].events; - index = pending_events[0].index; - note_on = pending_events[0].note_on; - offset = pending_events[0].offset; - cookie = pending_events[0].cookie; - offset = pending_events[0].offset; - time = pending_events[0].time; - len--; - pending_events[0] = pending_events[len]; - // sift down - long loc = 0; - long loc_child = FIRST_CHILD(loc); - while (loc_child < len) { - if (loc_child + 1 < len) { - if (earlier(loc_child + 1, loc_child)) { - loc_child++; - } - } - if (earlier(loc_child, loc)) { - Alg_pending_event temp = pending_events[loc]; - pending_events[loc] = pending_events[loc_child]; - pending_events[loc_child] = temp; - loc = loc_child; - loc_child = FIRST_CHILD(loc); - } else { - loc_child = len; - } - } - // printf("After remove:"); show(); - return true; -} - - -Alg_seq::Alg_seq(const char *filename, bool smf, double *offset_ptr) -{ - basic_initialization(); - ifstream inf(filename, smf ? ios::binary | ios::in : ios::in); - if (inf.fail()) { - error = alg_error_open; - return; - } - if (smf) { - error = alg_smf_read(inf, this); - if (offset_ptr) *offset_ptr = 0.0; - } else { - error = alg_read(inf, this, offset_ptr); - } - inf.close(); -} - - -Alg_seq::Alg_seq(istream &file, bool smf, double *offset_ptr) -{ - basic_initialization(); - if (smf) { - error = alg_smf_read(file, this); - if (offset_ptr) *offset_ptr = 0.0; - } else { - error = alg_read(file, this, offset_ptr); - } -} - -void Alg_seq::seq_from_track(Alg_track_ref tr) -{ - type = 's'; - // copy everything - set_beat_dur(tr.get_beat_dur()); - set_real_dur(tr.get_real_dur()); - // copy time_map - set_time_map(new Alg_time_map(tr.get_time_map())); - units_are_seconds = tr.get_units_are_seconds(); - - if (tr.get_type() == 's') { - Alg_seq_ref s = *(tr.to_alg_seq()); - channel_offset_per_track = s.channel_offset_per_track; - add_track(s.tracks() - 1); - // copy each track - for (int i = 0; i < tracks(); i++) { - Alg_track_ref from_track = *(s.track(i)); - Alg_track_ref to_track = *(track(i)); - to_track.set_beat_dur(from_track.get_beat_dur()); - to_track.set_real_dur(from_track.get_real_dur()); - if (from_track.get_units_are_seconds()) - to_track.convert_to_seconds(); - for (int j = 0; j < from_track.length(); j++) { - Alg_event_ptr event = copy_event(from_track[j]); - to_track.append(event); - } - } - } else if (tr.get_type() == 't') { - add_track(0); - channel_offset_per_track = 0; - Alg_track_ptr to_track = track(0); - to_track->set_beat_dur(tr.get_beat_dur()); - to_track->set_real_dur(tr.get_real_dur()); - for (int j = 0; j < tr.length(); j++) { - Alg_event_ptr event = copy_event(tr[j]); - to_track->append(event); - } - } else { - assert(false); // expected track or sequence - } -} - - -int Alg_seq::tracks() -{ - return track_list.length(); -} - - -Alg_track_ptr Alg_seq::track(int i) -{ - assert(0 <= i && i < track_list.length()); - return &(track_list[i]); -} - -//#pragma warning(disable: 4715) // ok not to return a value here - -Alg_event_ptr const &Alg_seq::operator[](int i) -{ - int ntracks = track_list.length(); - int tr = 0; - while (tr < ntracks) { - Alg_track *a_track = track(tr); - if (a_track && i < a_track->length()) { - return (*a_track)[i]; - } else if (a_track) { - i -= a_track->length(); - } - tr++; - } - throw std::out_of_range{"Alg_seq::operator[] - Index out of range."}; -} -//#pragma warning(default: 4715) - - -void Alg_seq::convert_to_beats() -{ - if (!units_are_seconds) return; - for (int i = 0; i < tracks(); i++) { - track(i)->convert_to_beats(); - } - // note that the Alg_seq inherits units_are_seconds from an - // empty track. Each track also has a (redundant) field called - // units are seconds. These should always be consistent. - units_are_seconds = false; -} - - -void Alg_seq::convert_to_seconds() -{ - if (units_are_seconds) return; - //printf("convert_to_seconds, tracks %d\n", tracks()); - //printf("last_tempo of seq: %g on map %x\n", - // get_time_map()->last_tempo, get_time_map()); - for (int i = 0; i < tracks(); i++) { - //printf("last_tempo of track %d: %g on %x\n", i, - // track(i)->get_time_map()->last_tempo, - // track(i)->get_time_map()); - track(i)->convert_to_seconds(); - } - // update our copy of last_note_off (which may or may not be valid) - last_note_off = time_map->beat_to_time(last_note_off); - // note that the Alg_seq inherits units_are_seconds from an - // empty track. Each track also has a (redundant) field called - // units are seconds. These should always be consistent. - units_are_seconds = true; -} - - -Alg_track_ptr Alg_seq::cut_from_track(int track_num, double start, - double dur, bool all) -{ - assert(track_num >= 0 && track_num < tracks()); - Alg_track_ptr tr = track(track_num); - return tr->cut(start, dur, all); -} - - -void Alg_seq::copy_time_sigs_to(Alg_seq *dest) -{ - // copy time signatures - for (int i = 0; i < time_sig.length(); i++) { - dest->time_sig.insert(time_sig[i].beat, time_sig[i].num, - time_sig[i].den); - } -} - - -void Alg_seq::set_time_map(Alg_time_map *map) -{ - Alg_track::set_time_map(map); - for (int i = 0; i < tracks(); i++) { - track(i)->set_time_map(map); - } -} - - -Alg_seq_ptr Alg_seq::cut(double start, double len, bool all) - // return sequence from start to start+len and modify this - // sequence by removing that time-span -{ - double dur = get_dur(); - // fix parameters to fall within existing sequence - if (start > dur) return nullptr; // nothing to cut - if (start < 0) start = 0; // can't start before sequence starts - if (start + len > dur) // can't cut after end: - len = dur - start; - - Alg_seq_ptr result = new Alg_seq(); - Alg_time_map_ptr map = new Alg_time_map(get_time_map()); - result->set_time_map(map); - copy_time_sigs_to(result); - result->units_are_seconds = units_are_seconds; - result->track_list.reset(); - - for (int i = 0; i < tracks(); i++) { - Alg_track_ptr cut_track = cut_from_track(i, start, len, all); - result->track_list.append(cut_track); - // initially, result->last_note_off is zero. We want to know the - // maximum over all cut_tracks, so compute that here: - result->last_note_off = MAX(result->last_note_off, - cut_track->last_note_off); - // since we're moving to a new sequence, change the track's time_map - result->track_list[i].set_time_map(map); - } - - // put units in beats to match time_sig's. Note that we need - // two different end times. For result, we want the time of the - // last note off, but for cutting out the time signatures in this, - // we use len. - double ts_start = start; - double ts_end = start + len; - double ts_dur = dur; - double ts_last_note_off = start + result->last_note_off; - if (units_are_seconds) { - ts_start = time_map->time_to_beat(ts_start); - ts_end = time_map->time_to_beat(ts_end); - ts_last_note_off = time_map->time_to_beat(ts_last_note_off); - ts_dur = time_map->time_to_beat(ts_dur); - } - // result is shifted from start to 0 and has length len, but - // time_sig and time_map are copies from this. Adjust time_sig, - // time_map, and duration fields in result. The time_sig and - // time_map data is retained out to last_note_off so that we have - // information for the entire duration of all the notes, even though - // this might extend beyond the duration of the track. (Warning: - // no info is retained for notes with negative times.) - result->time_sig.trim(ts_start, ts_last_note_off); - result->time_map->trim(start, start + result->last_note_off, - result->units_are_seconds); - // even though there might be notes sticking out beyond len, the - // track duration is len, not last_note_off. (Warning: if all is - // true, there may also be notes at negative offsets. These times - // cannot be mapped between beat and time representations, so there - // may be subtle bugs or unexpected behaviors in that case.) - result->set_dur(len); - - // we sliced out a portion of each track, so now we need to - // slice out the corresponding sections of time_sig and time_map - // as well as to adjust the duration. - time_sig.cut(ts_start, ts_end, ts_dur); - time_map->cut(start, len, units_are_seconds); - set_dur(dur - len); - - return result; -} - - -void Alg_seq::insert_silence_in_track(int track_num, double t, double len) -{ - Alg_track_ptr tr = track(track_num); - tr->insert_silence(t, len); -} - - -void Alg_seq::insert_silence(double t, double len) -{ - for (int i = 0; i < tracks(); i++) { - insert_silence_in_track(i, t, len); - } - double t_beats = t; - double len_beats = len; - // insert into time_sig array; use time_sig_paste, - // which requires us to build a simple time_sig array - if (units_are_seconds) { - time_map->insert_time(t, len); - t_beats = time_map->time_to_beat(t); - len_beats = time_map->time_to_beat(t + len) - t_beats; - } else { - time_map->insert_beats(t_beats, len_beats); - } - time_sig.insert_beats(t_beats, len_beats); - // Final duration is defined to be t + len + whatever was - // in the sequence after t (if any). This translates to - // t + len + max(dur - t, 0) - set_dur(t + len + MAX(get_dur() - t, 0.0)); -} - - -Alg_track_ptr Alg_seq::copy_track(int track_num, double t, double len, bool all) -{ - return track_list[track_num].copy(t, len, all); -} - - -Alg_seq *Alg_seq::copy(double start, double len, bool all) -{ - // fix parameters to fall within existing sequence - if (start > get_dur()) return nullptr; // nothing to copy - if (start < 0) start = 0; // can't copy before sequence starts - if (start + len > get_dur()) // can't copy after end: - len = get_dur() - start; - - // return (new) sequence from start to start + len - Alg_seq_ptr result = new Alg_seq(); - Alg_time_map_ptr map = new Alg_time_map(get_time_map()); - result->set_time_map(map); - copy_time_sigs_to(result); - result->units_are_seconds = units_are_seconds; - result->track_list.reset(); - - for (int i = 0; i < tracks(); i++) { - Alg_track_ptr copy = copy_track(i, start, len, all); - result->track_list.append(copy); - result->last_note_off = MAX(result->last_note_off, - copy->last_note_off); - // since we're copying to a new seq, change the track's time_map - result->track_list[i].set_time_map(map); - } - - // put units in beats to match time_sig's. Note that we need - // two different end times. For result, we want the time of the - // last note off, but for cutting out the time signatures in this, - // we use len. - double ts_start = start; - double ts_end = start + len; - double ts_last_note_off = start + result->last_note_off; - if (units_are_seconds) { - ts_start = time_map->time_to_beat(ts_start); - ts_end = time_map->time_to_beat(ts_end); - ts_last_note_off = time_map->time_to_beat(ts_last_note_off); - } - - result->time_sig.trim(ts_start, ts_last_note_off); - result->time_map->trim(start, start + result->last_note_off, - units_are_seconds); - result->set_dur(len); - return result; -} - - -void Alg_seq::paste(double start, Alg_seq *seq) -{ - // Insert seq at time, opening up space for it. - // To manipulate time map, we need units as beats. - // Save original form so we can convert back if necessary. - bool units_should_be_seconds = units_are_seconds; - bool seq_units_should_be_seconds = seq->get_units_are_seconds(); - if (units_are_seconds) { - start = time_map->time_to_beat(start); - convert_to_beats(); - } - seq->convert_to_beats(); - - // do a paste on each track - int i; - for (i = 0; i < seq->tracks(); i++) { - if (i >= tracks()) { - add_track(i); - } - track(i)->paste(start, seq->track(i)); - } - // make sure all tracks were opened up for an insert, even if - // there is nothing to insert - while (i < tracks()) { - track(i)->insert_silence(start, seq->get_dur()); - i++; - } - // paste in tempo track - time_map->paste(start, seq); - // paste in time signatures - time_sig.paste(start, seq); - set_dur(get_beat_dur() + seq->get_dur()); - assert(!seq->units_are_seconds && !units_are_seconds); - if (units_should_be_seconds) { - convert_to_seconds(); - } - if (seq_units_should_be_seconds) { - seq->convert_to_seconds(); - } -} - - -void Alg_seq::merge(double t, Alg_event_list_ptr seq) -{ - // seq must be an Alg_seq: - assert(seq->get_type() == 's'); - Alg_seq_ptr s = (Alg_seq_ptr) seq; - for (int i = 0; i < s->tracks(); i++) { - if (tracks() <= i) add_track(i); - track(i)->merge(t, s->track(i)); - } -} - - -void Alg_seq::silence_track(int track_num, double start, double len, bool all) -{ - // remove events in [time, time + len) and close gap - Alg_track_ptr tr = track(track_num); - tr->silence(start, len, all); -} - - -void Alg_seq::silence(double t, double len, bool all) -{ - for (int i = 0; i < tracks(); i++) { - silence_track(i, t, len, all); - } -} - - -void Alg_seq::clear_track(int track_num, double start, double len, bool all) -{ - // remove events in [time, time + len) and close gap - Alg_track_ptr tr = track(track_num); - tr->clear(start, len, all); -} - - -void Alg_seq::clear(double start, double len, bool all) -{ - // Fix parameters to fall within existing sequence - double dur = get_dur(); - if (start > dur) return; // nothing to cut - if (start < 0) start = 0; // can't start before sequence starts - if (start + len > dur) // can't cut after end: - len = dur - start; - - for (int i = 0; i < tracks(); i++) - clear_track(i, start, len, all); - - // Put units in beats to match time_sig's. - double ts_start = start; - double ts_end = start + len; - double ts_dur = dur; - if (units_are_seconds) { - ts_start = time_map->time_to_beat(ts_start); - ts_end = time_map->time_to_beat(ts_end); - ts_dur = time_map->time_to_beat(ts_dur); - } - - // we sliced out a portion of each track, so now we need to - // slice out the corresponding sections of time_sig and time_map - // as well as to adjust the duration. - time_sig.cut(ts_start, ts_end, ts_dur); - time_map->cut(start, len, units_are_seconds); - set_dur(dur - len); -} - - -Alg_event_list_ptr Alg_seq::find_in_track(int track_num, double t, double len, - bool all, long channel_mask, - long event_type_mask) -{ - return track(track_num)->find(t, len, all, channel_mask, event_type_mask); -} - - -Alg_seq::~Alg_seq() -{ - int i, j; - // Tracks does not delete Alg_events elements - for (j = 0; j < track_list.length(); j++) { - Alg_track ¬es = track_list[j]; - // Alg_events does not delete notes - for (i = 0; i < notes.length(); i++) { - Alg_event_ptr event = notes[i]; - delete event; - } - } -} - - -long Alg_seq::seek_time(double time, int track_num) -// find index of first score event after time -{ - long i; - Alg_events ¬es = track_list[track_num]; - for (i = 0; i < notes.length(); i++) { - if (notes[i]->time > time) { - break; - } - } - return i; -} - - -bool Alg_seq::insert_beat(double time, double beat) -// insert a time,beat pair -// return true or false (false indicates an error, no update) -// it is an error to imply a negative tempo or to insert at -// a negative time -{ - if (time < 0 || beat < 0) return false; - if (time == 0.0 && beat > 0) - time = 0.000001; // avoid infinite tempo, offset time by 1us - if (time == 0.0 && beat == 0.0) - return true; // (0,0) is already in the map! - convert_to_beats(); // beats are invariant when changing tempo - time_map->insert_beat(time, beat); - return true; -} - - -// input is time, return value is time -double Alg_seq::nearest_beat_time(double time, double *beat) -{ - double b = time_map->time_to_beat(time); - b = time_sig.nearest_beat(b); - if (beat) *beat = b; - return time_map->beat_to_time(b); -} - - -bool Alg_seq::stretch_region(double b0, double b1, double dur) -{ - bool units_should_be_seconds = units_are_seconds; - convert_to_beats(); - bool result = time_map->stretch_region(b0, b1, dur); - if (units_should_be_seconds) convert_to_seconds(); - return result; -} - - -bool Alg_seq::insert_tempo(double bpm, double beat) -{ - double bps = bpm / 60.0; // convert to beats per second - // change the tempo at the given beat until the next beat event - if (beat < 0) return false; - convert_to_beats(); // beats are invariant when changing tempo - double time = time_map->beat_to_time(beat); - long i = time_map->locate_time(time); - if (i >= time_map->beats.len || !within(time_map->beats[i].time, time, 0.000001)) { - insert_beat(time, beat); - } - // now i is index of beat where tempo will change - if (i == time_map->beats.len - 1) { - time_map->last_tempo = bps; - time_map->last_tempo_flag = true; - } else { // adjust all future beats - // compute the difference in beats - double diff = time_map->beats[i + 1].beat - time_map->beats[i].beat; - // convert beat difference to seconds at new tempo - diff = diff / bps; - // figure out old time difference: - double old_diff = time_map->beats[i + 1].time - time; - // compute difference too - diff = diff - old_diff; - // apply new_diff to score and beats - while (i < time_map->beats.len) { - time_map->beats[i].time = time_map->beats[i].time + diff; - i++; - } - } - return true; -} - - -void Alg_seq::add_event(Alg_event_ptr event, int track_num) - // add_event puts an event in a given track (track_num). - // The track must exist. The time and duration of the - // event are interpreted according to whether the Alg_seq - // is currently in beats or seconds (see convert_to_beats()) -{ - track_list[track_num].insert(event); -/* - if (event->is_note()) { - Alg_note_ptr n = (Alg_note_ptr) event; - trace("note %d at %g for %g\n", n->get_identifier(), n->time, n->dur); - } - */ -} - - -double Alg_seq::get_tempo(double beat) -{ - return time_map->get_tempo(beat); -} - - -bool Alg_seq::set_tempo(double bpm, double start_beat, double end_beat) -// set tempo from start_beat to end_beat -{ - // this is an optimization, the test is repeated in Alg_time_seq::set_tempo() - if (start_beat >= end_beat) return false; - bool units_should_be_seconds = units_are_seconds; - convert_to_beats(); - double dur = get_dur(); - bool result = time_map->set_tempo(bpm, start_beat, end_beat); - // preserve sequence duration in beats when tempo changes - set_dur(dur); - if (units_should_be_seconds) convert_to_seconds(); - return result; -} - - -double Alg_seq::get_bar_len(double beat) -{ - return time_sig.get_bar_len(beat); -} - - -void Alg_seq::set_time_sig(double beat, double num, double den) -{ - time_sig.insert(beat, num, den); -} - - -void Alg_seq::beat_to_measure(double beat, long *measure, double *m_beat, - double *num, double *den) -{ - // return [measure, beat, num, den] - double m = 0; // measure number - double bpm; - int tsx; - bpm = 4; - // assume 4/4 if no time signature - double prev_beat = 0; - double prev_num = 4; - double prev_den = 4; - - if (beat < 0) beat = 0; // negative measures treated as zero - - for (tsx = 0; tsx < time_sig.length(); tsx++) { - if (time_sig[tsx].beat <= beat) { - // round m up to an integer (but allow for a small - // numerical inaccuracy) - m = m + (long) (0.99 + (time_sig[tsx].beat - prev_beat) / bpm); - bpm = time_sig[tsx].num * 4 / time_sig[tsx].den; - prev_beat = time_sig[tsx].beat; - prev_num = time_sig[tsx].num; - prev_den = time_sig[tsx].den; - } else { - m = m + (beat - prev_beat) / bpm; - *measure = (long) m; - *m_beat = (m - *measure) * bpm; - *num = prev_num; - *den = prev_den; - return; - } - } - // if we didn't return yet, compute after last time signature - Alg_time_sig initial(0, 4, 4); - Alg_time_sig &prev = initial; - if (tsx > 0) { // use last time signature - prev = time_sig[time_sig.length() - 1]; - } - bpm = prev.num * 4 / prev.den; - m = m + (beat - prev.beat) / bpm; - *measure = (long) m; - *m_beat = (m - *measure) * bpm; - *num = prev.num; - *den = prev.den; -} - -/* -void Alg_seq::set_events(Alg_event_ptr *events, long len, long max) -{ - convert_to_seconds(); // because notes are in seconds - notes.set_events(events, len, max); -} -*/ - - -void Alg_iterator::begin_seq(Alg_seq_ptr s, void *cookie, double offset) -{ - // keep an array of indexes into tracks - // printf("new pending\n"); - int i; - for (i = 0; i < s->track_list.length(); i++) { - if (s->track_list[i].length() > 0) { - insert(&(s->track_list[i]), 0, true, cookie, offset); - } - } -} - - -Alg_event_ptr Alg_iterator::next(bool *note_on, void **cookie_ptr, - double *offset_ptr, double end_time) - // return the next event in time from any track -{ - bool on; - double when; - if (!remove_next(events_ptr, index, on, cookie, offset, when)) { - return nullptr; - } - if (note_on) *note_on = on; - Alg_event_ptr event = (*events_ptr)[index]; - if (on) { - if (note_off_flag && event->is_note() && - (end_time == 0 || - (*events_ptr)[index]->get_end_time() + offset < end_time)) { - // this was a note-on, so insert pending note-off - insert(events_ptr, index, false, cookie, offset); - } - // for both note-ons and updates, insert next event (at index + 1) - // DO NOT INCREMENT index: it must be preserved for request_note_off() - if (index + 1 < events_ptr->length() && - (end_time == 0 || // zero means ignore end time - // stop iterating when end time is reached - (*events_ptr)[index + 1]->time + offset < end_time)) { - insert(events_ptr, index + 1, true, cookie, offset); - } - } - if (cookie_ptr) *cookie_ptr = cookie; - if (offset_ptr) *offset_ptr = offset; - return event; -} - - -void Alg_iterator::request_note_off() -{ - assert(index >= 0 && index < events_ptr->length()); - insert(events_ptr, index, false, cookie, offset); -} - - -void Alg_iterator::end() -{ -} - - -void Alg_seq::merge_tracks() -{ - long sum = 0; - long i; - for (i = 0; i < track_list.length(); i++) { - sum = sum + track(i)->length(); - } - // preallocate array for efficiency: - Alg_event_ptr *notes = new Alg_event_ptr[sum]; - Alg_iterator iterator(this, false); - iterator.begin(); - long notes_index = 0; - - Alg_event_ptr event; - while ((event = iterator.next())) { - notes[notes_index++] = event; - } - track_list.reset(); // don't need them any more - add_track(0); - track(0)->set_events(notes, sum, sum); - iterator.end(); -} - - -void Alg_seq::set_in_use(bool flag) -{ - Alg_track::set_in_use(flag); - track_list.set_in_use(flag); -} - - -// sr_letter_to_type = {"i": 'Integer', "r": 'Real', "s": 'String', -// "l": 'Logical', "a": 'Symbol'} - - diff --git a/plugins/MidiImport/portsmf/allegro.h b/plugins/MidiImport/portsmf/allegro.h deleted file mode 100644 index 67e922790..000000000 --- a/plugins/MidiImport/portsmf/allegro.h +++ /dev/null @@ -1,1124 +0,0 @@ -// Portsmf (also known as Allegro): -// music representation system, with -// extensible in-memory sequence structure -// upward compatible with MIDI -// implementations in C++ and Serpent -// external, text-based representation -// compatible with Aura -// -// SERIALBUFFER CLASS -// -// The Serial_buffer class is defined to support serialization and -// unserialization. A Serial_buffer is just a block of memory with -// a length and a read/write pointer. When writing, it can expand. -// -// SERIALIZATION -// -// The Alg_track class has static members: -// ser_buf -- a Serial_buffer -// When objects are serialized, they are first written to -// ser_buf, which is expanded whenever necessary. Then, when -// the length is known, new memory is allocated and the data -// is copied to a correctly-sized buffer and returned to caller. -// The "external" (callable from outside the library) -// serialization functions are: -// Alg_track::serialize() -// Alg_seq::serialize() -// The "internal" serialization functions to be called from within -// the library are: -// Alg_track::serialize_track(bool text) -// Alg_seq::serialize_seq(bool text) -// Alg_track::serialize_parameter( -// Alg_parameter *parm, bool text) -// These internal serialize functions append data to ser_buf The text -// flag says to write an ascii representation as opposed to binary. -// -// UNSERIALIZATION: -// -// The Alg_track class has a static member: -// unserialize(char *buffer, long len) -// that will unserialize anything -- an Alg_track or an Alg_seq. -// No other function should be called from outside the library. -// Internal unserialize functions are: -// Alg_seq::unserialize_seq() -// Alg_track::unserialize_track() -// Alg_track::unserialize_parameter(Alg_parameter_ptr parm_ptr) -// Just as serialization uses ser_buf for output, unserialization uses -// unser_buf for reading. unser_buf is another static member of Alg_track. - -#ifndef ALLEGRO_H -#define ALLEGRO_H -#include -#include -#include -#include - -#define ALG_EPS 0.000001 // epsilon -#define ALG_DEFAULT_BPM 100.0 // default tempo - -// are d1 and d2 within epsilon of each other? -bool within(double d1, double d2, double epsilon); - -char *heapify(const char *s); // put a string on the heap - - -// Alg_attribute is an atom in the symbol table -// with the special addition that the last -// character is prefixed to the string; thus, -// the attribute 'tempor' (a real) is stored -// as 'rtempor'. To get the string name, just -// use attribute+1. -typedef const char *Alg_attribute; -#define alg_attr_name(a) ((a) + 1) -#define alg_attr_type(a) (*(a)) - -// Alg_atoms is a symbol table of Alg_attributes and other -// unique strings -class Alg_atoms { -public: - Alg_atoms() { - maxlen = len = 0; - atoms = nullptr; - } - // Note: the code is possibly more correct and faster without the - // following destructor, which will only run after the program takes - // a normal exit. Cleaning up after the program exit slows down the exit, - // and will cause problems if any other destructor tries to reference an - // Alg_atom (which will now be freed). The advantage of this code is - // that Alg_atoms will not be reported as memory leaks by automation - // that doesn't know better. -RBD - virtual ~Alg_atoms() { - for (int i = 0; i < len; i++) { - delete atoms[i]; - } - if (atoms) delete [] atoms; - } - // insert/lookup an atttribute - Alg_attribute insert_attribute(Alg_attribute attr); - // insert/lookup attribute by name (without prefixed type) - Alg_attribute insert_string(const char *name); -private: - long maxlen; - long len; - Alg_attribute *atoms; - - // insert an Attriubute not in table after moving attr to heap - Alg_attribute insert_new(const char *name, char attr_type); - void expand(); // make more space -}; - -extern Alg_atoms symbol_table; - - -// an attribute/value pair. Since Alg_attribute names imply type, -// we try to keep attributes and values packaged together as -// Alg_parameter class -typedef class Alg_parameter { -public: - // This constructor guarantees that an Alg_parameter can be - // deleted safely without further initialization. It does not - // do anything useful, so it is expected that the creator will - // set attr and store a value in the appropriate union field. - Alg_attribute attr; - union { - double r;// real - const char *s; // string - long i; // integer - bool l; // logical - const char *a; // symbol (atom) - }; // anonymous union - - Alg_parameter() { attr = "i"; } - ~Alg_parameter(); - void copy(Alg_parameter *); // copy from another parameter - const char attr_type() { return alg_attr_type(attr); } - const char *attr_name() { return alg_attr_name(attr); } - void set_attr(Alg_attribute a) { attr = a; } - void show(); -} *Alg_parameter_ptr; - - -// a list of attribute/value pairs -typedef class Alg_parameters { -public: - class Alg_parameters *next; - Alg_parameter parm; - - Alg_parameters(Alg_parameters *list) { - next = list; - } - - //~Alg_parameters() { } - - // each of these routines takes address of pointer to the list - // insertion is performed without checking whether or not a - // parameter already exists with this attribute. See find() and - // remove_key() to assist in checking for and removing existing - // parameters. - // Note also that these insert_* methods convert name to an - // attribute. If you have already done the symbol table lookup/insert - // you can do these operations faster (in which case we should add - // another set of functions that take attributes as arguments.) - static void insert_real(Alg_parameters **list, const char *name, double r); - // insert string will copy string to heap - static void insert_string(Alg_parameters **list, const char *name, - const char *s); - static void insert_integer(Alg_parameters **list, const char *name, long i); - static void insert_logical(Alg_parameters **list, const char *name, bool l); - static void insert_atom(Alg_parameters **list, const char *name, - const char *s); - static Alg_parameters *remove_key(Alg_parameters **list, const char *name); - // find an attribute/value pair - Alg_parameter_ptr find(Alg_attribute attr); -} *Alg_parameters_ptr; - - -// these are type codes associated with certain attributes -// see Alg_track::find() where these are bit positions in event_type_mask -#define ALG_NOTE 0 // This is a note, not an update -#define ALG_GATE 1 // "gate" -#define ALG_BEND 2 // "bend" -#define ALG_CONTROL 3 // "control" -#define ALG_PROGRAM 4 // "program" -#define ALG_PRESSURE 5 // "pressure" -#define ALG_KEYSIG 6 // "keysig" -#define ALG_TIMESIG_NUM 7 // "timesig_num" -#define ALG_TIMESIG_DEN 8 // "timesig_den" -#define ALG_OTHER 9 // any other value - -// abstract superclass of Alg_note and Alg_update: -typedef class Alg_event { -protected: - bool selected; - char type; // 'e' event, 'n' note, 'u' update - long key; // note identifier - static const char* description; // static buffer for debugging (in Alg_event) -public: - double time; - long chan; - virtual void show() = 0; - // Note: there is no Alg_event() because Alg_event is an abstract class. - bool is_note() { return (type == 'n'); } // tell whether an Alg_event is a note - bool is_update() { return (type == 'u'); } // tell whether an Alg_event is a parameter update - char get_type() { return type; } // return 'n' for note, 'u' for update - int get_type_code(); // 1 = volume change, 2 = pitch bend, - // 3 = control change, 4 = program change, - // 5 = pressure change, 6 = key signature, - // 7 = time sig numerator, 8 = time sig denominator - bool get_selected() { return selected; } - void set_selected(bool b) { selected = b; } - // Note: notes are identified by a (channel, identifier) pair. - // For midi, the identifier is the key number (pitch). The identifier - // does not have to represent pitch; it's main purpose is to identify - // notes so that they can be named by subsequent update events. - long get_identifier() { return key; } // get MIDI key or note identifier of note or update - void set_identifier(long i) { key = i; } // set the identifier - // In all of these set_ methods, strings are owned by the caller and - // copied as necessary by the callee. For notes, an attribute/value - // pair is added to the parameters list. For updates, the single - // attribute/value parameter pair is overwritten. In all cases, the - // attribute (first argument) must agree in type with the second arg. - // The last letter of the attribute implies the type (see below). - void set_parameter(Alg_parameter_ptr new_parameter); - void set_string_value(const char *attr, const char *value); - void set_real_value(const char *attr, double value); - void set_logical_value(const char *attr, bool value); - void set_integer_value(const char *attr, long value); - void set_atom_value(const char *attr, const char *atom); - - // Some note methods. These fail (via assert()) if this is not a note: - // - float get_pitch();// get pitch in steps -- use this even for MIDI - float get_loud(); // get loudness (MIDI velocity) - // times are in seconds or beats, depending upon the units_are_seconds - // flag in the containing sequence - double get_start_time(); // get start time in seconds or beats - double get_end_time(); // get end time in seconds or beats - double get_duration(); // get duration in seconds or beats - void set_pitch(float); - void set_loud(float); - void set_duration(double); - - // Notes have lists of attribute values. Attributes are converted - // to/from strings in this API to avoid explicit use of Alg_attribute - // types. Attribute names end with a type designation: 's', 'r', 'l', - // 'i', or 'a'. - // - bool has_attribute(const char *attr); // test if note has attribute/value pair - char get_attribute_type(const char *attr); // get the associated type: - // 's' = string, - // 'r' = real (double), 'l' = logical (bool), 'i' = integer (long), - // 'a' = atom (char *), a unique string stored in Alg_seq - // get the string value - const char *get_string_value(const char *attr, const char *value = nullptr); - // get the real value - double get_real_value(const char *attr, double value = 0.0); - // get the logical value - bool get_logical_value(const char *attr, bool value = false); - // get the integer value - long get_integer_value(const char *attr, long value = 0); - // get the atom value - const char *get_atom_value(const char *attr, const char *value = nullptr); - void delete_attribute(const char *attr); // delete an attribute/value pair - // (ignore if no matching attribute/value pair exists) - - // Some attribute/value methods. These fail if this is not an update. - // Attributes are converted to/from strings to avoid explicit use - // of Alg_attribute types. - // - const char *get_attribute(); // get the update's attribute (string) - char get_update_type(); // get the update's type: 's' = string, - // 'r' = real (double), 'l' = logical (bool), 'i' = integer (long), - // 'a' = atom (char *), a unique string stored in Alg_seq - const char *get_string_value(); // get the update's string value - // Notes: Caller does not own the return value. Do not modify. - // Do not use after underlying Alg_seq is modified. - double get_real_value(); // get the update's real value - bool get_logical_value(); // get the update's logical value - long get_integer_value(); // get the update's integer value - const char *get_atom_value(); // get the update's atom value - // Notes: Caller does not own the return value. Do not modify. - // The return value's lifetime is forever. - - // Auxiliary function to aid in editing tracks - // Returns true if the event overlaps the given region - bool overlap(double t, double len, bool all); - - const char *GetDescription(); // computes a text description of this event - // the result is in a static buffer, not thread-safe, just for debugging. - Alg_event() { selected = false; } - virtual ~Alg_event() = default; -} *Alg_event_ptr; - - -typedef class Alg_note : public Alg_event { -public: - ~Alg_note() override; - Alg_note(Alg_note *); // copy constructor - float pitch; // pitch in semitones (69 = A440) - float loud; // dynamic corresponding to MIDI velocity - double dur; // duration in seconds (normally to release point) - Alg_parameters_ptr parameters; // attribute/value pair list - Alg_note() { type = 'n'; parameters = nullptr; } - void show() override; -} *Alg_note_ptr; - - -typedef class Alg_update : public Alg_event { -public: - ~Alg_update() override = default; - Alg_update(Alg_update *); // copy constructor - Alg_parameter parameter; // an update contains one attr/value pair - - - Alg_update() { type = 'u'; } - void show() override; -} *Alg_update_ptr; - - -// a sequence of Alg_event objects -typedef class Alg_events { -private: - long maxlen; - void expand(); -protected: - long len; - Alg_event_ptr *events; // events is array of pointers -public: - // sometimes, it is nice to have the time of the last note-off. - // In the current implementation, - // this field is set by append to indicate the time of the - // last note-off in the current unit, so it should be correct after - // creating a new track and adding notes to it. It is *not* - // updated after uninsert(), so use it with care. - double last_note_off; - // initially false, in_use can be used to mark "do not delete". If an - // Alg_events instance is deleted while "in_use", an assertion will fail. - bool in_use; - virtual int length() { return len; } - Alg_event_ptr &operator[](int i) { - assert(i >= 0 && i < len); - return events[i]; - } - Alg_events() { - maxlen = len = 0; - events = nullptr; - last_note_off = 0; - in_use = false; - } - // destructor deletes the events array, but not the - // events themselves - virtual ~Alg_events(); - void set_events(Alg_event_ptr *e, long l, long m) { - if (events) delete [] events; - events = e; len = l; maxlen = m; } - // for use by Alg_track and Alg_seq - void insert(Alg_event_ptr event); - void append(Alg_event_ptr event); - Alg_event_ptr uninsert(long index); -} *Alg_events_ptr; - -class Alg_track; - -typedef class Alg_event_list : public Alg_events { -protected: - char type; // 'e' Alg_event_list, 't' Alg_track, 's' Alg_seq - static const char *last_error_message; - Alg_track *events_owner; // if this is an Alg_event_list, - // the events are owned by an Alg_track or an Alg_seq - static int sequences; // to keep track of sequence numbers - int sequence_number; // this sequence number is incremented - // whenever an edit is performed on an Alg_track or Alg_seq. - // When an Alg_event_list is created to contain pointers to - // a subset of an Alg_track or Alg_seq (the events_owner), - // the Alg_event_list gets a copy of the events_owner's - // sequence_number. If the events_owner is edited, the pointers - // in this Alg_event_list will become invalid. This is detected - // (for debugging) as differing sequence_numbers. - - // every event list, track, and seq has a duration. - // Usually the duration is set when the list is constructed, e.g. - // when you extract from 10 to 15 seconds, the duration is 5 secs. - // The duration does not tell you when is the last note-off. - // duration is recorded in both beats and seconds: - double beat_dur; - double real_dur; -public: - // the client should not create one of these, but these are - // returned from various track and seq operations. An - // Alg_event_list "knows" the Alg_track or Alg_seq that "owns" - // the events. All events in an Alg_event_list must belong - // to the same Alg_track or Alg_seq structure. - // When applied to an Alg_seq, events are enumerated track - // by track with increasing indices. This operation is not - // particularly fast on an Alg_seq. - virtual Alg_event_ptr const &operator[](int i); - Alg_event_list() { sequence_number = 0; - beat_dur = 0.0; real_dur = 0.0; events_owner = nullptr; type = 'e'; } - Alg_event_list(Alg_track *owner); - - char get_type() { return type; } - Alg_track *get_owner() { return events_owner; } - - // The destructor does not free events because they are owned - // by a track or seq structure. - ~Alg_event_list() override; - - // Returns the duration of the sequence in beats or seconds - double get_beat_dur() { return beat_dur; } - void set_beat_dur(double d) { beat_dur = d; } - double get_real_dur() { return real_dur; } - void set_real_dur(double d) { real_dur = d; } - - // Events are stored in time order, so when you change the time of - // an event, you must adjust the position. When you call set_start_time - // on an Alg_event_list, the Alg_event_list is not modified, but the - // Alg_track that "owns" the event is modified. If the owner is an - // Alg_seq, this may require searching the seq for the track containing - // the event. This will mean a logN search of every track in the seq - // (but if this turns out to be a problem, we can store each event's - // track owner in the Alg_event_list.) - virtual void set_start_time(Alg_event *event, double); - // get text description of run-time errors detected, clear error - const char *get_last_error_message() { return last_error_message; } - // Implementation hint: keep a sequence number on each Alg_track that is - // incremented anytime there is a structural change. (This behavior is - // inherited by Alg_seq as well.) Copy the sequence number to any - // Alg_event_list object when it is created. Whenever you access an - // Alg_event_list, using operator[], assert that the Alg_event_list sequence - // number matches the Alg_seq sequence number. This will guarantee that you - // do not try to retain pointers to events beyond the point where the events - // may no longer exist. -} *Alg_event_list_ptr, &Alg_event_list_ref; - - -// Alg_beat is used to contruct a tempo map -typedef class Alg_beat { -public: - Alg_beat(double t, double b) { - time = t; beat = b; } - Alg_beat() = default; - double time; - double beat; -} *Alg_beat_ptr; - - -// Alg_beats is a list of Alg_beat objects used in Alg_seq -typedef class Alg_beats { -private: - long maxlen; - void expand(); -public: - long len; - Alg_beat_ptr beats; - Alg_beat &operator[](int i) { - assert(i >= 0 && i < len); - return beats[i]; - } - Alg_beats() { - maxlen = len = 0; - beats = nullptr; - expand(); - beats[0].time = 0; - beats[0].beat = 0; - len = 1; - } - ~Alg_beats() { - if (beats) delete[] beats; - } - void insert(long i, Alg_beat_ptr beat); -} *Alg_beats_ptr; - - -typedef class Alg_time_map { -private: - int refcount; -public: - Alg_beats beats; // array of Alg_beat - double last_tempo; - bool last_tempo_flag; - Alg_time_map() { - last_tempo = ALG_DEFAULT_BPM / 60.0; // note: this value ignored until - // last_tempo_flag is set; nevertheless, the default - // tempo is 100. - last_tempo_flag = true; - refcount = 0; - } - Alg_time_map(Alg_time_map *map); // copy constructor - long length() { return beats.len; } - void show(); - long locate_time(double time); - long locate_beat(double beat); - double beat_to_time(double beat); - double time_to_beat(double time); - // Time map manipulations: it is prefered to call the corresponding - // methods in Alg_seq. If you manipulate an Alg_time_map directly, - // you should take care to convert all tracks that use the time map - // to beats or seconds as appropriate: Normally if you insert a beat - // you want tracks to be in time units and if you insert a tempo change - // you want tracks to be in beat units. - void insert_beat(double time, double beat); // add a point to the map - bool insert_tempo(double tempo, double beat); // insert a tempo change - // get the tempo starting at beat - double get_tempo(double beat); - // set the tempo over a region - bool set_tempo(double tempo, double start_beat, double end_beat); - bool stretch_region(double b0, double b1, double dur); - void cut(double start, double len, bool units_are_seconds); - void trim(double start, double end, bool units_are_seconds); - void paste(double start, Alg_track *tr); - // insert a span of time. If start is at a tempo change, then - // the span of time runs at the changed tempo - void insert_time(double start, double len); - // insert a span of beats. If start is at a tempo change, the - // tempo change takes effect before the inserted beats - void insert_beats(double start, double len); - void dereference() { - if (--refcount <= 0) delete this; - } - void reference() { - refcount++; - } -} *Alg_time_map_ptr; - - -// Serial_buffer is an abstract class with common elements of -// Serial_read_buffer and Serial_write_buffer -class Serial_buffer { - protected: - char *buffer; - char *ptr; - long len; - public: - Serial_buffer() { - buffer = nullptr; - ptr = nullptr; - len = 0; - } - virtual ~Serial_buffer() = default; - - long get_posn() { return (long) (ptr - buffer); } - long get_len() { return len; } -}; - - -typedef class Serial_read_buffer : public Serial_buffer { -public: - // note that a Serial_read_buffer is initialized for reading by - // setting buffer, but it is not the Serial_read_buffer's responsibility - // to delete the buffer (owner might want to reuse it), so the destructor - // does nothing. - ~Serial_read_buffer() override = default; -#if defined(_WIN32) -//#pragma warning(disable: 546) // cast to int is OK, we only want low 7 bits -//#pragma warning(disable: 4311) // type cast pointer to long warning -#endif - void get_pad() { while ((intptr_t) ptr & 7) ptr++; } -#if defined(_WIN32) -//#pragma warning(default: 4311 546) -#endif - // Prepare to read n bytes from buf. The caller must manage buf: it is - // valid until reading is finished, and it is caller's responsibility - // to free buf when it is no longer needed. - void init_for_read(void *buf, long n) { - buffer = (char *) buf; - ptr = (char *) buf; - len = n; - } - char get_char() { return *ptr++; } - void unget_chars(int n) { ptr -= n; } // undo n get_char() calls - long get_int32() { long i = *((long *) ptr); ptr += 4; return i; } - float get_float() { float f = *((float *) ptr); ptr += 4; return f; } - double get_double() { double d = *((double *) ptr); ptr += sizeof(double); - return d; } - const char *get_string() { char *s = ptr; char *fence = buffer + len; - assert(ptr < fence); (void)fence; // unused variable - while (*ptr++) assert(ptr < fence); - get_pad(); - return s; } - void check_input_buffer(long needed) { - assert(get_posn() + needed <= len); } -} *Serial_read_buffer_ptr; - - -typedef class Serial_write_buffer: public Serial_buffer { - public: - // Note: allegro.cpp declares one static instance of Serial_buffer to - // reduce large memory (re)allocations when serializing tracks for UNDO. - // This destructor will only run when the program exits, which will only - // add overhead to the exit process, but it will eliminate an incorrect - // report of memory leakage from automation that doesn't know better. -RBD - ~Serial_write_buffer() override { - if (buffer) delete [] buffer; - } - void init_for_write() { ptr = buffer; } - // store_long writes a long at a given offset - void store_long(long offset, long value) { - assert(offset <= get_posn() - 4); - long *loc = (long *) (buffer + offset); - *loc = value; - } - void check_buffer(long needed); - void set_string(const char *s) { - char *fence = buffer + len; - assert(ptr < fence); (void)fence; // unused variable - // two brackets surpress a g++ warning, because this is an - // assignment operator inside a test. - while ((*ptr++ = *s++)) assert(ptr < fence); - // 4311 is type cast pointer to long warning - // 4312 is type cast long to pointer warning -#if defined(_WIN32) -//#pragma warning(disable: 4311 4312) -#endif - assert((char *)(((long long) (ptr + 7)) & ~7) <= fence); -#if defined(_WIN32) -//#pragma warning(default: 4311 4312) -#endif - pad(); } - void set_int32(long v) { *((long *) ptr) = v; ptr += 4; } - void set_double(double v) { *((double *) ptr) = v; ptr += 8; } - void set_float(float v) { *((float *) ptr) = v; ptr += 4; } - void set_char(char v) { *ptr++ = v; } -#if defined(_WIN32) -//#pragma warning(disable: 546) // cast to int is OK, we only want low 7 bits -//#pragma warning(disable: 4311) // type cast pointer to long warning -#endif - void pad() { while ((intptr_t) ptr & 7) set_char(0); } -#if defined(_WIN32) -//#pragma warning(default: 4311 546) -#endif - void *to_heap(long *len) { - *len = get_posn(); - char *newbuf = new char[*len]; - memcpy(newbuf, buffer, *len); - return newbuf; - } -} *Serial_write_buffer_ptr; - -typedef class Alg_seq *Alg_seq_ptr; - -typedef class Alg_track : public Alg_event_list { -protected: - Alg_time_map *time_map; - bool units_are_seconds; - char *get_string(char **p, long *b); - long get_int32(char **p, long *b); - double get_double(char **p, long *b); - float get_float(char **p, long *b); - static Serial_read_buffer ser_read_buf; - static Serial_write_buffer ser_write_buf; - void serialize_parameter(Alg_parameter *parm); - // *buffer_ptr points to binary data, bytes_ptr points to how many - // bytes have been used so far, len is length of binary data - void unserialize_parameter(Alg_parameter_ptr parm_ptr); -public: - void serialize_track(); - void unserialize_track(); - Alg_event_ptr const &operator[](int i) override { - assert(i >= 0 && i < len); - return events[i]; - } - Alg_track() { units_are_seconds = false; time_map = nullptr; - set_time_map(nullptr); type = 't'; } - // initialize empty track with a time map - Alg_track(Alg_time_map *map, bool seconds); - - Alg_event_ptr copy_event(Alg_event_ptr event); // make a complete copy - - Alg_track(Alg_track &track); // copy constructor, does not copy time_map - // copy constructor: event_list is copied, map is installed and referenced - Alg_track(Alg_event_list_ref event_list, Alg_time_map_ptr map, - bool units_are_seconds); - ~Alg_track() override { // note: do not call set_time_map(NULL)! - if (time_map) time_map->dereference(); - time_map = nullptr; } - - // Returns a buffer containing a serialization of the - // file. It will be an ASCII representation unless text is true. - // *buffer gets a newly allocated buffer pointer. The caller must free it. - // *len gets the length of the serialized track - virtual void serialize(void **buffer, long *bytes); - - // Try to read from a memory buffer. Automatically guess - // whether it's MIDI or text. - static Alg_track *unserialize(void *buffer, long len); - - // If the track is really an Alg_seq and you need to access an - // Alg_seq method, coerce to an Alg_seq with this function: - Alg_seq_ptr to_alg_seq() { - return (get_type() == 's' ? (Alg_seq_ptr) this : nullptr); } - - // Are we using beats or seconds? - bool get_units_are_seconds() { return units_are_seconds; } - // Change units - virtual void convert_to_beats(); - virtual void convert_to_seconds(); - void set_dur(double dur); - double get_dur() { return (units_are_seconds ? real_dur : beat_dur); } - - // Every Alg_track may have an associated time_map. If no map is - // specified, or if you set_time_map(NULL), then the behavior - // should be as if there is a constant tempo of 100 beats/minute - // (this constant is determined by ALG_DEFAULT_BPM). - // Recommendation: create a static global tempo map object. When - // any operation that needs a tempo map gets NULL, use the global - // tempo map. (Exception: any operation that would modify the - // tempo map should raise an error -- you don't want to change the - // default tempo map.) - virtual void set_time_map(Alg_time_map *map); - Alg_time_map *get_time_map() { return time_map; } - - // Methods to create events. The returned event is owned by the caller. - // Use delete to get rid of it unless you call add() -- see below. - // - Alg_note *create_note(double time, int channel, int identifier, - float pitch, float loudness, double duration); - // Note: after create_update(), caller should use set_*_value() to - // initialize the attribute/value pair: - Alg_update *create_update(double time, int channel, int identifier); - // Adds a new event - it is automatically inserted into the - // correct order in the sequence based on its timestamp. - // The ownership passes from the caller to this Alg_seq. The - // event is not copied. - virtual void add(Alg_event *event) { insert(event); } - - // - // Editing regions - // - - // Deletes the notes that start within the given region - // and returns them in a new sequence. The start times - // of the notes in the returned sequence are shifted - // by -t. The notes after the region get shifted over - // to fill the gap. In an Alg_seq, the tempo track is edited - // in a similar way - // and the cut tempo information is retained in the new seq. - // ONLY NOTES THAT START WITHIN THE REGION ARE CUT unless - // "all" is true in which case all notes that intersect - // the region are copied. CUT NOTES - // MAY EXTEND BEYOND THE DURATION OF THE RESULTING SEQ. - // The return type is the same as this (may be Alg_seq). - // All times including len are interpreted according to - // units_are_seconds in the track. - virtual Alg_track *cut(double t, double len, bool all); - - // Like cut() but doesn't remove the notes from the original - // sequence. The Alg_events are copied, not shared. ONLY EVENTS - // THAT START WITHIN THE REGION ARE COPIED unless "all" is true - // in which case all notes that intersect the region are - // copied. COPIED NOTES MAY - // EXTEND BEYOND THE DURATION OF THE RESULTING SEQ. - // The return type is the same as this (may be Alg_seq). - virtual Alg_track *copy(double t, double len, bool all); - - // Inserts a sequence in the middle, shifting some notes - // over by the duration of the seq, which is first converted - // to the same units (seconds or beats) as this. (This makes - // a differece because the pasted data may change the tempo, - // and notes that overlap the borders will then experience - // a tempo change.) - // THE SEQ PARAMETER IS NOT MODIFIED, AND Alg_event's ARE - // COPIED, NOT SHARED. - // The type of seq must be Alg_seq if seq is an Alg_seq, or - // Alg_track if seq is an Alg_track or an Alg_event_list. - virtual void paste(double t, Alg_event_list *seq); // Shifts notes - - // Merges two sequences with a certain offset. The offset is - // interpreted as either beats or seconds according to the - // current units of this, and seq is converted to the same - // units as this. Except for a possible conversion to beats - // or seconds, the tempo track of seq (if any) is ignored. - // (There is no way to merge tempo tracks.) - // THE SEQ PARAMETER IS NOT MODIFIED, AND Alg_event's ARE - // COPIED, NOT SHARED. - // The type of seq must be Alg_seq if seq is an Alg_seq, or - // Alg_track if seq is an Alg_track or an Alg_event_list. - virtual void merge(double t, Alg_event_list_ptr seq); - - // Deletes and shifts notes to fill the gap. The tempo track - // is also modified accordingly. ONLY EVENTS THAT START WITHIN - // THE REGION ARE DELETED unless "all" is true, in which case - // all notes that intersect the region are cleared. - // NOTES THAT EXTEND FROM BEFORE THE - // REGION INTO THE REGION RETAIN THEIR DURATION IN EITHER - // BEATS OR SECONDS ACCORDING TO THE CURRENT UNITS OF this. - virtual void clear(double t, double len, bool all); - - // Deletes notes but doesn't shift. If the "all" argument - // is true, deletes all notes that intersect the range at all, - // not just those that start within it. The tempo track is - // not affected. - virtual void silence(double t, double len, bool all); - - // Simply shifts notes past time t over by len, which is given - // in either beats or seconds according to the units of this. - // The resulting interveal (t, t+len) may in fact contain notes - // that begin before t. The durations of notes are not changed. - // If this is an Alg_seq, the tempo track is expanded at t also. - virtual void insert_silence(double t, double len); - - // - // Accessing for screen display - // - - // A useful generic function to retrieve only certain - // types of events. The masks should be bit-masks defined - // somewhere else. Part of the mask allows us to search for - // selected events. If this is an Alg_seq, search all tracks - // (otherwise, call track[i].find()) - // If channel_mask == 0, accept ALL channels - virtual Alg_event_list *find(double t, double len, bool all, - long channel_mask, long event_type_mask); - - virtual void set_in_use(bool flag) { in_use = flag; } - // - // MIDI playback - // - // See Alg_iterator -} *Alg_track_ptr, &Alg_track_ref; - - -// Alg_time_sig represents a single time signature; -// although not recommended, time_signatures may have arbitrary -// floating point values, e.g. 4.5 beats per measure -typedef class Alg_time_sig { -public: - double beat; // when does this take effect? - double num; // what is the "numerator" (top number?) - double den; // what is the "denominator" (bottom number?) - Alg_time_sig(double b, double n, double d) { - beat = b; num = n; den = d; - } - Alg_time_sig() { - beat = 0; num = 0; den = 0; - } - void beat_to_measure(double beat, double *measure, double *m_beat, - double *num, double *den); - -} *Alg_time_sig_ptr; - - -// Alg_time_sigs is a dynamic array of time signatures -// -// The default (empty) time_sigs has 4/4 time at beat 0. -// Each time_sig object in time_sigs represents the beginning -// of a measure. If there is a beat missing, e.g. in the first -// measure, you can represent this by inserting another -// time_sig at the next measure beginning. Each time_sig implies -// an infinite sequence of full measures until the next time_sig. -// If you insert a time_sig and one already exist near the same -// beat, the old one is replaced, thus re-barring every measure -// until the next time_sig. -class Alg_time_sigs { -private: - long maxlen; - void expand(); // make more space - long len; - Alg_time_sig_ptr time_sigs; -public: - Alg_time_sigs() { - maxlen = len = 0; - time_sigs = nullptr; - } - Alg_time_sig &operator[](int i) { // fetch a time signature - assert(i >= 0 && i < len); - return time_sigs[i]; - } - ~Alg_time_sigs() { - if (time_sigs) delete[] time_sigs; - } - void show(); - long length() { return len; } - int find_beat(double beat); - // get the number of beats per measure starting at beat - double get_bar_len(double beat); - void insert(double beat, double num, double den, bool force = false); - void cut(double start, double end, double dur); // remove from start to end - void trim(double start, double end); // retain just start to end - void paste(double start, Alg_seq *seq); - void insert_beats(double beat, double len); // insert len beats at beat - // find the nearest beat (see Alg_seq::nearest_beat) to beat - double nearest_beat(double beat); -}; - - -// a sequence of Alg_events objects -typedef class Alg_tracks { -private: - long maxlen; - void expand(); - void expand_to(int new_max); - long len; -public: - Alg_track_ptr *tracks; // tracks is array of pointers - Alg_track &operator[](int i) { - assert(i >= 0 && i < len); - return *tracks[i]; - } - long length() { return len; } - Alg_tracks() { - maxlen = len = 0; - tracks = nullptr; - } - ~Alg_tracks(); - // Append a track to tracks. This Alg_tracks becomes the owner of track. - void append(Alg_track_ptr track); - void add_track(int track_num, Alg_time_map_ptr time_map, bool seconds); - void reset(); - void set_in_use(bool flag); // handy to set in_use flag on all tracks -} *Alg_tracks_ptr; - - -typedef enum { - alg_no_error = 0, // no error reading Allegro or MIDI file - alg_error_open = -800, // could not open Allegro or MIDI file - alg_error_syntax // something found in the file that could not be parsed; - // generally you should ignore syntax errors or look at the printed error - // messages because there are some things in standard midi files that we do - // not handle; (maybe we should only set alg_error_syntax when there is a - // real problem with the file as opposed to when there is some warning - // message for the user) -} Alg_error; - - -typedef struct Alg_pending_event { - void *cookie; // client-provided sequence identifier - Alg_events *events; // the array of events - long index; // offset of this event - bool note_on; // is this a note-on or a note-off (if applicable)? - double offset; // time offset for events - double time; // time for this event -} *Alg_pending_event_ptr; - - -typedef class Alg_iterator { -private: - long maxlen; - void expand(); - void expand_to(int new_max); - long len; - Alg_seq_ptr seq; - Alg_pending_event *pending_events; - // the next four fields are mainly for request_note_off() - Alg_events_ptr events_ptr; // remembers events containing current event - long index; // remembers index of current event - void *cookie; // remembers the cookie associated with next event - double offset; - void show(); - bool earlier(int i, int j); - void insert(Alg_events_ptr events, long index, bool note_on, - void *cookie, double offset); - // returns the info on the next pending event in the priority queue - bool remove_next(Alg_events_ptr &events, long &index, bool ¬e_on, - void *&cookie, double &offset, double &time); -public: - bool note_off_flag; // remembers if we are iterating over note-off - // events as well as note-on and update events - long length() { return len; } - Alg_iterator(Alg_seq_ptr s, bool note_off) { - seq = s; - note_off_flag = note_off; - maxlen = len = 0; - pending_events = nullptr; - } - // Normally, iteration is over the events in the one sequence used - // to instatiate the iterator (see above), but with this method, you - // can add more sequences to the iteration. Events are returned in - // time order, so effectively sequence events are merged. - // The optional offset is added to each event time of sequence s - // before merging/sorting. You should call begin_seq() for each - // sequence to be included in the iteration unless you call begin() - // (see below). - void begin_seq(Alg_seq_ptr s, void *cookie = nullptr, double offset = 0.0); - ~Alg_iterator(); - // Prepare to enumerate events in order. If note_off_flag is true, then - // iteration_next will merge note-off events into the sequence. If you - // call begin(), you should not normally call begin_seq(). See above. - void begin(void *cookie = nullptr) { begin_seq(seq, cookie); } - // return next event (or NULL). If iteration_begin was called with - // note_off_flag = true, and if note_on is not NULL, then *note_on - // is set to true when the result value represents a note-on or update. - // (With note_off_flag, each Alg_note event is returned twice, once - // at the note-on time, with *note_on == true, and once at the note-off - // time, with *note_on == false. If a cookie_ptr is passed, then the - // cookie corresponding to the event is stored at that address - // If end_time is 0, iterate through the entire sequence, but if - // end_time is non_zero, stop iterating at the last event before end_time - Alg_event_ptr next(bool *note_on = nullptr, void **cookie_ptr = nullptr, - double *offset_ptr = nullptr, double end_time = 0); - // Sometimes, the caller wants to receive note-off events for a subset - // of the notes, typically the notes that are played and need to be - // turned off. In this case, when a note is turned on, the client - // should call request_note_off(). This will insert a note-off into - // the queue for the most recent note returned by next(). - void request_note_off(); - void end(); // clean up after enumerating events -} *Alg_iterator_ptr; - - -// An Alg_seq is an array of Alg_events, each a sequence of Alg_event, -// with a tempo map and a sequence of time signatures -// -typedef class Alg_seq : public Alg_track { -protected: - Alg_iterator_ptr pending; // iterator used internally by Alg_seq methods - void serialize_seq(); - Alg_error error; // error code set by file readers - // an internal function used for writing Allegro track names - Alg_event_ptr write_track_name(std::ostream &file, int n, - Alg_events &events); -public: - int channel_offset_per_track; // used to encode track_num into channel - Alg_tracks track_list; // array of Alg_events - Alg_time_sigs time_sig; - int beat_x; - void basic_initialization() { - error = alg_no_error; - units_are_seconds = true; type = 's'; - channel_offset_per_track = 0; - add_track(0); // default is one empty track - } - Alg_seq() { - basic_initialization(); - } - // copy constructor -- if track is an Alg_seq, make a copy; if - // track is just an Alg_track, the track becomes track 0 - Alg_seq(Alg_track_ref track) { seq_from_track(track); } - Alg_seq(Alg_track_ptr track) { seq_from_track(*track); } - void seq_from_track(Alg_track_ref tr); - // create from file: - Alg_seq(std::istream &file, bool smf, double *offset_ptr = nullptr); - // create from filename - Alg_seq(const char *filename, bool smf, double *offset_ptr = nullptr); - ~Alg_seq() override; - int get_read_error() { return error; } - void serialize(void **buffer, long *bytes) override; - void copy_time_sigs_to(Alg_seq *dest); // a utility function - void set_time_map(Alg_time_map *map) override; - - // encode sequence structure into contiguous, moveable memory block - // address of newly allocated memory is assigned to *buffer, which must - // be freed by caller; the length of data is assigned to *len - void unserialize_seq(); - - // write an ascii representation to file - void write(std::ostream &file, bool in_secs, double offset = 0.0); - // returns true on success - bool write(const char *filename, double offset = 0.0); - void smf_write(std::ostream &file); - bool smf_write(const char *filename); - - // Returns the number of tracks - int tracks(); - - // create a track - void add_track(int track_num) { - track_list.add_track(track_num, get_time_map(), units_are_seconds); - } - - // Return a particular track. This Alg_seq owns the track, so the - // caller must not delete the result. - Alg_track_ptr track(int); - - Alg_event_ptr const &operator[](int i) override; - - void convert_to_seconds() override; - void convert_to_beats() override; - - Alg_track_ptr cut_from_track(int track_num, double start, double dur, - bool all); - Alg_seq *cut(double t, double len, bool all) override; - void insert_silence_in_track(int track_num, double t, double len); - void insert_silence(double t, double len) override; - Alg_track_ptr copy_track(int track_num, double t, double len, bool all); - Alg_seq *copy(double start, double len, bool all) override; - void paste(double start, Alg_seq *seq); - void clear(double t, double len, bool all) override; - void merge(double t, Alg_event_list_ptr seq) override; - void silence(double t, double len, bool all) override; - void clear_track(int track_num, double start, double len, bool all); - void silence_track(int track_num, double start, double len, bool all); - Alg_event_list_ptr find_in_track(int track_num, double t, double len, - bool all, long channel_mask, - long event_type_mask); - - // find index of first score event after time - long seek_time(double time, int track_num); - bool insert_beat(double time, double beat); - // return the time of the beat nearest to time, also returns beat - // number through beat. This will correspond to an integer number - // of beats from the nearest previous time signature or 0.0, but - // since time signatures need not be on integer beat boundaries - // the beat location may not be on an integer beat (beat locations - // are measured from the beginning which is beat 0. - double nearest_beat_time(double time, double *beat); - // warning: insert_tempo may change representation from seconds to beats - bool insert_tempo(double bpm, double beat); - // change the duration from b0 to b1 (beats) to dur (seconds) by - // scaling the intervening tempos - bool stretch_region(double b0, double b1, double dur); - // add_event takes a pointer to an event on the heap. The event is not - // copied, and this Alg_seq becomes the owner and freer of the event. - void add_event(Alg_event_ptr event, int track_num); - void add(Alg_event_ptr event) override { assert(false); } // call add_event instead - // get the tempo starting at beat - double get_tempo(double beat); - bool set_tempo(double bpm, double start_beat, double end_beat); - - // get the bar length in beats starting at beat - double get_bar_len(double beat); - void set_time_sig(double beat, double num, double den); - void beat_to_measure(double beat, long *measure, double *m_beat, - double *num, double *den); - // void set_events(Alg_event_ptr *events, long len, long max); - void merge_tracks(); // move all track data into one track - void set_in_use(bool flag) override; // set in_use flag on all tracks -} *Alg_seq_ptr, &Alg_seq_ref; - - -// see Alg_seq::Alg_seq() constructors that read from files -// the following are for internal library implementation and are -// moved to *_internal.h header files. -//Alg_seq_ptr alg_read(std::istream &file, Alg_seq_ptr new_seq); -//Alg_seq_ptr alg_smf_read(std::istream &file, Alg_seq_ptr new_seq); -#endif diff --git a/plugins/MidiImport/portsmf/allegrord.cpp b/plugins/MidiImport/portsmf/allegrord.cpp deleted file mode 100644 index 2d9553d8a..000000000 --- a/plugins/MidiImport/portsmf/allegrord.cpp +++ /dev/null @@ -1,779 +0,0 @@ -#include "assert.h" -#include "stdlib.h" -#include "string.h" -#include "ctype.h" -#include -#include -#include -#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 - // -- 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(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 or K[A-G] or P[A-G] -// (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" - // so it must be "K" so 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 -*/ diff --git a/plugins/MidiImport/portsmf/allegroserial.cpp b/plugins/MidiImport/portsmf/allegroserial.cpp deleted file mode 100644 index 3d4357775..000000000 --- a/plugins/MidiImport/portsmf/allegroserial.cpp +++ /dev/null @@ -1,2 +0,0 @@ -// allegroserial.cpp -- convert track to memory buffer and back to structure - diff --git a/plugins/MidiImport/portsmf/allegrosmfrd.cpp b/plugins/MidiImport/portsmf/allegrosmfrd.cpp deleted file mode 100644 index 5df95e88a..000000000 --- a/plugins/MidiImport/portsmf/allegrosmfrd.cpp +++ /dev/null @@ -1,458 +0,0 @@ -// midifile reader - -#include "stdio.h" -#include "string.h" -#include "assert.h" -#include -#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(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 = ¬e_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, ¶meter); - 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, ¶meter); - meta_channel = -1; -} - - -void Alg_midifile_reader::Mf_pitchbend(int chan, int c1, int c2) -{ - Alg_parameter parameter; - parameter.set_attr(symbol_table.insert_string("bendr")); - parameter.r = ((c2 << 7) + c1) / 8192.0 - 1.0; - update(chan, -1, ¶meter); - meta_channel = -1; -} - - -void Alg_midifile_reader::Mf_program(int chan, int program) -{ - Alg_parameter parameter; - parameter.set_attr(symbol_table.insert_string("programi")); - parameter.i = program; - update(chan, -1, ¶meter); - meta_channel = -1; -} - - -void Alg_midifile_reader::Mf_chanpressure(int chan, int val) -{ - Alg_parameter parameter; - parameter.set_attr(symbol_table.insert_string("pressurer")); - parameter.r = val / 127.0; - update(chan, -1, ¶meter); - meta_channel = -1; -} - - -void Alg_midifile_reader::binary_msg(int len, 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, ¶meter); -} - - -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); -} diff --git a/plugins/MidiImport/portsmf/allegrosmfwr.cpp b/plugins/MidiImport/portsmf/allegrosmfwr.cpp deleted file mode 100644 index 0e27dd12c..000000000 --- a/plugins/MidiImport/portsmf/allegrosmfwr.cpp +++ /dev/null @@ -1,638 +0,0 @@ -// allegrosmfwr.cpp -- Allegro Standard Midi File Write - -#include -#include -#include -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 ¬es = 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; -} diff --git a/plugins/MidiImport/portsmf/allegrowr.cpp b/plugins/MidiImport/portsmf/allegrowr.cpp deleted file mode 100644 index 3e7e98ca9..000000000 --- a/plugins/MidiImport/portsmf/allegrowr.cpp +++ /dev/null @@ -1,179 +0,0 @@ -// allegrowr.cpp -- write sequence to an Allegro file (text) - -#include "assert.h" -#include -#include -#include -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 -// 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 [] - 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 ¬es = track_list[j]; - if (j != 0) update_to_skip = write_track_name(file, j, notes); - // now write the notes at beat positions - for (i = 0; i < notes.length(); i++) { - Alg_event_ptr e = notes[i]; - // if we already wrote this event as a track or sequence name, - // do not write it again - if (e == update_to_skip) continue; - double start = e->time; - if (in_secs) { - file << "T" << TIMFMT << start; - } else { - file << "TW" << TIMFMT << start / 4; - } - // write the channel as Vn or V- - if (e->chan == -1) file << " V-"; - else file << " V" << e->chan; - // write the note or update data - if (e->is_note()) { - Alg_note_ptr n = (Alg_note_ptr) e; - double dur = n->dur; - file << " K" << n->get_identifier() << - " P" << GFMT << n->pitch; - if (in_secs) { - file << " U" << TIMFMT << dur; - } else { - file << " Q" << TIMFMT << dur; - } - file << " L" << GFMT << n->loud; - Alg_parameters_ptr p = n->parameters; - while (p) { - parameter_print(file, &(p->parm)); - p = p->next; - } - } else { // an update - assert(e->is_update()); - Alg_update_ptr u = (Alg_update_ptr) e; - if (u->get_identifier() != -1) { - file << " K" << u->get_identifier(); - } - parameter_print(file, &(u->parameter)); - } - file << "\n"; - } - } -} - -bool Alg_seq::write(const char *filename, double offset) -{ - ofstream file(filename); - if (file.fail()) return false; - write(file, units_are_seconds, offset); - file.close(); - return true; -} diff --git a/plugins/MidiImport/portsmf/license.txt b/plugins/MidiImport/portsmf/license.txt deleted file mode 100644 index 6f86ce0d4..000000000 --- a/plugins/MidiImport/portsmf/license.txt +++ /dev/null @@ -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. - */ diff --git a/plugins/MidiImport/portsmf/mfmidi.cpp b/plugins/MidiImport/portsmf/mfmidi.cpp deleted file mode 100644 index ff7b61abf..000000000 --- a/plugins/MidiImport/portsmf/mfmidi.cpp +++ /dev/null @@ -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(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; -} diff --git a/plugins/MidiImport/portsmf/mfmidi.h b/plugins/MidiImport/portsmf/mfmidi.h deleted file mode 100644 index f19721246..000000000 --- a/plugins/MidiImport/portsmf/mfmidi.h +++ /dev/null @@ -1,101 +0,0 @@ -#include - -#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; -}; - - diff --git a/plugins/MidiImport/portsmf/strparse.cpp b/plugins/MidiImport/portsmf/strparse.cpp deleted file mode 100644 index 547424ec8..000000000 --- a/plugins/MidiImport/portsmf/strparse.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#include -#include -// #include -- 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); -} - - diff --git a/plugins/MidiImport/portsmf/strparse.h b/plugins/MidiImport/portsmf/strparse.h deleted file mode 100644 index 046b1ddd3..000000000 --- a/plugins/MidiImport/portsmf/strparse.h +++ /dev/null @@ -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); diff --git a/plugins/MidiImport/portsmf/trace.cpp b/plugins/MidiImport/portsmf/trace.cpp deleted file mode 100644 index 8cc994345..000000000 --- a/plugins/MidiImport/portsmf/trace.cpp +++ /dev/null @@ -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 -} diff --git a/plugins/MidiImport/portsmf/trace.h b/plugins/MidiImport/portsmf/trace.h deleted file mode 100644 index 52a23a69b..000000000 --- a/plugins/MidiImport/portsmf/trace.h +++ /dev/null @@ -1,2 +0,0 @@ -void trace(char *format, ...); -