Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

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 }