Ejemplo n.º 1
0
Alg_event_ptr Alg_seq::write_track_name(ostream &file, int n, 
                                        Alg_events &events)
// write #track <n> <trackname-or-sequencename>
// if we write the name on the "#track" line, then we do *not* want
// to write again as an update: "-seqnames:"Jordu", so if we do
// find a name and write it, return a pointer to it so the track
// writer knows what update (if any) to skip
{
    Alg_event_ptr e = NULL;
    file << "#track " << n;
    const char *attr = symbol_table.insert_string(
                               n == 0 ? "seqnames" : "tracknames");
    // search for name in events with timestamp of 0
    for (int i = 0; i < events.length(); i++) {
        e = events[i];
        if (e->time > 0) break;
        if (e->is_update()) {
            Alg_update_ptr u = (Alg_update_ptr) e;
            if (u->parameter.attr == attr) {
                file << " " << u->parameter.s;
                break;
            }
        }
    }
    file << endl;
    return e;
}
Ejemplo n.º 2
0
/*				EDIT_TRANSCRIPTION
	edit the allegro time map structure according
	to the warping and output a midi file and transcription
	file 

*/
void edit_transcription(Alg_seq &seq , bool warp, FILE *outf, 
                        const char *midi_filename, const char *beat_filename) {
    int note_x = 1;
    seq.convert_to_seconds();
    Alg_iterator iter(&seq, false); // no note-offs
    iter.begin();
    Alg_event_ptr e = iter.next();

    while (e) {
        if (e->is_note()) {
            Alg_note_ptr n = (Alg_note_ptr) e;
            fprintf(outf, "%d %ld %d %d ", 
                    note_x++, n->chan, ROUND(n->pitch), ROUND(n->loud));
            // now compute onset time mapped to audio time
            double start = n->time;
            double finish = n->time + n->dur;
            if (warp) {
                start = sa.map_time(start);
                finish = sa.map_time(finish);
            }
            fprintf(outf, "%.3f %.3f\n", start, finish-start);
        }
        e = iter.next();
    }
    iter.end();
    fclose(outf);
    if (warp) {
        // align the midi file and write out 	
        sa.midi_tempo_align(seq);
        seq.smf_write(midi_filename);
        print_beat_map(seq, beat_filename);
    }
}
Ejemplo n.º 3
0
bool ImportMIDI(wxString fName, NoteTrack * dest)
{
   if (fName.Length() <= 4){
      wxMessageBox( _("Could not open file ") + fName + _(": Filename too short."));
      return false;
   }

   bool is_midi = false;
   if (fName.Right(4).CmpNoCase(wxT(".mid")) == 0 || fName.Right(5).CmpNoCase(wxT(".midi")) == 0)
      is_midi = true;
   else if(fName.Right(4).CmpNoCase(wxT(".gro")) != 0) {
      wxMessageBox( _("Could not open file ") + fName + _(": Incorrect filetype."));
      return false;
   }

   wxFFile mf(fName, wxT("rb"));
   if (!mf.IsOpened()) {
      wxMessageBox( _("Could not open file ") + fName + wxT("."));
      return false;
   }

   double offset = 0.0;
   Alg_seq_ptr new_seq = new Alg_seq(fName.mb_str(), is_midi, &offset);

   //Should we also check if(seq->tracks() == 0) ?
   if(new_seq->get_read_error() == alg_error_open){
      wxMessageBox( _("Could not open file ") + fName + wxT("."));
      mf.Close();
      delete new_seq;
      return false;
   }

   dest->SetSequence(new_seq);
   dest->SetOffset(offset);
   wxString trackNameBase = fName.AfterLast(wxFILE_SEP_PATH).BeforeLast('.');
   dest->SetName(trackNameBase);
   mf.Close();
   // the mean pitch should be somewhere in the middle of the display
   Alg_iterator iterator(new_seq, false);
   iterator.begin();
   // for every event
   Alg_event_ptr evt;
   int note_count = 0;
   int pitch_sum = 0;
   while ((evt = iterator.next())) {
      // if the event is a note
       if (evt->get_type() == 'n') {
           Alg_note_ptr note = (Alg_note_ptr) evt;
           pitch_sum += (int) note->pitch;
           note_count++;
       }
   }
   int mean_pitch = (note_count > 0 ? pitch_sum / note_count : 60);
   // initial track is about 27 half-steps high; if bottom note is C,
   // then middle pitch class is D. Round mean_pitch to the nearest D:
   int mid_pitch = ((mean_pitch - 2 + 6) / 12) * 12 + 2;
   dest->SetBottomNote(mid_pitch - 14);
   return true;
}
Ejemplo n.º 4
0
void NoteTrack::WarpAndTransposeNotes(double t0, double t1,
                                      const TimeWarper &warper,
                                      double semitones)
{
   // Since this is a duplicate and duplicates convert mSeq to
   // a text string for saving as XML, we probably have to
   // duplicate again to get back an mSeq
   NoteTrack *nt = this;
   double offset = nt->GetOffset(); // track is shifted this amount
   if (!mSeq) { // replace saveme with an (unserialized) duplicate
      nt = (NoteTrack *) this->Duplicate();
      wxASSERT(!mSeq && nt->mSeq && !nt->mSerializationBuffer);
      // swap mSeq and Buffer between this and nt
      nt->mSerializationBuffer = mSerializationBuffer;
      nt->mSerializationLength = mSerializationLength;
      mSerializationBuffer = NULL;
      mSerializationLength = 0;
      mSeq = nt->mSeq;
      nt->mSeq = NULL;
      delete nt; // delete the duplicate
   }
   mSeq->convert_to_seconds(); // make sure time units are right
   t1 -= offset; // adjust time range to compensate for track offset
   t0 -= offset;
   if (t1 > mSeq->get_dur()) { // make sure t0, t1 are within sequence
      t1 = mSeq->get_dur();
      if (t0 >= t1) return;
   }
   Alg_iterator iter(mSeq, false);
   iter.begin();
   Alg_event_ptr event;
   while ((event = iter.next()) && event->time < t1) {
      if (event->is_note() && event->time >= t0 &&
          // Allegro data structure does not restrict channels to 16.
          // Since there is not way to select more than 16 channels,
          // map all channel numbers mod 16. This will have no effect
          // on MIDI files, but it will allow users to at least select
          // all channels on non-MIDI event sequence data.
          IsVisibleChan(event->chan % 16)) {
         event->set_pitch(event->get_pitch() + semitones);
      }
   }
   iter.end();
   // now, use warper to warp the tempo map
   mSeq->convert_to_beats(); // beats remain the same
   Alg_time_map_ptr map = mSeq->get_time_map();
   map->insert_beat(t0, map->time_to_beat(t0));
   map->insert_beat(t1, map->time_to_beat(t1));
   int i, len = map->length();
   for (i = 0; i < len; i++) {
      Alg_beat &beat = map->beats[i];
      beat.time = warper.Warp(beat.time + offset) - offset;
   }
   // about to redisplay, so might as well convert back to time now
   mSeq->convert_to_seconds();
}
Ejemplo n.º 5
0
int find_midi_duration(Alg_seq &seq, float *dur) 
{
    *dur = 0.0F;
    int nnotes = 0;
    int i, j;
    seq.convert_to_seconds();
    for (j = 0; j < seq.track_list.length(); j++) {
        Alg_events &notes = (seq.track_list[j]);
            
        for (i = 0; i < notes.length(); i++) {
            Alg_event_ptr e = notes[i];
            if (e->is_note()) {
                Alg_note_ptr n = (Alg_note_ptr) e;
                float note_end = float(n->time + n->dur);
                if (note_end > *dur) *dur = note_end;
                nnotes++;
            }
        }
    }
    return nnotes; 
}
Ejemplo n.º 6
0
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;
}
Ejemplo n.º 7
0
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 &notes = track_list[j];
        if (j != 0) update_to_skip = write_track_name(file, j, notes);
        // now write the notes at beat positions
        for (i = 0; i < notes.length(); i++) {
            Alg_event_ptr e = notes[i];
            // if we already wrote this event as a track or sequence name,
            // do not write it again
            if (e == update_to_skip) continue;
            double start = e->time;
            if (in_secs) {
                file << "T" << TIMFMT << start;
            } else {
                file << "TW" << TIMFMT << start / 4;
            }
            // write the channel as Vn or V-
            if (e->chan == -1) file << " V-";
            else file << " V" << e->chan;
            // write the note or update data
            if (e->is_note()) {
                Alg_note_ptr n = (Alg_note_ptr) e;
                double dur = n->dur;
                file << " K" << n->get_identifier() << 
                        " P" << GFMT << n->pitch;
                if (in_secs) {
                    file << " U" << TIMFMT << dur;
                } else {
                    file << " Q" << TIMFMT << dur;
                }
                file << " L" << GFMT << n->loud; 
                Alg_parameters_ptr p = n->parameters;
                while (p) {
                    parameter_print(file, &(p->parm));
                    p = p->next;
                }
            } else { // an update
                assert(e->is_update());
                Alg_update_ptr u = (Alg_update_ptr) e;
                if (u->get_identifier() != -1) {
                    file << " K" << u->get_identifier();
                }
                parameter_print(file, &(u->parameter));
            }
            file << "\n";
        }
    }
}