# $Id: Conductor.pm,v 1.2 2002/09/17 21:14:01 ology Exp $

# POE::Framework::MIDI::Conductor

package POE::Framework::MIDI::Conductor;
use strict;
my $VERSION = '0.1a';

sub new
{
        my($self,$class) = ({},shift);
        $self->{cfg} = shift or die "i need a config hashref to start the conductor..";
        bless($self,$class);
	$self->{bars} = {};
	return $self;
}

sub musician_names
{
	my $self = shift;
	my $musicians = $self->{cfg}->{musicians} or die "no musicians defined in config";
	my $to_return;
	for(@$musicians)
	{
		push @$to_return,$_->{name};
	}
	return $to_return;
}

# register a bar/phrase in the internal datastructure
sub add_bar
{
	my ($self,$hashref) = @_;
	$self->{bars}->{$hashref->{musician_name}}->{$hashref->{barnum}} = $hashref->{bar};
	
}

# convert event objects (note, rest, bar, etc) to perl code for
# midi simple.   this gets pretty ugly.

sub render
{
	my ($self,$filename) = @_ or die "render needs a filename to render to";
	my $musicians = $self->{cfg}->{musicians};
	my $perlcode = $self->perl_head;
	
	for my $musician (@$musicians)
	{
		my $events = $self->{bars}->{$musician->{name}};
		my $musician_perlcode = $self->musician_subroutine_header($musician);
		my $channel = $musician->{channel};
		my $patch = $musician->{patch};		
		
		for my $event ( sort 
			{ $events->{$a}->bar_number  <=> $events->{$b}->bar_number }
			keys %$events)
			{
				# $this_event is a bar, a phrase or maybe a noop
				my $this_event = $events->{$event};
				
				# we need the events out of the bar or phrase
				my $eventstack = $this_event->events;
				my $printwrap = 5; # how many events to print per line..so we can debug later
				my $wrap;
				for my $stackitem (@$eventstack)
				{
					++$wrap;
					$musician_perlcode .= $self->perl_from_event($stackitem,$musician->{name}) . '; ';					
					if($wrap >= $printwrap)
					{
						$musician_perlcode .="\n\t";
						$wrap = 0;	
					}
				}
			}
		
		$perlcode .= $musician_perlcode . "\n}";					
	}
	if($self->{cfg}->{debug})
	{
		open(DEBUG, ">debug.perl");
		print DEBUG $perlcode;
		close DEBUG;
	}	
	
	eval "$perlcode" or die "uh oh - bad stuff happened during rendering: " . join "\n", $@;
}

# header chunk of perl code for eventual eval
sub perl_head
{
	my $self = shift;
	my $code = 
	
	qq!
# magically generated by $0
	
use MIDI::Simple;
no strict 'subs';
new_score;
set_tempo 500000;

synch(! ;	
	# add the subroutine refs
	for(@{$self->{cfg}->{musicians}})
	{
		$code .= "\\&$_->{name},";		
	}	
	$code =~ s/\,$/)\;\n\n/;

	$code .= "\nwrite_score '$self->{cfg}->{filename}';\n\n";
	return $code;
}

# create perlcode from an event for eventual
# eval to MIDI::Simple code.  


sub perl_from_event
{
	my ($self,$event,$musician_name) = @_;
	
	# what type of event do we have? 
	
	# should MIDI::Bar recursively call this sub for notes and rests, or keep
	# the seperate code? 
	my $eventname = ref($event);
	if(ref($event) eq 'POE::Framework::MIDI::Bar') 
	
	{
		my $stack 	= $event->events; # get the bar's eventstack
		
		
		for my $stackitem (@$stack)
		{
			
			if($eventname eq 'POE::Framework::MIDI::Note')
			{
				my $duration 	= $stackitem->duration;
				my $note 	= $stackitem->note;
				# n wn, Cs4
				return "n $duration, $note";				
			}
			elsif($eventname eq 'POE::Framework::MIDI::Rest')
			{
				my $duration 	= $stackitem->duration;	
				return "r $duration";				
			}
		}		
	}
	elsif($eventname eq 'POE::Framework::MIDI::Note')
	{
		my $duration 	= $event->duration;
		my $note 	= $event->note;
				# n wn, Cs4
		return "n $duration, $note";	
	}
	elsif($eventname eq 'POE::Framework::MIDI::Rest')
	{
		my $duration 	= $event->duration;	
		return "r $duration";	
	}
	else
	{
		warn 'Unhandled event type ' . $event . ' ignored';	
	}
}

# the perl code to start a musician subroutine for MIDI::Simplels 'synch' function
sub musician_subroutine_header
{
	my ($self,$musician) = @_;
	return qq!
	
sub $musician->{name} {
	noop c$musician->{channel}, f, o4;
	patch_change $musician->{channel}, $musician->{patch};
	instrument_name $musician->{name};		

	!;	
}

# eventually this should evolve into a way for musicians or $self to query about
# what any musician played in any bar for follow the leader-type-stuff, and so on.

sub query
{
	my $self = shift;
	my $querystring = shift or die  __PACKAGE__ . "query needs a querystring";
	print "query $querystring\n"; 	
}


1;

=head1 NAME 

POE::Framework::MIDI::Conductor 0.1a

=head1 SYNOPSIS

A framework for algorithmic MIDI composition using POE

=head1 DESCRIPTION

The Conductor object creates Musician objects which generate MIDI event streams
and run them through rules and transformations.  The Conductor then renders the
events down to perl code that MIDI::Simple can handle, and then runs that code
to write a .mid file

=head1 USAGE

This package is used by the POEConductor module internally

=head1 BUGS

None that i know of - but the renderer could probably be better implemented.

=head1 AUTHOR

	Steve McNabb
	CPAN ID: JUSTSOMEGUY
	steve@justsomeguy.com
	http://justsomeguy.com/code/POE/POE-Framework-MIDI 

=head1 COPYRIGHT

Copyright (c) 2002 Steve McNabb. All rights reserved.
This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.

The full text of the license can be found in the
LICENSE file included with this module.

=head1 SEE ALSO

perl(1).

=cut
