-- Program name : harmonie ; -- Description : Ordonnanceur. -- Copyright © 2009 Manuel De Girardi
-- Ce programme est un logiciel libre ; vous pouvez le redistribuer et/ou le modifier au titre
-- des clauses de la Licence Publique Générale GNU, telle que publiée par la Free
--Software Foundation ; soit la version 2 de la Licence, ou (à votre discrétion) une version
--ultérieure quelconque. Ce programme est distribué dans l'espoir qu'il sera utile, mais
--SANS AUCUNE GARANTIE ; sans même une garantie implicite de COMMERCIABILITE
--ou DE CONFORMITE A UNE UTILISATION PARTICULIERE. Voir la Licence Publique
--Générale GNU pour plus de détails. Vous devriez avoir reçu un exemplaire de la Licence
--Publique Générale GNU avec ce programme ; si ce n'est pas le cas, écrivez à la Free
--Software Foundation Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-------------------------------------------------------------------------------
-- Author : Manuel De Girardi
-- Date : 2009/08/13
-- Version : 0.0.0-1a
-- Description : Compositeur-intepreteur de musique automatique.
-------------------------------------------------------------------------------
with Ada.Characters.Handling;
with Ada.Characters.Latin_1;
with Ada.Strings, Ada.Strings.Fixed;
with Pragmarc.Ansi_Tty_Control;
with Calendar, Time_To_Date;
with Ada.Integer_Text_Io;
use Ada.Characters;
use Ada.Characters.Handling;
use Ada.Strings, Ada.Strings.Fixed;
use Pragmarc.Ansi_Tty_Control;
use Calendar, Time_To_Date;
use Ada.Integer_Text_Io;
with Text_Io;
use Text_Io;
with Midi, Midi.File;
use Midi, Midi.File;
with PragmARC.REM_NN_Wrapper;
use PragmARC.REM_NN_Wrapper;
with PragmARC.ANSI_TTY_Control;
with PragmARC.Math.Functions;
with Ada.Characters;
use Ada.Characters;
with Ada.Characters.Latin_1;
with Message;
with Text_Io;
with Portmidi, Porttime;
with System;
use Message;
use Text_Io;
use Portmidi, Porttime;
use System;
with Musician, Partition, Midi_Implementation;
use Musician, Partition, Midi_Implementation;
package body Midi_Engine is
Harmonie_Hex : constant String := "harmonie.hex";
Harmonie_Flt : constant String := "harmonie.flt";
package Real_Io is new Text_Io.Float_Io(Real);
package Real_Math is new PragmARC.Math.Functions (Supplied_Real => Real);
procedure Get_Input (Pattern : in Positive;
Input : out Node_Set;
Desired : out Node_Set) is
File : File_Type;
begin
Open(File, In_File, Harmonie_Flt);
for I in 1..Pattern loop
for I in Input'range loop
Real_Io.Get(File, Input(I));
end loop;
for I in Input'range loop
Real_Io.Get(File, Desired(I));
end loop;
Put('.');
end loop;
Close(File);
end Get_Input;
procedure Initialize is
Char : Character;
Data_Length : constant := 1;
File : File_Type;
begin
loop
Put("Are you sure you want do this? (y/n) : " );
Get_Immediate(Char);
case Char is
when 'Y' | 'y' =>
Create(file, Out_File, Harmonie_flt);
for J in 1..24 loop
Real_Io.put(File, 0.0);
end loop;
for J in 1..24 loop
Real_Io.put(File, 0.0);
end loop;
Close(File);
declare
package Harmonie_REM_NN_init is new REM_NN(Num_Input_Nodes => 24,
Num_Hidden_Nodes => 24,
Num_Output_Nodes => 24,
New_Random_Weights => True,
Input_To_Output_Connections => False,
Num_Patterns => Data_length,
Get_Input => Get_Input);
Converged : float := 0.002;
Response : Harmonie_REM_NN_Init.Output_Set := (others => 0.0);
Desired_Output : array (1..Data_Length) of Harmonie_REM_NN_init.Output_Set;
RMS_Error : Real := 0.0;
Error : Real := 0.0;
begin
Desired_Output :=(others => (others => 0.0));
Open(File, In_File, Harmonie_flt);
for I in 1..Data_Length loop
for J in Harmonie_REM_NN_init.Output_Set'range loop
Real_Io.Get(File, Desired_output(I)(J));
end loop;
for J in Harmonie_REM_NN_init.Output_Set'range loop
Real_Io.Get(File, Desired_output(I)(J));
end loop;
end loop;
Close(File);
All_Patterns :
for Pattern in 1..Data_length loop
Harmonie_REM_NN_init.Respond (Pattern, Response);
Harmonie_REM_NN_init.Train;
for I in Response'Range loop
Error := Error + (Desired_Output(Pattern)(i) - Response(i) );
end loop;
RMS_Error := RMS_Error + ((Error/Real(Response'Length)) ** 2);
Error := 0.0;
end loop All_Patterns;
Put(" => RMS_Error: " );
RMS_Error := Real_Math.Sqrt(RMS_Error / Real (Data_length)) ;
Real_Io.Put(RMS_Error);
Harmonie_REM_NN_init.Save_Weights;
end;
exit;
when 'N' | 'n' =>
exit;
when others =>
null;
end case;
end loop;
null;
end Initialize;
procedure Print(S : String) is
File : File_Type;
begin
begin
Open(File, append_File, Harmonie_Hex);
exception
when Name_Error =>
Create(File, out_File,Harmonie_Hex);
end;
Put_Line(File, S);
Close(File);
end Print;
Log_Out : Log_Out_Function := Print'Access;
procedure Dump is new Dump_To_Screen(Log_Out);
Dump_Ptr : Event_Handler := Dump'Access;
procedure Load_File is
Line : String(1..256);
Last : Natural;
File : Midifile;
Hex, Flt : File_Type;
Current_Chunk : aliased Chunk;
Data_Length : Natural := 1;
begin
Put("Enter filname of MIDI file : " );
Get_Line(Line, Last);
if Last /= 0 then
File := Read(Line(1..Last));
for I in 1..GetTrackCount(File) loop
Current_Chunk := GetChunk(File, I);
parse(Current_Chunk, Dump'access);
New_Line;
end loop;
end if;
Put_Line("Please, wait ! " );
Open(Hex, In_File, Harmonie_hex);
while not End_Of_File(Hex) loop
Get_Line(Hex, Line, Last);
case Line(2) is
when 'i' => -- Midi Event
null;
when 'e' => -- Metat Event
null;
when '-' => -- Tic
null;
when others => -- Value
null;
end case;
Put('.');
end loop;
Close(Hex);
New_Line;
Put("Load artificial neural network" );
New_Line;
declare
package Harmonie_REM_NN_Trai is new REM_NN(Num_Input_Nodes => 24,
Num_Hidden_Nodes => 24,
Num_Output_Nodes => 24,
New_Random_Weights => false,
Input_To_Output_Connections => False,
Num_Patterns => Data_length,
Get_Input => Get_Input);
Converged : float := 0.002;
Response : Harmonie_REM_NN_Trai.Output_Set := (others => 0.0);
Desired_Output : array (1..Data_Length) of Harmonie_REM_NN_trai.Output_Set;
RMS_Error : Real := 0.0;
Error : Real := 0.0;
Max_Epochs : Positive := 10000;
Epoch : Natural := 0;
begin
loop
Put (PragmARC.ANSI_TTY_Control.Position (29, 1) );
Put ("Epoch" );
put (Integer'Image (Epoch) );
All_Patterns :
for Pattern in 1..Data_length loop
Harmonie_REM_NN_Trai.Respond (Pattern, Response);
Harmonie_REM_NN_Trai.Train;
for I in Response'Range loop
Error := Error + (Desired_Output(Pattern)(i) - Response(i) );
end loop;
RMS_Error := RMS_Error + ((Error/Real(Response'Length)) ** 2);
Error := 0.0;
end loop All_Patterns;
Put(" => RMS_Error: " );
RMS_Error := Real_Math.Sqrt(RMS_Error / Real (Data_length)) ;
Real_Io.Put(RMS_Error);
if (RMS_Error < Real(Converged)) or
(Epoch > Max_Epochs) then
exit;
end if;
RMS_Error := 0.0;
Epoch := Epoch + 1;
end loop;
Harmonie_REM_NN_trai.Save_Weights;
end;
exception
when Name_Error =>
null;
end Load_File;
procedure Run is
type T_Register is array (1..24) of Real;
Register : T_Register := (others => 0.0);
Prob_File : File_Type;
prob_Filename : constant String := "harmonie.flt";
procedure Get_Input (Pattern : in Positive;
Input : out Node_Set;
Desired : out Node_Set) is
File : File_Type;
begin
Open(File, In_File, prob_Filename);
for I in 1..Pattern loop
for I in Input'range loop
Real_Io.Get(File, Input(I));
end loop;
end loop;
Close(File);
Desired := (others => Real(0.0));
end Get_Input;
Clear_Screen : constant String := Latin_1.Esc & "[;H" & Latin_1.Esc & "[2J";
task Harmonie is
entry Start;
entry Receive(Char : in Character);
entry Halt;
end Harmonie;
task Compositor is
entry Initialize;
entry Start;
entry Stop;
entry Halt;
end Compositor;
type T_Orchester is array (Positive range <> ) of T_Musician;
Orchester : T_Orchester(1..1);
type T_Oeuvre is array(1..Orchester'Length) of T_Partition;
task body Compositor is
End_Of_Task : Boolean := False;
Entract : Boolean := False;
Pt_Error : PtError;
Pm_Error : PmError;
Resolution : Integer := 1;
PtCallback,
UserData : System.Address := Null_Address;
Oeuvre : T_Oeuvre;
begin
Pt_Error := Pt_Start(Resolution, PtCallback, UserData);
Pm_Error := Pm_Initialize;
Oeuvre(1) := (5, new T_Staff(1..256));
Oeuvre(1).Staff(1) := Bank_Select_MSB(15, 0);
Oeuvre(1).Staff(2) := Bank_Select_LSB(15, 0);
Oeuvre(1).Staff(3) := Program_change(15,0);
Oeuvre(1).Staff(4) := Note_On(0, 60, 100);
Oeuvre(1).Staff(5) := Note_off(0, 60);
accept Initialize do
Orchester(1).Initialize(6);
for I in Orchester'Range loop
Orchester(I).adjust(Oeuvre(I));
end loop;
end Initialize;
while not End_Of_Task loop
accept Start do
for I in Orchester'Range loop
Orchester(I).start;
end loop;
Entract := False;
end Start;
loop
select
accept Stop do
for I in Orchester'Range loop
Oeuvre(I) := (3, new T_Staff(1..256));
Oeuvre(I).Staff(1) := NRPM_MSB(15, 0);
Oeuvre(I).Staff(2) := NRPM_LSB(15, 2);
Oeuvre(I).Staff(3) := Data_Entry_MSB(15,0);
end loop;
for I in Orchester'Range loop
Orchester(I).receive(Oeuvre(I));
end loop;
delay 1.0;
for I in Orchester'Range loop
Orchester(I).Stop;
end loop;
Entract := True;
end Stop;
or
accept Halt do
if Entract then
for I in Orchester'Range loop
Put("Compositor::START" );
Orchester(I).start;
end loop;
end if;
for I in Orchester'Range loop
Put("Compositor::HALT" );
Orchester(I).Halt;
end loop;
Entract := True;
End_Of_Task := True;
end Halt;
or
delay 0.0;
declare
package Harmonie_REM_NN_Expl is new REM_NN
(Num_Input_Nodes => T_Register'length,
Num_Hidden_Nodes => T_Register'length,
Num_Output_Nodes => T_Register'length,
Input_To_Output_Connections => False,
New_Random_Weights => False,
Num_Patterns => 1,
Get_Input => Get_Input);
Response : Harmonie_REM_NN_Expl.Output_Set;
begin
Harmonie_REM_NN_Expl.Respond (1, Response);
for I in response'range loop
Register(i) := Real(Response(i));
end loop;
for I in Register'Range loop
if Register(I) >= -0.250 and Register(I) < 0.250 then
Register(I) := 0.0;
elsif Register(I) >= 0.750 and Register(I) < 1.250 then
Register(I) := 1.0;
end if;
end loop;
end;
end select;
exit when Entract;
end loop;
end loop;
Pm_Error := Pm_Terminate;
end Compositor;
task body Harmonie is
Date : String(1..80) := (others => ' ');
Banner : String(1..80) := (others => ' ');
Help : String(1..80) := (others => ' ');
End_Of_Task : Boolean := False;
Line : String(1..256) := (others => ' ');
Last : Natural := 0;
type T_Command is (Null_Item, Start, Stop);
function Value(Command : String) return T_Command is
Result : T_Command := Null_Item;
begin
if Ada.Strings.Fixed.Index(Command , " ", Ada.Strings.Fixed.Index_Non_Blank(command)) /= 0 then
return T_Command'Value(Command(Ada.Strings.Fixed.Index_Non_Blank(Command)..Ada.Strings.Fixed.Index(Command, " ", Ada.Strings.Fixed.Index_Non_Blank(Command)) - 1));
else
return T_Command'Value(Command(Index_Non_Blank(Command)..Command'last));
end if;
exception
when Constraint_Error =>
Return Result;
end Value;
command : T_Command;
Run : Boolean := True;
begin
accept Start do
Compositor.Initialize;
Compositor.Start;
end Start;
Move("Commands :STOP, START; 'Esc' to quit.", Help, Error, Center);
Move("""Welcome to Harmonie.""", Banner, Error, Center);
while not End_Of_Task loop
Move((80 * ' '), Date, Error, Center);
Move(Full_Datify_String(Clock), Date, Error, Center);
select
accept Receive(Char : in Character) do
Put(Clear_Screen);
Put_line(Banner);
Put_Line(Date);
Put_line(help);
if Is_Control(Char) then
case Char is
when Character'Val(10) =>
Command := Value(Line(1..Last));
Last := 0;
case Command is
when Stop =>
if Run then
Compositor.Stop;
Run := False;
end if;
when Start =>
if not Run then
Compositor.Start;
Run := True;
end if;
when others =>
null;
end case;
when Character'Val(127) =>
if Last /= 0 then
Line(Last) := ' ';
Last := Last - 1;
end if;
when others =>
null;
end case;
elsif Is_Special(Char) then
null;
else
if Last < Line'Length then
Line(Last+1) := Char;
Last := Last + 1;
end if;
end if;
if Last /= 0 then
Put(Position((30 - Last/80),1));
put(Line(1..Last));
end if;
end Receive;
or
accept Halt do
if not Run then
Compositor.Start;
end if;
Compositor.Halt;
End_Of_Task := True;
end Halt;
or
delay 0.05;
Put(Clear_Screen);
Put_line(Banner);
Put_Line(Date);
Put_line(help);
if Last /= 0 then
Put(Position((30 - Last/80),1));
put(Line(1..Last));
end if;
end select;
end loop;
end Harmonie;
Char : Character;
begin
Harmonie.Start;
loop
Get_Immediate(Char);
case Char is
when Character'Val(27) =>
Harmonie.Halt;
exit;
when others =>
Harmonie.Receive(Char);
end case;
end loop;
New_Line;
end Run;
end Midi_Engine;