33import java .io .File ;
44import java .io .IOException ;
55
6+ import javax .sound .midi .MidiUnavailableException ;
67import javax .sound .sampled .AudioFormat ;
78import javax .sound .sampled .AudioInputStream ;
89import javax .sound .sampled .AudioSystem ;
1314import javax .sound .sampled .LineUnavailableException ;
1415import javax .sound .sampled .UnsupportedAudioFileException ;
1516
17+ import org .nlogo .api .ExtensionException ;
1618import org .nlogo .api .ExtensionManager ;
1719
1820public class SoundExtension extends org .nlogo .api .DefaultClassManager {
21+ private boolean isHeadless = true ;
1922
2023 /**
2124 * Names of the drums. <p>
@@ -124,41 +127,44 @@ public void load(org.nlogo.api.PrimitiveManager primManager) {
124127 */
125128 public void runOnce (org .nlogo .api .ExtensionManager em )
126129 throws org .nlogo .api .ExtensionException {
127- try {
128-
129- // Open a synthesizer
130- synth = javax .sound .midi .MidiSystem .getSynthesizer ();
131- synth .open ();
132-
133- // Get array of synth channels
134- channels = synth .getChannels ();
135- // initializing channels to 0 to fix bug #1027
136- // channels have program=0 to begin with, but this additional initialization
137- // seems to make them work properly for instrument 0 (acoustic grand piano)
138- // when they arent initialized like this, then when inst 0 is used
139- // the channel plays the wrong instrument.
140- // maybe someday we can figure out why, but for now this is sufficient. -JC 7/2/10
141- for (javax .sound .midi .MidiChannel ch : channels ) {
142- ch .programChange (0 );
143- }
130+ isHeadless = !em .workspaceContext ().workspaceGUI ();
144131
145- javax .sound .midi .Soundbank soundbank = synth .getDefaultSoundbank ();
132+ if (!isHeadless ) {
133+ try {
134+ // Open a synthesizer
135+ synth = javax .sound .midi .MidiSystem .getSynthesizer ();
136+ synth .open ();
137+
138+ // Get array of synth channels
139+ channels = synth .getChannels ();
140+ // initializing channels to 0 to fix bug #1027
141+ // channels have program=0 to begin with, but this additional initialization
142+ // seems to make them work properly for instrument 0 (acoustic grand piano)
143+ // when they arent initialized like this, then when inst 0 is used
144+ // the channel plays the wrong instrument.
145+ // maybe someday we can figure out why, but for now this is sufficient. -JC 7/2/10
146+ for (javax .sound .midi .MidiChannel ch : channels ) {
147+ ch .programChange (0 );
148+ }
146149
147- if (soundbank == null ) {
148- try {
149- java .io .InputStream soundbankStream = getClass ().getClassLoader ().getResourceAsStream ("soundbank-min.gm" );
150- java .io .BufferedInputStream bufferedSoundbankStream = new java .io .BufferedInputStream (soundbankStream );
151- soundbank = javax .sound .midi .MidiSystem .getSoundbank (bufferedSoundbankStream );
152- } catch (java .io .IOException e ) {
153- throw new org .nlogo .api .ExtensionException ("Failed to load soundbank: " + e .toString ());
154- } catch (javax .sound .midi .InvalidMidiDataException e ) {
155- throw new org .nlogo .api .ExtensionException ("Failed to load soundbank: " + e .toString ());
150+ javax .sound .midi .Soundbank soundbank = synth .getDefaultSoundbank ();
151+
152+ if (soundbank == null ) {
153+ try {
154+ java .io .InputStream soundbankStream = getClass ().getClassLoader ().getResourceAsStream ("soundbank-min.gm" );
155+ java .io .BufferedInputStream bufferedSoundbankStream = new java .io .BufferedInputStream (soundbankStream );
156+ soundbank = javax .sound .midi .MidiSystem .getSoundbank (bufferedSoundbankStream );
157+ } catch (java .io .IOException e ) {
158+ throw new org .nlogo .api .ExtensionException ("Failed to load soundbank: " + e .toString ());
159+ } catch (javax .sound .midi .InvalidMidiDataException e ) {
160+ throw new org .nlogo .api .ExtensionException ("Failed to load soundbank: " + e .toString ());
161+ }
156162 }
157- }
158163
159- boolean loaded = synth .loadAllInstruments (soundbank );
160- } catch (javax .sound .midi .MidiUnavailableException ex ) {
161- throw new org .nlogo .api .ExtensionException ("MIDI is not available" );
164+ boolean loaded = synth .loadAllInstruments (soundbank );
165+ } catch (MidiUnavailableException ex ) {
166+ throw new ExtensionException ("MIDI is not available." );
167+ }
162168 }
163169 }
164170
@@ -177,7 +183,10 @@ public void unload(ExtensionManager em) {
177183 */
178184 static void startNote (int instrument , int note , int velocity ) {
179185 javax .sound .midi .MidiChannel channel = ensureChannel (instrument );
180- channel .noteOn (note , velocity );
186+
187+ if (channel != null ) {
188+ channel .noteOn (note , velocity );
189+ }
181190 }
182191
183192 /**
@@ -210,8 +219,10 @@ static void stopNotes(int instrument) {
210219 * Stops all notes.
211220 */
212221 static void stopNotes () {
213- // the api says stop messages are recevied by all channels
214- channels [0 ].allNotesOff ();
222+ if (channels .length > 0 ) {
223+ // the api says stop messages are recevied by all channels
224+ channels [0 ].allNotesOff ();
225+ }
215226 }
216227
217228 /**
@@ -224,19 +235,24 @@ static void stopNotes() {
224235 */
225236 static void playNote (int instrument , int note , int velocity , int duration ) {
226237 javax .sound .midi .MidiChannel channel = ensureChannel (instrument );
227- channel .noteOn (note , velocity );
228238
229- // start the stop thread
230- if (duration > -1 ) {
231- (new StopNoteThread (channel , note , duration )).start ();
239+ if (channel != null ) {
240+ channel .noteOn (note , velocity );
241+
242+ // start the stop thread
243+ if (duration > -1 ) {
244+ (new StopNoteThread (channel , note , duration )).start ();
245+ }
232246 }
233247 }
234248
235249
236250 static void playNoteLater (int instrument , int note , int velocity , int duration , int delay ) {
237251 javax .sound .midi .MidiChannel channel = ensureChannel (instrument );
238- new PlayNoteThread (channel , note , velocity , duration , delay )
239- .start ();
252+
253+ if (channel != null ) {
254+ new PlayNoteThread (channel , note , velocity , duration , delay ).start ();
255+ }
240256 }
241257
242258 /**
@@ -246,7 +262,9 @@ static void playNoteLater(int instrument, int note, int velocity, int duration,
246262 * @param velocity the speed at which the drum was hit, from 0 to 128
247263 */
248264 static void playDrum (int drum , int velocity ) {
249- channels [PERCUSSION_CHANNEL ].noteOn (drum , velocity );
265+ if (channels .length > 0 ) {
266+ channels [PERCUSSION_CHANNEL ].noteOn (drum , velocity );
267+ }
250268 }
251269
252270
@@ -462,7 +480,7 @@ private static javax.sound.midi.MidiChannel getChannel(int instrument) {
462480 private static javax .sound .midi .MidiChannel ensureChannel (int instrument ) {
463481 javax .sound .midi .MidiChannel channel = getChannel (instrument );
464482
465- if (channel != null ) {
483+ if (channel != null || channels . length == 0 ) {
466484 return channel ;
467485 }
468486
0 commit comments