~ubuntu-branches/ubuntu/precise/lmms/precise-proposed

« back to all changes in this revision

Viewing changes to plugins/midi_import/midi_import.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Alessio Treglia
  • Date: 2010-11-12 13:32:32 UTC
  • mfrom: (1.1.9 upstream) (3.1.8 sid)
  • Revision ID: james.westby@ubuntu.com-20101112133232-vdld83iyji8srqti
Tags: 0.4.7-2ubuntu1
* Re-sync with Debian (LP: #606533):
  - Replace build-dep on libwine-dev with libwine-dev-unstable to build
    against the newer Wine.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * midi_import.cpp - support for importing MIDI-files
3
 
 *
4
 
 * Copyright (c) 2005-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
5
 
 *
6
 
 * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
7
 
 * 
8
 
 * This program is free software; you can redistribute it and/or
9
 
 * modify it under the terms of the GNU General Public
10
 
 * License as published by the Free Software Foundation; either
11
 
 * version 2 of the License, or (at your option) any later version.
12
 
 *
13
 
 * This program is distributed in the hope that it will be useful,
14
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
 
 * General Public License for more details.
17
 
 *
18
 
 * You should have received a copy of the GNU General Public
19
 
 * License along with this program (see COPYING); if not, write to the
20
 
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21
 
 * Boston, MA 02110-1301 USA.
22
 
 *
23
 
 */
24
 
 
25
 
 
26
 
#include <QtXml/QDomDocument>
27
 
#include <QtCore/QDir>
28
 
#include <QtGui/QApplication>
29
 
#include <QtGui/QMessageBox>
30
 
#include <QtGui/QProgressDialog>
31
 
 
32
 
#include "midi_import.h"
33
 
#include "track_container.h"
34
 
#include "instrument_track.h"
35
 
#include "automation_track.h"
36
 
#include "automation_pattern.h"
37
 
#include "config_mgr.h"
38
 
#include "pattern.h"
39
 
#include "instrument.h"
40
 
#include "main_window.h"
41
 
#include "debug.h"
42
 
#include "embed.h"
43
 
#include "song.h"
44
 
 
45
 
#include "portsmf/allegro.h"
46
 
 
47
 
#define makeID(_c0, _c1, _c2, _c3) \
48
 
                ( 0 | \
49
 
                ( ( _c0 ) | ( ( _c1 ) << 8 ) | ( ( _c2 ) << 16 ) | ( ( _c3 ) << 24 ) ) )
50
 
 
51
 
 
52
 
 
53
 
extern "C"
54
 
{
55
 
 
56
 
plugin::descriptor PLUGIN_EXPORT midiimport_plugin_descriptor =
57
 
{
58
 
        STRINGIFY( PLUGIN_NAME ),
59
 
        "MIDI Import",
60
 
        QT_TRANSLATE_NOOP( "pluginBrowser",
61
 
                                "Filter for importing MIDI-files into LMMS" ),
62
 
        "Tobias Doerffel <tobydox/at/users/dot/sf/dot/net>",
63
 
        0x0100,
64
 
        plugin::ImportFilter,
65
 
        NULL,
66
 
        NULL,
67
 
        NULL
68
 
} ;
69
 
 
70
 
}
71
 
 
72
 
 
73
 
midiImport::midiImport( const QString & _file ) :
74
 
        importFilter( _file, &midiimport_plugin_descriptor ),
75
 
        m_events(),
76
 
        m_timingDivision( 0 )
77
 
{
78
 
}
79
 
 
80
 
 
81
 
 
82
 
 
83
 
midiImport::~midiImport()
84
 
{
85
 
}
86
 
 
87
 
 
88
 
 
89
 
 
90
 
bool midiImport::tryImport( trackContainer * _tc )
91
 
{
92
 
        if( openFile() == FALSE )
93
 
        {
94
 
                return( FALSE );
95
 
        }
96
 
 
97
 
#ifdef LMMS_HAVE_FLUIDSYNTH
98
 
        if( engine::hasGUI() &&
99
 
                configManager::inst()->defaultSoundfont().isEmpty() )
100
 
        {
101
 
                QMessageBox::information( engine::getMainWindow(),
102
 
                        tr( "Setup incomplete" ),
103
 
                        tr( "You do not have set up a default soundfont in "
104
 
                                "the settings dialog (Edit->Settings). "
105
 
                                "Therefore no sound will be played back after "
106
 
                                "importing this MIDI file. You should download "
107
 
                                "a General MIDI soundfont, specify it in "
108
 
                                "settings dialog and try again." ) );
109
 
        }
110
 
#else
111
 
        if( engine::hasGUI() )
112
 
        {
113
 
                QMessageBox::information( engine::getMainWindow(),
114
 
                        tr( "Setup incomplete" ),
115
 
                        tr( "You did not compile LMMS with support for "
116
 
                                "SoundFont2 player, which is used to add default "
117
 
                                "sound to imported MIDI files. "
118
 
                                "Therefore no sound will be played back after "
119
 
                                "importing this MIDI file." ) );
120
 
        }
121
 
#endif
122
 
 
123
 
        switch( readID() )
124
 
        {
125
 
                case makeID( 'M', 'T', 'h', 'd' ):
126
 
                        printf( "midiImport::tryImport(): found MThd\n");
127
 
                        return( readSMF( _tc ) );
128
 
 
129
 
                case makeID( 'R', 'I', 'F', 'F' ):
130
 
                        printf( "midiImport::tryImport(): found RIFF\n");
131
 
                        return( readRIFF( _tc ) );
132
 
 
133
 
                default:
134
 
                        printf( "midiImport::tryImport(): not a Standard MIDI "
135
 
                                                                "file\n" );
136
 
                        return( FALSE );
137
 
        }
138
 
}
139
 
 
140
 
 
141
 
 
142
 
 
143
 
class smfMidiCC
144
 
{
145
 
 
146
 
public:
147
 
        smfMidiCC() :
148
 
                at( NULL ),
149
 
                ap( NULL ),
150
 
                lastPos( 0 )
151
 
        { }
152
 
        
153
 
        automationTrack * at;
154
 
        automationPattern * ap;
155
 
        midiTime lastPos;
156
 
        
157
 
        smfMidiCC & create( trackContainer * _tc )
158
 
        {
159
 
                if( !at )
160
 
                {
161
 
                        at = dynamic_cast<automationTrack *>(
162
 
                                        track::create( track::AutomationTrack, _tc ) );
163
 
                }
164
 
                return *this;
165
 
        }
166
 
 
167
 
 
168
 
        void clear()
169
 
        {
170
 
                at = NULL;
171
 
                ap = NULL;
172
 
                lastPos = 0;
173
 
        }
174
 
 
175
 
 
176
 
        smfMidiCC & putValue( midiTime time, automatableModel * objModel, float value )
177
 
        {
178
 
                if( !ap || time > lastPos + DefaultTicksPerTact )
179
 
                {
180
 
                        midiTime pPos = midiTime( time.getTact(), 0 );
181
 
                        ap = dynamic_cast<automationPattern*>(
182
 
                                at->createTCO(0) );
183
 
                        ap->movePosition( pPos );
184
 
                }
185
 
                ap->addObject( objModel );
186
 
 
187
 
                lastPos = time;
188
 
                time = time - ap->startPosition();
189
 
                ap->putValue( time, value, false );
190
 
                ap->changeLength( midiTime( time.getTact() + 1, 0 ) ); 
191
 
 
192
 
                return *this;
193
 
        }
194
 
};
195
 
 
196
 
 
197
 
 
198
 
class smfMidiChannel
199
 
{
200
 
 
201
 
public:
202
 
        smfMidiChannel() :
203
 
                it( NULL ),
204
 
                p( NULL ),
205
 
                it_inst( NULL ),
206
 
                isSF2( false ),
207
 
                hasNotes( false ),
208
 
                lastEnd( 0 )
209
 
        { }
210
 
        
211
 
        instrumentTrack * it;
212
 
        pattern * p;
213
 
        instrument * it_inst;
214
 
        bool isSF2; 
215
 
        bool hasNotes;
216
 
        midiTime lastEnd;
217
 
        
218
 
        smfMidiChannel * create( trackContainer * _tc )
219
 
        {
220
 
                if( !it ) {
221
 
                        it = dynamic_cast<instrumentTrack *>(
222
 
                        track::create( track::InstrumentTrack, _tc ) );
223
 
 
224
 
#ifdef LMMS_HAVE_FLUIDSYNTH
225
 
                        it_inst = it->loadInstrument( "sf2player" );
226
 
                
227
 
                        if( it_inst )
228
 
                        {
229
 
                                isSF2 = true;
230
 
                                it_inst->loadFile( configManager::inst()->defaultSoundfont() );
231
 
                                it_inst->getChildModel( "bank" )->setValue( 0 );
232
 
                                it_inst->getChildModel( "patch" )->setValue( 0 );
233
 
                        }
234
 
                        else
235
 
                        {
236
 
                                it_inst = it->loadInstrument( "patman" );
237
 
                        }       
238
 
#else
239
 
                        it_inst = it->loadInstrument( "patman" );
240
 
#endif
241
 
                        
242
 
                        lastEnd = 0;
243
 
                }
244
 
                return this;
245
 
        }
246
 
 
247
 
 
248
 
        void addNote( note & n )
249
 
        {
250
 
                if( !p || n.pos() > lastEnd + DefaultTicksPerTact )
251
 
                {
252
 
                        midiTime pPos = midiTime(n.pos().getTact(), 0 );
253
 
                        p = dynamic_cast<pattern *>( it->createTCO( 0 ) );
254
 
                        p->movePosition( pPos );
255
 
                }
256
 
                hasNotes = true;
257
 
                lastEnd = n.pos() + n.length();
258
 
                n.setPos( n.pos( p->startPosition() ) );
259
 
                p->addNote( n, false );
260
 
        }
261
 
 
262
 
};
263
 
 
264
 
 
265
 
bool midiImport::readSMF( trackContainer * _tc )
266
 
{
267
 
        QString filename = file().fileName();
268
 
        closeFile();
269
 
 
270
 
        const int preTrackSteps = 2;
271
 
        QProgressDialog pd( trackContainer::tr( "Importing MIDI-file..." ),
272
 
        trackContainer::tr( "Cancel" ), 0, preTrackSteps, engine::getMainWindow() );
273
 
        pd.setWindowTitle( trackContainer::tr( "Please wait..." ) );
274
 
        pd.setWindowModality(Qt::WindowModal);
275
 
        pd.setMinimumDuration( 0 );
276
 
 
277
 
        pd.setValue( 0 );
278
 
 
279
 
        Alg_seq_ptr seq = new Alg_seq(filename.toLocal8Bit(), true);
280
 
        seq->convert_to_beats();
281
 
 
282
 
        pd.setMaximum( seq->tracks()  + preTrackSteps );
283
 
        pd.setValue( 1 );
284
 
        
285
 
        // 128 CC + Pitch Bend
286
 
        smfMidiCC ccs[129];
287
 
        smfMidiChannel chs[256];
288
 
 
289
 
        meterModel & timeSigMM = engine::getSong()->getTimeSigModel();
290
 
        automationPattern * timeSigNumeratorPat = 
291
 
                automationPattern::globalAutomationPattern( &timeSigMM.numeratorModel() );
292
 
        automationPattern * timeSigDenominatorPat = 
293
 
                automationPattern::globalAutomationPattern( &timeSigMM.denominatorModel() );
294
 
        
295
 
        // TODO: adjust these to Time.Sig changes
296
 
        double beatsPerTact = 4; 
297
 
        double ticksPerBeat = DefaultTicksPerTact / beatsPerTact;
298
 
 
299
 
        // Time-sig changes
300
 
        Alg_time_sigs * timeSigs = &seq->time_sig;
301
 
        for( int s = 0; s < timeSigs->length(); ++s )
302
 
        {
303
 
                Alg_time_sig timeSig = (*timeSigs)[s];
304
 
                // Initial timeSig, set song-default value
305
 
                if(/* timeSig.beat == 0*/ true )
306
 
                {
307
 
                        // TODO set song-global default value
308
 
                        printf("Another timesig at %f\n", timeSig.beat);
309
 
                        timeSigNumeratorPat->putValue( timeSig.beat*ticksPerBeat, timeSig.num );
310
 
                        timeSigDenominatorPat->putValue( timeSig.beat*ticksPerBeat, timeSig.den );
311
 
                }
312
 
                else
313
 
                {
314
 
                }
315
 
 
316
 
        }
317
 
 
318
 
        pd.setValue( 2 );
319
 
 
320
 
        // Tempo stuff
321
 
        automationPattern * tap = _tc->tempoAutomationPattern();
322
 
        if( tap )
323
 
        {
324
 
                tap->clear();
325
 
                Alg_time_map * timeMap = seq->get_time_map();
326
 
                Alg_beats & beats = timeMap->beats;
327
 
                for( int i = 0; i < beats.len - 1; i++ )
328
 
                {
329
 
                        Alg_beat_ptr b = &(beats[i]);
330
 
                        double tempo = ( beats[i + 1].beat - b->beat ) /
331
 
                                                   ( beats[i + 1].time - beats[i].time );
332
 
                        tap->putValue( b->beat * ticksPerBeat, tempo * 60.0 );
333
 
                }
334
 
                if( timeMap->last_tempo_flag )
335
 
                {
336
 
                        Alg_beat_ptr b = &( beats[beats.len - 1] );
337
 
                        tap->putValue( b->beat * ticksPerBeat, timeMap->last_tempo * 60.0 );
338
 
                }
339
 
        }
340
 
 
341
 
        // Song events
342
 
        for( int e = 0; e < seq->length(); ++e )
343
 
        {
344
 
                Alg_event_ptr evt = (*seq)[e];
345
 
 
346
 
                if( evt->is_update() )
347
 
                {
348
 
                        printf("Unhandled SONG update: %d %f %s\n", 
349
 
                                        evt->get_type_code(), evt->time, evt->get_attribute() );
350
 
                }
351
 
        }
352
 
 
353
 
        // Tracks
354
 
        for( int t = 0; t < seq->tracks(); ++t )
355
 
        {
356
 
                Alg_track_ptr trk = seq->track( t );
357
 
                pd.setValue( t + preTrackSteps );
358
 
 
359
 
                for( int c = 0; c < 129; c++ )
360
 
                {
361
 
                        ccs[c].clear();
362
 
                }
363
 
 
364
 
                // Now look at events
365
 
                for( int e = 0; e < trk->length(); ++e )
366
 
                {
367
 
                        Alg_event_ptr evt = (*trk)[e];
368
 
 
369
 
                        if( evt->chan == -1 )
370
 
                        {
371
 
                                printf("MISSING GLOBAL THINGY\n");
372
 
                                printf("     %d %d %f %s\n", (int) evt->chan, 
373
 
                                        evt->get_type_code(), evt->time,
374
 
                                                        evt->get_attribute() );
375
 
                                // Global stuff
376
 
                        }
377
 
                        else if( evt->is_note() && evt->chan < 256 )
378
 
                        {
379
 
                                smfMidiChannel * ch = chs[evt->chan].create( _tc );
380
 
                                Alg_note_ptr noteEvt = dynamic_cast<Alg_note_ptr>( evt );
381
 
 
382
 
                                note n( noteEvt->get_duration() * ticksPerBeat,
383
 
                                                noteEvt->get_start_time() * ticksPerBeat,
384
 
                                                noteEvt->get_identifier() - 12,
385
 
                                                noteEvt->get_loud());
386
 
                                ch->addNote( n );
387
 
                                
388
 
                        }
389
 
                        
390
 
                        else if( evt->is_update() )
391
 
                        {
392
 
                                smfMidiChannel * ch = chs[evt->chan].create( _tc );
393
 
 
394
 
                                double time = evt->time*ticksPerBeat;
395
 
                                QString update( evt->get_attribute() );
396
 
 
397
 
                                if( update == "programi" )
398
 
                                {
399
 
                                        long prog = evt->get_integer_value();
400
 
                                        if( ch->isSF2 )
401
 
                                        {
402
 
                                                ch->it_inst->getChildModel( "bank" )->setValue( 0 );
403
 
                                                ch->it_inst->getChildModel( "patch" )->setValue( prog );
404
 
                                        }
405
 
                                        else {
406
 
                                                const QString num = QString::number( prog );
407
 
                                                const QString filter = QString().fill( '0', 3 - num.length() ) + num + "*.pat";
408
 
                                                const QString dir = "/usr/share/midi/"
409
 
                                                                "freepats/Tone_000/";
410
 
                                                const QStringList files = QDir( dir ).
411
 
                                                entryList( QStringList( filter ) );
412
 
                                                if( ch->it_inst && !files.empty() )
413
 
                                                {
414
 
                                                        ch->it_inst->loadFile( dir+files.front() );
415
 
                                                }
416
 
                                        }
417
 
                                }
418
 
                                else if( update == "tracknames" )
419
 
                                {
420
 
                                        QString trackName( evt->get_string_value() );
421
 
                                        ch->it->setName( trackName );
422
 
                                        //ch.p->setName( trackName );
423
 
                                }
424
 
 
425
 
                                else if( update.startsWith( "control" ) || update == "bendr" )
426
 
                                {
427
 
                                        int ccid = update.mid( 7, update.length()-8 ).toInt();
428
 
                                        if( update == "bendr" )
429
 
                                        {
430
 
                                                ccid = 128;
431
 
                                        }
432
 
                                        if( ccid <= 128 )
433
 
                                        {
434
 
                                                double cc = evt->get_real_value();
435
 
                                                automatableModel * objModel = NULL;
436
 
 
437
 
                                                switch( ccid ) 
438
 
                                                {
439
 
                                                        case 0:
440
 
                                                                if( ch->isSF2 && ch->it_inst )
441
 
                                                                {
442
 
                                                                        objModel = ch->it_inst->getChildModel( "bank" );
443
 
                                                                        printf("BANK SELECT %f %d\n", cc, (int)(cc*127.0));
444
 
                                                                        cc *= 127.0f;
445
 
                                                                }
446
 
                                                                break;
447
 
 
448
 
                                                        case 7:
449
 
                                                                objModel = ch->it->volumeModel();
450
 
                                                                cc *= 100.0f;
451
 
                                                                break;
452
 
 
453
 
                                                        case 10:
454
 
                                                                objModel = ch->it->panningModel();
455
 
                                                                cc = cc * 200.f - 100.0f;
456
 
                                                                break;
457
 
 
458
 
                                                        case 128:
459
 
                                                                objModel = ch->it->pitchModel();
460
 
                                                                cc = cc * 100.0f;
461
 
                                                                break;
462
 
                                                }
463
 
 
464
 
                                                if( objModel )
465
 
                                                {
466
 
                                                        if( time == 0 && objModel )
467
 
                                                        {
468
 
                                                                objModel->setInitValue( cc );
469
 
                                                        }
470
 
                                                        else
471
 
                                                        {
472
 
                                                                ccs[ccid].create( _tc );
473
 
                                                                ccs[ccid].putValue( time, objModel, cc );
474
 
                                                        }
475
 
                                                }
476
 
                                        }
477
 
                                }
478
 
                                else {
479
 
                                        printf("Unhandled update: %d %d %f %s\n", (int) evt->chan, 
480
 
                                                        evt->get_type_code(), evt->time, evt->get_attribute() );
481
 
                                }
482
 
                        }
483
 
                }
484
 
        }
485
 
 
486
 
        delete seq;
487
 
        
488
 
        
489
 
        for( int c=0; c < 256; ++c )
490
 
        {
491
 
                if( !chs[c].hasNotes && chs[c].it )
492
 
                {
493
 
                        printf(" Should remove empty track\n");
494
 
                        // must delete trackView first - but where is it?
495
 
                        //_tc->removeTrack( chs[c].it );
496
 
                        //it->deleteLater();
497
 
                }
498
 
        }
499
 
 
500
 
        return true;
501
 
}
502
 
 
503
 
 
504
 
 
505
 
 
506
 
bool midiImport::readRIFF( trackContainer * _tc )
507
 
{
508
 
                // skip file length
509
 
        skip( 4 );
510
 
 
511
 
                // check file type ("RMID" = RIFF MIDI)
512
 
                if( readID() != makeID( 'R', 'M', 'I', 'D' ) )
513
 
        {
514
 
invalid_format:
515
 
                                printf( "midiImport::readRIFF(): invalid file format\n" );
516
 
                                return( FALSE );
517
 
                }
518
 
                // search for "data" chunk
519
 
                while( 1 )
520
 
        {
521
 
                                int id = readID();
522
 
                                int len = read32LE();
523
 
                                if( file().atEnd() )
524
 
                {
525
 
data_not_found:
526
 
                                                printf( "midiImport::readRIFF(): data chunk not "
527
 
                                                                "found\n" );
528
 
                                                return( FALSE );
529
 
                                }
530
 
                                if( id == makeID( 'd', 'a', 't', 'a' ) )
531
 
                {
532
 
                                                break;
533
 
                }
534
 
                                if( len < 0 )
535
 
                {
536
 
                                                goto data_not_found;
537
 
                }
538
 
                                skip( ( len + 1 ) & ~1 );
539
 
                }
540
 
                // the "data" chunk must contain data in SMF format
541
 
                if( readID() != makeID( 'M', 'T', 'h', 'd' ) )
542
 
        {
543
 
                                goto invalid_format;
544
 
        }
545
 
                return( readSMF( _tc ) );
546
 
}
547
 
 
548
 
 
549
 
 
550
 
 
551
 
void midiImport::error( void )
552
 
{
553
 
        printf( "midiImport::readTrack(): invalid MIDI data (offset %#x)\n",
554
 
                                                (unsigned int) file().pos() );
555
 
}
556
 
 
557
 
 
558
 
 
559
 
extern "C"
560
 
{
561
 
 
562
 
// neccessary for getting instance out of shared lib
563
 
plugin * PLUGIN_EXPORT lmms_plugin_main( model *, void * _data )
564
 
{
565
 
        return( new midiImport( static_cast<const char *>( _data ) ) );
566
 
}
567
 
 
568
 
 
569
 
}
570
 
 
571
 
 
572
 
#undef pos
573
 
#undef setValue