示例#1
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;
}
示例#2
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";
        }
    }
}