package com.moseph.mra.agent;
import static jade.lang.acl.ACLMessage.REQUEST;
import static java.util.logging.Level.FINE;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static java.lang.Math.*;
import static com.moseph.mra.MRAUtilities.*;
import static com.moseph.mra.MRAConstants.*;
import static com.moseph.mra.midi.MidiUtilities.*;
import jade.core.AID;
import jade.core.Runtime;
import jade.core.behaviours.Behaviour;
import jade.core.behaviours.ThreadedBehaviourFactory;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;
import jade.wrapper.*;
import java.awt.event.ActionEvent;
import java.io.*;
import java.util.*;
import javax.sound.midi.*;
import javax.sound.midi.MidiDevice.Info;
import javax.swing.*;
import javax.swing.Action;
import com.moseph.mra.*;
import com.moseph.mra.agent.gui.MIDISelector;
import com.moseph.mra.experiments.MIDIOutput;
import com.moseph.mra.midi.*;
import com.moseph.mra.parser.MRAParser;
public class Conductor extends MusicallyAwareAgent
{
double currentPosition = 0.0;
static double fragmentSize = 1.0;
SongInfo info;
Map<String, PartIndex> partsByName = new HashMap<String, PartIndex>();
ConductorDataBuffer data = new ConductorDataBuffer();
BasicSequencerThread sequencer;
boolean playLeadIn = true;
boolean allowInput = true;
String outputDir;
String outputFilename;
boolean playMetronome = false;
static boolean noDeviceRequest = false;
static Score currentScore = new Score();
static boolean storeOutput = false;
boolean mergeAllInputs = false;
boolean openAllDevices = true;
boolean echoInputNotesToConsole = false;
static boolean echoRecording = false;
double runTime = Double.POSITIVE_INFINITY;
//List<String> recordAgentStringDefs = new Vector<String>();
List<RecordAgentDetails> passedRecordAgents = new Vector<RecordAgentDetails>();
MidiPatchbay patchbay = MidiPatchbay.getInstance();
boolean waitForEnterAtStart = true;
public static void main( String[] args )
{
MidiDevice.Info[] devices = MidiSystem.getMidiDeviceInfo();
for ( MidiDevice.Info d : devices )
{
System.out.println( d.getName() + ": " + d.getDescription() );
}
}
protected void initialise()
{
super.initialise();
setQueueSize( 400 );
System.out.println( "Conductor Starting with count in: " + countIn );
System.out.println( "Setting up" );
try
{
doFileParse();
info = new SongInfo( piece.getTimeSignature(), (int) piece.getBPM(), 384 );
System.out.println( info + "" );
}
catch( Exception e )
{
System.out.println( "Could not make piece: " + e );
info = new SongInfo( new TimeSignature( 4, 4 ), 120, 384 );
}
info.printSongInfo();
addBehaviour( musicCollection() );
getMidiDevice();
}
protected void startSequencer( OutputWrapper output, Sequencer sequencerDevice, List<RecordAgentDetails> agents )
{
sequencer = new SequencerThread( this, data, info, countIn, fragmentSize, output, sequencerDevice );
//Do any extra setup in here...
if( !playLeadIn ) sequencer.setPlayLeadIn( false );
if( playMetronome ) sequencer.setPlayMetronome( true );
sequencer.setRecordAgents( agents );
sequencer.initialise();
Thread t = new Thread( new ThreadGroup( "Sequencer" ), sequencer, "Sequencer", (int)200e6 );
t.setPriority( Thread.MAX_PRIORITY );
if( waitForEnterAtStart )
{
System.out.println( "+++++++++++++++++\n\n+++++++++++++++++\nPress enter to start\n++++++++++++++");
waitForEnter();
}
System.out.println( "%%%%%%%%%%%%%%%%%%%%%%\nStarting sequencer! (" + runTime + " beats)");
t.start();
}
void getMidiDevice()
{
final JFrame f = new JFrame( "Choose MIDI Output Device");
if( noDeviceRequest )
{
System.out.println( "No device to be selected!");
createRecordingAgents( passedRecordAgents );
startSequencer( patchbay.getDefaultOutput(), (Sequencer)getSequencerDevices().get(0), passedRecordAgents );
return;
}
f.setSize( 300, 500 );
final MIDISelector s = new MIDISelector()
{
public void devicesSelected( OutputWrapper output, Sequencer sequencer )
{
List<RecordAgentDetails> rad = getRecordAgentDetails();
rad.addAll( passedRecordAgents );
createRecordingAgents( rad );
startSequencer( output, sequencer, rad );
f.setVisible( false );
}
};
f.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
f.add( s );
f.setVisible( true );
}
void createRecordingAgents( List<RecordAgentDetails> agents )
{
ContainerController controller = getContainerController();
if( openAllDevices ) openAllDevices();
for( RecordAgentDetails agent : agents )
{
try
{
System.out.println( "++++> Creating Agent! " + agent.name );
agent.setAgentID( new AID( agent.getName(), false ) );
FragmentAdaptor adaptor = new FragmentAdaptor( null, info, new Fragment() );
adaptor.setChannel( agent.getChannel() );
if( echoRecording )
{
System.out.println( "Conductor enabling echo recording");
adaptor.setEchoOutput( true );
}
try
{
List<InputWrapper> recDevs = new Vector<InputWrapper>();
recDevs.add( agent.getInput() );
//if( mergeAllInputs ) recDevs = patchbay.getInputs();
for( InputWrapper dev : recDevs )
{
Receiver recv = adaptor;
if( echoInputNotesToConsole ) recv = new TeeReceiver( adaptor, "[" + agent.getName() + "]" );
//System.out.println( "Setting up with device: " + dev.getDeviceInfo().getName() );
dev.setReceiver( recv );
}
} catch( Exception e )
{
System.err.println( "Could not create input link: " + e );
e.printStackTrace();
}
agent.setAdaptor( adaptor );
AgentController a = controller.createNewAgent( agent.name, "com.moseph.mra.agent.RecordAgent", new Object[] { agent } );
a.start();
} catch (StaleProxyException e)
{
System.err.println( "Could not create record agent: " + e );
e.printStackTrace();
}
}
}
/***************************************************************************
* * Behaviours * *
**************************************************************************/
// One behaviour collects them
Behaviour musicCollection()
{
return new BlockingReceiver( this, getFragmentTemplate() )
{
void receivedMessage( ACLMessage m )
{
//System.out.println( "Got input: " + getFragmentFromMessage( m ) + " from " + m.getSender().getLocalName() );
data.addFragment( getFragmentFromMessage( m ), m.getSender() );
}
};
}
/***************************************************************************
* * Scheduling using the sequencer * *
**************************************************************************/
/***************************************************************************
* * Sending, receiving and scheduling msuic * *
**************************************************************************/
void askForNextFragment()
{
// if( currentPosition >= countIn ) askForFragment( currentPosition,
// fragmentSize );
askForFragment( currentPosition, fragmentSize );
}
void askForFragment( double start, double length )
{
log.log( FINE, "Conductor (" + getName() + ") asking for music from "
+ start + " to " + ( start + length ) );
ACLMessage req = getMessageToAllMusicians();
for ( DFAgentDescription ad : getMusicians() )
if( !ad.getName().getLocalName().equals( "space" )
&& !ad.getName().getLocalName().equals( "osc" ) )
data.addRecipient( ad.getName() );
req.setPerformative( REQUEST );
req.setContent( MUSIC_REQUEST );
req.addUserDefinedParameter( START_PARAM, start + "" );
req.addUserDefinedParameter( LENGTH_PARAM, length + "" );
// System.out.println( "Asking for music from " + start + " to " +
// (start+length) + " from " + asked.size() );
send( req );
}
void disseminateCurrentFragment()
{
/*
System.out.printf( "++ Free mem: %04f, Total: %04f, Max: %04f\n",
(double)Runtime.getRuntime().freeMemory() / 1000000,
(double)Runtime.getRuntime().totalMemory() / 1000000,
(double)Runtime.getRuntime().maxMemory() / 1000000 );
*/
log.log( FINE, "Conductor (" + getName()
+ ") disseminating music from " + currentPosition + " to "
+ ( currentPosition + fragmentSize ) );
ACLMessage inf = getMessageToAllMusicians();
data.getCurrentScore().forceLength( fragmentSize );
putScoreIntoMessage( data.getCurrentScore(), currentPosition, fragmentSize, inf );
send( inf );
if( storeOutput ) currentScore.add( data.getCurrentScore(), currentPosition );
if( currentPosition >= runTime )
{
System.out.println( "Writing to: " + outputDir + outputFilename );
sequencer.writeToFile( outputDir + outputFilename );
System.out.println( "Stopping sequencer");
sequencer.endSequence();
Runtime.instance().setCloseVM( false );
/*
try
{
System.out.println( "Killing container");
getContainerController().kill();
} catch (StaleProxyException e)
{
System.out.println( "Couldn't kill container: " + e );
e.printStackTrace();
}
*/
//System.out.println( "Shutting down runtime");
//Runtime.instance().shutDown();
System.out.println( "THANATOS!");
System.exit( 0 );
//die();
}
}
void incrementTime( double amount )
{
currentPosition += amount;
}
public List<MessageTemplate> getKnownMessages()
{
List<MessageTemplate> sup = super.getKnownMessages();
sup.add( getFragmentTemplate() );
return sup;
}
public String[] getServiceNames()
{
return new String[] { CONDUCTOR_SERVICE };
}
public void otherQuitting( AID aid )
{
}
void applyArgument( AgentArgument a )
{
System.out.println( "Applying Arguments: " + a );
if( a.arg.equalsIgnoreCase( "PlayLeadIn")) playLeadIn = Boolean.parseBoolean( a.val );
else if( a.arg.equalsIgnoreCase( "AllowInput")) allowInput = Boolean.parseBoolean( a.val );
else if( a.arg.equalsIgnoreCase( "PlayMetronome")) playMetronome = Boolean.parseBoolean( a.val );
else if( a.arg.equalsIgnoreCase( "OutputDir" )) outputDir = a.val;
else if( a.arg.equalsIgnoreCase( "OutputFilename" )) outputFilename = a.val;
else if( a.arg.equalsIgnoreCase( "NoDeviceRequest" )) noDeviceRequest = Boolean.parseBoolean( a.val );
else if( a.arg.equalsIgnoreCase( "EchoRecording" )) echoRecording = Boolean.parseBoolean( a.val );
else if( a.arg.equalsIgnoreCase( "RunTime" )) runTime = Double.parseDouble( a.val );
else if( a.arg.equalsIgnoreCase( "RecordAgent" ))
{
System.out.println( "__+++++++++__________ Making new record agent: " + a.val );
passedRecordAgents.add( new RecordAgentDetails( a.val ) );
}
//else if( a.arg.equalsIgnoreCase( "FragmentSize" )) fragmentSize = Double.parseDouble( a.val );
else super.applyArgument( a );
}
public String getFileToWrite( double position )
{
if( experiment != null && subExperiment != null && outputDir != null )
return outputDir + experiment + "-" + subExperiment + ".mid";
System.out.println( ">" +outputDir + ", " + experiment + "-" + subExperiment );
return "output/output" + ((int)currentPosition) + ".mid";
}
public static boolean isStoreOutput()
{
return storeOutput;
}
public static void setStoreOutput( boolean storeOutput )
{
Conductor.storeOutput = storeOutput;
}
public static Score getCurrentScore()
{
return currentScore;
}
public static double getFragmentSize()
{
return fragmentSize;
}
public static void setNoDeviceRequest( boolean noDeviceRequest )
{
Conductor.noDeviceRequest = noDeviceRequest;
}
public boolean isEchoInputNotesToConsole()
{
return echoInputNotesToConsole;
}
public void setEchoInputNotesToConsole( boolean echoInputNotesToConsole )
{
this.echoInputNotesToConsole = echoInputNotesToConsole;
}
public static void setFragmentSize( double fragmentSize )
{
Conductor.fragmentSize = fragmentSize;
}
public boolean isEchoRecording()
{
return echoRecording;
}
public void setEchoRecording( boolean echoRecording )
{
this.echoRecording = echoRecording;
}
public static void setScheduleRealtimeFragments( boolean sched )
{
BasicSequencerThread.setScheduleRealtimeFragments( sched );
}
public static void openAllDevices()
{
for( Info i : MidiSystem.getMidiDeviceInfo() )
{
try
{
MidiSystem.getMidiDevice( i ).open();
} catch (MidiUnavailableException e)
{
System.out.println( "Conductor could not open device " + i.getName() + ": " + e );
e.printStackTrace();
}
}
}
}