~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: 2008-06-19 00:30:30 UTC
  • mto: (5.1.2 sid)
  • mto: This revision was merged to the branch mainline in revision 3.
  • Revision ID: james.westby@ubuntu.com-20080619003030-h719szrhsngou7c6
Tags: upstream-1.0
ImportĀ upstreamĀ versionĀ 1.0

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'