~ubuntu-branches/ubuntu/oneiric/tuxguitar/oneiric

« back to all changes in this revision

Viewing changes to src/org/herac/tuxguitar/player/impl/MidiPlayerImpl.java

  • Committer: Bazaar Package Importer
  • Author(s): Philippe Coval
  • Date: 2007-02-04 01:41:23 UTC
  • Revision ID: james.westby@ubuntu.com-20070204014123-9pv7okph0iaiqkvw
Tags: upstream-0.9.1
ImportĀ upstreamĀ versionĀ 0.9.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Created on 24-nov-2005
 
3
 *
 
4
 * TODO To change the template for this generated file go to
 
5
 * Window - Preferences - Java - Code Style - Code Templates
 
6
 */
 
7
package org.herac.tuxguitar.player.impl;
 
8
 
 
9
import java.io.File;
 
10
import java.io.IOException;
 
11
import java.io.OutputStream;
 
12
import java.util.ArrayList;
 
13
import java.util.Iterator;
 
14
import java.util.List;
 
15
 
 
16
import javax.sound.midi.Instrument;
 
17
import javax.sound.midi.InvalidMidiDataException;
 
18
import javax.sound.midi.MidiMessage;
 
19
import javax.sound.midi.MidiSystem;
 
20
import javax.sound.midi.MidiUnavailableException;
 
21
import javax.sound.midi.Receiver;
 
22
import javax.sound.midi.Sequencer;
 
23
import javax.sound.midi.Soundbank;
 
24
import javax.sound.midi.Synthesizer;
 
25
import javax.sound.midi.Transmitter;
 
26
 
 
27
import org.eclipse.swt.widgets.Composite;
 
28
import org.eclipse.swt.widgets.ToolBar;
 
29
import org.herac.tuxguitar.gui.TuxGuitar;
 
30
import org.herac.tuxguitar.gui.system.config.ConfigKeys;
 
31
import org.herac.tuxguitar.gui.system.config.ConfigEditor;
 
32
import org.herac.tuxguitar.gui.system.config.items.Option;
 
33
import org.herac.tuxguitar.gui.system.config.items.SoundOption;
 
34
import org.herac.tuxguitar.gui.util.SystemError;
 
35
import org.herac.tuxguitar.player.base.MidiControllers;
 
36
import org.herac.tuxguitar.player.base.MidiPlayer;
 
37
import org.herac.tuxguitar.player.base.MidiSequenceParser;
 
38
import org.herac.tuxguitar.song.managers.SongManager;
 
39
import org.herac.tuxguitar.song.models.Duration;
 
40
import org.herac.tuxguitar.song.models.InstrumentString;
 
41
import org.herac.tuxguitar.song.models.Note;
 
42
import org.herac.tuxguitar.song.models.SongTrack;
 
43
 
 
44
/**
 
45
 * @author julian
 
46
 * 
 
47
 * TODO To change the template for this generated type comment go to Window - Preferences - Java - Code Style - Code Templates
 
48
 */
 
