From 1e39d1210dc779a2e8c671b802cdb576b41d3a25 Mon Sep 17 00:00:00 2001 From: TonyChyi Date: Fri, 6 Jan 2017 03:07:07 +0800 Subject: [PATCH] Move onto drumstick and ditch portsmf * Added Cakewalk file format import support * Added Overture file format import support * Enhanced MIDI CC Message handling --- .travis/linux..install.sh | 2 +- data/locale/en.ts | 80 +- include/SmfMidiCC.h | 50 + include/SmfMidiChannel.h | 58 + plugins/CMakeLists.txt | 2 +- plugins/MidiImport/CMakeLists.txt | 8 - plugins/MidiImport/MidiImport.cpp | 618 ---- plugins/MidiImport/portsmf/README.txt | 32 - plugins/MidiImport/portsmf/algrd_internal.h | 4 - .../MidiImport/portsmf/algsmfrd_internal.h | 3 - plugins/MidiImport/portsmf/allegro.cpp | 2866 ----------------- plugins/MidiImport/portsmf/allegro.h | 945 ------ plugins/MidiImport/portsmf/allegrord.cpp | 753 ----- plugins/MidiImport/portsmf/allegroserial.cpp | 2 - plugins/MidiImport/portsmf/allegrosmfrd.cpp | 445 --- plugins/MidiImport/portsmf/allegrosmfwr.cpp | 649 ---- plugins/MidiImport/portsmf/allegrowr.cpp | 181 -- plugins/MidiImport/portsmf/license.txt | 40 - plugins/MidiImport/portsmf/mfmidi.cpp | 485 --- plugins/MidiImport/portsmf/mfmidi.h | 98 - plugins/MidiImport/portsmf/strparse.cpp | 87 - plugins/MidiImport/portsmf/strparse.h | 18 - plugins/MidiImport/portsmf/trace.cpp | 25 - plugins/MidiImport/portsmf/trace.h | 2 - plugins/SmfImport/CMakeLists.txt | 18 + plugins/SmfImport/SmfImport.cpp | 226 ++ .../MidiImport.h => SmfImport/SmfImport.h} | 44 +- plugins/SmfImport/commonReader.cpp | 326 ++ plugins/SmfImport/commonReader.h | 107 + plugins/SmfImport/midiReader.cpp | 146 + plugins/SmfImport/midiReader.h | 52 + plugins/SmfImport/oveReader.cpp | 171 + plugins/SmfImport/oveReader.h | 54 + plugins/SmfImport/wrkReader.cpp | 138 + plugins/SmfImport/wrkReader.h | 54 + src/core/CMakeLists.txt | 3 + src/core/SmfMidiCC.cpp | 81 + src/core/SmfMidiChannel.cpp | 91 + src/core/Song.cpp | 4 + 39 files changed, 1656 insertions(+), 7312 deletions(-) create mode 100644 include/SmfMidiCC.h create mode 100644 include/SmfMidiChannel.h delete mode 100644 plugins/MidiImport/CMakeLists.txt delete mode 100644 plugins/MidiImport/MidiImport.cpp 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 create mode 100644 plugins/SmfImport/CMakeLists.txt create mode 100644 plugins/SmfImport/SmfImport.cpp rename plugins/{MidiImport/MidiImport.h => SmfImport/SmfImport.h} (74%) create mode 100644 plugins/SmfImport/commonReader.cpp create mode 100644 plugins/SmfImport/commonReader.h create mode 100644 plugins/SmfImport/midiReader.cpp create mode 100644 plugins/SmfImport/midiReader.h create mode 100644 plugins/SmfImport/oveReader.cpp create mode 100644 plugins/SmfImport/oveReader.h create mode 100644 plugins/SmfImport/wrkReader.cpp create mode 100644 plugins/SmfImport/wrkReader.h create mode 100644 src/core/SmfMidiCC.cpp create mode 100644 src/core/SmfMidiChannel.cpp diff --git a/.travis/linux..install.sh b/.travis/linux..install.sh index 7b591feb962..d5e64a717d4 100644 --- a/.travis/linux..install.sh +++ b/.travis/linux..install.sh @@ -3,7 +3,7 @@ PACKAGES="cmake libsndfile-dev fftw3-dev libvorbis-dev libogg-dev libasound2-dev libjack-dev libsdl-dev libsamplerate0-dev libstk0-dev libfluidsynth-dev portaudio19-dev wine-dev g++-multilib libfltk1.3-dev - libgig-dev libsoundio-dev" + libgig-dev libsoundio-dev libdrumstick-dev" # Help with unmet dependencies PACKAGES="$PACKAGES libjack0" diff --git a/data/locale/en.ts b/data/locale/en.ts index 400ed17c996..43083bf0385 100644 --- a/data/locale/en.ts +++ b/data/locale/en.ts @@ -227,6 +227,9 @@ If you're interested in translating LMMS in another language or want to imp The JACK server seems to have been shutdown and starting a new instance failed. Therefore LMMS is unable to proceed. You should save your project and restart JACK and LMMS. + + + AudioJack::setupWidget CLIENT-NAME @@ -2245,6 +2248,9 @@ You can remove and move FX channels in the context menu, which is accessed by ri FX-Mixer + + + FxMixerView::FxChannelView FX Fader %1 @@ -4026,6 +4032,10 @@ Please visit http://lmms.sf.net/wiki for documentation on LMMS. Enable note labels in piano roll + + Save project template + + MeterDialog @@ -4064,25 +4074,6 @@ Please visit http://lmms.sf.net/wiki for documentation on LMMS. - - MidiImport - - Setup incomplete - - - - You do not have 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. - - - - 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. - - - - Track - - - MidiJack @@ -6237,6 +6228,21 @@ Remember to also save your project manually. + + SmfImport + + Setup incomplete + + + + You do not have 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. + + + + 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. + + + Song @@ -6315,6 +6321,18 @@ Remember to also save your project manually. LMMS Error report + + Overture projects + + + + Cakewalk projects + + + + Save project + + SongEditor @@ -6703,8 +6721,11 @@ Please make sure you have read-permission to the file and the directory containi Please wait... + + + TrackContainer::TrackContainer - Importing MIDI-file... + Cancel @@ -6966,6 +6987,10 @@ Please make sure you have read-permission to the file and the directory containi Decrement version number + + already exists. Do you want to replace it? + + VestigeInstrumentView @@ -7716,6 +7741,13 @@ Please make sure you have read-permission to the file and the directory containi + + commonReader + + Track + + + dynProcControlDialog @@ -9084,10 +9116,6 @@ Double clicking any of the plugins will bring up information on the ports.plugin for using arbitrary LADSPA-effects inside LMMS. - - Filter for importing MIDI-files into LMMS - - Emulation of the MOS6581 and MOS8580 SID. This chip was used in the Commodore 64 computer. @@ -9213,6 +9241,10 @@ This chip was used in the Commodore 64 computer. Filter for exporting MIDI-files from LMMS + + Filter for importing MIDI-like files into LMMS + + sf2Instrument diff --git a/include/SmfMidiCC.h b/include/SmfMidiCC.h new file mode 100644 index 00000000000..62332a8b94f --- /dev/null +++ b/include/SmfMidiCC.h @@ -0,0 +1,50 @@ +/* + * smfMidiCC.h - support for importing MIDI files + * + * Copyright (c) 2005-2014 Tobias Doerffel + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef SMF_MIDI_CC_H +#define SMF_MIDI_CC_H + +#include + +#include "TrackContainer.h" +#include "AutomationTrack.h" +#include "AutomationPattern.h" +#include "MidiTime.h" + +class SmfMidiCC +{ + +public: + SmfMidiCC(); + + AutomationTrack * at; + AutomationPattern * ap; + MidiTime lastPos; + + SmfMidiCC & create(TrackContainer* tc, QString tn ); + SmfMidiCC & putValue( MidiTime time, AutomatableModel * objModel, float value ); + void clear(); +}; + +#endif diff --git a/include/SmfMidiChannel.h b/include/SmfMidiChannel.h new file mode 100644 index 00000000000..e4e5abbd9a7 --- /dev/null +++ b/include/SmfMidiChannel.h @@ -0,0 +1,58 @@ +/* + * smfMidiChannel.h - support for importing MIDI files + * + * Copyright (c) 2005-2014 Tobias Doerffel + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef SMF_MIDI_CHANNEL_H +#define SMF_MIDI_CHANNEL_H + +#include + +#include "InstrumentTrack.h" +#include "Pattern.h" +#include "Instrument.h" +#include "MidiTime.h" + + + + +class SmfMidiChannel +{ + +public: + SmfMidiChannel(); + + InstrumentTrack * it; + Pattern* p; + Instrument * it_inst; + bool isSF2; + bool hasNotes; + MidiTime lastEnd; + QString trackName; + + SmfMidiChannel * create( TrackContainer* tc, QString tn ); + + void addNote( Note & n ); + +}; + +#endif diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 943c8088c59..40f12dac9ff 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -55,7 +55,7 @@ IF("${PLUGIN_LIST}" STREQUAL "") ladspa_browser LadspaEffect lb302 - MidiImport + SmfImport # For Midi, Overture and Cakewalk files. # MidiExport - temporarily disabled, MIDI export is broken MultitapEcho monstro diff --git a/plugins/MidiImport/CMakeLists.txt b/plugins/MidiImport/CMakeLists.txt deleted file mode 100644 index 9fd6b6876e5..00000000000 --- a/plugins/MidiImport/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -INCLUDE(BuildPlugin) - -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) diff --git a/plugins/MidiImport/MidiImport.cpp b/plugins/MidiImport/MidiImport.cpp deleted file mode 100644 index e5a9b02a31c..00000000000 --- a/plugins/MidiImport/MidiImport.cpp +++ /dev/null @@ -1,618 +0,0 @@ -/* - * MidiImport.cpp - support for importing MIDI files - * - * Copyright (c) 2005-2014 Tobias Doerffel - * - * This file is part of LMMS - http://lmms.io - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program (see COPYING); if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301 USA. - * - */ - - -#include -#include -#include -#include -#include - -#include "MidiImport.h" -#include "TrackContainer.h" -#include "InstrumentTrack.h" -#include "AutomationTrack.h" -#include "AutomationPattern.h" -#include "ConfigManager.h" -#include "Pattern.h" -#include "Instrument.h" -#include "GuiApplication.h" -#include "MainWindow.h" -#include "MidiTime.h" -#include "debug.h" -#include "embed.h" -#include "Song.h" - -#include "portsmf/allegro.h" - -#define makeID(_c0, _c1, _c2, _c3) \ - ( 0 | \ - ( ( _c0 ) | ( ( _c1 ) << 8 ) | ( ( _c2 ) << 16 ) | ( ( _c3 ) << 24 ) ) ) - - - -extern "C" -{ - -Plugin::Descriptor PLUGIN_EXPORT midiimport_plugin_descriptor = -{ - STRINGIFY( PLUGIN_NAME ), - "MIDI Import", - QT_TRANSLATE_NOOP( "pluginBrowser", - "Filter for importing MIDI-files into LMMS" ), - "Tobias Doerffel ", - 0x0100, - Plugin::ImportFilter, - NULL, - NULL, - NULL -} ; - -} - - -MidiImport::MidiImport( const QString & _file ) : - ImportFilter( _file, &midiimport_plugin_descriptor ), - m_events(), - m_timingDivision( 0 ) -{ -} - - - - -MidiImport::~MidiImport() -{ -} - - - - -bool MidiImport::tryImport( TrackContainer* tc ) -{ - if( openFile() == false ) - { - return false; - } - -#ifdef LMMS_HAVE_FLUIDSYNTH - if( gui != NULL && - ConfigManager::inst()->defaultSoundfont().isEmpty() ) - { - QMessageBox::information( gui->mainWindow(), - tr( "Setup incomplete" ), - tr( "You do not have 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." ) ); - } -#else - if( gui ) - { - QMessageBox::information( gui->mainWindow(), - 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." ) ); - } -#endif - - switch( readID() ) - { - case makeID( 'M', 'T', 'h', 'd' ): - printf( "MidiImport::tryImport(): found MThd\n"); - return readSMF( tc ); - - case makeID( 'R', 'I', 'F', 'F' ): - printf( "MidiImport::tryImport(): found RIFF\n"); - return readRIFF( tc ); - - default: - printf( "MidiImport::tryImport(): not a Standard MIDI " - "file\n" ); - return false; - } -} - - - - -class smfMidiCC -{ - -public: - smfMidiCC() : - at( NULL ), - ap( NULL ), - lastPos( 0 ) - { } - - AutomationTrack * at; - AutomationPattern * ap; - MidiTime lastPos; - - smfMidiCC & create( TrackContainer* tc, QString tn ) - { - 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::AutomationTrack, tc ) ); - } - if( tn != "") { - at->setName( tn ); - } - return *this; - } - - - void clear() - { - at = NULL; - ap = NULL; - lastPos = 0; - } - - - smfMidiCC & putValue( MidiTime time, AutomatableModel * objModel, float value ) - { - if( !ap || time > lastPos + DefaultTicksPerTact ) - { - MidiTime pPos = MidiTime( time.getTact(), 0 ); - ap = dynamic_cast( - at->createTCO(0) ); - ap->movePosition( pPos ); - ap->addObject( objModel ); - } - - lastPos = time; - time = time - ap->startPosition(); - ap->putValue( time, value, false ); - ap->changeLength( MidiTime( time.getTact() + 1, 0 ) ); - - return *this; - } -}; - - - -class smfMidiChannel -{ - -public: - smfMidiChannel() : - it( NULL ), - p( NULL ), - it_inst( NULL ), - isSF2( false ), - hasNotes( false ), - lastEnd( 0 ) - { } - - InstrumentTrack * it; - Pattern* p; - Instrument * it_inst; - bool isSF2; - bool hasNotes; - MidiTime lastEnd; - QString trackName; - - smfMidiChannel * create( TrackContainer* tc, QString tn ) - { - if( !it ) { - // Keep LMMS responsive - qApp->processEvents(); - it = dynamic_cast( Track::create( Track::InstrumentTrack, tc ) ); - -#ifdef LMMS_HAVE_FLUIDSYNTH - it_inst = it->loadInstrument( "sf2player" ); - - if( it_inst ) - { - isSF2 = true; - it_inst->loadFile( ConfigManager::inst()->defaultSoundfont() ); - it_inst->childModel( "bank" )->setValue( 0 ); - it_inst->childModel( "patch" )->setValue( 0 ); - } - else - { - it_inst = it->loadInstrument( "patman" ); - } -#else - it_inst = it->loadInstrument( "patman" ); -#endif - trackName = tn; - if( trackName != "") { - it->setName( tn ); - } - lastEnd = 0; - // General MIDI default - it->pitchRangeModel()->setInitValue( 2 ); - } - return this; - } - - - void addNote( Note & n ) - { - if( !p || n.pos() > lastEnd + DefaultTicksPerTact ) - { - MidiTime pPos = MidiTime( n.pos().getTact(), 0 ); - p = dynamic_cast( it->createTCO( 0 ) ); - p->movePosition( pPos ); - } - hasNotes = true; - lastEnd = n.pos() + n.length(); - n.setPos( n.pos( p->startPosition() ) ); - p->addNote( n, false ); - } - -}; - - -bool MidiImport::readSMF( TrackContainer* tc ) -{ - QString filename = file().fileName(); - closeFile(); - - const int preTrackSteps = 2; - QProgressDialog pd( TrackContainer::tr( "Importing MIDI-file..." ), - TrackContainer::tr( "Cancel" ), 0, preTrackSteps, gui->mainWindow() ); - pd.setWindowTitle( TrackContainer::tr( "Please wait..." ) ); - pd.setWindowModality(Qt::WindowModal); - pd.setMinimumDuration( 0 ); - - pd.setValue( 0 ); - - Alg_seq_ptr seq = new Alg_seq(filename.toLocal8Bit(), true); - seq->convert_to_beats(); - - pd.setMaximum( seq->tracks() + preTrackSteps ); - pd.setValue( 1 ); - - // 128 CC + Pitch Bend - smfMidiCC ccs[129]; - smfMidiChannel chs[256]; - - MeterModel & timeSigMM = Engine::getSong()->getTimeSigModel(); - AutomationPattern * timeSigNumeratorPat = - AutomationPattern::globalAutomationPattern( &timeSigMM.numeratorModel() ); - AutomationPattern * timeSigDenominatorPat = - AutomationPattern::globalAutomationPattern( &timeSigMM.denominatorModel() ); - - // TODO: adjust these to Time.Sig changes - double beatsPerTact = 4; - double ticksPerBeat = DefaultTicksPerTact / beatsPerTact; - - // Time-sig changes - Alg_time_sigs * timeSigs = &seq->time_sig; - for( int s = 0; s < timeSigs->length(); ++s ) - { - Alg_time_sig timeSig = (*timeSigs)[s]; - // Initial timeSig, set song-default value - if(/* timeSig.beat == 0*/ true ) - { - // TODO set song-global default value - printf("Another timesig at %f\n", timeSig.beat); - timeSigNumeratorPat->putValue( timeSig.beat*ticksPerBeat, timeSig.num ); - timeSigDenominatorPat->putValue( timeSig.beat*ticksPerBeat, timeSig.den ); - } - else - { - } - - } - - pd.setValue( 2 ); - - // Tempo stuff - AutomationPattern * tap = tc->tempoAutomationPattern(); - 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_beat_ptr b = &(beats[i]); - double tempo = ( beats[i + 1].beat - b->beat ) / - ( beats[i + 1].time - beats[i].time ); - tap->putValue( b->beat * ticksPerBeat, tempo * 60.0 ); - } - if( timeMap->last_tempo_flag ) - { - Alg_beat_ptr b = &( beats[beats.len - 1] ); - tap->putValue( b->beat * ticksPerBeat, timeMap->last_tempo * 60.0 ); - } - } - - // Song events - for( int e = 0; e < seq->length(); ++e ) - { - Alg_event_ptr evt = (*seq)[e]; - - if( evt->is_update() ) - { - printf("Unhandled SONG update: %d %f %s\n", - evt->get_type_code(), evt->time, evt->get_attribute() ); - } - } - - // Tracks - for( int t = 0; t < seq->tracks(); ++t ) - { - QString trackName = QString( tr( "Track" ) + " %1" ).arg( t ); - Alg_track_ptr trk = seq->track( t ); - pd.setValue( t + preTrackSteps ); - - for( int c = 0; c < 129; c++ ) - { - ccs[c].clear(); - } - - // Now look at events - for( int e = 0; e < trk->length(); ++e ) - { - Alg_event_ptr evt = (*trk)[e]; - - if( evt->chan == -1 ) - { - bool handled = false; - if( evt->is_update() ) - { - QString attr = evt->get_attribute(); - if( attr == "tracknames" && evt->get_update_type() == 's' ) { - trackName = evt->get_string_value(); - 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" ); - } - } - else if( evt->is_note() && evt->chan < 256 ) - { - smfMidiChannel * ch = chs[evt->chan].create( tc, trackName ); - Alg_note_ptr noteEvt = dynamic_cast( evt ); - int ticks = noteEvt->get_duration() * ticksPerBeat; - Note n( (ticks < 1 ? 1 : ticks ), - noteEvt->get_start_time() * ticksPerBeat, - noteEvt->get_identifier() - 12, - noteEvt->get_loud()); - ch->addNote( n ); - - } - - else if( evt->is_update() ) - { - smfMidiChannel * ch = chs[evt->chan].create( tc, trackName ); - - double time = evt->time*ticksPerBeat; - QString update( evt->get_attribute() ); - - if( update == "programi" ) - { - long prog = evt->get_integer_value(); - if( ch->isSF2 ) - { - ch->it_inst->childModel( "bank" )->setValue( 0 ); - ch->it_inst->childModel( "patch" )->setValue( prog ); - } - else { - const QString num = QString::number( prog ); - const QString filter = QString().fill( '0', 3 - num.length() ) + num + "*.pat"; - const QString dir = "/usr/share/midi/" - "freepats/Tone_000/"; - const QStringList files = QDir( dir ). - entryList( QStringList( filter ) ); - if( ch->it_inst && !files.empty() ) - { - ch->it_inst->loadFile( dir+files.front() ); - } - } - } - - else if( update.startsWith( "control" ) || update == "bendr" ) - { - int ccid = update.mid( 7, update.length()-8 ).toInt(); - if( update == "bendr" ) - { - ccid = 128; - } - if( ccid <= 128 ) - { - double cc = evt->get_real_value(); - AutomatableModel * objModel = NULL; - - switch( ccid ) - { - case 0: - if( ch->isSF2 && ch->it_inst ) - { - objModel = ch->it_inst->childModel( "bank" ); - printf("BANK SELECT %f %d\n", cc, (int)(cc*127.0)); - cc *= 127.0f; - } - break; - - case 7: - objModel = ch->it->volumeModel(); - cc *= 100.0f; - break; - - case 10: - objModel = ch->it->panningModel(); - cc = cc * 200.f - 100.0f; - break; - - case 128: - objModel = ch->it->pitchModel(); - cc = cc * 100.0f; - break; - default: - //TODO: something useful for other CCs - break; - } - - if( objModel ) - { - if( time == 0 && objModel ) - { - objModel->setInitValue( cc ); - } - else - { - if( ccs[ccid].at == NULL ) { - ccs[ccid].create( tc, trackName + " > " + ( - objModel != NULL ? - objModel->displayName() : - QString("CC %1").arg(ccid) ) ); - } - 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() ); - } - } - } - } - - delete seq; - - - for( int c=0; c < 256; ++c ) - { - if( !chs[c].hasNotes && chs[c].it ) - { - printf(" Should remove empty track\n"); - // must delete trackView first - but where is it? - //tc->removeTrack( chs[c].it ); - //it->deleteLater(); - } - } - - // Set channel 10 to drums as per General MIDI's orders - if( chs[9].hasNotes && chs[9].it_inst && chs[9].isSF2 ) - { - // AFAIK, 128 should be the standard bank for drums in SF2. - // If not, this has to be made configurable. - chs[9].it_inst->childModel( "bank" )->setValue( 128 ); - chs[9].it_inst->childModel( "patch" )->setValue( 0 ); - } - - return true; -} - - - - -bool MidiImport::readRIFF( TrackContainer* tc ) -{ - // skip file length - skip( 4 ); - - // check file type ("RMID" = RIFF MIDI) - if( readID() != makeID( 'R', 'M', 'I', 'D' ) ) - { -invalid_format: - qWarning( "MidiImport::readRIFF(): invalid file format" ); - return false; - } - - // search for "data" chunk - while( 1 ) - { - const int id = readID(); - const int len = read32LE(); - if( file().atEnd() ) - { -data_not_found: - 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 ); - } - - // the "data" chunk must contain data in SMF format - if( readID() != makeID( 'M', 'T', 'h', 'd' ) ) - { - 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 * PLUGIN_EXPORT lmms_plugin_main( Model *, void * _data ) -{ - return new MidiImport( QString::fromUtf8( - static_cast( _data ) ) ); -} - - -} - diff --git a/plugins/MidiImport/portsmf/README.txt b/plugins/MidiImport/portsmf/README.txt deleted file mode 100644 index 0f36c9e4ee7..00000000000 --- 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 3b77adc4cd2..00000000000 --- a/plugins/MidiImport/portsmf/algrd_internal.h +++ /dev/null @@ -1,4 +0,0 @@ -/* algread_internal.h -- interface between allegro.cpp and allegrord.cpp */ - -Alg_error alg_read(std::istream &file, Alg_seq_ptr new_seq); - diff --git a/plugins/MidiImport/portsmf/algsmfrd_internal.h b/plugins/MidiImport/portsmf/algsmfrd_internal.h deleted file mode 100644 index 75cc0093bc7..00000000000 --- a/plugins/MidiImport/portsmf/algsmfrd_internal.h +++ /dev/null @@ -1,3 +0,0 @@ -/* algsmfrd_internal.h -- interface from allegrosmfrd.cpp to allegro.cpp */ - -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 653ff4c07db..00000000000 --- a/plugins/MidiImport/portsmf/allegro.cpp +++ /dev/null @@ -1,2866 +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 "debug.h" -#include "stdlib.h" -#include "stdio.h" -#include "string.h" -#include "memory.h" -#include -#include -using namespace std; -#include "allegro.h" -#include "algrd_internal.h" -#include "algsmfrd_internal.h" -// #include "trace.h" -- only needed for debugging -#include "math.h" - -#define STREQL(x, y) (strcmp(x, y) == 0) -#define MAX(x, y) ((x) > (y) ? (x) : (y)) - -// 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_buffer Alg_track::ser_buf; // declare the static variable - -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% - char **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) -{ - 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:%d", attr_name(), (int) 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, 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, char *name, 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, 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, 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, char *name, 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 = NULL; - return p; // caller should free this pointer - } - list = &((*list)->next); - } - return NULL; -} - - -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 NULL; -} - - -int Alg_event::get_type_code() -{ - if (!is_note()) { - const char* attr = get_attribute(); - if (STREQL(attr, "gate")) // volume change - return ALG_GATE; - if (STREQL(attr, "bend")) // 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, "program")) // program change - return ALG_PROGRAM; - if (STREQL(attr, "pressure")) // pressure change - return ALG_PRESSURE; - if (STREQL(attr, "keysig")) // key signature - return ALG_KEYSIG; - if (STREQL(attr, "timesig_num")) // time signature numerator - return ALG_TIMESIG_NUM; - if (STREQL(attr, "timesig_den")) // 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(char *a, 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 = NULL; // do this to prevent string from being freed -} - - -void Alg_event::set_real_value(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(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(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(char *a, 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(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 != NULL; -} - - -char Alg_event::get_attribute_type(char *a) -{ - assert(is_note()); - assert(a); - return a[strlen(a) - 1]; -} - - -char *Alg_event::get_string_value(char *a, 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(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(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(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; -} - - -char *Alg_event::get_atom_value(char *a, 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 == NULL ? NULL : - symbol_table.insert_string(value)); -} - - -void Alg_event::delete_attribute(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(); -} - - -char *Alg_event::get_string_value() -{ - assert(is_update()); - Alg_update* update = (Alg_update *) this; - assert(get_update_type() == 's'); - return update->parameter.a; -} - - -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; -} - - -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 ends within 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 %d, dur %g, key %d, " - "pitch %g, loud %g, attributes ", - time, (int) chan, dur, (int) 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]; - 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() -{ - // 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 &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 = Alg_track_ptr(); - 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); - 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]; - } - } else { - mbi = &beats[i - 1]; - mbi1 = &beats[i]; - } - // whether w 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 - while (i < beats.len) { - beats[i].time = beats[i].time + diff; - i++; - } - } - return true; -} - - -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); -} - - -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 = NULL; - 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 = NULL; - 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 = NULL; - 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_buf.init_for_write(); - serialize_track(); - *buffer = ser_buf.to_heap(bytes); -} - - -void Alg_seq::serialize(void **buffer, long *bytes) -{ - assert(get_type() == 's'); - ser_buf.init_for_write(); - serialize_seq(); - *buffer = ser_buf.to_heap(bytes); -} - - -void Serial_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 - memcpy(new_buffer, buffer, len); // copy from old buffer - ptr = new_buffer + (ptr - buffer); // relocate ptr to new 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 = 48 + 16 * time_map->beats.len + 24 * time_sig.length(); - ser_buf.check_buffer(needed); - ser_buf.set_char('A'); - ser_buf.set_char('L'); - ser_buf.set_char('G'); - ser_buf.set_char('S'); - long length_offset = ser_buf.get_posn(); - ser_buf.set_int32(0); // leave room to come back and write length - ser_buf.set_int32(channel_offset_per_track); - ser_buf.set_int32(units_are_seconds); - ser_buf.set_double(time_map->last_tempo); - ser_buf.set_int32(time_map->last_tempo_flag); - ser_buf.set_int32(time_map->beats.len); - for (i = 0; i < time_map->beats.len; i++) { - ser_buf.set_double(time_map->beats[i].time); - ser_buf.set_double(time_map->beats[i].beat); - } - ser_buf.set_int32(time_sig.length()); - ser_buf.pad(); - for (i = 0; i < time_sig.length(); i++) { - ser_buf.set_double(time_sig[i].beat); - ser_buf.set_double(time_sig[i].num); - ser_buf.set_double(time_sig[i].den); - } - ser_buf.set_int32(tracks()); - ser_buf.pad(); - for (i = 0; i < tracks(); i++) { - track(i)->serialize_track(); - } - // do not include ALGS, include padding at end - ser_buf.store_long(length_offset, ser_buf.get_posn() - length_offset); -} - - -void Alg_track::serialize_track() -{ - // to simplify the code, copy from parameter addresses to locals - int j; - ser_buf.check_buffer(32); - ser_buf.set_char('A'); - ser_buf.set_char('L'); - ser_buf.set_char('G'); - ser_buf.set_char('T'); - long length_offset = ser_buf.get_posn(); // save location for track length - ser_buf.set_int32(0); // room to write track length - ser_buf.set_int32(units_are_seconds); - ser_buf.set_double(beat_dur); - ser_buf.set_double(real_dur); - ser_buf.set_int32(len); - for (j = 0; j < len; j++) { - ser_buf.check_buffer(24); - Alg_event *event = (*this)[j]; - ser_buf.set_int32(event->get_selected()); - ser_buf.set_int32(event->get_type()); - ser_buf.set_int32(event->get_identifier()); - ser_buf.set_int32(event->chan); - ser_buf.set_double(event->time); - if (event->is_note()) { - ser_buf.check_buffer(20); - Alg_note *note = (Alg_note *) event; - ser_buf.set_float(note->pitch); - ser_buf.set_float(note->loud); - ser_buf.set_double(note->dur); - long parm_num_offset = ser_buf.get_posn(); - long parm_num = 0; - ser_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_buf.store_long(parm_num_offset, parm_num); - } else { - assert(event->is_update()); - Alg_update *update = (Alg_update *) event; - serialize_parameter(&(update->parameter)); - } - ser_buf.check_buffer(7); // maximum padding possible - ser_buf.pad(); - } - // write length, not including ALGT, including padding at end - ser_buf.store_long(length_offset, ser_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_buf.check_buffer(len); - ser_buf.set_string(parm->attr_name()); - ser_buf.pad(); - switch (parm->attr_type()) { - case 'r': - ser_buf.check_buffer(8); - ser_buf.set_double(parm->r); - break; - case 's': - ser_buf.check_buffer(strlen(parm->s) + 1); - ser_buf.set_string(parm->s); - break; - case 'i': - ser_buf.check_buffer(4); - ser_buf.set_int32(parm->i); - break; - case 'l': - ser_buf.check_buffer(4); - ser_buf.set_int32(parm->l); - break; - case 'a': - ser_buf.check_buffer(strlen(parm->a) + 1); - ser_buf.set_string(parm->a); - break; - } -} - - - -Alg_track *Alg_track::unserialize(void *buffer, long len) -{ - assert(len > 8); - ser_buf.init_for_read(buffer, len); - bool alg = ser_buf.get_char() == 'A' && - ser_buf.get_char() == 'L' && - ser_buf.get_char() == 'G'; - assert(alg); - char c = ser_buf.get_char(); - if (c == 'S') { - Alg_seq *seq = new Alg_seq; - seq->unserialize_seq(); - return seq; - } else { - assert(c == 'T'); - Alg_track *track = new Alg_track; - track->unserialize_track(); - return track; - } -} - - -void Alg_seq::unserialize_seq() -{ - ser_buf.check_input_buffer(28); - long len = ser_buf.get_int32(); - assert(ser_buf.get_len() >= len); - channel_offset_per_track = ser_buf.get_int32(); - units_are_seconds = (bool) ser_buf.get_int32(); - time_map = new Alg_time_map(); - time_map->last_tempo = ser_buf.get_double(); - time_map->last_tempo_flag = (bool) ser_buf.get_int32(); - long beats = ser_buf.get_int32(); - ser_buf.check_input_buffer(beats * 16 + 4); - int i; - for (i = 0; i < beats; i++) { - double time = ser_buf.get_double(); - double beat = ser_buf.get_double(); - time_map->insert_beat(time, beat); - // printf("time_map: %g, %g\n", time, beat); - } - long time_sig_len = ser_buf.get_int32(); - ser_buf.get_pad(); - ser_buf.check_input_buffer(time_sig_len * 24 + 8); - for (i = 0; i < time_sig_len; i++) { - double beat = ser_buf.get_double(); - double num = ser_buf.get_double(); - double den = ser_buf.get_double(); - time_sig.insert(beat, num, den); - } - long tracks_num = ser_buf.get_int32(); - ser_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_buf.get_posn() == len + 4); -} - - -void Alg_track::unserialize_track() -{ - ser_buf.check_input_buffer(32); - assert(ser_buf.get_char() == 'A'); - assert(ser_buf.get_char() == 'L'); - assert(ser_buf.get_char() == 'G'); - assert(ser_buf.get_char() == 'T'); - long offset = ser_buf.get_posn(); // stored length does not include 'ALGT' - long bytes = ser_buf.get_int32(); - assert(bytes <= ser_buf.get_len() - offset); - units_are_seconds = (bool) ser_buf.get_int32(); - beat_dur = ser_buf.get_double(); - real_dur = ser_buf.get_double(); - int event_count = ser_buf.get_int32(); - for (int i = 0; i < event_count; i++) { - ser_buf.check_input_buffer(24); - long selected = ser_buf.get_int32(); - char type = (char) ser_buf.get_int32(); - long key = ser_buf.get_int32(); - long channel = ser_buf.get_int32(); - double time = ser_buf.get_double(); - if (type == 'n') { - ser_buf.check_input_buffer(20); - float pitch = ser_buf.get_float(); - float loud = ser_buf.get_float(); - double dur = ser_buf.get_double(); - Alg_note *note = - create_note(time, channel, key, pitch, loud, dur); - note->set_selected(selected); - long param_num = ser_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(NULL); - 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); - unserialize_parameter(&(update->parameter)); - append(update); - } - ser_buf.get_pad(); - } - assert(offset + bytes == ser_buf.get_posn()); -} - - -void Alg_track::unserialize_parameter(Alg_parameter_ptr parm_ptr) -{ - char *attr = ser_buf.get_string(); - parm_ptr->attr = symbol_table.insert_string(attr); - switch (parm_ptr->attr_type()) { - case 'r': - ser_buf.check_input_buffer(8); - parm_ptr->r = ser_buf.get_double(); - break; - case 's': - parm_ptr->s = heapify(ser_buf.get_string()); - break; - case 'i': - ser_buf.check_input_buffer(4); - parm_ptr->i = ser_buf.get_int32(); - break; - case 'l': - ser_buf.check_input_buffer(4); - parm_ptr->l = (bool) ser_buf.get_int32(); - break; - case 'a': - parm_ptr->a = symbol_table.insert_attribute(ser_buf.get_string()); - break; - } -} - - -void Alg_track::set_time_map(Alg_time_map *map) -{ - if (time_map) time_map->dereference(); - if (map == NULL) { - 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 = false; - if (seq->get_type() == 'e') { - assert(seq->get_owner()->get_units_are_seconds() == 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() -{ - 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) -{ - // 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))) { - return; // redundant inserts are ignored here - } - // make room for new event - if (maxlen <= len) expand(); - len++; - // 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; - 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; -} - - -void Alg_time_sigs::cut(double start, double end) -{ - // remove time_sig's from start to start+len -- these must be - // in beats (not seconds) - // now rewrite time_sig[]: copy from i_in to i_out (more or less) - int i_in = 0; - int i_out = 0; - // first, figure out where to begin cut region - i_in = find_beat(start); - i_out = i_in; - // scan to end of cut region - while (i_in < len && time_sigs[i_in].beat < end) { - i_in = i_in + 1; - } - // change time_sig at start if necessary - // there's a time_sig that was skipped if i_in > i_out. - // if that's true and the next time change is at end, we're - // ok because it will be copied, but if the next time change - // is after end, then maybe we should insert a time change - // corresponding to what's in effect at end. We can skip this - // insert if it corresponds to whatever is in effect at start - if (i_in > i_out && i_in < len && - time_sigs[i_in].beat > end + ALG_EPS && - (i_out == 0 || time_sigs[i_out - 1].num != time_sigs[i_in - 1].num || - time_sigs[i_out - 1].den != time_sigs[i_in - 1].den)) { - time_sigs[i_out] = time_sigs[i_in - 1]; - time_sigs[i_out].beat = start; - } - // scan from end to len(time_sig) - while (i_in < length()) { - Alg_time_sig &ts = time_sigs[i_in]; - ts.beat = ts.beat - (end - start); - time_sigs[i_out] = ts; - i_in = i_in + 1; - i_out = i_out + 1; - } - len = i_out; -} - - -void Alg_time_sigs::trim(double start, double end) -{ - // remove time_sig's not in [start, start+end) - // units must be in beats (not seconds) - // copy from i_in to i_out as we scan time_sig array - int i_in = 0; - int i_out = 0; - // first, skip time signatures up to start - i_in = find_beat(start); - // 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; - } - // scan 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; -} - - -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 - // 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))) { - num_after_splice = time_sigs[i-1].num; - den_after_splice = time_sigs[i-1].den; - } else if (i < len && time_sigs[i].beat <= start + ALG_EPS) { - num_after_splice = time_sigs[i].num; - den_after_splice = time_sigs[i].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(); - // now insert initial time_signature at start. This may create - // an extra measure if seq does not begin on a measure boundary - insert(start, 4, 4); // in case seq uses default starting signature - //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(); i++) { - insert(start + from[i].beat, from[i].num, from[i].den); - } - //printf("time_sig::insert after pasting in sigs\n"); - //show(); - // now insert time signature at end of splice - insert(start + dur, 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 beat, double len) -{ - int i; - // find the time_sig entry in effect at t - for (i = 0; i < len; i++) { - if (time_sigs[i].beat < beat + ALG_EPS) { - break; - } - } - // now, increase beat times by len - for (; i < len; i++) { - time_sigs[i].beat += len; - } -} - - -Alg_tracks::~Alg_tracks() -{ - // Alg_events objects (track data) are not deleted, only the array - if (tracks) { - delete[] tracks; - } -} - - -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++) { - delete tracks[i]; - } - if (tracks) delete [] tracks; - tracks = NULL; - len = 0; - maxlen = 0; // Modified by Ning Hu Nov.19 2002 -} - - -Alg_seq::Alg_seq(const char *filename, bool smf) -{ - 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); - } else { - error = alg_read(inf, this); - } - inf.close(); -} - - -Alg_seq::Alg_seq(istream &file, bool smf) -{ - basic_initialization(); - if (smf) { - error = alg_smf_read(file, this); - } else { - error = alg_read(file, this); - } -} - -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 GCC diagnostic ignored "-Wreturn-type" // ok not to return a value here -Alg_event_ptr &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++; - } - assert(false); // out of bounds -} - - -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 -{ - // fix parameters to fall within existing sequence - if (start > get_dur()) return NULL; // nothing to cut - if (start < 0) start = 0; // can't start before sequence starts - if (start + len > get_dur()) // can't cut after end: - len = get_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_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 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); - time_map->cut(start, len, units_are_seconds); - set_dur(get_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); - } - if (time_sig.length() > 0) { - time_sig.insert_beats(t_beats, len_beats); - } -} - - -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 NULL; // 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; open 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 - if (start > get_dur()) return; // nothing to cut - if (start < 0) start = 0; // can't start before sequence starts - if (start + len > get_dur()) // can't cut after end: - len = get_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; - if (units_are_seconds) { - ts_start = time_map->time_to_beat(ts_start); - ts_end = time_map->time_to_beat(ts_end); - } - - // 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); - time_map->cut(start, len, units_are_seconds); - set_dur(get_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; -} - - -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); - } - */ -} - - -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(); - bool result = time_map->set_tempo(bpm, start_beat, end_beat); - if (units_should_be_seconds) convert_to_seconds(); - return result; -} - - -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_seq::iteration_begin() -{ - // keep an array of indexes into tracks - current = new long[track_list.length()]; - int i; - for (i = 0; i < track_list.length(); i++) { - current[i] = 0; - } -} - - -Alg_event_ptr Alg_seq::iteration_next() - // return the next event in time from any track -{ - long cur; // a track index - // find lowest next time of any track: - double next = 1000000.0; - int i, track = 0; - for (i = 0; i < track_list.length(); i++) { - Alg_track &tr = track_list[i]; - cur = current[i]; - if (cur < tr.length() && tr[cur]->time < next) { - next = tr[cur]->time; - track = i; - } - } - if (next < 1000000.0) { - return track_list[track][current[track]++]; - } else { - return NULL; - } -} - - -void Alg_seq::iteration_end() -{ - delete[] current; -} - - -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]; - iteration_begin(); - long notes_index = 0; - - Alg_event_ptr event; - while (( event = iteration_next() )) { - notes[notes_index++] = event; - } - track_list.reset(); // don't need them any more - add_track(0); - track(0)->set_events(notes, sum, sum); - iteration_end(); -} - - -// 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 e83d4b46375..00000000000 --- a/plugins/MidiImport/portsmf/allegro.h +++ /dev/null @@ -1,945 +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__ -#define __ALLEGRO__ -#include "debug.h" - -#include "lmmsconfig.h" - -#define ALG_EPS 0.000001 // epsilon -#define ALG_DEFAULT_BPM 100.0 // default tempo - -// are d1 and d2 within epsilon of each other? -bool within(double d1, double d2, double epsilon); - -char *heapify(const char *s); // put a string on the heap - - -// Alg_attribute is an atom in the symbol table -// with the special addition that the last -// character is prefixed to the string; thus, -// the attribute 'tempor' (a real) is stored -// as 'rtempor'. To get the string name, just -// use attribute+1. -typedef char *Alg_attribute; -#define alg_attr_name(a) ((a) + 1) -#define alg_attr_type(a) (*(a)) - -// Alg_atoms is a symbol table of Alg_attributes and other -// unique strings -class Alg_atoms { -public: - Alg_atoms() { - maxlen = len = 0; - atoms = NULL; - } - // insert/lookup an atttribute - Alg_attribute insert_attribute(Alg_attribute attr); - // insert/lookup attribute by name (without prefixed type) - Alg_attribute insert_string(const char *name); -private: - long maxlen; - long len; - char **atoms; - - // insert an Attriubute not in table after moving attr to heap - Alg_attribute insert_new(const char *name, char attr_type); - void expand(); // make more space -}; - -extern Alg_atoms symbol_table; - - -// an attribute/value pair. Since Alg_attribute names imply type, -// we try to keep attributes and values packaged together as -// Alg_parameter class -typedef class Alg_parameter { -public: - ~Alg_parameter(); - Alg_attribute attr; - union { - double r;// real - char *s; // string - long i; // integer - bool l; // logical - char *a; // symbol (atom) - }; // anonymous union - void copy(Alg_parameter *); // copy from another parameter - char attr_type() { return alg_attr_type(attr); } - char *attr_name() { return alg_attr_name(attr); } - void set_attr(Alg_attribute a) { attr = a; } - void show(); -} *Alg_parameter_ptr; - - -// a list of attribute/value pairs -typedef class Alg_parameters { -public: - class Alg_parameters *next; - Alg_parameter parm; - - Alg_parameters(Alg_parameters *list) { - next = list; - } - - //~Alg_parameters() { } - - // each of these routines takes address of pointer to the list - // insertion is performed without checking whether or not a - // parameter already exists with this attribute. See find() and - // remove_key() to assist in checking for and removing existing - // parameters. - // Note also that these insert_* methods convert name to an - // attribute. If you have already done the symbol table lookup/insert - // you can do these operations faster (in which case we should add - // another set of functions that take attributes as arguments.) - static void insert_real(Alg_parameters **list, char *name, double r); - // insert string will copy string to heap - static void insert_string(Alg_parameters **list, char *name, char *s); - static void insert_integer(Alg_parameters **list, char *name, long i); - static void insert_logical(Alg_parameters **list, char *name, bool l); - static void insert_atom(Alg_parameters **list, char *name, char *s); - static Alg_parameters *remove_key(Alg_parameters **list, 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(char *attr, char *value); - void set_real_value(char *attr, double value); - void set_logical_value(char *attr, bool value); - void set_integer_value(char *attr, long value); - void set_atom_value(char *attr, char *atom); - - // Some note methods. These fail (via assert()) if this is not a note: - // - float get_pitch();// get pitch in steps -- use this even for MIDI - float get_loud(); // get loudness (MIDI velocity) - // times are in seconds or beats, depending upon the units_are_seconds - // flag in the containing sequence - double get_start_time(); // get start time in seconds or beats - double get_end_time(); // get end time in seconds or beats - double get_duration(); // get duration in seconds or beats - void set_pitch(float); - void set_loud(float); - void set_duration(double); - - // Notes have lists of attribute values. Attributes are converted - // to/from strings in this API to avoid explicit use of Alg_attribute - // types. Attribute names end with a type designation: 's', 'r', 'l', - // 'i', or 'a'. - // - bool has_attribute(char *attr); // test if note has attribute/value pair - char get_attribute_type(char *attr); // get the associated type: - // 's' = string, - // 'r' = real (double), 'l' = logical (bool), 'i' = integer (long), - // 'a' = atom (char *), a unique string stored in Alg_seq - char *get_string_value(char *attr, char *value = NULL); // get the string value - double get_real_value(char *attr, double value = 0.0); // get the real value - bool get_logical_value(char *attr, bool value = false); // get the logical value - long get_integer_value(char *attr, long value = 0); // get the integer value - char *get_atom_value(char *attr, char *value = NULL); // get the atom value - void delete_attribute(char *attr); // delete an attribute/value pair - // (ignore if no matching attribute/value pair exists) - - // Some attribute/value methods. These fail if this is not an update. - // Attributes are converted to/from strings to avoid explicit use - // of Alg_attribute types. - // - const char *get_attribute(); // get the update's attribute (string) - char get_update_type(); // get the update's type: 's' = string, - // 'r' = real (double), 'l' = logical (bool), 'i' = integer (long), - // 'a' = atom (char *), a unique string stored in Alg_seq - char *get_string_value(); // get the update's string value - // Notes: Caller does not own the return value. Do not modify. - // Do not use after underlying Alg_seq is modified. - double get_real_value(); // get the update's real value - bool get_logical_value(); // get the update's logical value - long get_integer_value(); // get the update's integer value - char *get_atom_value(); // get the update's atom value - // Notes: Caller does not own the return value. Do not modify. - // The return value's lifetime is forever. - - // Auxiliary function to aid in editing tracks - // Returns true if the event overlaps the given region - bool overlap(double t, double len, bool all); - - const char *GetDescription(); // computes a text description of this event - // the result is in a static buffer, not thread-safe, just for debugging. - Alg_event() { selected = false; } - virtual ~Alg_event() {} -} *Alg_event_ptr; - - -typedef class Alg_note : public Alg_event { -public: - virtual ~Alg_note(); - Alg_note(Alg_note *); // copy constructor - float pitch; // pitch in semitones (69 = A440) - float loud; // dynamic corresponding to MIDI velocity - double dur; // duration in seconds (normally to release point) - Alg_parameters_ptr parameters; // attribute/value pair list - Alg_note() { type = 'n'; parameters = NULL; } - void show(); -} *Alg_note_ptr; - - -typedef class Alg_update : public Alg_event { -public: - virtual ~Alg_update() {}; - Alg_update(Alg_update *); // copy constructor - Alg_parameter parameter; // an update contains one attr/value pair - - - Alg_update() { type = 'u'; } - void show(); -} *Alg_update_ptr; - - -// a sequence of Alg_event objects -typedef class Alg_events { -private: - long maxlen; - void expand(); -protected: - long len; - Alg_event_ptr *events; // events is array of pointers -public: - // sometimes, it is nice to have the time of the last note-off. - // In the current implementation, - // this field is set by append to indicate the time of the - // last note-off in the current unit, so it should be correct after - // creating a new track and adding notes to it. It is *not* - // updated after uninsert(), so use it with care. - double last_note_off; - virtual int length() { return len; } - Alg_event_ptr &operator[](int i) { - assert(i >= 0 && i < len); - return events[i]; - } - Alg_events() { - maxlen = len = 0; - events = NULL; - last_note_off = 0; - } - // destructor deletes the events array, but not the - // events themselves - ~Alg_events(); - void set_events(Alg_event_ptr *e, long l, long m) { - if (events) delete [] events; - events = e; len = l; maxlen = m; } - // for use by Alg_track and Alg_seq - void insert(Alg_event_ptr event); - void append(Alg_event_ptr event); - Alg_event_ptr uninsert(long index); -} *Alg_events_ptr; - -class Alg_track; - -typedef class Alg_event_list : public Alg_events { -protected: - char type; // 'e' Alg_event_list, 't' Alg_track, 's' Alg_seq - static const char *last_error_message; - Alg_track *events_owner; // if this is an Alg_event_list, - // the events are owned by an Alg_track or an Alg_seq - static int sequences; // to keep track of sequence numbers - int sequence_number; // this sequence number is incremented - // whenever an edit is performed on an Alg_track or Alg_seq. - // When an Alg_event_list is created to contain pointers to - // a subset of an Alg_track or Alg_seq (the events_owner), - // the Alg_event_list gets a copy of the events_owner's - // sequence_number. If the events_owner is edited, the pointers - // in this Alg_event_list will become invalid. This is detected - // (for debugging) as differing sequence_numbers. - - // every event list, track, and seq has a duration. - // Usually the duration is set when the list is constructed, e.g. - // when you extract from 10 to 15 seconds, the duration is 5 secs. - // The duration does not tell you when is the last note-off. - // duration is recorded in both beats and seconds: - double beat_dur; - double real_dur; -public: - // the client should not create one of these, but these are - // returned from various track and seq operations. An - // Alg_event_list "knows" the Alg_track or Alg_seq that "owns" - // the events. All events in an Alg_event_list must belong - // to the same Alg_track or Alg_seq structure. - // When applied to an Alg_seq, events are enumerated track - // by track with increasing indices. This operation is not - // particularly fast on an Alg_seq. - virtual Alg_event_ptr &operator[](int i); - Alg_event_list() { sequence_number = 0; - beat_dur = 0.0; real_dur = 0.0; events_owner = NULL; type = 'e'; } - Alg_event_list(Alg_track *owner); - - char get_type() { return type; } - Alg_track *get_owner() { return events_owner; } - - // The destructor does not free events because they are owned - // by a track or seq structure. - virtual ~Alg_event_list(); - - // Returns the duration of the sequence in beats or seconds - double get_beat_dur() { return beat_dur; } - void set_beat_dur(double d) { beat_dur = d; } - double get_real_dur() { return real_dur; } - void set_real_dur(double d) { real_dur = d; } - - // Events are stored in time order, so when you change the time of - // an event, you must adjust the position. When you call set_start_time - // on an Alg_event_list, the Alg_event_list is not modified, but the - // Alg_track that "owns" the event is modified. If the owner is an - // Alg_seq, this may require searching the seq for the track containing - // the event. This will mean a logN search of every track in the seq - // (but if this turns out to be a problem, we can store each event's - // track owner in the Alg_event_list.) - virtual void set_start_time(Alg_event *event, double); - // get text description of run-time errors detected, clear error - const char *get_last_error_message() { return last_error_message; } - // Implementation hint: keep a sequence number on each Alg_track that is - // incremented anytime there is a structural change. (This behavior is - // inherited by Alg_seq as well.) Copy the sequence number to any - // Alg_event_list object when it is created. Whenever you access an - // Alg_event_list, using operator[], assert that the Alg_event_list sequence - // number matches the Alg_seq sequence number. This will guarantee that you - // do not try to retain pointers to events beyond the point where the events - // may no longer exist. -} *Alg_event_list_ptr, &Alg_event_list_ref; - - -// Alg_beat is used to contruct a tempo map -typedef class Alg_beat { -public: - Alg_beat(double t, double b) { - time = t; beat = b; } - Alg_beat() {}; - double time; - double beat; -} *Alg_beat_ptr; - - -// Alg_beats is a list of Alg_beat objects used in Alg_seq -typedef class Alg_beats { -private: - long maxlen; - void expand(); -public: - long len; - Alg_beat_ptr beats; - Alg_beat &operator[](int i) { - assert(i >= 0 && i < len); - return beats[i]; - } - Alg_beats() { - maxlen = len = 0; - beats = NULL; - expand(); - beats[0].time = 0; - beats[0].beat = 0; - len = 1; - } - ~Alg_beats() { - if (beats) delete[] beats; - } - void insert(long i, Alg_beat_ptr beat); -} *Alg_beats_ptr; - - -typedef class Alg_time_map { -private: - int refcount; -public: - Alg_beats beats; // array of Alg_beat - double last_tempo; - bool last_tempo_flag; - Alg_time_map() { - last_tempo = ALG_DEFAULT_BPM / 60.0; // note: this value ignored until - // last_tempo_flag is set; nevertheless, the default - // tempo is 100. - last_tempo_flag = true; - refcount = 0; - } - Alg_time_map(Alg_time_map *map); // copy constructor - long length() { return beats.len; } - void show(); - long locate_time(double time); - long locate_beat(double beat); - double beat_to_time(double beat); - double time_to_beat(double time); - // Time map manipulations: it is prefered to call the corresponding - // methods in Alg_seq. If you manipulate an Alg_time_map directly, - // you should take care to convert all tracks that use the time map - // to beats or seconds as appropriate: Normally if you insert a beat - // you want tracks to be in time units and if you insert a tempo change - // you want tracks to be in beat units. - void insert_beat(double time, double beat); // add a point to the map - bool insert_tempo(double tempo, double beat); // insert a tempo change - // set the tempo over a region - bool set_tempo(double tempo, double start_beat, double end_beat); - void cut(double start, double len, bool units_are_seconds); - void trim(double start, double end, bool units_are_seconds); - void paste(double start, Alg_track *tr); - // insert a span of time. If start is at a tempo change, then - // the span of time runs at the changed tempo - void insert_time(double start, double len); - // insert a span of beats. If start is at a tempo change, the - // tempo change takes effect before the inserted beats - void insert_beats(double start, double len); - void dereference() { - if (--refcount <= 0) delete this; - } - void reference() { - refcount++; - } -} *Alg_time_map_ptr; - - -typedef class Serial_buffer { -private: - char *buffer; - char *ptr; - long len; -public: - Serial_buffer() { - buffer = NULL; - ptr = NULL; - len = 0; - } - void init_for_write() { ptr = buffer; } - long get_posn() { return (long) (ptr - buffer); } - long get_len() { return len; } - // store_long writes a long at a given offset - void store_long(long offset, long value) { - assert(offset <= get_posn() - 4); - long *loc = (long *) (buffer + offset); - *loc = value; - } - void check_buffer(long needed); - void set_string(char *s) { - char *fence = buffer + len; - assert(ptr < fence); - while ((*ptr++ = *s++)) assert(ptr < fence); - // assert((char *)(((long) (ptr + 7)) & ~7) <= fence); - pad(); } - void set_int32(long v) { *((long *) ptr) = v; ptr += 4; } - void set_double(double v) { *((double *) ptr) = v; ptr += 8; } - void set_float(float v) { *((float *) ptr) = v; ptr += 4; } - void set_char(char v) { *ptr++ = v; } -#ifdef LMMS_BUILD_WIN64 - void pad() { while (((long long) ptr) & 7) set_char(0); } -#else - void pad() { while (((long) ptr) & 7) set_char(0); } -#endif - void *to_heap(long *len) { - *len = get_posn(); - char *newbuf = new char[*len]; - memcpy(newbuf, buffer, *len); - return newbuf; - } - void init_for_read(void *buf, long n) { - buffer = (char *) buf; - ptr = (char *) buf; - len = n; - } - char get_char() { return *ptr++; } - long get_int32() { long i = *((long *) ptr); ptr += 4; return i; } - float get_float() { float f = *((float *) ptr); ptr += 4; return f; } - double get_double() { double d = *((double *) ptr); ptr += sizeof(double); - return d; } - char *get_string() { char *s = ptr; char *fence = buffer + len; - assert(ptr < fence); - while (*ptr++) assert(ptr < fence); - get_pad(); - return s; } -#ifdef LMMS_BUILD_WIN64 - void get_pad() { while (((long long) ptr) & 7) ptr++; } -#else - void get_pad() { while (((long) ptr) & 7) ptr++; } -#endif - void check_input_buffer(long needed) { - assert(get_posn() + needed <= len); } -} *Serial_buffer_ptr; - -typedef class Alg_seq *Alg_seq_ptr; - -typedef class Alg_track : public Alg_event_list { -protected: - Alg_time_map *time_map; - bool units_are_seconds; - char *get_string(char **p, long *b); - long get_int32(char **p, long *b); - double get_double(char **p, long *b); - float get_float(char **p, long *b); - static Serial_buffer ser_buf; - void serialize_parameter(Alg_parameter *parm); - // *buffer_ptr points to binary data, bytes_ptr points to how many - // bytes have been used so far, len is length of binary data - void unserialize_parameter(Alg_parameter_ptr parm_ptr); -public: - void serialize_track(); - void unserialize_track(); - virtual Alg_event_ptr &operator[](int i) { - assert(i >= 0 && i < len); - return events[i]; - } - Alg_track() { units_are_seconds = false; time_map = NULL; - set_time_map(NULL); type = 't'; } - // initialize empty track with a time map - Alg_track(Alg_time_map *map, bool seconds); - - Alg_event_ptr copy_event(Alg_event_ptr event); // make a complete copy - - Alg_track(Alg_track &track); // copy constructor, does not copy time_map - // copy constructor: event_list is copied, map is installed and referenced - Alg_track(Alg_event_list_ref event_list, Alg_time_map_ptr map, - bool units_are_seconds); - virtual ~Alg_track() { set_time_map(NULL); } - - // Returns a buffer containing a serialization of the - // file. It will be an ASCII representation unless text is true. - // *buffer gets a newly allocated buffer pointer. The caller must free it. - // *len gets the length of the serialized track - virtual void serialize(void **buffer, long *bytes); - - // Try to read from a memory buffer. Automatically guess - // whether it's MIDI or text. - static Alg_track *unserialize(void *buffer, long len); - - // If the track is really an Alg_seq and you need to access an - // Alg_seq method, coerce to an Alg_seq with this function: - Alg_seq_ptr to_alg_seq() { - return (get_type() == 's' ? (Alg_seq_ptr) this : NULL); } - - // Are we using beats or seconds? - bool get_units_are_seconds() { return units_are_seconds; } - // Change units - virtual void convert_to_beats(); - virtual void convert_to_seconds(); - void set_dur(double dur); - double get_dur() { return (units_are_seconds ? real_dur : beat_dur); } - - // Every Alg_track may have an associated time_map. If no map is - // specified, or if you set_time_map(NULL), then the behavior - // should be as if there is a constant tempo of 100 beats/minute - // (this constant is determined by ALG_DEFAULT_BPM). - // Recommendation: create a static global tempo map object. When - // any operation that needs a tempo map gets NULL, use the global - // tempo map. (Exception: any operation that would modify the - // tempo map should raise an error -- you don't want to change the - // default tempo map.) - virtual void set_time_map(Alg_time_map *map); - Alg_time_map *get_time_map() { return time_map; } - - // Methods to create events. The returned event is owned by the caller. - // Use delete to get rid of it unless you call add() -- see below. - // - Alg_note *create_note(double time, int channel, int identifier, - float pitch, float loudness, double duration); - // Note: after create_update(), caller should use set_*_value() to - // initialize the attribute/value pair: - Alg_update *create_update(double time, int channel, int identifier); - // Adds a new event - it is automatically inserted into the - // correct order in the sequence based on its timestamp. - // The ownership passes from the caller to this Alg_seq. The - // event is not copied. - virtual void add(Alg_event *event) { insert(event); } - - // - // Editing regions - // - - // Deletes the notes that start within the given region - // and returns them in a new sequence. The start times - // of the notes in the returned sequence are shifted - // by -t. The notes after the region get shifted over - // to fill the gap. In an Alg_seq, the tempo track is edited - // in a similar way - // and the cut tempo information is retained in the new seq. - // ONLY NOTES THAT START WITHIN THE REGION ARE CUT unless - // "all" is true in which case all notes that intersect - // the region are copied. CUT NOTES - // MAY EXTEND BEYOND THE DURATION OF THE RESULTING SEQ. - // The return type is the same as this (may be Alg_seq). - // All times including len are interpreted according to - // units_are_seconds in the track. - virtual Alg_track *cut(double t, double len, bool all); - - // Like cut() but doesn't remove the notes from the original - // sequence. The Alg_events are copied, not shared. ONLY EVENTS - // THAT START WITHIN THE REGION ARE COPIED unless "all" is true - // in which case all notes that intersect the region are - // copied. COPIED NOTES MAY - // EXTEND BEYOND THE DURATION OF THE RESULTING SEQ. - // The return type is the same as this (may be Alg_seq). - virtual Alg_track *copy(double t, double len, bool all); - - // Inserts a sequence in the middle, shifting some notes - // over by the duration of the seq, which is first converted - // to the same units (seconds or beats) as this. (This makes - // a differece because the pasted data may change the tempo, - // and notes that overlap the borders will then experience - // a tempo change.) - // THE SEQ PARAMETER IS NOT MODIFIED, AND Alg_event's ARE - // COPIED, NOT SHARED. - // The type of seq must be Alg_seq if seq is an Alg_seq, or - // Alg_track if seq is an Alg_track or an Alg_event_list. - virtual void paste(double t, Alg_event_list *seq); // Shifts notes - - // Merges two sequences with a certain offset. The offset is - // interpreted as either beats or seconds according to the - // current units of this, and seq is converted to the same - // units as this. Except for a possible conversion to beats - // or seconds, the tempo track of seq (if any) is ignored. - // (There is no way to merge tempo tracks.) - // THE SEQ PARAMETER IS NOT MODIFIED, AND Alg_event's ARE - // COPIED, NOT SHARED. - // The type of seq must be Alg_seq if seq is an Alg_seq, or - // Alg_track if seq is an Alg_track or an Alg_event_list. - virtual void merge(double t, Alg_event_list_ptr seq); - - // Deletes and shifts notes to fill the gap. The tempo track - // is also modified accordingly. ONLY EVENTS THAT START WITHIN - // THE REGION ARE DELETED unless "all" is true, in which case - // all notes that intersect the region are cleared. - // NOTES THAT EXTEND FROM BEFORE THE - // REGION INTO THE REGION RETAIN THEIR DURATION IN EITHER - // BEATS OR SECONDS ACCORDING TO THE CURRENT UNITS OF this. - virtual void clear(double t, double len, bool all); - - // Deletes notes but doesn't shift. If the "all" argument - // is true, deletes all notes that intersect the range at all, - // not just those that start within it. The tempo track is - // not affected. - virtual void silence(double t, double len, bool all); - - // Simply shifts notes past time t over by len, which is given - // in either beats or seconds according to the units of this. - // The resulting interveal (t, t+len) may in fact contain notes - // that begin before t. The durations of notes are not changed. - // If this is an Alg_seq, the tempo track is expanded at t also. - virtual void insert_silence(double t, double len); - - // - // Accessing for screen display - // - - // A useful generic function to retrieve only certain - // types of events. The masks should be bit-masks defined - // somewhere else. Part of the mask allows us to search for - // selected events. If this is an Alg_seq, search all tracks - // (otherwise, call track[i].find()) - // If channel_mask == 0, accept ALL channels - virtual Alg_event_list *find(double t, double len, bool all, - long channel_mask, long event_type_mask); - - // - // MIDI playback - // - // See Alg_iterator -} *Alg_track_ptr, &Alg_track_ref; - - -// Alg_time_sig represents a single time signature; -// although not recommended, time_signatures may have arbitrary -// floating point values, e.g. 4.5 beats per measure -typedef class Alg_time_sig { -public: - double beat; // when does this take effect? - double num; // what is the "numerator" (top number?) - double den; // what is the "denominator" (bottom number?) - Alg_time_sig(double b, double n, double d) { - beat = b; num = n; den = d; - } - Alg_time_sig() { - beat = 0; num = 0; den = 0; - } - void beat_to_measure(double beat, double *measure, double *m_beat, - double *num, double *den); - -} *Alg_time_sig_ptr; - - -// Alg_time_sigs is a dynamic array of time signatures -// -// The default (empty) time_sigs has 4/4 time at beat 0. -// Each time_sig object in time_sigs represents the beginning -// of a measure. If there is a beat missing, e.g. in the first -// measure, you can represent this by inserting another -// time_sig at the next measure beginning. Each time_sig implies -// an infinite sequence of full measures until the next time_sig. -// If you insert a time_sig and one already exist near the same -// beat, the old one is replaced, thus re-barring every measure -// until the next time_sig. -class Alg_time_sigs { -private: - long maxlen; - void expand(); // make more space - long len; - Alg_time_sig_ptr time_sigs; -public: - Alg_time_sigs() { - maxlen = len = 0; - time_sigs = NULL; - } - Alg_time_sig &operator[](int i) { // fetch a time signature - assert(i >= 0 && i < len); - return time_sigs[i]; - } - ~Alg_time_sigs() { - if (time_sigs) delete[] time_sigs; - } - void show(); - long length() { return len; } - int find_beat(double beat); - void insert(double beat, double num, double den); - void cut(double start, double end); // remove from start to end - void trim(double start, double end); // retain just start to end - void paste(double start, Alg_seq *seq); - void insert_beats(double beat, double len); // insert len beats at beat -}; - - -// a sequence of Alg_events objects -typedef class Alg_tracks { -private: - long maxlen; - void expand(); - void expand_to(int new_max); - long len; -public: - Alg_track_ptr *tracks; // tracks is array of pointers - Alg_track &operator[](int i) { - assert(i >= 0 && i < len); - return *tracks[i]; - } - long length() { return len; } - Alg_tracks() { - maxlen = len = 0; - tracks = NULL; - } - ~Alg_tracks(); - // Append a track to tracks. This Alg_tracks becomes the owner of track. - void append(Alg_track_ptr track); - void add_track(int track_num, Alg_time_map_ptr time_map, bool seconds); - void reset(); -} *Alg_tracks_ptr; - - -typedef enum { - alg_no_error = 0, // no error reading Allegro or MIDI file - alg_error_open = -800, // could not open Allegro or MIDI file - alg_error_syntax // something found in the file that could not be parsed; - // generally you should ignore syntax errors or look at the printed error messages - // because there are some things in standard midi files that we do not handle; - // (maybe we should only set alg_error_syntax when there is a real problem with - // the file as opposed to when there is some warning message for the user) -} Alg_error; - - -// An Alg_seq is an array of Alg_events, each a sequence of Alg_event, -// with a tempo map and a sequence of time signatures -// -typedef class Alg_seq : public Alg_track { -protected: - long *current; // array of indexes used by iteration methods - void serialize_seq(); - Alg_error error; // error code set by file readers - // an internal function used for writing Allegro track names - Alg_event_ptr write_track_name(std::ostream &file, int n, - Alg_events &events); -public: - int channel_offset_per_track; // used to encode track_num into channel - Alg_tracks track_list; // array of Alg_events - Alg_time_sigs time_sig; - int beat_x; - void basic_initialization() { - error = alg_no_error; - units_are_seconds = true; type = 's'; - channel_offset_per_track = 0; - add_track(0); // default is one empty track - } - Alg_seq() { - basic_initialization(); - } - // copy constructor -- if track is an Alg_seq, make a copy; if - // track is just an Alg_track, the track becomes track 0 - Alg_seq(Alg_track_ref track) { seq_from_track(track); } - Alg_seq(Alg_track_ptr track) { seq_from_track(*track); } - void seq_from_track(Alg_track_ref tr); - Alg_seq(std::istream &file, bool smf); // create from file - Alg_seq(const char *filename, bool smf); // create from filename - ~Alg_seq(); - int get_read_error() { return error; } - void serialize(void **buffer, long *bytes); - void copy_time_sigs_to(Alg_seq *dest); // a utility function - void set_time_map(Alg_time_map *map); - - // encode sequence structure into contiguous, moveable memory block - // address of newly allocated memory is assigned to *buffer, which must - // be freed by caller; the length of data is assigned to *len - void unserialize_seq(); - - // write an ascii representation to file - void write(std::ostream &file, bool in_secs); - // returns true on success - bool write(const char *filename); - void smf_write(std::ofstream &file); - bool smf_write(const char *filename); - - // Returns the number of tracks - int tracks(); - - // create a track - void add_track(int track_num) { - track_list.add_track(track_num, get_time_map(), units_are_seconds); - } - - // Return a particular track. This Alg_seq owns the track, so the - // caller must not delete the result. - Alg_track_ptr track(int); - - virtual Alg_event_ptr &operator[](int i); - - virtual void convert_to_seconds(); - virtual void convert_to_beats(); - - Alg_track_ptr cut_from_track(int track_num, double start, double dur, - bool all); - Alg_seq *cut(double t, double len, bool all); - void insert_silence_in_track(int track_num, double t, double len); - void insert_silence(double t, double len); - Alg_track_ptr copy_track(int track_num, double t, double len, bool all); - Alg_seq *copy(double start, double len, bool all); - void paste(double start, Alg_seq *seq); - virtual void clear(double t, double len, bool all); - virtual void merge(double t, Alg_event_list_ptr seq); - virtual void silence(double t, double len, bool all); - void clear_track(int track_num, double start, double len, bool all); - void silence_track(int track_num, double start, double len, bool all); - Alg_event_list_ptr find_in_track(int track_num, double t, double len, - bool all, long channel_mask, - long event_type_mask); - - // find index of first score event after time - long seek_time(double time, int track_num); - bool insert_beat(double time, double beat); - // warning: insert_tempo may change representation from seconds to beats - bool insert_tempo(double bpm, double beat); - - // add_event takes a pointer to an event on the heap. The event is not - // copied, and this Alg_seq becomes the owner and freer of the event. - void add_event(Alg_event_ptr event, int track_num); - void add(Alg_event_ptr event) { assert(false); } // call add_event instead - // warning: set_tempo may change representation from seconds to beats - bool set_tempo(double bpm, double start_beat, double end_beat); - void set_time_sig(double beat, double num, double den); - void beat_to_measure(double beat, long *measure, double *m_beat, - double *num, double *den); - // void set_events(Alg_event_ptr *events, long len, long max); - void merge_tracks(); // move all track data into one track - void iteration_begin(); // prepare to enumerate events in order - Alg_event_ptr iteration_next(); // return next event (or NULL) - void iteration_end(); // clean up after enumerating events -} *Alg_seq_ptr, &Alg_seq_ref; - - -// see Alg_seq::Alg_seq() constructors that read from files -// the following are for internal library implementation and are -// moved to *_internal.h header files. -//Alg_seq_ptr alg_read(std::istream &file, Alg_seq_ptr new_seq); -//Alg_seq_ptr alg_smf_read(std::istream &file, Alg_seq_ptr new_seq); -#endif diff --git a/plugins/MidiImport/portsmf/allegrord.cpp b/plugins/MidiImport/portsmf/allegrord.cpp deleted file mode 100644 index 7a1f5beed6f..00000000000 --- a/plugins/MidiImport/portsmf/allegrord.cpp +++ /dev/null @@ -1,753 +0,0 @@ -#include "debug.h" -#include "stdlib.h" -#include "string.h" -#include "ctype.h" -#include "trace.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; - - 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, 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; -} - - -Alg_error alg_read(istream &file, Alg_seq_ptr new_seq) - // read a sequence from allegro file -{ - assert(new_seq); - Alg_reader alg_reader(&file, new_seq); - bool err = alg_reader.parse(); - return (err ? alg_error_syntax : alg_no_error); -} - - -void Alg_reader::readline() -{ - // a word about memory management: this Alg_reader has a - // member variable input_line that holds a line of input - // it is reused for each line. input_line is parsed by - // line_parser, which holds a reference to input_line - line_parser_flag = false; - if (getline(*file, input_line)) { - line_parser.init(&input_line); - line_parser_flag = true; - error_flag = false; - } -} - - -Alg_parameters_ptr Alg_reader::process_attributes( - Alg_parameters_ptr attributes, double time) -{ - // print "process_attributes:", attributes - bool ts_flag = false; - if (attributes) { - Alg_parameters_ptr a; - bool in_seconds = seq->get_units_are_seconds(); - if ((a = Alg_parameters::remove_key(&attributes, "tempor"))) { - double tempo = a->parm.r; - seq->insert_tempo(tempo, seq->get_time_map()->time_to_beat(time)); - } - if ((a = Alg_parameters::remove_key(&attributes, "beatr"))) { - double beat = a->parm.r; - seq->insert_beat(time, beat); - } - if ((a = Alg_parameters::remove_key(&attributes, "timesig_numr"))) { - tsnum = a->parm.r; - ts_flag = true; - } - if ((a = Alg_parameters::remove_key(&attributes, "timesig_denr"))) { - tsden = a->parm.r; - ts_flag = true; - } - if (ts_flag) { - seq->set_time_sig(seq->get_time_map()->time_to_beat(time), - tsnum, tsden); - } - if (in_seconds) seq->convert_to_seconds(); - } - return attributes; // in case it was modified -} - - -bool Alg_reader::parse() -{ - int voice = 0; - int key = 60; - double loud = 100.0; - double pitch = 60.0; - double dur = 1.0; - double time = 0.0; - int track_num = 0; - seq->convert_to_seconds(); - //seq->set_real_dur(0.0); // just in case it's not initialized already - readline(); - bool valid = false; // ignore blank lines - while (line_parser_flag) { - bool time_flag = false; - bool next_flag = false; - double next; - bool voice_flag = false; - bool loud_flag = false; - bool dur_flag = false; - bool new_pitch_flag = false; // "P" syntax or "A"-"G" syntax - double new_pitch = 0.0; - bool new_key_flag = false; // "K" syntax - int new_key = 0; - Alg_parameters_ptr attributes = NULL; - if (line_parser.peek() == '#') { - // look for #track - line_parser.get_nonspace_quoted(field); - if (streql(field.c_str(), "#track")) { - line_parser.get_nonspace_quoted(field); // number - field.insert(0, " "); // need char at beginning because - // parse_int ignores the first character of the argument - track_num = parse_int(field); - seq->add_track(track_num); - } - // maybe we have a sequence or track name - line_parser.get_remainder(field); - // if there is a non-space character after #track n then - // use it as sequence or track name. Note that because we - // skip over spaces, a sequence or track name cannot begin - // with leading blanks. Another decision is that the name - // must be at time zero - if (field.length() > 0) { - // insert the field as sequence name or track name - Alg_update_ptr update = new Alg_update; - update->chan = -1; - update->time = 0; - update->set_identifier(-1); - // sequence name is whatever is on track 0 - // other tracks have track names - const char *attr = - (track_num == 0 ? "seqnames" : "tracknames"); - update->parameter.set_attr(symbol_table.insert_string(attr)); - update->parameter.s = heapify(field.c_str()); - seq->add_event(update, track_num); - } - } else { - // we must have a track to insert into - if (seq->tracks() == 0) seq->add_track(0); - line_parser.get_nonspace_quoted(field); - char pk = line_parser.peek(); - // attributes are parsed as two adjacent nonspace_quoted tokens - // so we have to conditionally call get_nonspace_quoted() again - if (pk && !isspace(pk)) { - string field2; - line_parser.get_nonspace_quoted(field2); - field.append(field2); - } - while (field[0]) { - char first = toupper(field[0]); - if (strchr("ABCDEFGKLPUSIQHW-", first)) { - valid = true; // it's a note or event - } - if (first == 'V') { - if (voice_flag) { - parse_error(field, 0, "Voice specified twice"); - } else { - voice = parse_chan(field); - } - voice_flag = true; - } else if (first == 'T') { - if (time_flag) { - parse_error(field, 0, "Time specified twice"); - } else { - time = parse_dur(field, 0.0); - } - time_flag = true; - } else if (first == 'N') { - if (next_flag) { - parse_error(field, 0, "Next specified twice"); - } else { - next = parse_dur(field, time); - } - next_flag = true; - } else if (first == 'K') { - if (new_key_flag) { - parse_error(field, 0, "Key specified twice"); - } else { - new_key = parse_key(field); - new_key_flag = true; - } - } else if (first == 'L') { - if (loud_flag) { - parse_error(field, 0, "Loudness specified twice"); - } else { - loud = parse_loud(field); - } - loud_flag = true; - } else if (first == 'P') { - if (new_pitch_flag) { - parse_error(field, 0, "Pitch specified twice"); - } else { - new_pitch = parse_pitch(field); - new_pitch_flag = true; - } - } else if (first == 'U') { - if (dur_flag) { - parse_error(field, 0, "Dur specified twice"); - } else { - dur = parse_dur(field, time); - dur_flag = true; - } - } else if (strchr("SIQHW", first)) { - if (dur_flag) { - parse_error(field, 0, "Dur specified twice"); - } else { - // prepend 'U' to field, copy EOS too - field.insert(0, 1, 'U'); - dur = parse_dur(field, time); - dur_flag = true; - } - } else if (strchr("ABCDEFG", first)) { - if (new_pitch_flag) { - parse_error(field, 0, "Pitch specified twice"); - } else { - // prepend 'P' to field - field.insert(0, 1, 'P'); - new_pitch = parse_pitch(field); - new_pitch_flag = true; - } - } else if (first == '-') { - Alg_parameter parm; - if (parse_attribute(field, &parm)) { // enter attribute-value pair - attributes = new Alg_parameters(attributes); - attributes->parm = parm; - parm.s = NULL; // protect string from deletion by destructor - } - } else { - parse_error(field, 0, "Unknown field"); - } - - if (error_flag) { - field[0] = 0; // exit the loop - } else { - line_parser.get_nonspace_quoted(field); - pk = line_parser.peek(); - // attributes are parsed as two adjacent nonspace_quoted - // tokens so we have to conditionally call - // get_nonspace_quoted() again - if (pk && !isspace(pk)) { - string field2; - line_parser.get_nonspace_quoted(field2); - field.append(field2); - } - } - } - // a case analysis: - // Key < 128 implies pitch unless pitch is explicitly given - // Pitch implies Key unless key is explicitly given, - // Pitch is rounded to nearest integer to determine the Key - // if necessary, so MIDI files will lose the pitch fraction - // A-G is a Pitch specification (therefore it implies Key) - // K60 P60 -- both are specified, use 'em - // K60 P60 C4 -- overconstrained, an error - // K60 C4 -- OK, but K60 is already implied by C4 - // K60 -- OK, pitch is 60 - // C4 P60 -- over constrained - // P60 -- OK, key is 60 - // P60.1 -- OK, key is 60 - // C4 -- OK, key is 60, pitch is 60 - // -- OK, key and pitch from before - // K200 P60 -- ok, pitch is 60 - // K200 with neither P60 nor C4 uses - // pitch from before - - // figure out what the key/instance is: - if (new_key_flag) { // it was directly specified - key = new_key; - } else if (new_pitch_flag) { - // pitch was specified, but key was not; get key from pitch - key = (int) (new_pitch + 0.5); // round to integer key number - } - if (new_pitch_flag) { - pitch = new_pitch; - } else if (key < 128 && new_key_flag) { - // no explicit pitch, but key < 128, so it implies pitch - pitch = key; - new_pitch_flag = true; - } - // now we've acquired new parameters - // if (it is a note, then enter the note - if (valid) { - // change tempo or beat - attributes = process_attributes(attributes, time); - // if there's a duration or pitch, make a note: - if (new_pitch_flag || dur_flag) { - Alg_note_ptr note_ptr = new Alg_note; - note_ptr->chan = voice; - note_ptr->time = time; - note_ptr->dur = dur; - note_ptr->set_identifier(key); - note_ptr->pitch = pitch; - note_ptr->loud = loud; - note_ptr->parameters = attributes; - seq->add_event(note_ptr, track_num); // sort later - if (seq->get_real_dur() < (time + dur)) seq->set_real_dur(time + dur); - } else { - int update_key = -1; - // key must appear explicitly; otherwise - // update applies to channel - if (new_key_flag) { - update_key = key; - } - if (loud_flag) { - Alg_update_ptr new_upd = new Alg_update; - new_upd->chan = voice; - new_upd->time = time; - new_upd->set_identifier(update_key); - new_upd->parameter.set_attr(symbol_table.insert_string("loudr")); - new_upd->parameter.r = pitch; - seq->add_event(new_upd, track_num); - if (seq->get_real_dur() < time) seq->set_real_dur(time); - } - if (attributes) { - while (attributes) { - Alg_update_ptr new_upd = new Alg_update; - new_upd->chan = voice; - new_upd->time = time; - new_upd->set_identifier(update_key); - new_upd->parameter = attributes->parm; - seq->add_event(new_upd, track_num); - Alg_parameters_ptr p = attributes; - attributes = attributes->next; - p->parm.s = NULL; // so we don't delete the string - delete p; - } - } - } - if (next_flag) { - time = time + next; - } else if (dur_flag || new_pitch_flag) { // a note: incr by dur - time = time + dur; - } - } - } - readline(); - } - if (!error_flag) { // why not convert even if there was an error? -RBD - seq->convert_to_seconds(); // make sure format is correct - } - // real_dur is valid, translate to beat_dur - seq->set_beat_dur((seq->get_time_map())->time_to_beat(seq->get_real_dur())); - return error_flag; -} - - -long Alg_reader::parse_chan(string &field) -{ - const char *int_string = field.c_str() + 1; - 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(); - for (int i = n; i < len; i++) { - char c = field[i]; - if (!isdigit(c)) { - if (c == '.' && !decimal) { - decimal = true; - } else { - return i; - } - } - } - return field.length(); -} - - -double Alg_reader::parse_real(string &field) -{ - 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, const char *message) -{ - int position = line_parser.pos - field.length() + offset; - error_flag = true; - puts(line_parser.str->c_str()); - for (int i = 0; i < position; i++) { - putc(' ', stdout); - } - putc('^', stdout); - printf(" %s\n", message); -} - - -double duration_lookup[] = { 0.25, 0.5, 1.0, 2.0, 4.0 }; - - -double Alg_reader::parse_dur(string &field, double base) -{ - 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}, - {NULL, 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; - if (s[pos] == '-') { - 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 e6b52f339f1..00000000000 --- 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 49e2ef03ee6..00000000000 --- a/plugins/MidiImport/portsmf/allegrosmfrd.cpp +++ /dev/null @@ -1,445 +0,0 @@ -// midifile reader - -#include "stdlib.h" -#include "stdio.h" -#include "string.h" -#include "debug.h" -#include -#include -#include "allegro.h" -#include "algsmfrd_internal.h" -#include "mfmidi.h" -#include "trace.h" - -using namespace std; - -typedef class Alg_pending { -public: - Alg_note_ptr note; - class Alg_pending *next; - Alg_pending(Alg_note_ptr n, class Alg_pending *list) { - note = n; next = list; } -} *Alg_pending_ptr; - - -class Alg_midifile_reader: public Midifile_reader { -public: - istream *file; - Alg_seq_ptr seq; - int divisions; - Alg_pending_ptr pending; - Alg_track_ptr track; - int track_number; // the number of the (current) track - // chan is actual_channel + channel_offset_per_track * track_num + - // channel_offset_per_track * port - long channel_offset_per_track; // used to encode track number into channel - // default is 0, set this to 0 to merge all tracks to 16 channels - long channel_offset_per_port; // used to encode port number into channel - // default is 16, set to 0 to ignore port prefix meta events - // while reading, this is channel_offset_per_track * track_num - int channel_offset; - - Alg_midifile_reader(istream &f, Alg_seq_ptr new_seq) { - file = &f; - pending = NULL; - seq = new_seq; - channel_offset_per_track = 0; - channel_offset_per_port = 16; - track_number = -1; // no tracks started yet, 1st will be #0 - meta_channel = -1; - port = 0; - } - // delete destroys the seq member as well, so set it to NULL if you - // copied the pointer elsewhere - ~Alg_midifile_reader(); - // the following is used to load the Alg_seq from the file: - bool parse(); - - void set_nomerge(bool flag) { Mf_nomerge = flag; } - void set_skipinit(bool flag) { Mf_skipinit = flag; } - long get_currtime() { return Mf_currtime; } - -protected: - int meta_channel; // the channel for meta events, set by MIDI chan prefix - int port; // value from the portprefix meta event - - double get_time(); - void update(int chan, int key, Alg_parameter_ptr param); - void *Mf_malloc(size_t size) { return malloc(size); } - void Mf_free(void *obj, size_t size) { free(obj); } - /* Methods to be called while processing the MIDI file. */ - void Mf_starttrack(); - void Mf_endtrack(); - int Mf_getc(); - void Mf_chanprefix(int chan); - void Mf_portprefix(int port); - void Mf_eot(); - void Mf_error(const char *); - void Mf_header(int,int,int); - void Mf_on(int,int,int); - void Mf_off(int,int,int); - void Mf_pressure(int,int,int); - void Mf_controller(int,int,int); - void Mf_pitchbend(int,int,int); - void Mf_program(int,int); - void Mf_chanpressure(int,int); - void binary_msg(int len, char *msg, const char *attr_string); - void Mf_sysex(int,char*); - void Mf_arbitrary(int,char*); - void Mf_metamisc(int,int,char*); - void Mf_seqnum(int); - void Mf_smpte(int,int,int,int,int); - void Mf_timesig(int,int,int,int); - void Mf_tempo(int); - void Mf_keysig(int,int); - void Mf_sqspecific(int,char*); - void Mf_text(int,int,char*); -}; - - -Alg_midifile_reader::~Alg_midifile_reader() -{ - while (pending) { - Alg_pending_ptr to_be_freed = pending; - pending = pending->next; - delete to_be_freed; - } - finalize(); // free Mf reader memory -} - - -bool Alg_midifile_reader::parse() -{ - channel_offset = 0; - seq->convert_to_beats(); - midifile(); - seq->set_real_dur(seq->get_time_map()->beat_to_time(seq->get_beat_dur())); - return midifile_error != 0; -} - - -void Alg_midifile_reader::Mf_starttrack() -{ - // printf("starting new track\n"); - // create a new track that will share the sequence time map - // since time is in beats, the seconds parameter is false - track_number++; - seq->add_track(track_number); // make sure track exists - track = seq->track(track_number); // keep pointer to current track - meta_channel = -1; - port = 0; -} - - -void Alg_midifile_reader::Mf_endtrack() -{ - // note: track is already part of seq, so do not add it here - // printf("finished track, length %d number %d\n", track->len, track_num / 100); - channel_offset += seq->channel_offset_per_track; - track = NULL; - double now = get_time(); - if (seq->get_beat_dur() < now) seq->set_beat_dur(now); - meta_channel = -1; - port = 0; -} - - -int Alg_midifile_reader::Mf_getc() -{ - return file->get(); -} - - -void Alg_midifile_reader::Mf_chanprefix(int chan) -{ - meta_channel = chan; -} - - -void Alg_midifile_reader::Mf_portprefix(int p) -{ - port = p; -} - - -void Alg_midifile_reader::Mf_eot() -{ - meta_channel = -1; - port = 0; -} - - -void Alg_midifile_reader::Mf_error(const char *msg) -{ - fprintf(stdout, "Midifile reader error: %s\n", msg); -} - - -void Alg_midifile_reader::Mf_header(int format, int ntrks, int division) -{ - if (format > 1) { - char msg[80]; - sprintf(msg, "file format %d not implemented", format); - Mf_error(msg); - } - divisions = division; -} - - -double Alg_midifile_reader::get_time() -{ - double beat = ((double) get_currtime()) / divisions; - return beat; -} - - -void Alg_midifile_reader::Mf_on(int chan, int key, int vel) -{ - assert(!seq->get_units_are_seconds()); - if (vel == 0) { - Mf_off(chan, key, vel); - return; - } - Alg_note_ptr note = new Alg_note(); - pending = new Alg_pending(note, pending); - /* trace("on: %d at %g\n", key, get_time()); */ - note->time = get_time(); - note->chan = chan + channel_offset + port * channel_offset_per_port; - note->dur = 0; - note->set_identifier(key); - note->pitch = (float) key; - note->loud = (float) vel; - track->append(note); - meta_channel = -1; -} - - -void Alg_midifile_reader::Mf_off(int chan, int key, int vel) -{ - double time = get_time(); - Alg_pending_ptr *p = &pending; - while (*p) { - if ((*p)->note->get_identifier() == key && - (*p)->note->chan == - chan + channel_offset + port * channel_offset_per_port) { - (*p)->note->dur = time - (*p)->note->time; - // trace("updated %d dur %g\n", (*p)->note->key, (*p)->note->dur); - Alg_pending_ptr to_be_freed = *p; - *p = to_be_freed->next; - delete to_be_freed; - } else { - p = &((*p)->next); - } - } - meta_channel = -1; -} - - -void Alg_midifile_reader::update(int chan, int key, Alg_parameter_ptr param) -{ - Alg_update_ptr update = new Alg_update; - update->time = get_time(); - update->chan = chan; - if (chan != -1) { - update->chan = chan + channel_offset + port * channel_offset_per_port; - } - update->set_identifier(key); - update->parameter = *param; - // prevent the destructor from destroying the string twice! - // the new Update takes the string from param - if (param->attr_type() == 's') param->s = NULL; - track->append(update); -} - - -void Alg_midifile_reader::Mf_pressure(int chan, int key, int val) -{ - Alg_parameter parameter; - parameter.set_attr(symbol_table.insert_string("pressurer")); - parameter.r = val / 127.0; - update(chan, key, ¶meter); - meta_channel = -1; -} - - -void Alg_midifile_reader::Mf_controller(int chan, int control, int val) -{ - Alg_parameter parameter; - char name[32]; - sprintf(name, "control%dr", control); - parameter.set_attr(symbol_table.insert_string(name)); - parameter.r = val / 127.0; - update(chan, -1, ¶meter); - meta_channel = -1; -} - - -void Alg_midifile_reader::Mf_pitchbend(int chan, int c1, int c2) -{ - Alg_parameter parameter; - parameter.set_attr(symbol_table.insert_string("bendr")); - parameter.r = ((c2 << 7) + c1) / 8192.0 - 1.0; - update(chan, -1, ¶meter); - meta_channel = -1; -} - - -void Alg_midifile_reader::Mf_program(int chan, int program) -{ - Alg_parameter parameter; - parameter.set_attr(symbol_table.insert_string("programi")); - parameter.i = program; - update(chan, -1, ¶meter); - meta_channel = -1; -} - - -void Alg_midifile_reader::Mf_chanpressure(int chan, int val) -{ - Alg_parameter parameter; - parameter.set_attr(symbol_table.insert_string("pressurer")); - parameter.r = val / 127.0; - update(chan, -1, ¶meter); - meta_channel = -1; -} - - -void Alg_midifile_reader::binary_msg(int len, char *msg, - const char *attr_string) -{ - Alg_parameter parameter; - char *hexstr = new char[len * 2 + 1]; - for (int i = 0; i < len; i++) { - sprintf(hexstr + 2 * i, "%02x", (0xFF & msg[i])); - } - parameter.s = hexstr; - parameter.set_attr(symbol_table.insert_string(attr_string)); - update(meta_channel, -1, ¶meter); -} - - -void Alg_midifile_reader::Mf_sysex(int len, char *msg) -{ - // sysex messages become updates with attribute sysexs and a hex string - binary_msg(len, msg, "sysexs"); -} - - -void Alg_midifile_reader::Mf_arbitrary(int len, char *msg) -{ - Mf_error("arbitrary data ignored"); -} - - -void Alg_midifile_reader::Mf_metamisc(int type, int len, char *msg) -{ - char text[128]; - sprintf(text, "metamsic data, type 0x%x, ignored", type); - Mf_error(text); -} - - -void Alg_midifile_reader::Mf_seqnum(int n) -{ - Mf_error("seqnum data ignored"); -} - - -static 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; - sprintf(text, "%sfps:%02dh:%02dm:%02ds:%02d.%02df", - fpsstr[fps], hours, mins, secs, frames, subframes); - Alg_parameter smpteoffset; - smpteoffset.s = heapify(text); - smpteoffset.set_attr(symbol_table.insert_string("smpteoffsets")); - update(meta_channel, -1, &smpteoffset); - // Mf_error("SMPTE data ignored"); -} - - -void Alg_midifile_reader::Mf_timesig(int i1, int i2, int i3, int i4) -{ - seq->set_time_sig(get_currtime() / divisions, i1, 1 << i2); -} - - -void Alg_midifile_reader::Mf_tempo(int tempo) -{ - double beat = get_currtime(); - beat = beat / divisions; // convert to quarters - // 6000000 us/min / n us/beat => beat / min - double bpm = 60000000.0 / tempo; - seq->insert_tempo(bpm, beat); -} - - -void Alg_midifile_reader::Mf_keysig(int key, int mode) -{ - Alg_parameter key_parm; - key_parm.set_attr(symbol_table.insert_string("keysigi")); - // use 0 for C major, 1 for G, -1 for F, etc., that is, - // the number of sharps, where flats are negative sharps - key_parm.i = key; //<<<---- fix this - // use -1 to mean "all channels" - update(meta_channel, -1, &key_parm); - Alg_parameter mode_parm; - mode_parm.set_attr(symbol_table.insert_string("modea")); - mode_parm.a = (mode == 0 ? symbol_table.insert_string("major") : - symbol_table.insert_string("minor")); - update(meta_channel, -1, &mode_parm); -} - - -void Alg_midifile_reader::Mf_sqspecific(int len, char *msg) -{ - // sequencer specific messages become updates with attribute sqspecifics - // and a hex string for the value - binary_msg(len, msg, "sqspecifics"); -} - - -char *heapify2(int len, char *s) -{ - char *h = new char[len + 1]; - memcpy(h, s, len); - h[len] = 0; - return h; -} - - -void Alg_midifile_reader::Mf_text(int type, int len, char *msg) -{ - Alg_parameter text; - text.s = heapify2(len, msg); - const char *attr = "miscs"; - if (type == 1) attr = "texts"; - else if (type == 2) attr = "copyrights"; - else if (type == 3) - attr = (track_number == 0 ? "seqnames" : "tracknames"); - else if (type == 4) attr = "instruments"; - else if (type == 5) attr = "lyrics"; - else if (type == 6) attr = "markers"; - else if (type == 7) attr = "cues"; - text.set_attr(symbol_table.insert_string(attr)); - update(meta_channel, -1, &text); -} - - -// parse file into a seq. -Alg_error alg_smf_read(istream &file, Alg_seq_ptr new_seq) -{ - assert(new_seq); - Alg_midifile_reader ar(file, new_seq); - bool err = ar.parse(); - ar.seq->set_real_dur(ar.seq->get_time_map()-> - beat_to_time(ar.seq->get_beat_dur())); - return (err ? alg_error_syntax : alg_no_error); -} diff --git a/plugins/MidiImport/portsmf/allegrosmfwr.cpp b/plugins/MidiImport/portsmf/allegrosmfwr.cpp deleted file mode 100644 index 5a76c44ed9f..00000000000 --- a/plugins/MidiImport/portsmf/allegrosmfwr.cpp +++ /dev/null @@ -1,649 +0,0 @@ -// allegrosmfwr.cpp -- Allegro Standard Midi File Write - -#include -#include -#include -#include -#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(); - long channels_per_track; // used to encode track number into chan field - // chan is actual_channel + channels_per_track * track_number - // default is 100, set this to 0 to merge all tracks to 16 channels - - void write(ofstream &file /* , midiFileFormat = 1 */); - -private: - long previous_divs; // time in ticks of most recently written event - - void write_track(int i); - void write_tempo(int divs, int tempo); - void write_tempo_change(int i); - void write_time_signature(int i); - void write_note(Alg_note_ptr note, bool on); - void write_update(Alg_update_ptr update); - void write_text(Alg_update_ptr update, char type); - void write_binary(int type_byte, char *msg); - void write_midi_channel_prefix(Alg_update_ptr update); - void write_smpteoffset(Alg_update_ptr update, char *s); - void write_data(int data); - int to_midi_channel(int channel); - int to_track(int channel); - - ostream *out_file; - - Alg_seq_ptr seq; - - int num_tracks; // number of tracks not counting tempo track - int division; // divisions per quarter note, default = 120 - int initial_tempo; - - int timesig_num; // numerator of time signature - int timesig_den; // denominator of time signature - double timesig_when; // time of time signature - - int keysig; // number of sharps (+) or flats (-), -99 for undefined - char keysig_mode; // 'M' or 'm' for major/minor - double keysig_when; // time of key signature - - void write_delta(double event_time); - void write_varinum(int num); - void write_16bit(int num); - void write_24bit(int num); - void write_32bit(int num); -}; - -#define ROUND(x) (int) ((x)+0.5) - -Alg_smf_write::Alg_smf_write(Alg_seq_ptr a_seq) -{ - out_file = NULL; - - // at 100bpm (a nominal tempo value), we would like a division - // to represent 1ms of time. So - // d ticks/beat * 100 beats/min = 60,000 ms/min * 1 tick/ms - // solving for d, d = 600 - division = 600; // divisions per quarter note - timesig_num = timesig_den = 0; // initially undefined - keysig = -99; - keysig_mode = 0; - initial_tempo = 500000; - - seq = a_seq; - - previous_divs = 0; // used to compute deltas for midifile -} - - -Alg_smf_write::~Alg_smf_write() -{ -} - - -// sorting is quite subtle due to rounding -// For example, suppose times from a MIDI file are exact, but in -// decimal round to TW0.4167 Q0.3333. Since the time in whole notes -// rounded up, this note will start late. Even though the duration -// rounded down, the amount is 1/4 as much because units are quarter -// notes. Therefore, the total roundup is 0.0001 beats. This is -// enough to cause the note to sort later in the queue, perhaps -// coming after a new note-on on the same pitch, and resulting in -// a turning on-off, on-off into on, on, off, off if data is moved -// to Allegro (ascii) format with rounding and then back to SMF. -// -// The solution here is to consider things that round to the same -// tick to be simultaneous. Then, be sure to deal with note-offs -// before note-ons. We're going to do that by using event_queue -// times that are rounded to the nearest tick time. Except note-offs -// are going to go in with times that are 1/4 tick earlier so they -// get scheduled first, but still end up on the same tick. -// -event_queue* push(event_queue *queue, event_queue *event) -{ - // printf("push: %.6g, %c, %d\n", event->time, event->type, event->index); - if (queue == NULL) { - event->next = NULL; - return event; - } - - event_queue *marker1 = NULL; - event_queue *marker2 = queue; - while (marker2 != NULL && marker2->time <= event->time) { - marker1 = marker2; - marker2 = marker2->next; - } - event->next = marker2; - if (marker1 != NULL) { - marker1->next=event; - return queue; - } else return event; -} - - -void print_queue(event_queue *q) -{ - printf("Printing queue. . .\n"); - event_queue *q2=q; - while (q2) { - printf("%c at %f ;", q2->type, q2->time); - q2 = q2->next; - } - printf("\nDone printing.\n"); -} - - -void Alg_smf_write::write_note(Alg_note_ptr note, bool on) -{ - double event_time = (on ? note->time : note->time + note->dur); - write_delta(event_time); - - //printf("deltaDivisions: %d, beats elapsed: %g, on? %c\n", deltaDivisions, note->time, on); - - char chan = (note->chan & 15); - int pitch = int(note->pitch + 0.5); - if (pitch < 0) { - pitch = pitch % 12; - } else if (pitch > 127) { - pitch = (pitch % 12) + 120; // put pitch in 10th octave - if (pitch > 127) pitch -= 12; // or 9th octave - } - out_file->put(0x90 + chan); - out_file->put(pitch); - if (on) { - int vel = (int) note->loud; - if (vel <= 0) vel = 1; - write_data(vel); - } else out_file->put(0); // note-off indicated by velocty zero -} - - -void Alg_smf_write::write_midi_channel_prefix(Alg_update_ptr update) -{ - if (update->chan >= 0) { // write MIDI Channel Prefix - write_delta(update->time); - out_file->put(0xFF); // Meta Event - out_file->put(0x20); // Type code for MIDI Channel Prefix - out_file->put(1); // length - out_file->put(to_midi_channel(update->chan)); - // one thing odd about the Std MIDI File spec is that once - // you turn on MIDI Channel Prefix, there seems to be no - // way to cancel it unless a non-Meta event shows up. We - // don't do any analysis to avoid assigning channels to - // meta events. - } -} - - -void Alg_smf_write::write_text(Alg_update_ptr update, char type) -{ - write_midi_channel_prefix(update); - write_delta(update->time); - out_file->put(0xFF); - out_file->put(type); - out_file->put((char) strlen(update->parameter.s)); - *out_file << update->parameter.s; -} - - -void Alg_smf_write::write_smpteoffset(Alg_update_ptr update, char *s) -{ - write_midi_channel_prefix(update); - write_delta(update->time); - out_file->put(0xFF); // meta event - out_file->put(0x54); // smpte offset type code - out_file->put(5); // length - for (int i = 0; i < 5; i++) *out_file << s[i]; -} - - -// write_data - limit data to the range of [0...127] and write it -void Alg_smf_write::write_data(int data) -{ - if (data < 0) data = 0; - else if (data > 0x7F) data = 0x7F; - - out_file->put(data); -} - - -int Alg_smf_write::to_midi_channel(int channel) -{ - // allegro track number is stored as multiple of 100 - // also mask off all but 4 channel bits just in case - if (channels_per_track > 0) channel %= channels_per_track; - return channel & 0xF; -} - - -int Alg_smf_write::to_track(int channel) -{ - if (channel == -1) return 0; - return channel / channels_per_track; -} - - -static char hex_to_nibble(char c) -{ - if (isalpha(c)) { - return 10 + (toupper(c) - 'A'); - } else { - return c - '0'; - } -} - - -static char hex_to_char(char *s) -{ - return (hex_to_nibble(s[0]) << 4) + hex_to_nibble(s[1]); -} - - -void Alg_smf_write::write_binary(int type_byte, char *msg) -{ - int len = strlen(msg) / 2; - out_file->put(type_byte); - write_varinum(len); - for (int i = 0; i < len; i++) { - out_file->put(hex_to_char(msg)); - msg += 2; - } -} - - -void Alg_smf_write::write_update(Alg_update_ptr update) -{ - char *name = update->parameter.attr_name(); - - /****Non-Meta Events****/ - if (!strcmp(name, "pressurer")) { - write_delta(update->time); - if (update->get_identifier() < 0) { // channel pressure message - out_file->put(0xD0 + to_midi_channel(update->chan)); - write_data((int)(update->parameter.r * 127)); - } else { // just 1 key -- poly pressure - out_file->put(0xA0 + to_midi_channel(update->chan)); - write_data(update->get_identifier()); - write_data((int)(update->parameter.r * 127)); - } - } else if (!strcmp(name, "programi")) { - write_delta(update->time); - out_file->put(0xC0 + to_midi_channel(update->chan)); - write_data(update->parameter.i); - } else if (!strcmp(name, "bendr")) { - int temp = ROUND(0x2000 * (update->parameter.r + 1)); - if (temp > 0x3fff) temp = 0x3fff; // 14 bits maximum - if (temp < 0) temp = 0; - int c1 = temp & 0x7F; // low 7 bits - int c2 = temp >> 7; // high 7 bits - write_delta(update->time); - out_file->put(0xE0 + to_midi_channel(update->chan)); - write_data(c1); - write_data(c2); - } else if (!strncmp(name, "control", 7) && - update->parameter.attr_type() == 'r') { - int ctrlnum = atoi(name + 7); - int val = ROUND(update->parameter.r * 127); - write_delta(update->time); - out_file->put(0xB0 + to_midi_channel(update->chan)); - write_data(ctrlnum); - write_data(val); - } else if (!strcmp(name, "sysexs") && - update->parameter.attr_type() == 's') { - char *s = update->parameter.s; - if (s[0] && s[1] && toupper(s[0]) == 'F' && s[1] == '0') { - s += 2; // skip the initial "F0" byte in message: it is implied - } - write_delta(update->time); - write_binary(0xF0, s); - } else if (!strcmp(name, "sqspecifics") && - update->parameter.attr_type() == 's') { - char *s = update->parameter.s; - write_delta(update->time); - out_file->put(0xFF); - write_binary(0x7F, s); - - /****Text Events****/ - } else if (!strcmp(name, "texts")) { - write_text(update, 0x01); - } else if (!strcmp(name, "copyrights")) { - write_text(update, 0x02); - } else if (!strcmp(name, "seqnames") || !strcmp(name, "tracknames")) { - write_text(update, 0x03); - } else if (!strcmp(name, "instruments")) { - write_text(update, 0x04); - } else if (!strcmp(name, "lyrics")) { - write_text(update, 0x05); - } else if (!strcmp(name, "markers")) { - write_text(update, 0x06); - } else if (!strcmp(name, "cues")) { - write_text(update, 0x07); - } else if (!strcmp(name, "miscs")) { - write_text(update, 0x08); - - /****Other Events****/ - } else if (!strcmp(name, "smpteoffsets")) { -#define decimal(p) (((p)[0] - '0') * 10 + ((p)[1] - '0')) - // smpteoffset is specified as "24fps:00h:10m:00s:11.00f" - // the following simple parser does not reject all badly - // formatted strings, but it should parse good strings ok - char *s = update->parameter.s; - int len = strlen(s); - char smpteoffset[5]; - if (len < 24) return; // not long enough, must be bad format - int fps = 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(0xFF); - out_file->put(0x59); - out_file->put(2); - // mask off high bits so that this value appears to be positive - // i.e. -1 -> 0xFF (otherwise, write_data will clip -1 to 0) - out_file->put(keysig & 0xFF); - out_file->put(keysig_mode == 'm'); - keysig = -99; - keysig_mode = false; - } - //printf("Update: %s, key: %g\n", update->parameter.attr_name(), update->key); -} - - -// see notes on event_queue::push, TICK_TIME converts from beat to -// the number of the nearest tick. The second parameter is an offset in -// quarter ticks. By scheduling with -1, note-offs should get dispatched -// first. Note that TICK_TIME only determines the order of events, so -// it is ok to change units from beats to ticks, saving a divide. -#define TICK_TIME(t, o) (ROUND((t) * division) + 0.25 * (o)) - -void Alg_smf_write::write_track(int i) -{ - int j = 0; // note index - Alg_events ¬es = seq->track_list[i]; - event_queue *pending = NULL; - if (notes.length() > 0) { - pending = new event_queue('n', TICK_TIME(notes[j]->time, 0), 0, NULL); - } - if (i == 0) { // track 0 may have tempo and timesig info - if (seq->get_time_map()->last_tempo_flag || seq->get_time_map()->beats.len > 0) { - pending = push(pending, new event_queue('c', 0.0, 0, NULL)); - } - if (seq->time_sig.length() > 0) { - pending = push(pending, new event_queue('s', - TICK_TIME(seq->time_sig[0].beat, 0), 0, NULL)); - } - } - while (pending) { - event_queue *current = pending; - pending = pending->next; - if (current->type == 'n') { - Alg_note_ptr n = (Alg_note_ptr) notes[current->index]; - if (n->is_note()) { - write_note(n, true); - pending = push(pending, new event_queue('o', - TICK_TIME(n->time + n->dur, -1), current->index, NULL)); - } else if (n->is_update()) { - Alg_update_ptr u = (Alg_update_ptr) n; - write_update(u); - } - int next = current->index + 1; - if (next < notes.length()) { - current->time = TICK_TIME(notes[next]->time, 0); - current->index = next; - pending = push(pending, current); - } - } else if (current->type == 'o') { //note-off - Alg_note_ptr n = (Alg_note_ptr) notes[current->index]; - write_note(n, false); - delete current; - } else if (current->type == 'c') { // tempo change - write_tempo_change(current->index); - current->index++; // -R - if (current->index < seq->get_time_map()->beats.len) { - current->time = - TICK_TIME(seq->get_time_map()-> - beats[current->index].beat, 0); - pending = push(pending, current); - } else { - delete current; - } - } else if (current->type == 's') { // time sig - write_time_signature(current->index); - current->index++; - if (current->index < seq->time_sig.length()) { - current->time = - TICK_TIME(seq->time_sig[current->index].beat, 0); - pending = push(pending, current); - } else { - delete current; - } - } - } -} - - -void Alg_smf_write::write_tempo(int divs, int tempo) -{ - // printf("Inserting tempo %f after %f clocks.\n", tempo, delta); - write_varinum(divs - previous_divs); - previous_divs = divs; - out_file->put(0xFF); - out_file->put(0x51); - out_file->put(0x03); - write_24bit((int)tempo); -} - - -void Alg_smf_write::write_tempo_change(int i) - // i is index of tempo map -{ - // extract tempo map - Alg_beats &b = seq->get_time_map()->beats; - double tempo; - long divs; - if (i < seq->get_time_map()->beats.len - 1) { - tempo = 1000000 * ((b[i+1].time - b[i].time) / - (b[i+1].beat - b[i].beat)); - divs = ROUND(b[i].beat * division); - write_tempo(divs, ROUND(tempo)); - } else if (seq->get_time_map()->last_tempo_flag) { // write the final tempo - divs = ROUND(division * b[i].beat); - tempo = (1000000.0 / seq->get_time_map()->last_tempo); - write_tempo(divs, ROUND(tempo)); - } -} - - -void Alg_smf_write::write_time_signature(int i) -{ - Alg_time_sigs &ts = seq->time_sig; - // write the time signature - long divs = ROUND(ts[i].beat * division); - write_varinum(divs - previous_divs); - out_file->put(0xFF); - out_file->put(0x58); // time signature - out_file->put(4); // length of message - out_file->put(ROUND(ts[i].num)); - int den = ROUND(ts[i].den); - int den_byte = 0; - while (den > 1) { // compute the log2 of denominator - den_byte++; - den >>= 1; - } - out_file->put(den_byte); - out_file->put(24); // clocks per quarter - out_file->put(8); // 32nd notes per 24 clocks -} - - - -void Alg_smf_write::write(ofstream &file) -{ - int track_len_offset; - int track_end_offset; - int track_len; - - out_file = &file; - - // Header - file << "MThd"; - - write_32bit(6); // chunk length - - write_16bit(1); // format 1 MIDI file - - write_16bit(seq->tracks()); // number of tracks - write_16bit(division); // divisions per quarter note - - - // write_ all tracks - seq->convert_to_beats(); - int i; - for (i = 0; i < seq->tracks(); i++) { - previous_divs = 0; - *out_file << "MTrk"; - track_len_offset = out_file->tellp(); - write_32bit(0); // track len placeholder - - write_track(i); - - // End of track event - write_varinum(0); // delta time - out_file->put(0xFF); - out_file->put(0x2F); - out_file->put(0x00); - - // Go back and write in the length of the track - track_end_offset = out_file->tellp(); - track_len = track_end_offset - track_len_offset - 4; - out_file->seekp(track_len_offset); - write_32bit(track_len); - out_file->seekp(track_end_offset); - } -} - - -void Alg_smf_write::write_16bit(int num) -{ - out_file->put((num & 0xFF00) >> 8); - out_file->put(num & 0xFF); -} - -void Alg_smf_write::write_24bit(int num) -{ - out_file->put((num & 0xFF0000) >> 16); - out_file->put((num & 0xFF00) >> 8); - out_file->put((num & 0xFF)); -} - -void Alg_smf_write::write_32bit(int num) -{ - out_file->put((num & 0xFF000000) >> 24); - out_file->put((num & 0xFF0000) >> 16); - out_file->put((num & 0xFF00) >> 8); - out_file->put((num & 0xFF)); -} - - -void Alg_smf_write::write_delta(double event_time) -{ - // divisions is ideal absolute time in divisions - long divisions = ROUND(division * event_time); - long delta_divs = divisions - previous_divs; - write_varinum(delta_divs); - previous_divs = divisions; -} - - -void Alg_smf_write::write_varinum(int value) -{ - if(value<0) value=0;//this line should not have to be here! - int buffer; - - buffer = value & 0x7f; - while ((value >>= 7) > 0) { - buffer <<= 8; - buffer |= 0x80; - buffer += (value & 0x7f); - } - - for(;;) { - out_file->put(buffer); - if (buffer & 0x80) - buffer >>= 8; - else - break; - } -} - - -void Alg_seq::smf_write(ofstream &file) -{ - Alg_smf_write writer(this); - writer.write(file); -} - -bool Alg_seq::smf_write(const char *filename) -{ - ofstream outf(filename, ios::binary | ios::out); - if (outf.fail()) return false; - smf_write(outf); - outf.close(); - return true; -} - diff --git a/plugins/MidiImport/portsmf/allegrowr.cpp b/plugins/MidiImport/portsmf/allegrowr.cpp deleted file mode 100644 index 3b266f84cac..00000000000 --- a/plugins/MidiImport/portsmf/allegrowr.cpp +++ /dev/null @@ -1,181 +0,0 @@ -// allegrowr.cpp -- write sequence to an Allegro file (text) - -#include "debug.h" -#include "stdlib.h" -#include -#include -#include -#include -#include -#include "memory.h" -using namespace std; -#include "strparse.h" -#include "allegro.h" - -// Note about precision: %g prints 6 significant digits. For 1ms precision, -// the maximum magnitude is 999.999, i.e. 1000s < 17minutes. For anything -// over 1000s, time in seconds will be printed with 10ms precision, which -// is not good. Therefore, times and durations are printed as %.4d, which -// gives 100us precision. -// The following define allows you to change this decision: -/* #define TIMFMT "%.4d" */ -#define TIMPREC 4 -#define TIMFMT fixed << setprecision(TIMPREC) -#define GFMT resetiosflags(ios::floatfield) << setprecision(6) - -void parameter_print(ostream &file, Alg_parameter_ptr p) -{ - file << " -" << p->attr_name() << ":"; - switch (p->attr_type()) { - case 'a': - file << "'" << alg_attr_name(p->a) << "'"; - break; - case 'i': - file << p->i; - break; - case 'l': - file << (p->l ? "true" : "false"); - break; - case 'r': - file << p->r; - break; - case 's': { - string str; - string_escape(str, p->s, "\""); - file << str; - break; - } - } -} - -Alg_event_ptr Alg_seq::write_track_name(ostream &file, int n, - Alg_events &events) -// write #track -// if we write the name on the "#track" line, then we do *not* want -// to write again as an update: "-seqnames:"Jordu", so if we do -// find a name and write it, return a pointer to it so the track -// writer knows what update (if any) to skip -{ - Alg_event_ptr e = NULL; - file << "#track " << n; - const char *attr = symbol_table.insert_string( - n == 0 ? "seqnames" : "tracknames"); - // search for name in events with timestamp of 0 - for (int i = 0; i < events.length(); i++) { - e = events[i]; - if (e->time > 0) break; - if (e->is_update()) { - Alg_update_ptr u = (Alg_update_ptr) e; - if (u->parameter.attr == attr) { - file << " " << u->parameter.s; - break; - } - } - } - file << endl; - return e; -} - - -void Alg_seq::write(ostream &file, bool in_secs) -{ - int i, j; - if (in_secs) convert_to_seconds(); - else convert_to_beats(); - Alg_event_ptr update_to_skip = write_track_name(file, 0, track_list[0]); - Alg_beats &beats = time_map->beats; - for (i = 0; i < beats.len - 1; i++) { - Alg_beat_ptr b = &(beats[i]); - if (in_secs) { - file << "T" << TIMFMT << b->time; - } else { - file << "TW" << TIMFMT << b->beat / 4; - } - double tempo = (beats[i + 1].beat - b->beat) / - (beats[i + 1].time - beats[i].time); - file << " -tempor:" << GFMT << tempo * 60 << "\n"; - } - if (time_map->last_tempo_flag) { // we have final tempo: - Alg_beat_ptr b = &(beats[beats.len - 1]); - if (in_secs) { - file << "T" << TIMFMT << b->time; - } else { - file << "TW" << TIMFMT << b->beat / 4; - } - file << " -tempor:" << GFMT << time_map->last_tempo * 60.0 << "\n"; - } - - // write the time signatures - for (i = 0; i < time_sig.length(); i++) { - Alg_time_sig &ts = time_sig[i]; - double time = ts.beat; - if (in_secs) { - file << "T" << TIMFMT << time << " V- -timesig_numr:" << - GFMT << ts.num << "\n"; - file << "T" << TIMFMT << time << " V- -timesig_denr:" << - GFMT << ts.den << "\n"; - } else { - double wholes = ts.beat / 4; - file << "TW" << TIMFMT << wholes << " V- -timesig_numr:" << - GFMT << ts.num << "\n"; - file << "TW" << TIMFMT << wholes << " V- -timesig_denr:" << - GFMT << ts.den << "\n"; - } - } - - for (j = 0; j < track_list.length(); j++) { - Alg_events ¬es = track_list[j]; - if (j != 0) update_to_skip = write_track_name(file, j, notes); - // now write the notes at beat positions - for (i = 0; i < notes.length(); i++) { - Alg_event_ptr e = notes[i]; - // if we already wrote this event as a track or sequence name, - // do not write it again - if (e == update_to_skip) continue; - double start = e->time; - if (in_secs) { - file << "T" << TIMFMT << start; - } else { - file << "TW" << TIMFMT << start / 4; - } - // write the channel as Vn or V- - if (e->chan == -1) file << " V-"; - else file << " V" << e->chan; - // write the note or update data - if (e->is_note()) { - Alg_note_ptr n = (Alg_note_ptr) e; - double dur = n->dur; - file << " K" << n->get_identifier() << - " P" << GFMT << n->pitch; - if (in_secs) { - file << " U" << TIMFMT << dur; - } else { - file << " Q" << TIMFMT << dur; - } - file << " L" << GFMT << n->loud; - Alg_parameters_ptr p = n->parameters; - while (p) { - parameter_print(file, &(p->parm)); - p = p->next; - } - } else { // an update - assert(e->is_update()); - Alg_update_ptr u = (Alg_update_ptr) e; - if (u->get_identifier() != -1) { - file << " K" << u->get_identifier(); - } - parameter_print(file, &(u->parameter)); - } - file << "\n"; - } - } -} - -bool Alg_seq::write(const char *filename) -{ - ofstream file(filename); - if (file.fail()) return false; - write(file, units_are_seconds); - file.close(); - return true; -} diff --git a/plugins/MidiImport/portsmf/license.txt b/plugins/MidiImport/portsmf/license.txt deleted file mode 100644 index 6f86ce0d4fa..00000000000 --- 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 52f93b83764..00000000000 --- a/plugins/MidiImport/portsmf/mfmidi.cpp +++ /dev/null @@ -1,485 +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" - -#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. */ -{ - 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: - (void) strcpy(buff,errmsg); - (void) strcat(buff,s); - mferror(buff); - return(0); -} - -int Midifile_reader::egetc() - /* read a single character and abort on EOF */ -{ - int c = Mf_getc(); - - if ( c == EOF ) { - mferror("premature EOF"); - return EOF; - } - Mf_toberead--; - return(c); -} - -int Midifile_reader::readheader() - /* read a header chunk */ -{ - int format, ntrks, division; - - if ( readmt("MThd",Mf_skipinit) == EOF ) - return(0); - - Mf_toberead = read32bit(); - if (midifile_error) return MIDIFILE_ERROR; - format = read16bit(); - if (midifile_error) return MIDIFILE_ERROR; - ntrks = read16bit(); - if (midifile_error) return MIDIFILE_ERROR; - division = read16bit(); - if (midifile_error) return MIDIFILE_ERROR; - - Mf_header(format,ntrks,division); - - /* flush any extra stuff, in case the length of header is not 6 */ - while ( Mf_toberead > 0 && !midifile_error) - (void) egetc(); - return(ntrks); -} - -void Midifile_reader::readtrack() - /* read a track chunk */ -{ - /* This array is indexed by the high half of a status byte. It's */ - /* value is either the number of bytes needed (1 or 2) for a channel */ - /* message, or 0 (meaning it's not a channel message). */ - static int chantype[] = { - 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 through 0x70 */ - 2, 2, 2, 2, 1, 1, 2, 0 /* 0x80 through 0xf0 */ - }; - long lookfor, lng; - int c, c1, type; - int sysexcontinue = 0; /* 1 if last message was an unfinished sysex */ - int running = 0; /* 1 when running status used */ - int status = 0; /* (possibly running) status byte */ - int needed; - - if ( readmt("MTrk",0) == EOF ) - return; - - Mf_toberead = read32bit(); - - if (midifile_error) return; - - Mf_currtime = 0L; - - Mf_starttrack(); - - while ( Mf_toberead > 0 ) { - - Mf_currtime += readvarinum(); /* delta time */ - if (midifile_error) return; - - c = egetc(); - if (midifile_error) return; - - if ( sysexcontinue && c != 0xf7 ) { - mferror("didn't find expected continuation of a sysex"); - return; - } - if ( (c & 0x80) == 0 ) { /* running status? */ - if ( status == 0 ) { - mferror("unexpected running status"); - return; - } - running = 1; - } else { - status = c; - running = 0; - } - - needed = chantype[ (status>>4) & 0xf ]; - - if ( needed ) { /* ie. is it a channel message? */ - - if ( running ) - c1 = c; - else { - c1 = egetc(); - if (midifile_error) return; - } - chanmessage( status, c1, (needed>1) ? egetc() : 0 ); - if (midifile_error) return; - continue;; - } - - switch ( c ) { - - case 0xff: /* meta event */ - - type = egetc(); - if (midifile_error) return; - /* watch out - Don't combine the next 2 statements */ - lng = readvarinum(); - if (midifile_error) return; - lookfor = Mf_toberead - lng; - msginit(); - - while ( Mf_toberead > lookfor ) { - char c = egetc(); - if (midifile_error) return; - msgadd(c); - } - metaevent(type); - break; - - case 0xf0: /* start of system exclusive */ - - /* watch out - Don't combine the next 2 statements */ - lng = readvarinum(); - if (midifile_error) return; - lookfor = Mf_toberead - lng; - msginit(); - msgadd(0xf0); - - while ( Mf_toberead > lookfor ) { - c = egetc(); - if (midifile_error) return; - msgadd(c); - } - if ( c==0xf7 || Mf_nomerge==0 ) - sysex(); - else - sysexcontinue = 1; /* merge into next msg */ - break; - - case 0xf7: /* sysex continuation or arbitrary stuff */ - - /* watch out - Don't combine the next 2 statements */ - lng = readvarinum(); - if (midifile_error) return; - lookfor = Mf_toberead - lng; - - if ( ! sysexcontinue ) - msginit(); - - while ( Mf_toberead > lookfor ) { - c = egetc(); - if (midifile_error) return; - msgadd(c); - } - if ( ! sysexcontinue ) { - Mf_arbitrary(msgleng(), msg()); - } - else if ( c == 0xf7 ) { - sysex(); - sysexcontinue = 0; - } - break; - default: - - badbyte(c); - - break; - } - } - Mf_endtrack(); - return; -} - -void Midifile_reader::badbyte(int c) -{ - char buff[32]; - - (void) sprintf(buff,"unexpected byte: 0x%02x",c); - mferror(buff); -} - -void Midifile_reader::metaevent(int type) -{ - int leng = msgleng(); - char *m = msg(); - - switch ( type ) { - case 0x00: - Mf_seqnum(to16bit(m[0],m[1])); - break; - case 0x01: /* Text event */ - case 0x02: /* Copyright notice */ - case 0x03: /* Sequence/Track name */ - case 0x04: /* Instrument name */ - case 0x05: /* Lyric */ - case 0x06: /* Marker */ - case 0x07: /* Cue point */ - case 0x08: - case 0x09: - case 0x0a: - case 0x0b: - case 0x0c: - case 0x0d: - case 0x0e: - case 0x0f: - /* These are all text events */ - Mf_text(type,leng,m); - break; - case 0x20: - Mf_chanprefix(m[0]); - break; - case 0x21: - Mf_portprefix(m[0]); - break; - case 0x2f: /* End of Track */ - Mf_eot(); - break; - case 0x51: /* Set tempo */ - Mf_tempo(to32bit(0,m[0],m[1],m[2])); - break; - case 0x54: - Mf_smpte(m[0],m[1],m[2],m[3],m[4]); - break; - case 0x58: - Mf_timesig(m[0],m[1],m[2],m[3]); - break; - case 0x59: - Mf_keysig(m[0],m[1]); - break; - case 0x7f: - Mf_sqspecific(leng,m); - break; - default: - Mf_metamisc(type,leng,m); - } -} - - -void Midifile_reader::sysex() -{ - Mf_sysex(msgleng(), msg()); -} - - -void Midifile_reader::chanmessage(int status, int c1, int c2) -{ - int chan = status & 0xf; - - switch ( status & 0xf0 ) { - case NOTEOFF: - Mf_off(chan,c1,c2); - break; - case NOTEON: - Mf_on(chan,c1,c2); - break; - case PRESSURE: - Mf_pressure(chan,c1,c2); - break; - case CONTROLLER: - Mf_controller(chan,c1,c2); - break; - case PITCHBEND: - Mf_pitchbend(chan,c1,c2); - break; - case PROGRAM: - Mf_program(chan,c1); - break; - case CHANPRESSURE: - Mf_chanpressure(chan,c1); - break; - } -} - -/* readvarinum - read a varying-length number, and return the */ -/* number of characters it took. */ - -long Midifile_reader::readvarinum() -{ - long value; - int c; - - c = egetc(); - if (midifile_error) return 0; - - value = (long) c; - if ( c & 0x80 ) { - value &= 0x7f; - do { - c = egetc(); - if (midifile_error) return 0; - value = (value << 7) + (c & 0x7f); - } while (c & 0x80); - } - return (value); -} - -long Midifile_reader::to32bit(int c1, int c2, int c3, int c4) -{ - long value = 0L; - - value = (c1 & 0xff); - value = (value<<8) + (c2 & 0xff); - value = (value<<8) + (c3 & 0xff); - value = (value<<8) + (c4 & 0xff); - return (value); -} - -int Midifile_reader::to16bit(int c1, int c2) -{ - return ((c1 & 0xff ) << 8) + (c2 & 0xff); -} - -long Midifile_reader::read32bit() -{ - int c1, c2, c3, c4; - - c1 = egetc(); if (midifile_error) return 0; - c2 = egetc(); if (midifile_error) return 0; - c3 = egetc(); if (midifile_error) return 0; - c4 = egetc(); if (midifile_error) return 0; - return to32bit(c1,c2,c3,c4); -} - -int Midifile_reader::read16bit() -{ - int c1, c2; - c1 = egetc(); if (midifile_error) return 0; - c2 = egetc(); if (midifile_error) return 0; - return to16bit(c1,c2); -} - -void Midifile_reader::mferror(const char *s) -{ - Mf_error(s); - midifile_error = 1; -} - -/* The code below allows collection of a system exclusive message of */ -/* arbitrary length. The Msgbuff is expanded as necessary. The only */ -/* visible data/routines are msginit(), msgadd(), msg(), msgleng(). */ - -#define MSGINCREMENT 128 - -Midifile_reader::Midifile_reader() -{ - Mf_nomerge = 0; - Mf_currtime = 0L; - Mf_skipinit = 0; - Mf_toberead = 0; - - Msgbuff = 0; /* message buffer */ - Msgsize = 0; /* Size of currently allocated Msg */ - Msgindex = 0; /* index of next available location in Msg */ -} - -void Midifile_reader::finalize() -{ - if (Msgbuff) Mf_free(Msgbuff, Msgsize); - Msgbuff = NULL; -} - - -void Midifile_reader::msginit() -{ - Msgindex = 0; -} - -char *Midifile_reader::msg() -{ - return(Msgbuff); -} - -int Midifile_reader::msgleng() -{ - return(Msgindex); -} - -void Midifile_reader::msgadd(int c) -{ - /* If necessary, allocate larger message buffer. */ - if ( Msgindex >= Msgsize ) - msgenlarge(); - Msgbuff[Msgindex++] = c; -} - -void Midifile_reader::msgenlarge() -{ - char *newmess; - char *oldmess = Msgbuff; - int oldleng = Msgsize; - - Msgsize += MSGINCREMENT; - newmess = (char *) Mf_malloc((sizeof(char) * Msgsize) ); - - /* copy old message into larger new one */ - if ( oldmess != 0 ) { - register char *p = newmess; - register char *q = oldmess; - register char *endq = &oldmess[oldleng]; - - for ( ; q!=endq ; p++,q++ ) - *p = *q; - Mf_free(oldmess, oldleng); - } - Msgbuff = newmess; -} diff --git a/plugins/MidiImport/portsmf/mfmidi.h b/plugins/MidiImport/portsmf/mfmidi.h deleted file mode 100644 index d0049294bc2..00000000000 --- a/plugins/MidiImport/portsmf/mfmidi.h +++ /dev/null @@ -1,98 +0,0 @@ -#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(const char *) = 0; - virtual void Mf_header(int,int,int) = 0; - virtual void Mf_on(int,int,int) = 0; - virtual void Mf_off(int,int,int) = 0; - virtual void Mf_pressure(int,int,int) = 0; - virtual void Mf_controller(int,int,int) = 0; - virtual void Mf_pitchbend(int,int,int) = 0; - virtual void Mf_program(int,int) = 0; - virtual void Mf_chanpressure(int,int) = 0; - virtual void Mf_sysex(int,char*) = 0; - virtual void Mf_arbitrary(int,char*) = 0; - virtual void Mf_metamisc(int,int,char*) = 0; - virtual void Mf_seqnum(int) = 0; - virtual void Mf_smpte(int,int,int,int,int) = 0; - virtual void Mf_timesig(int,int,int,int) = 0; - virtual void Mf_tempo(int) = 0; - virtual void Mf_keysig(int,int) = 0; - virtual void Mf_sqspecific(int,char*) = 0; - virtual void Mf_text(int,int,char*) = 0; - -private: - long Mf_toberead; - - long readvarinum(); - long read32bit(); - int read16bit(); - void msgenlarge(); - char *msg(); - int readheader(); - void readtrack(); - void sysex(); - void msginit(); - int egetc(); - int msgleng(); - - int readmt(const char *, int); - long to32bit(int,int,int,int); - int to16bit(int,int); - void mferror(const char *); - void badbyte(int); - void metaevent(int); - void msgadd(int); - void chanmessage(int,int,int); - - 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 7665b4ae058..00000000000 --- 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; - } - } -} - - -char *escape_chars[] = { (char *) "\\n", (char *)"\\t", (char *)"\\\\", (char *)"\\r", (char *) "\\\""}; - - -void string_escape(string &result, 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])) { - char *chars = (char *)"\n\t\\\r\""; - char *special = strchr(chars, str[i]); - if (special) { - result.append(escape_chars[special - chars]); - } else { - result.append(1, str[i]); - } - } else { - result.append(1, str[i]); - } - } - result.append(1, quote[0]); -} - -void String_parse::get_remainder(std::string &field) -{ - field.clear(); - skip_space(); - int len = str->length() - pos; - if ((*str)[len - 1] == '\n') { // if str ends in newline, - len--; // reduce length to ignore newline - } - field.insert(0, *str, pos, len); -} - - diff --git a/plugins/MidiImport/portsmf/strparse.h b/plugins/MidiImport/portsmf/strparse.h deleted file mode 100644 index 74f01591974..00000000000 --- 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, char *s, const char *quote); diff --git a/plugins/MidiImport/portsmf/trace.cpp b/plugins/MidiImport/portsmf/trace.cpp deleted file mode 100644 index 7c1999db570..00000000000 --- 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(msg, 256, format, args); - va_end(args); -#ifdef _DEBUG - _CrtDbgReport(_CRT_WARN, NULL, NULL, NULL, msg); -#else - printf(msg); -#endif -} diff --git a/plugins/MidiImport/portsmf/trace.h b/plugins/MidiImport/portsmf/trace.h deleted file mode 100644 index 5726f0c5de6..00000000000 --- a/plugins/MidiImport/portsmf/trace.h +++ /dev/null @@ -1,2 +0,0 @@ -void trace(char *format, ...); - diff --git a/plugins/SmfImport/CMakeLists.txt b/plugins/SmfImport/CMakeLists.txt new file mode 100644 index 00000000000..c161b1deb27 --- /dev/null +++ b/plugins/SmfImport/CMakeLists.txt @@ -0,0 +1,18 @@ +# Importer for SMF-like file format, such as .mid, .ove, .wrk + +INCLUDE(BuildPlugin) + +# check for drumstick-file +PKG_CHECK_MODULES(DRUMSTICK_FILE REQUIRED drumstick-file>=0.5.0) +IF(NOT DRUMSTICK_FILE_FOUND) + MESSAGE(FATAL_ERROR "LMMS requires libdrumstick-file and drumstick-dev >= 0.5.0 - please install, remove CMakeCache.txt and try again!") +ENDIF(NOT DRUMSTICK_FILE_FUND) + +IF(LMMS_BUILD_APPLE) + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") +ENDIF(LMMS_BUILD_APPLE) + +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexceptions -ldrumstick-file -Wno-reorder") + +BUILD_PLUGIN(smfimport SmfImport.cpp commonReader.cpp midiReader.cpp oveReader.cpp wrkReader.cpp + MOCFILES SmfImport.h commonReader.h midiReader.h oveReader.h wrkReader.h ) diff --git a/plugins/SmfImport/SmfImport.cpp b/plugins/SmfImport/SmfImport.cpp new file mode 100644 index 00000000000..04e5ff0b43b --- /dev/null +++ b/plugins/SmfImport/SmfImport.cpp @@ -0,0 +1,226 @@ +/* + * SmfImport.h - support for importing SMF-liked files + * + * Copyright (c) 2016-2017 Tony Chyi + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include +#include +#include +#include +#include + +#include "TrackContainer.h" +#include "InstrumentTrack.h" +#include "AutomationTrack.h" +#include "AutomationPattern.h" +#include "ConfigManager.h" +#include "Pattern.h" +#include "Instrument.h" +#include "GuiApplication.h" +#include "MainWindow.h" +#include "MidiTime.h" +#include "debug.h" +#include "embed.h" +#include "Song.h" + +#include "SmfImport.h" +#include "midiReader.h" +#include "oveReader.h" +#include "wrkReader.h" + +extern "C" +{ + +Plugin::Descriptor PLUGIN_EXPORT smfimport_plugin_descriptor = +{ + STRINGIFY( PLUGIN_NAME ), + "SMF Import", + QT_TRANSLATE_NOOP( "pluginBrowser", + "Filter for importing MIDI-like files into LMMS" ), + "Tony Chyi ", + 0x0100, + Plugin::ImportFilter, + NULL, + NULL, + NULL +} ; + +} + + + +SmfImport::SmfImport(const QString &_file): + ImportFilter(_file, &smfimport_plugin_descriptor) +{ + +} + +SmfImport::~SmfImport() +{ + closeFile(); +} + +bool SmfImport::tryImport(TrackContainer *tc) +{ + +#ifdef LMMS_HAVE_FLUIDSYNTH + if( gui != NULL && ConfigManager::inst()->defaultSoundfont().isEmpty() ) + { + QMessageBox::information( gui->mainWindow(), + tr( "Setup incomplete" ), + tr( "You do not have 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." ) ); + } +#else + if( gui ) + { + QMessageBox::information( gui->mainWindow(), + 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." ) ); + } + return false; +#endif + return true; + + if( openFile() == false ) + { + return false; + } + + filename = file().fileName(); + + switch( readID() ) + { + case makeID( 'M', 'T', 'h', 'd' ): + printf( "SmfImport::tryImport(): found MThd\n"); + return readSMF( tc ); + + case makeID( 'R', 'I', 'F', 'F' ): + printf( "SmfImport::tryImport(): found RIFF\n"); + return readRIFF( tc ); + + case makeID( 'O', 'V', 'S', 'C' ): + printf("SmfImport::tryImport(): found OVSC\n"); + return readOve( tc ); + + case makeID( 'C', 'A', 'K', 'E'): + printf("SmfImport::tryImport(): found CAKEWALK\n"); + return readWrk( tc ); + + default: + printf( "MidiImport::tryImport(): not a Standard MIDI " + "file\n" ); + return false; + } + +} + +bool SmfImport::readWrk(TrackContainer *tc) +{ + wrkReader mr(tc); + mr.read(filename); + return true; +} + +bool SmfImport::readOve( TrackContainer *tc) +{ + oveReader mr(tc); + mr.read(filename); + return true; +} + +bool SmfImport::readSMF( TrackContainer *tc) +{ + midiReader mr(tc); + mr.read(filename); + return true; +} + +bool SmfImport::readRIFF( TrackContainer* tc ) +{ + // skip file length + skip( 4 ); + + // check file type ("RMID" = RIFF MIDI) + if( readID() != makeID( 'R', 'M', 'I', 'D' ) ) + { +invalid_format: + qWarning( "MidiImport::readRIFF(): invalid file format" ); + return false; + } + + // search for "data" chunk + while( 1 ) + { + const int id = readID(); + const int len = read32LE(); + if( file().atEnd() ) + { +data_not_found: + 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 ); + } + + // the "data" chunk must contain data in SMF format + if( readID() != makeID( 'M', 'T', 'h', 'd' ) ) + { + goto invalid_format; + } + return readSMF( tc ); +} + +void SmfImport::error() +{ + +} + + +extern "C" +{ + +// necessary for getting instance out of shared lib +Plugin * PLUGIN_EXPORT lmms_plugin_main( Model *, void * _data ) +{ + return new SmfImport( QString::fromUtf8( + static_cast( _data ) ) ); +} + + +} diff --git a/plugins/MidiImport/MidiImport.h b/plugins/SmfImport/SmfImport.h similarity index 74% rename from plugins/MidiImport/MidiImport.h rename to plugins/SmfImport/SmfImport.h index d4c7a0f4a58..8b080e13d20 100644 --- a/plugins/MidiImport/MidiImport.h +++ b/plugins/SmfImport/SmfImport.h @@ -1,7 +1,7 @@ /* - * MidiImport.h - support for importing MIDI-files + * SmfImport.cpp - support for importing SMF-liked files * - * Copyright (c) 2005-2014 Tobias Doerffel + * Copyright (c) 2016-2017 Tony Chyi * * This file is part of LMMS - http://lmms.io * @@ -22,39 +22,38 @@ * */ -#ifndef _MIDI_IMPORT_H -#define _MIDI_IMPORT_H +#ifndef SMF_IMPORT_H +#define SMF_IMPORT_H #include #include #include +#include #include "MidiEvent.h" #include "ImportFilter.h" - -class MidiImport : public ImportFilter +class SmfImport : public ImportFilter { - Q_OBJECT + Q_OBJECT public: - MidiImport( const QString & _file ); - virtual ~MidiImport(); + SmfImport(const QString & _file); + virtual ~SmfImport(); - virtual PluginView * instantiateView( QWidget * ) + virtual PluginView * instantiateView(QWidget *) { - return( NULL ); + return NULL; } - private: - virtual bool tryImport( TrackContainer* tc ); + virtual bool tryImport(TrackContainer *tc); bool readSMF( TrackContainer* tc ); bool readRIFF( TrackContainer* tc ); - bool readTrack( int _track_end, QString & _track_name ); - - void error( void ); + bool readOve( TrackContainer* tc); + bool readWrk( TrackContainer* tc); + void error(); inline int readInt( int _bytes ) { @@ -100,8 +99,8 @@ class MidiImport : public ImportFilter } } } - } - return( !file().atEnd() ? value : -1 ); + } + return( !file().atEnd() ? value : -1 ); } inline int readID() @@ -117,12 +116,7 @@ class MidiImport : public ImportFilter } } - - typedef QVector > EventVector; - EventVector m_events; - int m_timingDivision; - -} ; - + QString filename; +}; #endif diff --git a/plugins/SmfImport/commonReader.cpp b/plugins/SmfImport/commonReader.cpp new file mode 100644 index 00000000000..7397842ea3d --- /dev/null +++ b/plugins/SmfImport/commonReader.cpp @@ -0,0 +1,326 @@ +/* + * commonReader.cpp - import backend. + * + * Copyright (c) 2016-2017 Tony Chyi + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "GuiApplication.h" +#include "TrackContainer.h" +#include "Engine.h" +#include "Song.h" +#include "AutomationPattern.h" +#include "MidiTime.h" +#include "MainWindow.h" + +#include "SmfMidiCC.h" +#include "SmfMidiChannel.h" + +#include "commonReader.h" + +#define CHECK_TRACK track = track==noTrack? chan:track; +#define PITCH_RANGE_RPN_CODE {track, 0} +#define CC_RPN_SEND rpn_data[0] = track; \ + rpn_data[1] = value; + +#define NOTE_EVENT_DEFINITION const int time = 0; \ + const int channel = time + 1; \ + const int note_pitch = channel + 1; \ + const int note_vol = note_pitch + 1; + +commonReader::commonReader(TrackContainer *tc, const char *hintText): + m_tc(tc), + beatsPerTact(4), + pitchBendMultiply( defaultPitchRange ), + pd(TrackContainer::tr(hintText), TrackContainer::tr("Cancel"), + 0, preTrackSteps, gui->mainWindow()) +{ + pd.setWindowTitle( TrackContainer::tr( "Please wait..." ) ); + pd.setWindowModality(Qt::WindowModal); + pd.setMinimumDuration( 0 ); + pd.setValue(0); + + + MeterModel & timeSigMM = Engine::getSong()->getTimeSigModel(); + timeSigNumeratorPat = AutomationPattern::globalAutomationPattern( + &timeSigMM.numeratorModel()); + timeSigDenominatorPat = AutomationPattern::globalAutomationPattern( + &timeSigMM.denominatorModel()); + + ticksPerBeat = DefaultTicksPerTact / beatsPerTact; + + if(note_list.size() != 0) + note_list.clear(); + + if(rpn_msbs.size() != 0) + rpn_msbs.clear(); + + if(rpn_lsbs.size() != 0) + rpn_lsbs.clear(); + + AutomationPattern * tap = m_tc->tempoAutomationPattern(); + tap->clear(); +} + +commonReader::~commonReader() +{ + printf("destory fileReader\n"); + + for( int c=0; c<256; c++){ + if( !chs[c].hasNotes && chs[c].it ) { + printf(" Should remove empty track\n"); + // must delete trackView first - but where is it? + //m_tc->removeTrack( chs[c].it ); + //chs[c].it->deleteLater(); + } + } + +} + +void commonReader::CCHandler(long tick, int track, int ctl, int value) +{ + QString trackName = QString( tr( "Track" ) + " %1").arg(track+1); + SmfMidiChannel * ch = chs[track].create( m_tc, trackName ); + AutomatableModel * objModel = NULL; + int * rpn_data = new int[2]; + + bool flag_rpn_msb = false; + bool flag_rpn_lsb = false; + + if( ctl <= 129 ) + { + switch( ctl ) + { + case bankEventId: + if( ch->isSF2 && ch->it_inst ) + { + objModel = ch->it_inst->childModel( "bank" ); + printf("Tick=%ld: BANK SELECT %d\n", tick, value); + } + break; + + case 6: + for(int c=0; c < rpn_msbs.size(); c++) + { + rpn_data = rpn_msbs[c]; + if(rpn_data[0] == track && rpn_data[1] == 0) + { + flag_rpn_msb = true; + rpn_msbs.removeAt(c); + delete rpn_data; + } + } + for(int c=0; c < rpn_lsbs.size(); c++) { + rpn_data = rpn_lsbs[c]; + if(rpn_data[0] == track && rpn_data[1] == 0) + { + flag_rpn_lsb = true; + rpn_lsbs.removeAt(c); + delete rpn_data; + } + } + + if(flag_rpn_lsb && flag_rpn_msb) + { + objModel = ch->it->pitchRangeModel(); + pitchBendMultiply = value; + } + break; + case volumeEventId: + objModel = ch->it->volumeModel(); + value = value * 100 / 127; + break; + case panEventId: + objModel = ch->it->panningModel(); + // value may be nagetive. + value = value * 100 / 127; + break; + + // RPN LSB + case 100: + CC_RPN_SEND + rpn_lsbs << rpn_data; + break; + + // RPN MSB + case 101: + CC_RPN_SEND + rpn_msbs << rpn_data; + break; + + case pitchBendEventId: + objModel = ch->it->pitchModel(); + value = value * 100 / 8192 * pitchBendMultiply; + break; + + case programEventId: + if( ch->isSF2 && ch->it_inst ) + objModel = ch->it_inst->childModel( "patch" ); + break; + default: + // TODO: something useful for other CCs + printf("Tick=%ld: Unused CC %d with value=%d\n", + tick, ctl, value); + break; + } + + if( objModel ) + { + if( tick == 0 ) + objModel->setInitValue( value ); + else + { + if( ccs[track][ctl].at == NULL ) + { + ccs[track][ctl].create( m_tc, trackName + " > " + objModel->displayName()); + } + ccs[track][ctl].putValue( tick*tickRate , objModel, value ); + } + } + } + +} + +void commonReader::programHandler(long tick, int chan, int patch, + int track) +{ + CHECK_TRACK + + QString trackName = QString( tr( "Track" ) + " %1").arg(chan+1); + SmfMidiChannel * ch = chs[track].create( m_tc, trackName ); + + if( ch->isSF2 ) + { + // AFAIK, 128 should be the standard bank for drums in SF2. + // If not, this has to be made configurable. + ch->it_inst->childModel( "bank" )->setValue(chan!=9 ? 0 : 128); + if(tick == 0) + ch->it_inst->childModel( "patch" )->setValue(patch); + else + CCHandler(tick, track, programEventId, patch); + } + else + { + const QString num = QString::number( patch ); + const QString filter = QString().fill( '0', 3 - num.length() ) + num + "*.pat"; + const QString dir = "/usr/share/midi/" + "freepats/Tone_000/"; + const QStringList files = QDir( dir ). + entryList( QStringList( filter ) ); + if( ch->it_inst && !files.empty() ) + { + ch->it_inst->loadFile( dir+files.front() ); + } + } + +} + +void commonReader::timeSigHandler(long tick, int num, int den) +{ + printf("Another timesig at %f\n", tick * tickRate); + timeSigNumeratorPat->putValue(tick * tickRate, num); + timeSigDenominatorPat->putValue(tick * tickRate, den); + pd.setValue(preTrackSteps); +} + +void commonReader::tempoHandler(long tick, int tempo) +{ + AutomationPattern * tap = m_tc->tempoAutomationPattern(); + if( tap ) { + tap->putValue((double)tick * tickRate, tempo); + } +} + +void commonReader::timeBaseHandler(int timebase) +{ + tickRate = ticksPerBeat / (double) timebase; +} + +// when use this, addNoteEvent() should be called later, or memeory leak. +void commonReader::insertNoteEvent(long tick, int chan, int pitch, + int vol, int track) +{ + NOTE_EVENT_DEFINITION + CHECK_TRACK + + int *note = new int[4]; + note[time] = tick; + note[channel] = track; + note[note_pitch] = pitch; + note[note_vol] = vol; + note_list << note; +} + +void commonReader::addNoteEvent(long tick, int chan, + int pitch, int track) +{ + NOTE_EVENT_DEFINITION + CHECK_TRACK + + + for(int c=0; c= note[time]) + { + int dur = tick - note[time]; + addNoteEvent(note[time], chan, pitch, note[note_vol], dur, track); + note_list.removeAt(c); + delete note; + break; + } + } + + if(pd.maximum() <= chan+preTrackSteps) + pd.setMaximum(pd.maximum()+preTrackSteps); + + if(pd.value() <= chan+preTrackSteps) + pd.setValue(chan+preTrackSteps); + +} + +void commonReader::addNoteEvent(long tick, int chan, int pitch, int vol, + int dur, int track) +{ + CHECK_TRACK + + QString trackName = QString( tr( "Track" ) + " %1").arg(track+1); + SmfMidiChannel * ch = chs[track].create( m_tc, trackName ); + double realDur = dur * tickRate; + Note n( (realDur < 1 ? 1 : realDur ), (double)tick * tickRate, pitch-12, vol ); + ch->addNote( n ); + +} + +void commonReader::errorHandler(const QString &errorStr) +{ + printf( "SmfImport::readFile(): got error %s\n", + errorStr.toStdString().c_str() ); +} diff --git a/plugins/SmfImport/commonReader.h b/plugins/SmfImport/commonReader.h new file mode 100644 index 00000000000..e7363e1422c --- /dev/null +++ b/plugins/SmfImport/commonReader.h @@ -0,0 +1,107 @@ +/* + * commonReader.h - import backend. + * + * Copyright (c) 2016-2017 Tony Chyi + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef COMMON_READER_H +#define COMMON_READER_H + +#include +#include +#include + +#include "TrackContainer.h" +#include "MeterModel.h" +#include "AutomationPattern.h" + +#include "SmfMidiCC.h" +#include "SmfMidiChannel.h" + +#define makeID(_c0, _c1, _c2, _c3) \ + ( 0 | \ + ( ( _c0 ) | ( ( _c1 ) << 8 ) | ( ( _c2 ) << 16 ) | ( ( _c3 ) << 24 ) ) ) + +const int defaultPitchRange = 2; +const int preTrackSteps = 2; + +const int bankEventId = 0; +const int volumeEventId = 7; +const int panEventId = 10; +const int pitchBendEventId = 128; +const int programEventId = 129; + +const int noTrack = -1; + +class commonReader : public QObject +{ + Q_OBJECT +public: + commonReader(TrackContainer *tc, const char *hintText); + ~commonReader(); +protected: + virtual void errorHandler(const QString &errorStr); + void CCHandler(long tick, int track, int ctl, int value); + void programHandler(long tick, int chan, int patch, int track=noTrack); + void timeSigHandler(long tick, int num, int den); + void tempoHandler(long tick, int tempo); + void timeBaseHandler(int timebase); + + void insertNoteEvent(long tick, int chan, int pitch, int vol, int track=noTrack); + + void addNoteEvent(long tick, int chan, int pitch, int track=noTrack); + void addNoteEvent(long tick, int chan, int pitch, int vol, int dur, int track=noTrack); + + + QProgressDialog pd; + TrackContainer *m_tc; + + // 128 CC + Pitch Bend + Program + SmfMidiCC ccs[256][130]; + SmfMidiChannel chs[256]; + + AutomationPattern * timeSigNumeratorPat; + AutomationPattern * timeSigDenominatorPat; + + double beatsPerTact; + double ticksPerBeat; + double tickRate; // convert ove tick to lmms tick. + + int pitchBendMultiply; + + /* + * record note event. + * tick, channel, pitch, vol. + */ + QList note_list; + + /* + * record rpn event. + * chan, value + */ + QList rpn_msbs; + QList rpn_lsbs; + + +}; + +#endif + diff --git a/plugins/SmfImport/midiReader.cpp b/plugins/SmfImport/midiReader.cpp new file mode 100644 index 00000000000..346ee522aef --- /dev/null +++ b/plugins/SmfImport/midiReader.cpp @@ -0,0 +1,146 @@ +/* + * midiReader.cpp - support for importing MIDI files + * + * Copyright (c) 2005-2014 Tobias Doerffel + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include +#include +#include +#include +#include + +#include "GuiApplication.h" +#include "TrackContainer.h" +#include "Engine.h" +#include "Song.h" +#include "AutomationPattern.h" +#include "MidiTime.h" +#include "MainWindow.h" + +#include "SmfMidiCC.h" +#include "SmfMidiChannel.h" + +#include "midiReader.h" + +midiReader::midiReader( TrackContainer* tc ) : + commonReader(tc, "Importing MIDI-file..." ), + m_seq(new drumstick::QSmf) +{ + // Connect to slots. + connect(m_seq, SIGNAL(signalSMFTimeSig(int,int,int,int)), + this, SLOT(timeSigEvent(int,int,int,int))); + + connect(m_seq, SIGNAL(signalSMFTempo(int)), + this, SLOT(tempoEvent(int))); + + connect(m_seq, SIGNAL(signalSMFError(QString)), + this, SLOT(errorHandler(QString))); + + connect(m_seq, SIGNAL(signalSMFCtlChange(int,int,int)), + this, SLOT(ctlChangeEvent(int,int,int))); + + connect(m_seq, SIGNAL(signalSMFPitchBend(int,int)), + this, SLOT(pitchBendEvent(int,int))); + + connect(m_seq, SIGNAL(signalSMFNoteOn(int,int,int)), + this, SLOT(noteOnEvent(int,int,int))); + + connect(m_seq, SIGNAL(signalSMFNoteOff(int,int,int)), + this, SLOT(noteOffEvent(int,int,int))); + + connect(m_seq, SIGNAL(signalSMFProgram(int,int)), + this, SLOT(programEvent(int,int))); + + connect(m_seq, SIGNAL(signalSMFHeader(int,int,int)), + this, SLOT(headerEvent(int,int,int))); + +} + +midiReader::~midiReader() +{ + printf("destroy midiReader\n"); + delete m_seq; +} + +void midiReader::read(QString &fileName) +{ + m_seq->readFromFile(fileName); +} + +// Slots below. + +// b0: Numerator +// b1: Denominator (exponent in a power of two) +// b2: Number of MIDI clocks per metronome click +// b3: Number of notated 32nd notes per 24 MIDI clocks +void midiReader::timeSigEvent(int b0, int b1, int b2, int b3) +{ + timeSigHandler(m_seq->getCurrentTime(), b0, 1<getCurrentTime(), 60000000/tempo); +} + +void midiReader::errorHandler(const QString &errorStr) +{ + printf( "MidiImport::readSMF(): got error %s at %ld\n", + errorStr.toStdString().c_str(), m_seq->getCurrentTime() ); +} + +void midiReader::ctlChangeEvent(int chan, int ctl, int value) +{ + CCHandler(m_seq->getCurrentTime(), chan, ctl, value); +} + +void midiReader::pitchBendEvent(int chan, int value) +{ + CCHandler(m_seq->getCurrentTime(), chan, pitchBendEventId, value); +} + +void midiReader::noteOnEvent(int chan, int pitch, int vol) +{ + if(vol != 0){ + insertNoteEvent(m_seq->getCurrentTime(), chan, pitch, vol); + } + else + { + addNoteEvent(m_seq->getCurrentTime(), chan, pitch); + } +} + +void midiReader::noteOffEvent(int chan, int pitch, int vol) +{ + addNoteEvent(m_seq->getCurrentTime(), chan, pitch); +} + +void midiReader::headerEvent(int format, int ntrks, int division) +{ + timeBaseHandler(division); + pd.setMaximum( ntrks + preTrackSteps ); +} + +void midiReader::programEvent(int chan, int patch) +{ + programHandler(m_seq->getCurrentTime(), chan, patch); +} diff --git a/plugins/SmfImport/midiReader.h b/plugins/SmfImport/midiReader.h new file mode 100644 index 00000000000..8b8fe5769fb --- /dev/null +++ b/plugins/SmfImport/midiReader.h @@ -0,0 +1,52 @@ +/* + * midiReader.h - support for importing MIDI files + * + * Copyright (c) 2005-2014 Tobias Doerffel + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef MIDI_READER_H +#define MIDI_READER_H + +#include + +#include "commonReader.h" +class midiReader : public commonReader +{ + Q_OBJECT +public: + midiReader(TrackContainer *tc); + ~midiReader(); + void read(QString &fileName); +public slots: + void headerEvent(int format, int ntrks, int division); + void noteOnEvent(int chan, int pitch, int vol); + void noteOffEvent(int chan, int pitch, int vol); + void ctlChangeEvent(int chan, int ctl, int value); + void pitchBendEvent(int chan, int value); + void programEvent(int chan, int patch); + void timeSigEvent(int b0, int b1, int b2, int b3); + void tempoEvent(int tempo); + void errorHandler(const QString& errorStr); +private: + drumstick::QSmf *m_seq; +}; + +#endif diff --git a/plugins/SmfImport/oveReader.cpp b/plugins/SmfImport/oveReader.cpp new file mode 100644 index 00000000000..9c844f1a8d8 --- /dev/null +++ b/plugins/SmfImport/oveReader.cpp @@ -0,0 +1,171 @@ +/* + * oveReader.cpp - support for importing Overture files + * + * Copyright (c) 2016-2017 Tony Chyi + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include +#include +#include +#include +#include + +#include "GuiApplication.h" +#include "TrackContainer.h" +#include "Engine.h" +#include "Song.h" +#include "AutomationPattern.h" +#include "MidiTime.h" +#include "MainWindow.h" + +#include "SmfMidiCC.h" +#include "SmfMidiChannel.h" + +#include "oveReader.h" + +oveReader::oveReader( TrackContainer* tc ) : + commonReader(tc, "Importing Overture file..."), + m_seq(new drumstick::QOve) +{ + // Connect to slots. + connect(m_seq, SIGNAL(signalOVEError(QString)), + this, SLOT(errorHandler(QString))); + + connect(m_seq, SIGNAL(signalOVEHeader(int,int)), + this, SLOT(fileHeader(int,int))); + + connect(m_seq, SIGNAL(signalOVENoteOn(int,long,int,int,int)), + this, SLOT(noteOnEvent(int,long,int,int,int))); + + connect(m_seq, SIGNAL(signalOVENoteOff(int,long,int,int,int)), + this, SLOT(noteOffEvent(int,long,int,int,int))); + + connect(m_seq, SIGNAL(signalOVECtlChange(int,long,int,int,int)), + this, SLOT(ctlChangeEvent(int,long,int,int,int))); + + connect(m_seq, SIGNAL(signalOVEPitchBend(int,long,int,int)), + this, SLOT(ctlChangeEvent(int,long,int,int,int))); + + connect(m_seq, SIGNAL(signalOVEProgram(int,long,int,int)), + this, SLOT(programEvent(int,long,int,int))); + + connect(m_seq, SIGNAL(signalOVETimeSig(int,long,int,int)), + this, SLOT(timeSigEvent(int,long,int,int))); + + connect(m_seq, SIGNAL(signalOVETempo(long,int)), + this, SLOT(tempoEvent(long,int))); + + connect(m_seq, SIGNAL(signalOVETrackPatch(int,int,int)), + this, SLOT(trackPatch(int,int,int))); + + connect(m_seq, SIGNAL(signalOVETrackVol(int,int,int)), + this, SLOT(trackVol(int,int,int))); + + connect(m_seq, SIGNAL(signalOVETrackBank(int,int,int)), + this, SLOT(trackBank(int,int,int))); + +} + +oveReader::~oveReader() +{ + printf("destroy oveReader\n"); + delete m_seq; +} + +void oveReader::read(QString &fileName) +{ + m_seq->readFromFile(fileName); +} + +// Slots below. +void oveReader::timeSigEvent(int bar, long tick, int num, int den) +{ + timeSigHandler(tick, num, den); +} + +void oveReader::tempoEvent(long tick, int tempo) +{ + tempoHandler(tick, tempo/100); +} + +void oveReader::errorHandler(const QString &errorStr) +{ + printf( "MidiImport::readSMF(): got error %s\n", + errorStr.toStdString().c_str() ); +} + +void oveReader::ctlChangeEvent(int track, long tick, int chan, int ctl, int value) +{ + CCHandler(tick, track, ctl, value); +} + +void oveReader::pitchBendEvent(int track, long tick, int chan, int value) +{ + CCHandler(tick, track, pitchBendEventId, value); +} + +void oveReader::noteOnEvent(int track, long tick, int chan, int pitch, int vol) +{ + + if(vol != 0){ + insertNoteEvent(tick, chan, pitch, vol, track); + } + else + { + addNoteEvent(tick, chan, pitch, track); + } +} + +void oveReader::noteOffEvent(int track, long tick, int chan, int pitch, int vol) +{ + addNoteEvent(tick, chan, pitch, track); +} + +/*void oveReader::headerEvent(int format, int ntrks, int division) +{ + tickRate = ticksPerBeat / (double)division; + pd.setMaximum( ntrks + preTrackSteps ); +}*/ + +void oveReader::programEvent(int track, long tick, int chan, int patch) +{ + programHandler(tick, chan, patch, track); +} + +void oveReader::trackPatch(int track, int chan, int patch){ + programHandler(0, chan, patch, track); +} + +void oveReader::trackBank(int track, int chan, int bank) +{ + CCHandler(0, track, bankEventId, bank); +} + +void oveReader::trackVol(int track, int chan, int vol) +{ + CCHandler(0, track, volumeEventId, vol); +} + +void oveReader::fileHeader(int quarter, int trackCount) +{ + timeBaseHandler(quarter); + pd.setMaximum(trackCount + preTrackSteps); +} diff --git a/plugins/SmfImport/oveReader.h b/plugins/SmfImport/oveReader.h new file mode 100644 index 00000000000..7651e9c1d70 --- /dev/null +++ b/plugins/SmfImport/oveReader.h @@ -0,0 +1,54 @@ +/* + * oveReader.h - support for importing Overture files + * + * Copyright (c) 2016-2017 Tony Chyi + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef OVE_READER_H +#define OVE_READER_H + +#include "commonReader.h" + +class oveReader : public commonReader +{ + Q_OBJECT +public: + oveReader(TrackContainer *tc); + ~oveReader(); + void read(QString &fileName); +public slots: + void errorHandler(const QString& errorStr); + void fileHeader(int quarter, int trackCount); + void noteOnEvent(int track, long tick, int chan, int pitch, int vol); + void noteOffEvent(int track, long tick, int chan, int pitch, int vol); + void ctlChangeEvent(int track, long tick, int chan, int ctl, int value); + void pitchBendEvent(int track, long tick, int chan, int value); + void programEvent(int track, long tick, int chan, int patch); + void timeSigEvent(int bar, long tick, int num, int den); + void tempoEvent(long tick, int tempo); + void trackPatch(int track, int chan, int patch); + void trackVol(int track, int chan, int vol); + void trackBank(int track, int chan, int bank); +private: + drumstick::QOve *m_seq; +}; + +#endif diff --git a/plugins/SmfImport/wrkReader.cpp b/plugins/SmfImport/wrkReader.cpp new file mode 100644 index 00000000000..24b6143906e --- /dev/null +++ b/plugins/SmfImport/wrkReader.cpp @@ -0,0 +1,138 @@ +/* + * wrkReader.cpp - support for importing Cakewalk files + * + * Copyright (c) 2016-2017 Tony Chyi + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include +#include +#include +#include +#include + +#include "GuiApplication.h" +#include "TrackContainer.h" +#include "Engine.h" +#include "Song.h" +#include "AutomationPattern.h" +#include "MidiTime.h" +#include "MainWindow.h" + +#include "SmfMidiCC.h" +#include "SmfMidiChannel.h" + +#include "wrkReader.h" + +wrkReader::wrkReader( TrackContainer* tc ) : + commonReader(tc, "Importing Cakewalk file..."), + m_seq(new drumstick::QWrk) +{ + // Connect to slots. + connect(m_seq, SIGNAL(signalWRKError(QString)), + this, SLOT(errorHandler(QString))); + connect(m_seq, SIGNAL(signalWRKTimeBase(int)), + this, SLOT(timeBase(int))); + connect(m_seq, SIGNAL(signalWRKNote(int,long,int,int,int,int)), + this, SLOT(noteEvent(int,long,int,int,int,int))); + connect(m_seq, SIGNAL(signalWRKCtlChange(int,long,int,int,int)), + this, SLOT(ctlChangeEvent(int,long,int,int,int))); + connect(m_seq, SIGNAL(signalWRKPitchBend(int,long,int,int)), + this, SLOT(pitchBendEvent(int,long,int,int))); + connect(m_seq, SIGNAL(signalWRKProgram(int,long,int,int)), + this, SLOT(programEvent(int,long,int,int))); + connect(m_seq, SIGNAL(signalWRKTimeSig(int,int,int)), + this, SLOT(timeSigEvent(int,int,int))); + connect(m_seq, SIGNAL(signalWRKTempo(long,int)), + this, SLOT(tempoEvent(long,int))); + connect(m_seq, SIGNAL(signalWRKTrackVol(int,int)), + this, SLOT(trackVol(int,int))); + connect(m_seq, SIGNAL(signalWRKTrackBank(int,int)), + this, SLOT(trackBank(int,int))); +} + +wrkReader::~wrkReader() +{ + printf("destroy wrkReader\n"); + delete m_seq; +} + +void wrkReader::read(QString &fileName) +{ + m_seq->readFromFile(fileName); +} + +// Slots below. + +void wrkReader::timeSigEvent(int bar, int num, int den) +{ + int tick = bar * num * ticksPerBeat; + timeSigHandler(tick, num, den); +} + +void wrkReader::tempoEvent(long time, int tempo) +{ + tempoHandler(time, tempo/100); +} + +void wrkReader::errorHandler(const QString &errorStr) +{ + printf( "MidiImport::readSMF(): got error %s\n", + errorStr.toStdString().c_str() ); +} + +void wrkReader::ctlChangeEvent(int track, long tick, int chan, int ctl, int value) +{ + CCHandler(tick, track, ctl, value); +} + +void wrkReader::pitchBendEvent(int track, long tick, int chan, int value) +{ + CCHandler(tick, track, pitchBendEventId, value); +} + +void wrkReader::noteEvent(int track, long time, int chan, int pitch, int vol, int dur) +{ + addNoteEvent(time, chan, pitch, vol, dur, track); +} + +void wrkReader::programEvent(int track, long tick, int chan, int patch) +{ + programHandler(tick, chan, patch, track); +} + +void wrkReader::trackPatch(int track, int patch){ + //programHandler(0, chan, patch, track); +} + +void wrkReader::trackBank(int track, int bank) +{ + //CCHandler(0, track, 0, bank); +} + +void wrkReader::trackVol(int track, int vol) +{ + CCHandler(0, track, volumeEventId, vol); +} + +void wrkReader::timeBase(int quarter) +{ + timeBaseHandler(quarter); +} diff --git a/plugins/SmfImport/wrkReader.h b/plugins/SmfImport/wrkReader.h new file mode 100644 index 00000000000..7d17c88a598 --- /dev/null +++ b/plugins/SmfImport/wrkReader.h @@ -0,0 +1,54 @@ +/* + * wrkReader.h - support for importing Cakewalk files + * + * Copyright (c) 2016-2017 Tony Chyi + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef WRK_READER_H +#define WRK_READER_H + +#include "commonReader.h" + +class wrkReader : public commonReader +{ + Q_OBJECT +public: + wrkReader(TrackContainer *tc); + ~wrkReader(); + void read(QString &fileName); +public slots: + void errorHandler(const QString& errorStr); + void timeBase(int timebase); + void noteEvent(int track, long time, int chan, int pitch, int vol, int dur); + void ctlChangeEvent(int track, long time, int chan, int ctl, int value); + void pitchBendEvent(int track, long time, int chan, int value); + void programEvent(int track, long time, int chan, int patch); + void timeSigEvent(int bar, int num, int den); + void tempoEvent(long time, int tempo); + void trackPatch(int track, int patch); + void trackVol(int track, int vol); + void trackBank(int track, int bank); + +private: + drumstick::QWrk *m_seq; +}; + +#endif diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 3ec3cb7ee9b..ea66fd03ffe 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -65,6 +65,9 @@ set(LMMS_SRCS core/TrackContainer.cpp core/VstSyncController.cpp + core/SmfMidiCC.cpp + core/SmfMidiChannel.cpp + core/audio/AudioAlsa.cpp core/audio/AudioDevice.cpp core/audio/AudioFileDevice.cpp diff --git a/src/core/SmfMidiCC.cpp b/src/core/SmfMidiCC.cpp new file mode 100644 index 00000000000..ac380d700c2 --- /dev/null +++ b/src/core/SmfMidiCC.cpp @@ -0,0 +1,81 @@ +/* + * SmfMidiCC.cpp - support for importing MIDI files + * + * Copyright (c) 2005-2014 Tobias Doerffel + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include + +#include "TrackContainer.h" +#include "AutomationTrack.h" +#include "AutomationPattern.h" +#include "MidiTime.h" + +#include "SmfMidiCC.h" + +SmfMidiCC::SmfMidiCC() : + at( NULL ), + ap( NULL ), + lastPos( 0 ) +{ } + +SmfMidiCC & SmfMidiCC::create(TrackContainer* tc, QString tn ) +{ + 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::AutomationTrack, tc ) ); + } + if( tn != "") { + at->setName( tn ); + } + return *this; +} + + +void SmfMidiCC::clear() +{ + at = NULL; + ap = NULL; + lastPos = 0; +} + +SmfMidiCC & SmfMidiCC::putValue( MidiTime time, AutomatableModel * objModel, float value ) +{ + if( !ap || time > lastPos + DefaultTicksPerTact ) + { + MidiTime pPos = MidiTime( time.getTact(), 0 ); + ap = dynamic_cast( + at->createTCO(0) ); + ap->movePosition( pPos ); + ap->addObject( objModel ); + } + + lastPos = time; + time = time - ap->startPosition(); + ap->putValue( time, value, false ); + ap->changeLength( MidiTime( time.getTact() + 1, 0 ) ); + + return *this; +} diff --git a/src/core/SmfMidiChannel.cpp b/src/core/SmfMidiChannel.cpp new file mode 100644 index 00000000000..8b891152b8b --- /dev/null +++ b/src/core/SmfMidiChannel.cpp @@ -0,0 +1,91 @@ +/* + * SmfMidiChannel.cpp - support for importing MIDI files + * + * Copyright (c) 2005-2014 Tobias Doerffel + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include + +#include "InstrumentTrack.h" +#include "Pattern.h" +#include "Instrument.h" +#include "MidiTime.h" +#include "ConfigManager.h" + +#include "SmfMidiChannel.h" + +SmfMidiChannel::SmfMidiChannel() : + it( NULL ), + p( NULL ), + it_inst( NULL ), + isSF2( false ), + hasNotes( false ), + lastEnd( 0 ) +{ } + +SmfMidiChannel * SmfMidiChannel::create( TrackContainer* tc, QString tn ) +{ + if( !it ) { + // Keep LMMS responsive + qApp->processEvents(); + it = dynamic_cast( Track::create( Track::InstrumentTrack, tc ) ); + +#ifdef LMMS_HAVE_FLUIDSYNTH + it_inst = it->loadInstrument( "sf2player" ); + + if( it_inst ) + { + isSF2 = true; + it_inst->loadFile( ConfigManager::inst()->defaultSoundfont() ); + it_inst->childModel( "bank" )->setValue( 0 ); + it_inst->childModel( "patch" )->setValue( 0 ); + } + else + { + it_inst = it->loadInstrument( "patman" ); + } +#else + it_inst = it->loadInstrument( "patman" ); +#endif + trackName = tn; + if( trackName != "") { + it->setName( tn ); + } + lastEnd = 0; + // General MIDI default + it->pitchRangeModel()->setInitValue( 2 ); + } + return this; +} + +void SmfMidiChannel::addNote( Note & n ) +{ + if( !p || n.pos() > lastEnd + DefaultTicksPerTact ) + { + MidiTime pPos = MidiTime( n.pos().getTact(), 0 ); + p = dynamic_cast( it->createTCO( 0 ) ); + p->movePosition( pPos ); + } + hasNotes = true; + lastEnd = n.pos() + n.length(); + n.setPos( n.pos( p->startPosition() ) ); + p->addNote( n, false ); +} diff --git a/src/core/Song.cpp b/src/core/Song.cpp index ebcdedb78af..ebbc71eb8ee 100644 --- a/src/core/Song.cpp +++ b/src/core/Song.cpp @@ -1187,6 +1187,10 @@ void Song::importProject() ConfigManager::inst()->userProjectsDir(), tr("MIDI sequences") + " (*.mid *.midi *.rmi);;" + + tr("Overture projects") + + " (*.ove);;" + + tr("Cakewalk projects") + + " (*.wrk);;" + tr("Hydrogen projects") + " (*.h2song);;" + tr("All file types") +