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; }
Alg_seq_ptr NoteTrack::MakeExportableSeq() { double offset = GetOffset(); if (offset == 0) return mSeq; // make a copy, deleting events that are shifted before time 0 double start = -offset; if (start < 0) start = 0; // notes that begin before "start" are not included even if they // extend past "start" (because "all" parameter is set to false) Alg_seq_ptr seq = mSeq->copy(start, mSeq->get_dur() - start, false); if (offset > 0) { // swap seq and mSeq so that Shift operates on the new copy Alg_seq_ptr old_seq = mSeq; mSeq = seq; Shift(offset); seq = mSeq; // undo the swap mSeq = old_seq; #ifdef OLD_CODE // now shift events by offset. This must be done with an integer // number of measures, so first, find the beats-per-measure double beats_per_measure = 4.0; Alg_time_sig_ptr tsp = NULL; if (seq->time_sig.length() > 0 && seq->time_sig[0].beat < ALG_EPS) { // there is an initial time signature tsp = &(seq->time_sig[0]); beats_per_measure = (tsp->num * 4) / tsp->den; } // also need the initial tempo double bps = ALG_DEFAULT_BPM / 60; Alg_time_map_ptr map = seq->get_time_map(); Alg_beat_ptr bp = &(map->beats[0]); if (bp->time < ALG_EPS) { // tempo change at time 0 if (map->beats.len > 1) { // compute slope to get tempo bps = (map->beats[1].beat - map->beats[0].beat) / (map->beats[1].time - map->beats[0].time); } else if (seq->get_time_map()->last_tempo_flag) { bps = seq->get_time_map()->last_tempo; } } // find closest number of measures to fit in the gap // number of measures is offset / measure_time double measure_time = beats_per_measure / bps; // seconds per measure int n = ROUND(offset / measure_time); if (n == 0) n = 1; // we will insert n measures. Compute the desired duration of each. measure_time = offset / n; bps = beats_per_measure / measure_time; // insert integer multiple of measures at beginning seq->convert_to_beats(); seq->insert_silence(0, beats_per_measure * n); // make sure time signature at 0 is correct if (tsp) { seq->set_time_sig(0, tsp->num, tsp->den); } // adjust tempo to match offset seq->set_tempo(bps * 60.0, 0, beats_per_measure * n); #endif } else { // if offset is negative, it might not be a multiple of beats, but // we want to preserve the relative positions of measures. I.e. we // should shift barlines and time signatures as well as notes. // Insert a time signature at the first bar-line if necessary. // Translate start from seconds to beats and call it beat: double beat = mSeq->get_time_map()->time_to_beat(start); // Find the time signature in mSeq in effect at start (beat): int i = mSeq->time_sig.find_beat(beat); // i is where you would insert a new time sig at beat, // Case 1: beat coincides with a time sig at i. Time signature // at beat means that there is a barline at beat, so when beat // is shifted to 0, the relative barline positions are preserved if (mSeq->time_sig.length() > 0 && within(beat, mSeq->time_sig[i].beat, ALG_EPS)) { // beat coincides with time signature change, so offset must // be a multiple of beats /* do nothing */ ; // Case 2: there is no time signature before beat. } else if (i == 0 && (mSeq->time_sig.length() == 0 || mSeq->time_sig[i].beat > beat)) { // If beat does not fall on an implied barline, we need to // insert a time signature. double measures = beat / 4.0; double imeasures = ROUND(measures); if (!within(measures, imeasures, ALG_EPS)) { double bar_offset = (int(measures) + 1) * 4.0 - beat; seq->set_time_sig(bar_offset, 4, 4); } // This case should never be true because if i == 0, either there // are no time signatures before beat (Case 2), // or there is one time signature at beat (Case 1) } else if (i == 0) { /* do nothing (might be good to assert(false)) */ ; // Case 3: i-1 must be the effective time sig position } else { i -= 1; // index the time signature in effect at beat Alg_time_sig_ptr tsp = &(mSeq->time_sig[i]); double beats_per_measure = (tsp->num * 4) / tsp->den; double measures = (beat - tsp->beat) / beats_per_measure; int imeasures = ROUND(measures); if (!within(measures, imeasures, ALG_EPS)) { // beat is not on a measure, so we need to insert a time sig // to force a bar line at the first measure location after // beat double bar = tsp->beat + beats_per_measure * (int(measures) + 1); double bar_offset = bar - beat; // insert new time signature at bar_offset in new sequence // It will have the same time signature, but the position will // force a barline to match the barlines in mSeq seq->set_time_sig(bar_offset, tsp->num, tsp->den); } // else beat coincides with a barline, so no need for an extra // time signature to force barline alignment } } return seq; }
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<Alg_note_ptr>( 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; }