/* * MeterManager.java * Eisenkraut * * Copyright (c) 2004-2016 Hanns Holger Rutz. All rights reserved. * * This software is published under the GNU General Public License v3+ * * * For further information, please contact Hanns Holger Rutz at * contact@sciss.de */ package de.sciss.eisenkraut.net; import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.swing.Timer; import de.sciss.app.BasicEvent; import de.sciss.app.EventManager; import de.sciss.gui.PeakMeterView; import de.sciss.jcollider.Bus; import de.sciss.jcollider.Constants; import de.sciss.jcollider.Group; import de.sciss.jcollider.OSCResponderNode; import de.sciss.jcollider.NodeWatcher; import de.sciss.jcollider.Server; import de.sciss.jcollider.ServerEvent; import de.sciss.jcollider.ServerListener; import de.sciss.jcollider.Synth; import de.sciss.net.OSCBundle; import de.sciss.net.OSCMessage; public class MeterManager implements OSCResponderNode.Action, Constants, ServerListener, ActionListener, EventManager.Processor { private List<Client> collAllClients = new ArrayList<Client>(); private List<Client> collActiveClients = new ArrayList<Client>(); private Map<PeakMeterView, Client> mapClients = new HashMap<PeakMeterView, Client>(); // key = MeterListener, value = MeterClient private Server server = null; private Bus bus = null; private Group grp = null; private int numCtrlChans = 0; private OSCBundle meterBangBndl = null; private OSCResponderNode resp = null; private final Timer meterTimer; private final SuperColliderClient sc; private int numTask = 0; private final EventManager elm; public MeterManager( SuperColliderClient sc ) { this.sc = sc; meterTimer = new javax.swing.Timer( 33, this ); elm = new EventManager( this ); sc.addServerListener( this ); } public void dispose() { sc.removeServerListener( this ); disposeServer(); } private void meterBang() { if( (server != null) && (meterBangBndl != null) ) { try { server.sendBundle( meterBangBndl ); } catch( IOException e1 ) { /* don't print coz the frequency might be high */ } } } // ------------- ActionListener interface ------------- public void actionPerformed( ActionEvent e ) { meterBang(); } // ------------- ServerListener interface ------------- public void serverAction( ServerEvent e ) { switch( e.getID() ) { case ServerEvent.STOPPED: setServer( null ); break; case ServerEvent.RUNNING: setServer( e.getServer() ); break; default: break; } } // ----------------- OSCResponderNode.Action interface ----------------- public void respond( OSCResponderNode r, OSCMessage msg, long time ) { elm.dispatchEvent( new Event( r, msg, time )); } // ----------------- EventManager.Processor interface ----------------- public void processEvent( BasicEvent be ) { final Event e = (Event) be; final OSCMessage msg = e.msg; final int busIndex = ((Number) msg.getArg( 0 )).intValue(); final int numVals = ((Number) msg.getArg( 1 )).intValue(); // getWhen doesn't provide a valid value i think // final long time = e.getWhen(); final long time = System.currentTimeMillis(); Client mc; if( (bus == null) || (busIndex != bus.getIndex()) ) return; for (Client collActiveClient : collActiveClients) { mc = collActiveClient; if (!mc.task) continue; for (int j = 0, k = 0, m = numVals + 2, off = mc.cOffset + 2; (k < mc.cNum) && (off < m); j++) { if (mc.channels[j] >= 0) { mc.peakRMSPairs[k++] = ((Number) msg.getArg(off++)).floatValue(); mc.peakRMSPairs[k++] = ((Number) msg.getArg(off++)).floatValue(); } else { mc.peakRMSPairs[k++] = 0f; mc.peakRMSPairs[k++] = 0f; off += 2; } } mc.view.meterUpdate(mc.peakRMSPairs, 0, time); } } private static void printError(String name, Throwable t) { System.err.println(name + " : " + t.getClass().getName() + " : " + t.getLocalizedMessage()); } private void disposeServer() { Client mc; meterTimer.stop(); if( resp != null ) resp.remove(); if( bus != null ) { bus.free(); bus = null; } grp = null; if( server == null ) return; for( int i = 0; i < collAllClients.size(); ) { mc = collAllClients.get( i ); if( mc.server == server ) { collAllClients.remove( i ); } else { i++; } } collActiveClients.clear(); server = null; meterBangBndl = null; } private void setServer( Server s ) { Client mc; disposeServer(); if( s == null ) return; server = s; for (Client collAllClient : collAllClients) { mc = collAllClient; if (mc.server == server) { collActiveClients.add(mc); } } resp = new OSCResponderNode(server, "/c_setn", this); resortClients(); } public void setListenerTask(PeakMeterView view, boolean task, OSCBundle bndl) { if (!EventQueue.isDispatchThread()) throw new IllegalMonitorStateException(); final Client mc = mapClients.get(view); if (mc == null) return; if (mc.task != task) { mc.task = task; if (mc.server == server) { final boolean weCreated = bndl == null; if( weCreated ) bndl = new OSCBundle(); for( int j = 0; j < mc.synths.length; j++ ) { if( mc.synths[ j ] != null ) { bndl.addPacket( mc.synths[ j ].runMsg( task )); } } if( weCreated && (bndl.getPacketCount() > 0) ) { try { server.sendBundle( bndl ); } catch( IOException e1 ) { printError( "setListenerTask", e1 ); } } if( task ) { if( ++numTask == 1 ) { meterTimer.restart(); } } else { if( --numTask == 0 ) { meterTimer.stop(); } } } } } public void addListener( PeakMeterView ml, Bus b, Group g, boolean task ) { final int[] channels = new int[ b.getNumChannels() ]; for( int i = 0, j = b.getIndex(); i < channels.length; ) { channels[ i++ ] = j++; } addListener( ml, b.getServer(), channels, g, task ); } public void addListener(PeakMeterView ml, Server s, int[] channels, Group g, boolean task) { final Client mc; if( !EventQueue.isDispatchThread() ) throw new IllegalMonitorStateException(); mc = new Client(ml, s, channels, g, task); if (mapClients.put(ml, mc) != null) throw new IllegalArgumentException("MeterListener was already registered"); collAllClients.add(mc); if (mc.server == server) { collActiveClients.add(mc); resortClients(); } } public void removeListener(PeakMeterView view) { final Client mc; final OSCBundle bndl; if (!EventQueue.isDispatchThread()) throw new IllegalMonitorStateException(); mc = mapClients.remove(view); if (mc == null) return; collAllClients.remove(mc); if (collActiveClients.remove(mc)) { bndl = new OSCBundle(); for (int i = 0; i < mc.synths.length; i++) { if (mc.synths[i] != null) { bndl.addPacket(mc.synths[i].freeMsg()); mc.synths[i] = null; } } if (bndl.getPacketCount() > 0) { try { if (server.isRunning()) server.sendBundle(bndl); resortClients(); } catch (IOException e1) { printError("removeMeterListener", e1); } } } } private void resortClients() { final NodeWatcher nw; int off = 0; OSCBundle bndl; Group g; boolean haveGrpTrig = false; Client mc; int srcChan; meterTimer.stop(); if( resp != null ) resp.remove(); bndl = new OSCBundle(); meterBangBndl = null; for (Client collActiveClient : collActiveClients) { mc = collActiveClient; for (int j = 0; j < mc.synths.length; j++) { if (mc.synths[j] != null) { bndl.addPacket(mc.synths[j].freeMsg()); mc.synths[j] = null; } } mc.setOffset(off); off += mc.cNum; } if( bus != null ) { bus.free(); bus = null; } numCtrlChans = off; if( bndl.getPacketCount() > 0 ) { try { if( server.isRunning() ) server.sendBundle( bndl ); } catch( IOException e1 ) { numCtrlChans = 0; printError( "resortClients", e1 ); } } numTask = 0; try { if( (server != null) && (numCtrlChans > 0) ) { nw = NodeWatcher.newFrom( server ); bndl = new OSCBundle(); bus = Bus.control( server, numCtrlChans ); if( bus != null ) { meterBangBndl = new OSCBundle(); meterBangBndl.addPacket(new OSCMessage("/c_getn", new Object[]{ bus.getIndex(), bus.getNumChannels()})); for (Client collActiveClient : collActiveClients) { mc = collActiveClient; if (mc.task) numTask++; if (mc.g != null) { g = mc.g; meterBangBndl.addPacket(new OSCMessage("/n_set", new Object[]{ g.getNodeID(), "t_trig", 1})); } else { if (grp == null) { grp = Group.basicNew(server); grp.setName("MeterManager"); bndl.addPacket(grp.newMsg(server.getDefaultGroup(), kAddToTail)); nw.register(grp); } g = grp; if (!haveGrpTrig) { meterBangBndl.addPacket(new OSCMessage("/n_set", new Object[]{ g.getNodeID(), "t_trig", 1})); haveGrpTrig = true; } } for (int j = 0, m = bus.getIndex() + mc.cOffset; j < mc.synths.length; j++, m += 2) { srcChan = mc.channels[j]; if (srcChan >= 0) { mc.synths[j] = Synth.basicNew("eisk-meter", server); bndl.addPacket(mc.synths[j].newMsg(g, new String[]{ "i_aInBs", "i_kOtBs"}, new float[]{ srcChan, m}, kAddToTail)); if (!mc.task) { bndl.addPacket(mc.synths[j].runMsg(false)); } nw.register(mc.synths[j]); } } } if( bndl.getPacketCount() > 0 ) { try { server.sendBundle( bndl ); if( resp != null ) resp.add(); if( numTask > 0 ) meterTimer.restart(); } catch( IOException e1 ) { printError( "resortClients", e1 ); } } } else { System.err.println( "MeterManager : ran out of control busses!" ); } } } catch( IOException e1 ) { e1.printStackTrace(); } } // ------------- internal classes ------------- @SuppressWarnings("serial") private static class Event extends BasicEvent { protected OSCMessage msg; protected Event(Object src, OSCMessage msg, long time) { super(src, 0, time); this.msg = msg; } public boolean incorporate(BasicEvent oldEvent) { if ((oldEvent instanceof Event) && (oldEvent.getSource() == getSource())) { final OSCMessage omsg = ((Event) oldEvent).msg; if (omsg.getName().equals(msg.getName()) && (omsg.getArgCount() == msg.getArgCount()) && omsg.getArg(0).equals(msg.getArg(0)) && // busIndex omsg.getArg(1).equals(msg.getArg(1))) { // numVals final Object[] fuseArgs = new Object[msg.getArgCount()]; fuseArgs[0] = msg.getArg(0); fuseArgs[1] = msg.getArg(1); for (int i = 2; i < fuseArgs.length; i++) { fuseArgs[i] = Math.max( ((Number) msg.getArg(i)).floatValue(), ((Number) omsg.getArg(i)).floatValue()); } msg = new OSCMessage(msg.getName(), fuseArgs); return true; } } return false; } } private static class Client { protected final float[] peakRMSPairs; protected final int cNum; protected int cOffset; protected final PeakMeterView view; protected final int[] channels; protected final Group g; protected final Synth[] synths; protected final Server server; protected boolean task; protected Client(PeakMeterView view, Server server, int[] channels, Group g, boolean task) { this.view = view; this.server = server; this.channels = channels; this.g = g; this.task = task; cNum = channels.length << 1; peakRMSPairs = new float[ cNum ]; synths = new Synth[ channels.length ]; } protected void setOffset(int cOffset) { this.cOffset = cOffset; } public String toString() { final StringBuilder sb = new StringBuilder(); sb.append("[ "); for (int i = 0; i < channels.length; i++) { if (i > 0) sb.append(", "); sb.append(channels[i]); } sb.append(" ]"); return ("MeterClient( " + view + ", " + server + ", " + sb.toString() + ", " + g + ", " + task + " )"); } } }