/* * Created on Feb 11, 2007 * * Copyright (c) 2006-2007 P.J.Leonard * * http://www.frinika.com * * This file is part of Frinika. * * Frinika is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * Frinika is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with Frinika; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /** * PJL 2008/4/5 made audioServer FrinikaAudioServer * */ package com.frinika.project; import java.util.List; import java.util.Observer; import java.util.Observable; import javax.swing.JFrame; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JComponent; import uk.org.toot.swingui.audioui.serverui.*; import com.frinika.global.FrinikaConfig; import com.frinika.toot.javasoundmultiplexed.*; // !!! import com.frinika.tootX.LatencyTesterPanel; import uk.org.toot.audio.server.AbstractAudioServer; import uk.org.toot.audio.server.AudioClient; import uk.org.toot.audio.server.AudioServer; import uk.org.toot.audio.server.AudioServerConfiguration; import uk.org.toot.audio.server.AudioServerServices; import uk.org.toot.audio.server.ExtendedAudioServer; import uk.org.toot.audio.server.IOAudioProcess; import uk.org.toot.audio.server.MultiIOJavaSoundAudioServer; import uk.org.toot.audio.server.SwitchedAudioClient; import static com.frinika.localization.CurrentLocale.getMessage; public class FrinikaAudioSystem { // order is important !!! private static SwitchedAudioClient mixerSwitch = new SwitchedAudioClient(); private static FrinikaAudioServer audioServer; // non real time wrapper private static AudioServer realAudioServer; private static AudioServerConfiguration serverConfig; private static Object thief = null; private static int bufferSize; private static IOAudioProcess defaultOut; private static JFrame configureFrame; /** * Skip seeing audio outputs if neccesary (e.g. standalone wav rendering) */ public static boolean usePhysicalAudioOutput = true; public static FrinikaAudioServer getAudioServer() { if (audioServer != null) return audioServer; try { // JJack will recognise this name when setting up the jjack client System.setProperty("jjack.client.name", "Frinika"); boolean multiplexIO = FrinikaConfig.MULTIPLEXED_AUDIO; // .getPropertyBoolean("multiplexed_audio"); if (!multiplexIO) { realAudioServer = new MultiIOJavaSoundAudioServer(); } else { System.out .println(" WARNING USING EXPERIMENTAL MULTIPLEXED AUDIO SERVER "); MultiplexedJavaSoundAudioServer s = new MultiplexedJavaSoundAudioServer(); realAudioServer = s; configureMultiplexed(s); } audioServer = new FrinikaAudioServer(realAudioServer); serverConfig = AudioServerServices .createServerConfiguration(realAudioServer); serverConfig.addObserver(new Observer() { public void update(Observable obs, Object obj) { saveServerConfig(); } }); loadServerConfigPost(); bufferSize = audioServer.createAudioBuffer("dummy") .getSampleCount(); audioServer.setClient(mixerSwitch); return audioServer; } catch (Exception e) { e.printStackTrace(); System.err.println(" Too frightened to carry on !!!"); System.exit(-1); return null; } } /** * This is intended for Step 1 of the SplashScreen audio server setup. This * called before the audio server has been created * * Create AudioServer instance. * * If one already exists it is a mistake. * * @return the audio server instance */ public static AudioServer getAudioServerInit() { assert (realAudioServer == null); try { // JJack will recognise this name when setting up the jjack client System.setProperty("jjack.client.name", "Frinika"); boolean multiplexIO = FrinikaConfig.MULTIPLEXED_AUDIO; // .getPropertyBoolean("multiplexed_audio"); if (!multiplexIO) { realAudioServer = new MultiIOJavaSoundAudioServer(); } else { System.out .println(" WARNING USING EXPERIMENTAL MULTIPLEXED AUDIO SERVER "); MultiplexedJavaSoundAudioServer s = new MultiplexedJavaSoundAudioServer(); realAudioServer = s; } audioServer = new FrinikaAudioServer(realAudioServer); serverConfig = AudioServerServices .createServerConfiguration(realAudioServer); serverConfig.addObserver(new Observer() { public void update(Observable obs, Object obj) { saveServerConfig(); } }); return realAudioServer; } catch (Exception e) { e.printStackTrace(); System.err.println(" Too frightened to carry on !!!"); System.exit(-1); return null; } } /** * step 2 * */ public static void intitIO() { // if (audioServer instanceof MultiplexedJavaSoundAudioServer) { // configureMultiplexed((MultiplexedJavaSoundAudioServer)audioServer); // } loadServerConfigPost(); bufferSize = audioServer.createAudioBuffer("dummy").getSampleCount(); audioServer.setClient(mixerSwitch); } // !!! private static void configureMultiplexed(MultiplexedJavaSoundAudioServer s) { List<String> list = s.getOutDeviceList(); Object a[] = new Object[list.size()]; a = list.toArray(a); String configKey = ((ExtendedAudioServer) realAudioServer).getConfigKey(); String configDev = null; configDev = FrinikaConfig.getProperty(configKey + ".outputDevice"); System.out.println(configDev); Object selectedValue = null; for (Object ae : a) { if (ae.equals(configDev)) { selectedValue = ae; break; } } if (selectedValue == null) { selectedValue = JOptionPane.showInputDialog(null, getMessage("setup.select_audio_output"), "output", JOptionPane.INFORMATION_MESSAGE, null, a, a[0]); System.out.println("|" + configDev + "|" + selectedValue + "|"); FrinikaConfig.setProperty(configKey + ".outputDevice",(String)selectedValue); FrinikaConfig.store(); } s.setOutDevice( (String)selectedValue); list = s.getInDeviceList(); list.add(0, "NONE"); a = new Object[list.size()]; a = list.toArray(a); configDev = FrinikaConfig.getProperty(configKey + ".inputDevice"); selectedValue = null; for (Object ae : a) { if (ae.equals(configDev)) { selectedValue = ae; break; } } if (selectedValue == null) { // Object selectedValue = JOptionPane.showInputDialog(null, getMessage("setup.select_audio_input"), "input", JOptionPane.INFORMATION_MESSAGE, null, a, a[0]); FrinikaConfig.setProperty(configKey + ".inputDevice",(String)selectedValue); FrinikaConfig.store(); } if (!((String) selectedValue).equals("NONE")) s.setInDevice((String) selectedValue); } static public String configureServerOutput() { if(usePhysicalAudioOutput==false) return null; List<String> list = realAudioServer.getAvailableOutputNames(); String outDev = null; String configKey = ((ExtendedAudioServer) realAudioServer).getConfigKey(); if (!list.isEmpty()) { Object a[] = new Object[list.size()]; a = list.toArray(a); String configDev = null; configDev = FrinikaConfig.getProperty(configKey + ".output"); System.out.println(configKey + "=" + configDev); if (list.size() > 1) { if (configDev != null) { for (String s : list) { if (s.equals(configDev)) { outDev = s; } } } if (outDev == null) { outDev = (String) JOptionPane.showInputDialog(null, getMessage("setup.select_audio_output"), "Output", JOptionPane.INFORMATION_MESSAGE, null, a, a[0]); } } else { outDev = list.get(0); } } FrinikaConfig.setProperty(configKey + ".output", outDev); return outDev; } /** * * sets a new mixer. * * @param mixer * new client for the server * @return true if success. (fail if it is stolen) */ static public boolean installClient(AudioClient mixer) { if (thief != null) { // errorMessage(" server hgas been stolen by "+ thief); return false; } mixerSwitch.installClient(mixer); return true; } /** * revert to previous mixer * */ static public void revertMixer() { if (thief != null) { // errorMessage(" server hgas been stolen by "+ thief); return; } mixerSwitch.revertClient(); } static public IOAudioProcess audioOutputDialog(JFrame frame, String prompt) throws Exception { List<String> list = audioServer.getAvailableOutputNames(); Object a[] = new Object[list.size()]; a = list.toArray(a); Object selectedValue = JOptionPane.showInputDialog(frame, getMessage("setup.select_audio_output"), prompt, JOptionPane.INFORMATION_MESSAGE, null, a, a[0]); if (selectedValue == null) return null; IOAudioProcess o = audioServer.openAudioOutput((String) selectedValue, "output"); if (defaultOut == null) defaultOut = o; return o; } static public IOAudioProcess getDefaultOutput(JFrame frame) { if (defaultOut == null) { try { audioOutputDialog(frame, getMessage("setup.select_default_output")); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } return defaultOut; } static public IOAudioProcess audioInputDialog(JFrame frame, String prompt) throws Exception { List<String> list = audioServer.getAvailableInputNames(); Object a[] = new Object[list.size()]; a = list.toArray(a); Object selectedValue = JOptionPane.showInputDialog(frame, getMessage("setup.select_audio_input"), prompt, JOptionPane.INFORMATION_MESSAGE, null, a, a[0]); return audioServer.openAudioInput((String) selectedValue, "input"); } /** * * Allow user to play with server parameters. * * Actually redundant because JavaSound is getFramePos is accurate * */ public static void latencyMeasureSet() { JFrame frame = new JFrame(); frame.setTitle("Latency Measure/Set"); long tootTotalLatency = ((AbstractAudioServer) realAudioServer) .getTotalLatencyFrames(); System.out.println(" Server latency = " + tootTotalLatency); JPanel panel = new JPanel(); LatencyTesterPanel lpanel = new LatencyTesterPanel(frame); panel.add(lpanel); frame.setContentPane(panel); frame.pack(); frame.setVisible(true); } /** * This method allows you to use a measured round trip latency to estimate * the extra latency not estimated directly by the audioServer. * * This will estimate the hardware latency so the getTotalLatency will * return a correct value even if you change audioServer bufer sizes. * * @param frames * measured total round trip latency. */ public static void setTotalLatency(int frames) { throw new UnsupportedOperationException(); // System.out.println(" Measured latency = " + frames); // ((AbstractAudioServer) realAudioServer).setHardwareLatencyFrames(0); // long tootLatency = ((AbstractAudioServer) realAudioServer) // .getTotalLatencyFrames(); // long hardwareLatency = frames - tootLatency; // ((AbstractAudioServer) realAudioServer) // .setHardwareLatencyFrames((int) (hardwareLatency / 2)); // long tootTotalLatency = ((AbstractAudioServer) realAudioServer) // .getTotalLatencyFrames(); // System.out.println(" Hardware latency , Server latency = " // + hardwareLatency + " " + tootTotalLatency); } // /** // * // * // * // * @return total round trip latency estimate // */ // static int getTotalLatency() { // return audioServer.getTotalLatencyFrames(); // } /** * * Allow user to play with server parameters. * */ public static void configure() { if (configureFrame != null) { configureFrame.setVisible(true); return; } final JComponent ui = AudioServerUIServices.createServerUI(realAudioServer, serverConfig); if (ui == null) return; // no server ui configureFrame = new JFrame(); configureFrame.setAlwaysOnTop(true); configureFrame.setContentPane(ui); configureFrame.pack(); configureFrame.setVisible(true); } /** * * @return sample rate of the audioServer */ public static double getSampleRate() { return audioServer.getSampleRate(); } private static void errorMessage(String msg) { try { throw new Exception(msg); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } JOptionPane.showMessageDialog(null, msg, "Frinika Message", JOptionPane.ERROR_MESSAGE); } /** * This allows you to have sole ownership of the server. Whilst stolen it is * not possible for any one else to use the server. The thief must return it * as sone as possible. This is intended to thwart anything that tries to be * helpful by automatically switching clients * * @param thief * @return the audioserver instance or null if the method fails. */ static AudioServer stealAudioServer(Object thief, AudioClient client) { if (FrinikaAudioSystem.thief != null) { errorMessage(" server has already been stolen by " + thief); return null; } installClient(client); FrinikaAudioSystem.thief = thief; return audioServer; } /** * return the audio server for general use. check no one cheats by * pretending they stole it !!! * * @param thief */ static void returnAudioServer(Object thief) { if (thief != FrinikaAudioSystem.thief) { errorMessage(" attempt to prented to be audio server thief by " + thief + " real thief was " + FrinikaAudioSystem.thief); return; } FrinikaAudioSystem.thief = null; mixerSwitch.revertClient(); } public static int getAudioBufferSize() { return bufferSize; } public static void loadServerConfigPost() { serverConfig.applyProperties(FrinikaConfig.getProperties()); } public static void saveServerConfig() { serverConfig.mergeInto(FrinikaConfig.getProperties()); FrinikaConfig.store(); } /** * Called on exit. Put code in here to clse on any devices . */ public static void close() { // TODO Auto-generated method stub } }