Source code: org/fluidsynth/api/ConfigManager.java
1 /*
2 * Copyright (C) 2003 Ken Ellinwood.
3 *
4 * This file is part of FluidGUI.
5 *
6 * FluidGUI is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 package org.fluidsynth.api;
22
23 import org.fluidsynth.api.settings.*;
24 import org.fluidsynth.api.event.*;
25 import org.fluidsynth.api.sf2.*;
26
27 import org.exolab.castor.xml.*;
28 import org.exolab.castor.mapping.*;
29
30 import java.io.*;
31 import java.util.*;
32 import java.util.regex.*;
33 import java.beans.*;
34 import javax.swing.filechooser.FileFilter;
35
36 /** Class to manage config files. Config files contain the aggragate
37 * details of user modified settings, loaded sound font, channel
38 * assignments, etc. The in-memory, run-time representation of a
39 * config file is a Configuration object. The Configuration object is
40 * serialized to XML for storage on disk and deserialized when loaded
41 * into memory. Serialization to/from XML is performed with the <a
42 * href="http://castor.exolab.org/xml-framework.html">Castor XML Framework</a>.
43 */
44
45 public class ConfigManager implements ExecutiveListener
46 {
47
48 static java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle("org/fluidsynth/gui/i18n/bundle");
49
50 /** file extension for config files. */
51 public static final String CONFIG_FILE_EXT = ".fsg";
52
53 /** Property name for the "configuration changed" property change event. */
54 public static final String PROP_CONFIGURATION = "ConfigManager.configuration";
55
56 private static Mapping metaMapping = new Mapping();
57 private static Mapping userMapping = new Mapping();
58
59 private static ConfigManager me;
60
61 private static FileFilter configFileFilter = new ConfigFileFilter();
62 private static FileFilter sf2FileFilter = new Sf2FileFilter();
63
64 private Configuration currentConfiguration = null;
65 private Configuration newConfig = null;
66
67 private PropertyChangeSupport support;
68
69 private Channel[] channelRegister = null;
70
71 // Supress settings/sf2 events during Configuration loading.
72 private boolean supressEvents = false;
73
74 // Pattern for parsing the response to a "settings" command.
75 static Pattern settingPattern = Pattern.compile("(\\S+)\\s*(.*)");
76
77 // Pattern for parsing the response to an "info" command.
78 static Pattern infoPattern = Pattern.compile("([^:]+):\\s*(.*)");
79
80 // Pattern for parsing the ID from a "load <sf2>" response.
81 static Pattern idPattern = Pattern.compile( "(\\d+)");
82
83 // Pattern for parsing bank/preset data from an "inst" resposne
84 static Pattern instPattern = Pattern.compile( "^(\\d+)-(\\d+)\\s+(.*)$");
85
86 /** Utility field holding list of SettingsEventListeners. */
87 private transient java.util.ArrayList settingsEventListenerList;
88
89 /** Utility field holding list of SoundFontEventListeners. */
90 private transient java.util.ArrayList soundFontEventListenerList;
91
92 /** Utility field holding list of ChannelEventListeners. */
93 private transient java.util.ArrayList channelEventListenerList;
94
95 /** Private constructor enforces the singleton pattern. */
96 private ConfigManager()
97 throws Exception
98 {
99 Log.info( this, "Loading XML mappings...");
100 // Load castor mapping files.
101 metaMapping.loadMapping( ConfigManager.class.getResource( "meta-mapping.xml"));
102
103 userMapping.loadMapping( ConfigManager.class.getResource( "user-mapping.xml"));
104
105 support = new PropertyChangeSupport( this);
106
107 }
108
109 /** Initialize the ConfigManager. This method must be called
110 * prior to {@link #onlyInstance}.
111 */
112 public static ConfigManager init()
113 throws Exception
114 {
115 if (me == null) {
116 me = new ConfigManager();
117 Executive.addListener( me);
118 }
119 return me;
120 }
121
122 /** Return the singleton instance of the class. */
123 public static ConfigManager onlyInstance()
124 {
125 return me;
126 }
127
128 /** Add a property change listener. */
129 public void addPropertyChangeListener( PropertyChangeListener listener)
130 {
131 support.addPropertyChangeListener( listener);
132 }
133
134 /** Remove a property change listener. */
135 public void removePropertyChangeListener( PropertyChangeListener listener)
136 {
137 support.removePropertyChangeListener( listener);
138 }
139
140 /** Get the currently loaded configuration. */
141 public Configuration getCurrentConfiguration()
142 {
143 return currentConfiguration;
144 }
145
146 /** Load a new configuration from the given pathname. The newly
147 * loaded configuration becomes the current configuration.
148 * Property change events are fired to notify listeners of the
149 * change.
150 */
151 public Configuration loadConfiguration( String configPathName)
152 throws Exception
153 {
154
155 Configuration oldConfig = currentConfiguration;
156
157 // Load the extra setting info
158 Reader reader = new InputStreamReader( ConfigManager.class.getResource( "settings-extra.xml").openStream());
159 Unmarshaller unmarshaller = new Unmarshaller(Settings.class);
160 unmarshaller.setMapping(metaMapping);
161 Settings settings = (Settings)unmarshaller.unmarshal(reader);
162 reader.close();
163
164 // Load user values
165 File userFile = null;
166 currentConfiguration = null;
167 channelRegister = null;
168 support.firePropertyChange( PROP_CONFIGURATION, oldConfig, null);
169
170 if (configPathName != null && (userFile = new File( configPathName)).exists()) {
171 Log.info(this,"Loading "+userFile.getCanonicalPath());
172 reader = new InputStreamReader( new FileInputStream( userFile));
173 unmarshaller = new Unmarshaller(Configuration.class);
174 unmarshaller.setMapping(userMapping);
175 newConfig = (Configuration)unmarshaller.unmarshal(reader);
176 reader.close();
177 settings.merge( newConfig.getSettings());
178 newConfig.setFile( userFile);
179 }
180 else newConfig = new Configuration( settings);
181
182
183 if (Executive.isRunning()) {
184 Executive.stop();
185 try { Thread.sleep( 2000); }
186 catch (InterruptedException x) {} // Ignore
187 }
188
189 newConfig.setSettings( settings);
190 supressEvents = true;
191
192 Executive.start( settings); // Will cause loader-phase-2 to be invoked
193
194 synchronized ( this) { // Wait for phase 2 to complete
195 try { wait(); }
196 catch (InterruptedException e) {}
197 }
198
199 supressEvents = false;
200
201 // Issue property change event
202 currentConfiguration = newConfig;
203 support.firePropertyChange( PROP_CONFIGURATION, null, currentConfiguration);
204
205 if (configPathName != null && !configPathName.equals( FluidGuiPrefs.getStartupConfigPathname()))
206 {
207 FluidGuiPrefs.setStartupConfigPathname( configPathName);
208 FluidGuiPrefs.save();
209 }
210
211 if (userFile != null) Log.info(this,"Finished loading "+userFile.getCanonicalPath());
212 return currentConfiguration;
213
214 }
215
216 // This thread is run anytime the Executive.start() method is
217 // called. It ensures that the settings are polled and merged
218 // (for correct display of available settings), and that the sound
219 // fonts and channels in the current configuration are
220 // loaded/assigned.
221 class LoaderPhase2 implements Runnable
222 {
223 public void run()
224 {
225 if (!newConfig.getSettingsMerged()) {
226 try {
227 newConfig.setSettings( mergeSynthSettings( newConfig.getSettings()));
228 newConfig.setSettingsMerged( true);
229 if (!supressEvents) {
230 // Fire settings changed
231 fireSettingsChanged( new SettingsEvent( this, newConfig, newConfig.getSettings()));
232 }
233 }
234 catch (Exception e) {
235 }
236 }
237
238 SoundFont[] sf2s = newConfig.getSoundFont();
239 for (int i = 0; i < sf2s.length; i++) {
240 try {
241 loadSoundFont( sf2s[i]);
242 }
243 catch (OperationFailedException e) {
244 Log.warning( this, e.getMessage());
245 }
246 }
247
248 if (channelRegister == null)
249 {
250 Setting s = newConfig.getSettings().lookup( "synth.midi-channels");
251 if (s != null) {
252 Integer count = (Integer)s.objectGet();
253 channelRegister = new Channel[ count.intValue()];
254 }
255 }
256
257 Channel[] channels = newConfig.getChannel();
258 for (int i = 0; i < channels.length; i++) {
259 try {
260 assignChannel( channels[i]);
261 }
262 catch (OperationFailedException e) {
263 Log.warning( this, e.getMessage());
264 }
265 }
266
267 synchChannels();
268
269 currentConfiguration = newConfig;
270
271 synchronized (ConfigManager.this) {
272 ConfigManager.this.notify();
273 }
274 }
275 }
276
277 /** Called when the synth starts */
278 public void started()
279 {
280 new Thread( new LoaderPhase2(), "FluidConfigLoader-Phase2").start();
281 }
282
283 /** Called when the synth stops for any reason. */
284 public void stopped()
285 {
286 synchronized (ConfigManager.this) {
287 ConfigManager.this.notify();
288 }
289 }
290
291 /** Merges the content of the Settings object passed as an
292 * argument into the info from a queries to fluidsynth for
293 * settings meta data. The merge is performed in such a way that
294 * the input values take precedence over values queried from the
295 * synth.
296 * @return the result of the merge.
297 */
298 private Settings mergeSynthSettings( Settings settings)
299 throws IOException, IllegalStateException
300 {
301
302 Settings result = new Settings();
303 Log.info(this,"Getting fluidsynth settings...");
304 List settingsOutput = null;
305 try {
306 settingsOutput = Executive.instance().invoke("settings");
307
308 Log.info(this,"Getting fluidsynth settings info...");
309 for (Iterator i = settingsOutput.iterator(); i.hasNext(); )
310 {
311 Matcher matcher = settingPattern.matcher( (String)i.next());
312 if (!matcher.matches()) continue;
313
314 String name = matcher.group(1);
315 String value = matcher.group(2);
316
317 String type = null;
318 String minValue = null;
319 String maxValue = null;
320 String defaultValue = null;
321 String realtime = null;
322 String options = null;
323
324 List output = Executive.instance().invoke("info " + name);
325
326 Iterator o = output.iterator();
327 o.next(); // skip setting name
328
329 while (o.hasNext())
330 {
331 matcher = infoPattern.matcher( (String)o.next());
332 if (!matcher.matches()) continue;
333
334 String n = matcher.group( 1);
335 String v = null;
336 if (matcher.groupCount() == 2) v = matcher.group( 2);
337
338 if (n.equals("Type")) type = v;
339 else if (n.equals("Minimum value")) minValue = v;
340 else if (n.equals("Maximum value")) maxValue = v;
341 else if (n.equals("Default value")) defaultValue = v;
342 else if (n.equals("Real-time")) realtime = v;
343 else if (n.equals("Options")) options = v;
344 }
345
346 Setting setting = SettingFactory.createSetting( type,
347 name,
348 value,
349 minValue,
350 maxValue,
351 defaultValue,
352 realtime,
353 options);
354
355 result.merge( setting);
356 }
357
358 result.merge( settings);
359
360 }
361 catch (IllegalStateException e) {
362 result = settings.getModifiedSettings();
363 }
364
365 return result;
366 }
367
368 /** Save the specified configuration to the given pathname. The
369 * saved configuration becomes the current configuration.
370 * Property change events are fired to notify listeners of the
371 * change.
372 */
373 public static void saveConfiguration( Configuration configuration, String pathname)
374 throws Exception
375 {
376 File file;
377
378 boolean markAsDefault = false;
379
380 if (pathname != null) {
381
382 if (pathname.lastIndexOf('.') < pathname.lastIndexOf( File.separatorChar))
383 pathname = pathname + CONFIG_FILE_EXT;
384
385 file = new File( pathname);
386 markAsDefault = true;
387 }
388 else file = configuration.getFile();
389
390 marshall( userMapping, configuration.getModifiedConfiguration(), file);
391 configuration.setFile( file);
392 configuration.clearDirty();
393 me.currentConfiguration = configuration;
394
395 me.support.firePropertyChange( PROP_CONFIGURATION, null, configuration);
396 if (markAsDefault) {
397 FluidGuiPrefs.setStartupConfigPathname( pathname);
398 FluidGuiPrefs.save();
399 }
400 }
401
402
403 static void marshall( Mapping mapping, Configuration configuration, File file)
404 throws Exception
405 {
406 // Create a File to marshal to
407 Writer writer = new FileWriter( file);
408
409 // Marshal the configuration object to XML
410 Marshaller marshaller = new Marshaller( writer);
411 marshaller.setMapping( mapping);
412 marshaller.setNamespaceMapping( "xsi", "http://www.w3.org/2001/XMLSchema-instance");
413 marshaller.marshal(configuration);
414
415 writer.close();
416
417 }
418
419
420 static class ConfigFileFilter extends FileFilter
421 {
422 public boolean accept(File f)
423 {
424 return f.isDirectory() || f.getName().endsWith( CONFIG_FILE_EXT);
425 }
426
427 public String getDescription()
428 {
429 return bundle.getString("config.file.description");
430 }
431 }
432
433 static class Sf2FileFilter extends FileFilter
434 {
435 public boolean accept(File f)
436 {
437 return f.isDirectory() || f.getName().toUpperCase().endsWith( ".SF2");
438 }
439
440 public String getDescription()
441 {
442 return bundle.getString("sf2.file.description");
443 }
444 }
445
446 /** Get the file filter that filters out all but config files. */
447 public static FileFilter getConfigFileFilter()
448 {
449 return configFileFilter;
450 }
451
452 /** Get the file filter that filters out all but sound font files. */
453 public static FileFilter getSf2FileFilter()
454 {
455 return sf2FileFilter;
456 }
457
458 /** Test harness. */
459
460 /* Test routine. */
461 static class TestListener implements ExecutiveListener
462 {
463 public void started()
464 {
465 System.out.println( Executive.getTerminationStatus());
466 }
467
468 public void stopped()
469 {
470 TerminationStatus status = Executive.getTerminationStatus();
471 System.out.println( status);
472 if (status == TerminationStatus.ABNORMAL_TERMINATION) {
473 System.out.println( Executive.getConsoleOutput());
474 }
475 else if (status == TerminationStatus.START_FAILED) {
476 System.out.println( Executive.getErrorMessage());
477 }
478 }
479 }
480
481 public static void main( String[] args)
482 {
483 try {
484
485 Status.onlyInstance().addPropertyChangeListener(
486 new PropertyChangeListener() {
487 public void propertyChange( PropertyChangeEvent evt) {
488 System.out.println( evt.getNewValue());
489 }
490 });
491
492 FluidGuiPrefs.init();
493 ConfigManager.init();
494
495 Executive.addListener( new TestListener());
496
497 if (args.length == 0) ConfigManager.onlyInstance().loadConfiguration( FluidGuiPrefs.getStartupConfigPathname());
498 else if (args.length > 0) {
499 ConfigManager.onlyInstance().loadConfiguration( args[0]);
500 if (args.length == 2)
501 ConfigManager.onlyInstance().loadNewSoundFont( args[1]);
502 else System.out.println("USAGE: ConfigManager [<config file>] [<sound font>]");
503 }
504
505 Configuration config = onlyInstance().getCurrentConfiguration();
506
507 SoundFont sf2 = config.lookupSoundFont( "/mnt/more/stuff/BuzzSongs/4gmgsmt.sf2");
508 Preset preset = sf2.getBanks()[0].getPresets()[14];
509 onlyInstance().assignNewChannel( new Channel( 5, preset));
510
511 saveConfiguration( config, null);
512
513 System.exit( 0);
514
515 }
516 catch (Exception e) {
517 e.printStackTrace();
518 }
519 }
520
521
522
523 /** Load a new sound font specifed by the given filesystem path.
524 * @throws OperationFailedException if the sound font at the
525 * given path is already loaded or error occur during loading.
526 */
527 public void loadNewSoundFont( String path)
528 throws OperationFailedException
529 {
530 SoundFont sf2 = currentConfiguration.lookupSoundFont( path);
531 if (sf2 != null)
532 throw new OperationFailedException(path + " is already loaded. ID=" + sf2.getId());
533
534 loadSoundFont( new SoundFont( path));
535 currentConfiguration.dirty();
536 synchChannels();
537 }
538
539 void loadSoundFont( SoundFont soundFont)
540 throws OperationFailedException
541 {
542 try {
543 Log.info( this, "Loading sound font: " + soundFont.getPath());
544 List l = Executive.instance().invoke("load " + soundFont.getPath());
545 String result = null;
546
547 if (l.size() > 0) result = (String)l.iterator().next();
548 else result = "failed";
549
550 if (result.indexOf("fail") >= 0) {
551 throw new OperationFailedException( result);
552 }
553
554 Matcher m = idPattern.matcher( result);
555 m.find();
556 String idStr = m.group(1);
557 Log.config( this, "Loaded sound font, id="+idStr);
558 int id = Integer.parseInt( idStr);
559
560 int oldId = soundFont.getId();
561
562 soundFont.setId( id);
563
564 if (oldId != -1) return; // Previously loaded, we already have bank/preset data
565
566 l = Executive.instance().invoke("inst " + idStr);
567
568 List banks = new LinkedList();
569 List presets = null;
570
571 Bank currBank = null;
572
573 for (Iterator i = l.iterator(); i.hasNext(); ) {
574 m = instPattern.matcher( (String)i.next());
575 if (m.matches()) {
576 int bank = Integer.parseInt( m.group(1));
577 int preset = Integer.parseInt( m.group(2));
578 String instName = m.group(3);
579
580 if (currBank == null || currBank.getId() != bank) {
581
582 if (presets != null)
583 currBank.setPresets(
584 (Preset[])presets.toArray( new Preset[ presets.size()]));
585
586 currBank = new Bank( soundFont, bank);
587 banks.add( currBank);
588 presets = new LinkedList();
589 }
590
591 presets.add( new Preset( currBank, preset, instName));
592 }
593 }
594 if (currBank != null && presets != null)
595 {
596 currBank.setPresets(
597 (Preset[])presets.toArray( new Preset[ presets.size()]));
598 }
599
600 soundFont.setBanks( (Bank[])banks.toArray( new Bank[ banks.size()]));
601
602 if (newConfig.lookupSoundFont( soundFont.getPath()) == null) {
603 newConfig.insertSoundFont( soundFont);
604 if (!supressEvents) {
605 // Fire sound font added
606 fireSoundFontAdded( new SoundFontEvent( this, newConfig, soundFont));
607 }
608 }
609 else {
610 if (!supressEvents) {
611 // Fire sound font updated
612 fireSoundFontUpdated( new SoundFontEvent( this, newConfig, soundFont));
613 }
614 }
615 }
616 catch (Exception e) {
617 throw new OperationFailedException( "Failed to load sound font", e);
618 }
619
620 }
621
622 public void unloadSoundFont( SoundFont sf2)
623 throws OperationFailedException
624 {
625 sf2 = newConfig.lookupSoundFont(sf2.getPath());
626 if (sf2 == null) return;
627
628 SoundFontEvent evt = new SoundFontEvent(this,newConfig,sf2);
629 fireSoundFontWillBeRemoved(evt);
630 newConfig.unloadSoundFont( sf2);
631 newConfig.dirty();
632 if (channelRegister == null) return;
633
634 for (int i = 0; i < channelRegister.length; i++) {
635 if (channelRegister[i] == null || channelRegister[i].getSoundFont() == null) continue;
636
637 if (channelRegister[i].getSoundFont().equals( sf2.getPath()))
638 {
639 channelRegister[i] = null;
640 fireChannelChanged( new ChannelEvent( this, new Channel( i, null)));
641 }
642 }
643 fireSoundFontRemoved(evt);
644 try {
645
646 if (Executive.isRunning()) {
647 List l = Executive.instance().invoke("unload " + sf2.getId());
648
649
650 if (l.size() > 0) {
651 StringBuffer result = new StringBuffer();
652 for (Iterator i = l.iterator(); i.hasNext(); ) {
653 result.append( (String)i.next());
654 result.append("\n");
655 }
656 throw new OperationFailedException( result.toString());
657 }
658
659 // Sync channels here?
660 }
661
662 }
663 catch (Exception e) {
664 e.printStackTrace();
665 throw new OperationFailedException( "Failed to unload " + sf2, e);
666 }
667
668
669 }
670
671 void synchChannels()
672 {
673 Pattern emptyChannelPattern = Pattern.compile("chan (\\d+), no preset");
674 Pattern verboseChannelPattern = Pattern.compile("chan (\\d+), sfont (\\d+), bank (\\d+), preset (\\d+), (.*)");
675
676 if (!Executive.isRunning() || channelRegister == null || newConfig == null) return;
677
678 try {
679 List output = Executive.instance().invoke("channels -verbose");
680
681 for (Iterator i = output.iterator(); i.hasNext(); )
682 {
683 String line = (String)i.next();
684
685 Matcher emptyChannelMatcher = emptyChannelPattern.matcher( line);
686
687 if (emptyChannelMatcher.matches()) {
688 int channelNum = Integer.parseInt( emptyChannelMatcher.group( 1));
689 if (channelRegister[channelNum] != null) {
690 channelRegister[channelNum] = null;
691 fireChannelChanged( new ChannelEvent( this, new Channel( channelNum, null)));
692 }
693 continue;
694 }
695 Matcher verboseChannelMatcher = verboseChannelPattern.matcher( line);
696 if (!verboseChannelMatcher.matches()) continue;
697
698 int channelNum = Integer.parseInt( verboseChannelMatcher.group( 1));
699 int sfontId = Integer.parseInt( verboseChannelMatcher.group( 2));
700 int bankNum = Integer.parseInt( verboseChannelMatcher.group( 3));
701 int presetNum = Integer.parseInt( verboseChannelMatcher.group( 4));
702
703
704 SoundFont sf = newConfig.lookupSoundFont( sfontId);
705 if (sf == null) continue;
706 Bank b = sf.lookupBank( bankNum);
707 if (b == null) continue;
708 Preset preset = b.lookupPreset( presetNum);
709 if (preset == null) continue;
710
711 if (channelRegister[ channelNum] == null || !preset.equals( channelRegister[ channelNum].getPreset()))
712 {
713 Channel nc = new Channel( channelNum, preset);
714 channelRegister[ channelNum] = nc;
715 fireChannelChanged( new ChannelEvent( this, nc));
716 }
717 }
718 }
719 catch (Exception e) {
720 Log.severe( this, "synchChannels() failed", e);
721 }
722
723 }
724
725 public SoundFont validateChannel( Channel channel)
726 throws OperationFailedException
727 {
728 SoundFont soundFont = newConfig.lookupSoundFont( channel.getSoundFont());
729 if (soundFont == null) throw new OperationFailedException( "Invalid sound font in " + channel);
730 if (channel.getPreset() == null) {
731 Bank bank = soundFont.lookupBank( channel.getBank());
732 if (bank != null) {
733 Preset preset = bank.lookupPreset( channel.getPresetId());
734 if (preset != null) channel.setPreset( preset);
735 else throw new OperationFailedException("Invalid preset in " + channel);
736 }
737 else throw new OperationFailedException( "Invalid bank in " + channel);
738 }
739 return soundFont;
740 }
741
742 public void assignNewChannel( Channel channel)
743 throws OperationFailedException
744 {
745 assignChannel( channel);
746 currentConfiguration.insertChannel( channel);
747 currentConfiguration.dirty();
748 }
749
750 void assignChannel( Channel channel)
751 throws OperationFailedException
752 {
753 Log.info( this, "Assigning " + channel);
754 SoundFont sf2 = validateChannel( channel);
755 try {
756
757 if (Executive.isRunning()) {
758 List l = Executive.instance().invoke("select " + channel.getNumber() + ' ' + sf2.getId() + ' ' +
759 channel.getBank() + ' ' + channel.getPresetId());
760
761 if (l.size() > 0) {
762 StringBuffer result = new StringBuffer();
763 for (Iterator i = l.iterator(); i.hasNext(); ) {
764 result.append( (String)i.next());
765 result.append("\n");
766 }
767 throw new OperationFailedException( result.toString());
768 }
769 }
770
771 try {
772 int cnum = channel.getNumber();
773 if (channelRegister[ cnum] == null ||
774 !channelRegister[ cnum].equals( channel.getPreset()))
775 {
776 channelRegister[ cnum] = channel;
777 if (!supressEvents) {
778 fireChannelChanged( new ChannelEvent( this, channel));
779 }
780
781 }
782 }
783 catch (Exception e) {
784 }
785 }
786 catch (Exception e) {
787 e.printStackTrace();
788 throw new OperationFailedException( "Failed to assign " + channel, e);
789 }
790
791 }
792
793 public Channel lookupChannel( int channelNumber)
794 {
795 if (channelRegister == null || channelNumber >= channelRegister.length) return null;
796 return channelRegister[channelNumber];
797 }
798
799 /** Registers SettingsEventListener to receive events.
800 * @param listener The listener to register.
801 *
802 */
803 public synchronized void addSettingsEventListener(org.fluidsynth.api.event.SettingsEventListener listener) {
804 if (settingsEventListenerList == null ) {
805 settingsEventListenerList = new java.util.ArrayList();
806 }
807 settingsEventListenerList.add(listener);
808 }
809
810 /** Removes SettingsEventListener from the list of listeners.
811 * @param listener The listener to remove.
812 *
813 */
814 public synchronized void removeSettingsEventListener(org.fluidsynth.api.event.SettingsEventListener listener) {
815 if (settingsEventListenerList != null ) {
816 settingsEventListenerList.remove(listener);
817 }
818 }
819
820 /** Notifies all registered listeners about the event.
821 *
822 * @param event The event to be fired
823 *
824 */
825 private void fireSettingsChanged(org.fluidsynth.api.event.SettingsEvent event) {
826 java.util.ArrayList list;
827 synchronized (this) {
828 if (settingsEventListenerList == null) return;
829 list = (java.util.ArrayList)settingsEventListenerList.clone();
830 }
831 for (int i = 0; i < list.size(); i++) {
832 ((org.fluidsynth.api.event.SettingsEventListener)list.get(i)).settingsChanged(event);
833 }
834 }
835
836 /** Registers SoundFontEventListener to receive events.
837 * @param listener The listener to register.
838 *
839 */
840 public synchronized void addSoundFontEventListener(org.fluidsynth.api.event.SoundFontEventListener listener) {
841 if (soundFontEventListenerList == null ) {
842 soundFontEventListenerList = new java.util.ArrayList();
843 }
844 soundFontEventListenerList.add(listener);
845 }
846
847 /** Removes SoundFontEventListener from the list of listeners.
848 * @param listener The listener to remove.
849 *
850 */
851 public synchronized void removeSoundFontEventListener(org.fluidsynth.api.event.SoundFontEventListener listener) {
852 if (soundFontEventListenerList != null ) {
853 soundFontEventListenerList.remove(listener);
854 }
855 }
856
857 /** Notifies all registered listeners about the event.
858 *
859 * @param event The event to be fired
860 *
861 */
862 private void fireSoundFontAdded(org.fluidsynth.api.event.SoundFontEvent event) {
863 java.util.ArrayList list;
864 synchronized (this) {
865 if (soundFontEventListenerList == null) return;
866 list = (java.util.ArrayList)soundFontEventListenerList.clone();
867 }
868 for (int i = 0; i < list.size(); i++) {
869 ((org.fluidsynth.api.event.SoundFontEventListener)list.get(i)).soundFontAdded(event);
870 }
871 }
872
873 /** Notifies all registered listeners about the event.
874 *
875 * @param event The event to be fired
876 *
877 */
878 private void fireSoundFontWillBeRemoved(org.fluidsynth.api.event.SoundFontEvent event) {
879 java.util.ArrayList list;
880 synchronized (this) {
881 if (soundFontEventListenerList == null) return;
882 list = (java.util.ArrayList)soundFontEventListenerList.clone();
883 }
884 for (int i = 0; i < list.size(); i++) {
885 ((org.fluidsynth.api.event.SoundFontEventListener)list.get(i)).soundFontWillBeRemoved(event);
886 }
887 }
888
889 /** Notifies all registered listeners about the event.
890 *
891 * @param event The event to be fired
892 *
893 */
894 private void fireSoundFontRemoved(org.fluidsynth.api.event.SoundFontEvent event) {
895 java.util.ArrayList list;
896 synchronized (this) {
897 if (soundFontEventListenerList == null) return;
898 list = (java.util.ArrayList)soundFontEventListenerList.clone();
899 }
900 for (int i = 0; i < list.size(); i++) {
901 ((org.fluidsynth.api.event.SoundFontEventListener)list.get(i)).soundFontRemoved(event);
902 }
903 }
904
905 /** Notifies all registered listeners about the event.
906 *
907 * @param event The event to be fired
908 *
909 */
910 private void fireSoundFontUpdated(org.fluidsynth.api.event.SoundFontEvent event) {
911 java.util.ArrayList list;
912 synchronized (this) {
913 if (soundFontEventListenerList == null) return;
914 list = (java.util.ArrayList)soundFontEventListenerList.clone();
915 }
916 for (int i = 0; i < list.size(); i++) {
917 ((org.fluidsynth.api.event.SoundFontEventListener)list.get(i)).soundFontUpdated(event);
918 }
919 }
920
921 /** Registers ChannelEventListener to receive events.
922 * @param listener The listener to register.
923 *
924 */
925 public synchronized void addChannelEventListener(org.fluidsynth.api.event.ChannelEventListener listener) {
926 if (channelEventListenerList == null ) {
927 channelEventListenerList = new java.util.ArrayList();
928 }
929 channelEventListenerList.add(listener);
930 }
931
932 /** Removes ChannelEventListener from the list of listeners.
933 * @param listener The listener to remove.
934 *
935 */
936 public synchronized void removeChannelEventListener(org.fluidsynth.api.event.ChannelEventListener listener) {
937 if (channelEventListenerList != null ) {
938 channelEventListenerList.remove(listener);
939 }
940 }
941
942 /** Notifies all registered listeners about the event.
943 *
944 * @param event The event to be fired
945 *
946 */
947 private void fireChannelChanged(org.fluidsynth.api.event.ChannelEvent event) {
948 java.util.ArrayList list;
949 synchronized (this) {
950 if (channelEventListenerList == null) return;
951 list = (java.util.ArrayList)channelEventListenerList.clone();
952 }
953 for (int i = 0; i < list.size(); i++) {
954 ((org.fluidsynth.api.event.ChannelEventListener)list.get(i)).channelChanged(event);
955 }
956 }
957
958 }