1   package fi.jyu.mit.Music;
2   
3   import java.util.LinkedList;
4   import java.util.List;
5   
6   import fi.jyu.mit.Music.Note.Delay;
7   
8   /**
9    * Puskuroitu äänijärjestelmä, jonka tarkoitus on tukea mahdollisuutta 
10   * syättää äänijärjestelmälle nuotteja, jotka voidaan toistaa
11   * joko pääohjelman säikeessä tai uudessa säikeessä.
12   * 
13   * Tälläin käskyt play ja playNote eivät toista saamiansa nuotteja heti,
14   * vaan ne puskuroidaan, eli varastoidaan. Soittaminen aloitetaan metodeilla
15   * run tai runAsync.
16   * 
17   * Metodilla run äänijärjestelmä aloittaa soittamisen nykyisessä säikeessä,
18   * kun taas runAsync tekee uuden säikeen joka sitten pyärii niin kauan kunnes
19   * soitto on loppunut.
20   * 
21   * Asynkronisen soittamisen voi aina lopettaa käskyllä stop.
22   */
23  public class BufferedMidiPlayer extends MidiPlayer implements BasicMidiPlayer {
24      /**
25       * Peritty luokka säiettä varten.
26       */
27      private class SoundSystemThread extends Thread {
28          public void run() {
29              boolean halted = false;
30              // Luodaan säikeelle kopio nuottipuskurista 
31              List<Note> localBuffer = new LinkedList<Note>(noteBuffer);
32              while (!localBuffer.isEmpty()) {
33                  if (halt) {
34                      halted = true;
35                      break;
36                  }
37                  Note n = localBuffer.remove(0);
38                  try  {
39                      if (n.isDelay()) {
40                          Thread.sleep(n.getLength());
41                          message(currentChannel + " Tauko         " + n.message());
42                      } else {
43                          channels[currentChannel].noteOn(n.getNote(), n.getVelocity());
44                          message( currentChannel + " Soitin nuotin " + n.message());
45                          Thread.sleep(n.getLength());
46                          channels[currentChannel].noteOff(n.getNote());
47                      }
48                  }
49                  catch (InterruptedException e) {
50                      if ( !n.isDelay() ) channels[currentChannel].noteOff(n.getNote());
51                  }
52              }
53              if (halted) {
54                  message("Soitto keskeytettiin!");
55              } else {
56                  message("Soitto päättynyt!");
57              }
58          }
59      }
60      
61      /**
62       * Pysäytyslippu.
63       */
64      private boolean halt = true;
65      
66      /**
67       * Nuottipuskuri. Soitetaan tulojärjestyksessä.
68       */
69      private List<Note> noteBuffer;
70      
71      public BufferedMidiPlayer() {
72          noteBuffer = new LinkedList<Note>();
73      }
74      
75      
76      public BufferedMidiPlayer(int channel) {
77          this();
78          selectChannel(channel);
79      }
80      
81      
82      /**
83       * Luo kopion puskuroiduista nuoteista
84       * @return kopio nuoteista
85       */
86      public List<Note>getCopyOfNotes() {
87          List<Note> notes = new LinkedList<Note>();
88          for (Note n:noteBuffer)
89              notes.add(n);
90          return notes;
91      }
92      
93      /**
94       * Palauttaa nuotit viitteenä jolloin niiden muuttaminen muuttaa soitettavia nuotteja
95       * @return nuotit
96       */
97      public List<Note>getNotes() {
98          return noteBuffer;
99      }
100     
101     /**
102      * Tallentaa nuotin puskuriin.
103      */
104     public void midiSoundNote(int note, int length, int velocity) {
105         noteBuffer.add(new Note(note, length, velocity));
106     }
107     
108     @Override
109     protected void rest(final int barLength) {
110         // Ei negatiivisia taukoja
111         if (barLength < 0)
112             return;
113         noteBuffer.add(new Note.Delay(barLength));
114     }
115     
116     
117     /**
118      * Aloittaa soiton kutsujan säikeessä, toisin sanoen keskeyttää pääohjelman toiminnan.
119      */
120     public void run() {
121         while (!noteBuffer.isEmpty()) {
122             Note n = noteBuffer.remove(0);
123             try  {
124                 if ( n.isDelay() ) {
125                     Thread.sleep(n.getLength());
126                     message("Tauko         " + n.message());
127                 } else {
128                     channels[currentChannel].noteOn(n.getNote(),n.getVelocity());
129                     message("Soitin nuotin " + n.message());
130                     Thread.sleep(n.getLength());
131                     channels[currentChannel].noteOff(n.getNote());
132                 }
133             }
134             catch (InterruptedException e) {
135                 if ( !n.isDelay() ) channels[currentChannel].noteOff(n.getNote());
136             }
137         }
138     }
139     
140     /**
141      * Aloittaa asynkronisen soiton, eli soitto ei keskeytä pääohjelman toimintaa.
142      */
143     public void runAsync() {
144         SoundSystemThread sst = new SoundSystemThread();
145         halt = false;
146         sst.start();
147     }
148     
149     // git+ssh://git@github.com/ane/SoundSystem.git
150     
151     /**
152      * Pysäyttää asynkronisen soiton.
153      */
154     public void stop() {
155         halt = true;
156     }
157 }
158