49
public class MidiPlayerImpl implements MidiPlayer {
 
50
 
 
51
        private static final int MAX_CHANNELS = 16;
 
52
        
 
53
    private SongManager songManager;
 
54
 
 
55
    private Sequencer sequencer;
 
56
    
 
57
    private Synthesizer synthesizer;
 
58
 
 
59
    private Receiver receiver;
 
60
    
 
61
    private Soundbank soundbank;
 
62
 
 
63
    private MidiMetaEventListener controller;
 
64
    
 
65
    private boolean running;
 
66
 
 
67
    private boolean paused;
 
68
    
 
69
    private boolean changeTickPosition;
 
70
 
 
71
    private long tickPosition;
 
72
    
 
73
    private boolean metronomeEnabled;
 
74
    
 
75
    private int metronomeTrack;
 
76
    
 
77
    private int infoTrack;
 
78
    
 
79
    private boolean anySolo;
 
80
    
 
81
    private List systemErrors;
 
82
    
 
83
    public MidiPlayerImpl() {        
 
84
        this.songManager = TuxGuitar.instance().getSongManager();
 
85
        this.controller = new MidiMetaEventListener();        
 
86
        this.systemErrors = new ArrayList();
 
87
                this.init();
 
88
                this.reset();        
 
89
    }
 
90
 
 
91
    /**
 
92
     * Inicia el Secuenciador y Sintetizador
 
93
     * @throws MidiUnavailableException 
 
94
     */
 
95
 
 
96
    public void init() {
 
97
        try{            
 
98
                getSynthesizer();
 
99
                getSequencer();
 
100
                
 
101
                //check soundbank
 
102
                boolean loadCustomSoundbank = TuxGuitar.instance().getConfig().getBooleanConfigValue(ConfigKeys.SOUNDBANK_CUSTOM);
 
103
                if(loadCustomSoundbank){
 
104
                        if(!loadSoundbank(new File(TuxGuitar.instance().getConfig().getStringConfigValue(ConfigKeys.SOUNDBANK_CUSTOM_PATH)))){                                  
 
105
                                this.systemErrors.add(SystemError.getError(TuxGuitar.getProperty("soundbank.error"), TuxGuitar.getProperty("soundbank.error.custom")));
 
106
                        }
 
107
                }               
 
108
        } catch (MidiUnavailableException e) {
 
109
                e.printStackTrace();
 
110
        }         
 
111
    }    
 
112
    
 
113
    /**
 
114
     * Retorna el Sequenciador
 
115
     * @throws MidiUnavailableException 
 
116
     */
 
117
    private Sequencer getSequencer() throws MidiUnavailableException {
 
118
        if (this.sequencer == null) {
 
119
                this.sequencer = MidiSystem.getSequencer(false);
 
120
            this.sequencer.addMetaEventListener(this.controller);
 
121
        }
 
122
        if (!this.sequencer.isOpen()) {
 
123
                this.sequencer.open();
 
124
        }            
 
125
        return this.sequencer;
 
126
    }
 
127
 
 
128
    /**
 
129
     * Retorna el Sintetizador
 
130
     * @throws MidiUnavailableException 
 
131
     */
 
132
    public Synthesizer getSynthesizer() throws MidiUnavailableException {
 
133
        if (this.synthesizer == null) {
 
134
                setSynthesizer(MidiSystem.getSynthesizer());
 
135
        } 
 
136
        return this.synthesizer;
 
137
    }
 
138
 
 
139
    /**
 
140
     * Retorna el Soundbank por defecto
 
141
     * @throws MidiUnavailableException 
 
142
     */
 
143
    public Soundbank getSoundbank(){
 
144
        if (this.soundbank == null) {      
 
145
                try{                            
 
146
                        Synthesizer synthesizer = getSynthesizer();
 
147
                        this.soundbank = synthesizer.getDefaultSoundbank();                     
 
148
                } catch (MidiUnavailableException e) {
 
149
                        e.printStackTrace();
 
150
                }   
 
151
        }
 
152
        return this.soundbank;
 
153
    }
 
154
    
 
155
    /**
 
156
     * Asigna un Synthesizer
 
157
     */
 
158
    public void setSynthesizer(Synthesizer synthesizer){
 
159
        try {
 
160
                this.soundbank = null;
 
161
                if(this.synthesizer != null && this.synthesizer.isOpen()){
 
162
                        this.synthesizer.close();
 
163
                }               
 
164
                this.synthesizer = synthesizer;
 
165
                if(this.synthesizer != null){
 
166
                        this.synthesizer.open();  
 
167
                                this.connect(this.synthesizer.getReceiver());
 
168
                }
 
169
                } catch (MidiUnavailableException e) {
 
170
                        e.printStackTrace();
 
171
                }                
 
172
    }
 
173
    
 
174
    /**
 
175
     * Conecta el Synthesizer al Sequencer
 
176
     */
 
177
    public void connect(Receiver receiver){
 
178
        this.receiver = receiver;
 
179
        this.connect();
 
180
    }
 
181
    
 
182
    /**
 
183
     * Conecta el Synthesizer al Sequencer
 
184
     */
 
185
    public void connect(){
 
186
        try {                                                                   
 
187
                Iterator it = getSequencer().getTransmitters().iterator();
 
188
                        while(it.hasNext()){
 
189
                                ((Transmitter)it.next()).close();
 
190
                        }
 
191
                        Transmitter transmitter = getSequencer().getTransmitter();
 
192
            transmitter.setReceiver(this.receiver);
 
193
                } catch (MidiUnavailableException e) {
 
194
                        e.printStackTrace();
 
195
                }   
 
196
    }    
 
197
 
 
198
    /**
 
199
     * Resetea los valores
 
200
     */
 
201
    public void reset(){
 
202
        this.stop();
 
203
        this.tickPosition = Duration.QUARTER_TIME;
 
204
        this.setChangeTickPosition(false);
 
205
        this.controller.reset();       
 
206
    }
 
207
 
 
208
    /**
 
209
     * Cierra el Secuenciador y Sintetizador
 
210
     * @throws MidiUnavailableException 
 
211
     */
 
212
    public void close(){
 
213
        this.stop();
 
214
        this.soundbank = null;          
 
215
        if (this.synthesizer != null) {
 
216
                this.synthesizer.close();
 
217
                this.synthesizer = null;
 
218
        }        
 
219
        if (this.sequencer != null) {
 
220
                this.sequencer.close();
 
221
                this.sequencer = null;
 
222
        }
 
223
    }
 
224
    
 
225
    /**
 
226
     * Para la reproduccion
 
227
     * @throws MidiUnavailableException 
 
228
     */
 
229
    public void stop(boolean paused) {        
 
230
        this.setPaused(paused);                  
 
231
        try{
 
232
                if(this.isRunning() && this.getSequencer().isOpen()){                           
 
233
                        this.allNotesOff();
 
234
                        this.systemReset();
 
235
                        this.getSequencer().stop();
 
236
                }       
 
237
        } catch (MidiUnavailableException e) {
 
238
                e.printStackTrace();
 
239
        }       
 
240
        this.setRunning(false);
 
241
    }    
 
242
    
 
243
    /**
 
244
     * Para la reproduccion
 
245
     * @throws MidiUnavailableException 
 
246
     */
 
247
    public void stop() {        
 
248
        this.stop(false);                               
 
249
    }
 
250
 
 
251
    public void pause(){
 
252
        this.stop(true); 
 
253
    }
 
254
    
 
255
    /**
 
256
     * Inicia la reproduccion
 
257
     * @throws MidiUnavailableException 
 
258
     */
 
259
    public synchronized void play(){     
 
260
        try {           
 
261
                this.stop();                                                                    
 
262
                this.addSecuence();
 
263
                this.updatePrograms();
 
264
                this.updateControllers();
 
265
                this.updateDefaultControllers();
 
266
                this.setMetronomeEnabled(isMetronomeEnabled());                 
 
267
                this.setChangeTickPosition(true);
 
268
                this.setRunning(true);                  
 
269
                this.getSequencer().start();
 
270
                new Thread(new Runnable() {
 
271
                        public synchronized void run() {
 
272
                                try {                   
 
273
                                        while (getSequencer().isRunning() && isRunning()) {
 
274
                                                if (isChangeTickPosition()) {
 
275
                                                        changeTickPosition();
 
276
                                                }       
 
277
                                                tickPosition =  getSequencer().getTickPosition();
 
278
                                                Thread.sleep(10);                                               
 
279
                                        }                    
 
280
                                        //FINISH
 
281
                                        if(isRunning()){
 
282
                                                if(tickPosition >= (getSequencer().getTickLength() - 500)){
 
283
                                                        reset();
 
284
                                                }else {
 
285
                                                        stop(isPaused());
 
286
                                                }               
 
287
                                        }
 
288
                                } catch (InterruptedException e) {
 
289
                                        e.printStackTrace();
 
290
                                } catch (MidiUnavailableException e) {
 
291
                                        reset();  
 
292
                                                e.printStackTrace();
 
293
                                        }
 
294
                        }
 
295
                }).start();     
 
296
        }catch (MidiUnavailableException e) {
 
297
                reset();        
 
298
                        e.printStackTrace();
 
299
                }
 
300
    }
 
301
 
 
302
    public void send(MidiMessage message){
 
303
        if(this.receiver != null){
 
304
                this.receiver.send(message,-1);
 
305
        }
 
306
    }
 
307
    
 
308
    /**
 
309
     * Asigna el valor a running
 
310
     */
 
311
    public void setRunning(boolean running) {
 
312
        this.running = running;
 
313
    }
 
314
 
 
315
    /**
 
316
     * Retorna True si esta reproduciendo
 
317
     */
 
318
    public boolean isRunning() {
 
319
        return this.running;
 
320
    }
 
321
 
 
322
    public boolean isPaused() {
 
323
                return paused;
 
324
        }
 
325
 
 
326
        public void setPaused(boolean paused) {
 
327
                this.paused = paused;
 
328
        }
 
329
 
 
330
        /**
 
331
     * Retorna True si hay cambios en la posicion
 
332
     */
 
333
    private boolean isChangeTickPosition() {
 
334
        return changeTickPosition;
 
335
    }
 
336
 
 
337
    /**
 
338
     * Asigna los cambios de la posicion
 
339
     */
 
340
    private void setChangeTickPosition(boolean changeTickPosition) {
 
341
        this.changeTickPosition = changeTickPosition;
 
342
    }
 
343
 
 
344
 
 
345
    /**
 
346
     * Indica la posicion del secuenciador
 
347
     * @throws MidiUnavailableException 
 
348
     */
 
349
    public void setTickPosition(long position) {
 
350
        setTickPosition(position,controller.getMove()); 
 
351
    }    
 
352
    
 
353
    /**
 
354
     * Indica la posicion del secuenciador
 
355
     * @throws MidiUnavailableException 
 
356
     */
 
357
    public void setTickPosition(long position,long move) {
 
358
        this.tickPosition = position;
 
359
        this.controller.setMove(move);
 
360
        this.setChangeTickPosition(true);
 
361
        if(!isRunning()){
 
362
                this.changeTickPosition();
 
363
        }
 
364
    }
 
365
    
 
366
    /**
 
367
     * Retorna el tick de la nota que esta reproduciendo
 
368
     */
 
369
    public long getTickPosition() {
 
370
        return this.tickPosition - this.controller.getMove();
 
371
    }
 
372
 
 
373
    private void changeTickPosition(){
 
374
        try{
 
375
                if(isRunning()){                                                
 
376
                        this.getSequencer().setTickPosition(tickPosition);                      
 
377
                        //reseteo los volumenes.                                                
 
378
                        //this.setMetronomeEnabled(isMetronomeEnabled());
 
379
                        //this.updateControllers();                                     
 
380
                }               
 
381
                } catch (MidiUnavailableException e) {
 
382
                        e.printStackTrace();
 
383
                }     
 
384
        setChangeTickPosition(false);           
 
385
    }
 
386
    
 
387
    /**
 
388
     * Agrega la Secuencia
 
389
     * @throws MidiUnavailableException 
 
390
     */
 
391
    public void addSecuence() {
 
392
        try{
 
393
                        MidiSequenceParser parser = new MidiSequenceParser(songManager,MidiSequenceParser.DEFAULT_PLAY_FLAGS);
 
394
                        MidiSequenceImpl sequence = new MidiSequenceImpl(songManager);
 
395
                        parser.parse(sequence);                 
 
396
                        this.getSequencer().setSequence(sequence.getSequence());                        
 
397
                        this.infoTrack = sequence.getInfoTrack();
 
398
                        this.metronomeTrack = sequence.getMetronomeTrack();                                                                             
 
399
                } catch (MidiUnavailableException e) {
 
400
                        e.printStackTrace();
 
401
                } catch (InvalidMidiDataException e) {
 
402
                        e.printStackTrace();
 
403
                }               
 
404
    }
 
405
 
 
406
    private void updateDefaultControllers(){            
 
407
                for(int channel = 0; channel < MAX_CHANNELS;channel ++){                        
 
408
                        this.send(MidiMessageUtils.controlChange(channel,MidiControllers.REGISTERED_PARAMETER_FINE,0));
 
409
                        this.send(MidiMessageUtils.controlChange(channel,MidiControllers.REGISTERED_PARAMETER_COARSE,0));        
 
410
                        this.send(MidiMessageUtils.controlChange(channel,MidiControllers.DATA_ENTRY_COARSE,12));
 
411
                        this.send(MidiMessageUtils.controlChange(channel,MidiControllers.REGISTERED_PARAMETER_COARSE,0));
 
412
                        this.send(MidiMessageUtils.controlChange(channel,MidiControllers.REGISTERED_PARAMETER_FINE,1));
 
413
                        this.send(MidiMessageUtils.controlChange(channel,MidiControllers.DATA_ENTRY_COARSE,64));
 
414
                        this.send(MidiMessageUtils.controlChange(channel,MidiControllers.REGISTERED_PARAMETER_FINE,127));                                               
 
415
                }
 
416
    }
 
417
    
 
418
    public void updatePrograms() {      
 
419
                Iterator it = this.songManager.getSong().getTracks().iterator();
 
420
                while(it.hasNext()){
 
421
                        SongTrack track = (SongTrack)it.next();
 
422
                        this.send(MidiMessageUtils.programChange(track.getChannel().getChannel(),track.getChannel().getInstrument()));
 
423
                        if(track.getChannel().getChannel() != track.getChannel().getEffectChannel()){
 
424
                                this.send(MidiMessageUtils.programChange(track.getChannel().getEffectChannel(),track.getChannel().getInstrument()));
 
425
                        }
 
426
                }                   
 
427
    }    
 
428
    
 
429
        public void updateControllers() {       
 
430
                try {
 
431
                        this.anySolo = false;                   
 
432
                        Iterator it = this.songManager.getSong().getTracks().iterator();
 
433
                        while(it.hasNext()){
 
434
                                SongTrack track = (SongTrack)it.next();
 
435
                                this.updateController(track);       
 
436
                                this.anySolo = ((!this.anySolo)?track.getChannel().isSolo():this.anySolo);
 
437
                        }               
 
438
                        this.afterUpdate();
 
439
                } catch (MidiUnavailableException e) {
 
440
                        e.printStackTrace();
 
441
                }         
 
442
    }
 
443
    
 
444
    private void updateController(SongTrack track) throws MidiUnavailableException  {                                                   
 
445
        int volume = (int)(((double)this.songManager.getSong().getVolume() / 10.0) * track.getChannel().getVolume());           
 
446
        int balance = track.getChannel().getBalance();                                                  
 
447
        updateController(track.getChannel().getChannel(),volume,balance);
 
448
        if(track.getChannel().getChannel() != track.getChannel().getEffectChannel()){
 
449
                updateController(track.getChannel().getEffectChannel(),volume,balance);
 
450
        }       
 
451
                getSequencer().setTrackMute(track.getNumber(),track.getChannel().isMute());
 
452
                getSequencer().setTrackSolo(track.getNumber(),track.getChannel().isSolo());       
 
453
    }
 
454
    
 
455
    private void updateController(int channel,int volume,int balance) throws MidiUnavailableException  {                
 
456
        this.send(MidiMessageUtils.controlChange(channel,MidiControllers.VOLUME,volume));
 
457
        this.send(MidiMessageUtils.controlChange(channel,MidiControllers.BALANCE,balance));             
 
458
    }
 
459
    
 
460
    private void afterUpdate() throws MidiUnavailableException{
 
461
        getSequencer().setTrackSolo(this.infoTrack,this.anySolo);
 
462
                getSequencer().setTrackSolo(this.metronomeTrack,(isMetronomeEnabled() && this.anySolo));
 
463
    }
 
464
    
 
465
    public boolean isMetronomeEnabled() {
 
466
                return metronomeEnabled;        
 
467
        }
 
468
 
 
469
        public void setMetronomeEnabled(boolean metronomeEnabled) {
 
470
                try {
 
471
                        this.metronomeEnabled = metronomeEnabled;       
 
472
                        this.getSequencer().setTrackMute(this.metronomeTrack,!isMetronomeEnabled());
 
473
                        this.getSequencer().setTrackSolo(this.metronomeTrack,(isMetronomeEnabled() && this.anySolo));
 
474
                } catch (MidiUnavailableException e) {                  
 
475
                        e.printStackTrace();
 
476
                }                       
 
477
        }
 
478
    
 
479
    public void allNotesOff(){          
 
480
        for(int channel = 0;channel < MAX_CHANNELS;channel ++){
 
481
                this.send(MidiMessageUtils.controlChange(channel, MidiControllers.ALL_NOTES_OFF,0));
 
482
                }                               
 
483
    }    
 
484
    
 
485
    private void systemReset(){         
 
486
        this.send(MidiMessageUtils.systemReset());      
 
487
    }
 
488
 
 
489
    public void playBeat(final SongTrack track,final List notes) {
 
490
        final int channel = track.getChannel().getChannel();
 
491
        final int program = track.getChannel().getInstrument();
 
492
        final int volume = (int)(((double)this.songManager.getSong().getVolume() / 10.0) * track.getChannel().getVolume());             
 
493
        final int balance = track.getChannel().getBalance();                            
 
494
                this.send(MidiMessageUtils.programChange(channel,program));
 
495
                this.send(MidiMessageUtils.controlChange(channel,MidiControllers.VOLUME,volume));
 
496
                this.send(MidiMessageUtils.controlChange(channel,MidiControllers.BALANCE,balance));                                             
 
497
        Iterator it = notes.iterator();
 
498
        while(it.hasNext()){
 
499
                final Note note = (Note)it.next();                                                                                      
 
500
                new Thread(new Runnable() {             
 
501
                        public void run() {
 
502
                                try {                                                           
 
503
                                int key = track.getOffset() + (note.getValue() + ((InstrumentString)track.getStrings().get(note.getString() - 1)).getValue());
 
504
                                int velocity = note.getVelocity();                                                                              
 
505
                                        send(MidiMessageUtils.noteOn(channel, key, velocity));                                                  
 
506
                                        Thread.sleep(500);
 
507
                                        send(MidiMessageUtils.noteOff(channel, key, velocity));
 
508
                                } catch (InterruptedException e) {                                      
 
509
                                        e.printStackTrace();
 
510
                                }
 
511
                        }               
 
512
                }).start();
 
513
        }                                               
 
514
    }
 
515
    
 
516
    public boolean loadSoundbank(File file){    
 
517
                try {                   
 
518
                        Soundbank soundbank = MidiSystem.getSoundbank(file);
 
519
                        if (soundbank != null && getSynthesizer().isSoundbankSupported(soundbank)){                                                                             
 
520
                                //unload the current soundbank
 
521
                                if(getSoundbank() != null){
 
522
                                        getSynthesizer().unloadAllInstruments(getSoundbank());
 
523
                                }
 
524
                                //load the new soundbank
 
525
                                getSynthesizer().loadAllInstruments(soundbank);
 
526
                                
 
527
                                this.soundbank = soundbank;
 
528
                                return true;
 
529
                        }    
 
530
                }catch (MidiUnavailableException e) {
 
531
                        e.printStackTrace();
 
532
                } catch (InvalidMidiDataException e) {
 
533
                        e.printStackTrace();
 
534
                } catch (IOException e) {
 
535
                        e.printStackTrace();
 
536
                }
 
537
                return false;
 
538
    }    
 
539
    
 
540
    public void write(OutputStream out){
 
541
        try {                   
 
542
                MidiSequenceParser parser = new MidiSequenceParser(songManager,MidiSequenceParser.DEFAULT_EXPORT_FLAGS);
 
543
                        MidiSequenceImpl sequence = new MidiSequenceImpl(songManager);
 
544
                        parser.parse(sequence);
 
545
                        MidiSystem.write(sequence.getSequence(),1,out);
 
546
                } catch (IOException e) {               
 
547
                        e.printStackTrace();
 
548
                }
 
549
    }
 
550
    
 
551
    public String getInstrumentName(int instrument){
 
552
        String name = null;
 
553
        Soundbank soundBank = getSoundbank();
 
554
        if(soundBank != null){
 
555
                Instrument[] instruments = soundBank.getInstruments();                  
 
556
                if(instrument >= 0 && instrument < instruments.length){
 
557
                        return instruments[instrument].getName();
 
558
                }
 
559
        }       
 
560
        return Integer.toString(instrument);
 
561
    }
 
562
 
 
563
        public List getSystemErrors() {                         
 
564
                return this.systemErrors;
 
565
        }
 
566
    
 
567
    public Option getConfigOption(ConfigEditor editor,ToolBar toolBar,Composite parent){
 
568
        return new SoundOption(editor,toolBar,parent);
 
569
    }
 
570
    
 
571
}
 
 
b'\\ No newline at end of file'