From 6a5d3343f494ea3f5be4ec6057a99e47ddbcb2b6 Mon Sep 17 00:00:00 2001 From: Frank Buss Date: Thu, 14 Sep 2017 18:29:58 +0200 Subject: [PATCH 1/3] first version of a MIDI parser with example additionally: changing Sine_Generator to phase accumulator and minor formatting changes --- examples/asl_examples.gpr | 2 +- examples/midi_synthesizer.adb | 113 ++++++++++++++++++++++++++++++++++ examples/midi_synthesizer.ads | 51 +++++++++++++++ examples/midi_test.adb | 59 ++++++++++++++++++ src/blit.adb | 3 +- src/midi.adb | 80 ++++++++++++++++++++++++ src/midi.ads | 43 +++++++++++++ src/waves.adb | 66 ++++++++++++-------- src/waves.ads | 9 ++- src/write_to_stdout.adb | 21 +------ src/write_to_stdout_once.adb | 20 ++++++ src/write_to_stdout_once.ads | 3 + 12 files changed, 420 insertions(+), 50 deletions(-) create mode 100644 examples/midi_synthesizer.adb create mode 100644 examples/midi_synthesizer.ads create mode 100644 examples/midi_test.adb create mode 100644 src/midi.adb create mode 100644 src/midi.ads create mode 100644 src/write_to_stdout_once.adb create mode 100644 src/write_to_stdout_once.ads diff --git a/examples/asl_examples.gpr b/examples/asl_examples.gpr index db8d5d6..80d7e2f 100644 --- a/examples/asl_examples.gpr +++ b/examples/asl_examples.gpr @@ -4,5 +4,5 @@ project ASL_Examples is for Object_Dir use "obj"; for Source_Dirs use ("."); for Main use ("simple_sine.adb", "simple_sine_2.adb", "audio.adb", - "audio_2.adb", "audio_3.adb"); + "audio_2.adb", "audio_3.adb", "midi_test"); end ASL_Examples; diff --git a/examples/midi_synthesizer.adb b/examples/midi_synthesizer.adb new file mode 100644 index 0000000..cc9a23e --- /dev/null +++ b/examples/midi_synthesizer.adb @@ -0,0 +1,113 @@ +with Ada.Text_IO; use Ada.Text_IO; + +package body MIDI_Synthesizer is + procedure Update_ADSR (Self : in out Synthesizer) is + Value : Float; + begin + for I in Self.ADSR_Config'Range loop + Value := + Float (Self.ADSR_Config (I).Value) * Self.ADSR_Config (I).Factor; + Self.Env0.Set_Value (I, Value); + --Put_Line (Standard_Error, "index: " & Integer'Image(I) & " = " & Float'Image(Value)); + end loop; + end Update_ADSR; + + function Create_Synthesizer return access Synthesizer is + Ret : constant access Synthesizer := new Synthesizer; + Base : Float := 8.1757989156; -- MIDI note C1 0 + Freq0 : access Fixed_Gen := Fixed; + Gen0 : access Sine_Generator := Create_Sine (Freq0); + Env0 : access ADSR := Create_ADSR (5, 50, 800, 0.5, null); + Mixer0 : access Mixer := Create_Mixer ((0 => (Gen0, 0.5)), Env => Env0); + begin + Ret.MIDI_Parser := Create_Parser (Ret); + for I in Ret.MIDI_Notes'Range loop + Ret.MIDI_Notes (I) := Base; + Base := Base * 1.059463094359; -- 2^(1/12) + end loop; + Ret.Freq0 := Freq0; + Ret.Env0 := Env0; + Ret.Mixer0 := Mixer0; + Ret.ADSR_Config := + ((16#66#, 1, 1, 100, 0.05), + (16#67#, 1, 1, 100, 0.05), + (16#68#, 10, 1, 100, 0.05), + (16#69#, 20, 1, 100, 0.05)); + Ret.Update_ADSR; + return Ret; + end Create_Synthesizer; + + procedure Parse_MIDI_Byte + (Self : in out Synthesizer; + Received : in Unsigned_8) + is + begin + Self.MIDI_Parser.Parse (Received); + end Parse_MIDI_Byte; + + procedure Note_On + (Self : in out Synthesizer; + Channel : in Unsigned_8; + Note : in Unsigned_8; + Velocity : in Unsigned_8) + is + begin + Self.Env0.Gate_On; + Self.Freq0.Set_Value (0, Self.MIDI_Notes (Integer (Note mod 128))); + end Note_On; + + procedure Note_Off + (Self : in out Synthesizer; + Channel : in Unsigned_8; + Note : in Unsigned_8; + Velocity : in Unsigned_8) + is + begin + Self.Env0.Gate_Off; + end Note_Off; + + -- Testing with an Arturia Beatstep Pro: + -- The 16 rotary encoders are configured for relative mode. + -- For increasing, the following sequence is sent: + -- B0 66 40 + -- B0 66 41 + -- For decreasing the following sequence: + -- B0 66 40 + -- B0 66 3F + -- where 66 specifies the encoder number, and goes up to 75 (all value hex). + procedure Control_Change + (Self : in out Synthesizer; + Channel : in Unsigned_8; + Controller_Number : in Unsigned_8; + Controller_Value : in Unsigned_8) + is + Encoder : Integer := 0; + Incr : Integer := 0; + begin + if Controller_Value = 16#41# then + Incr := 1; + elsif Controller_Value = 16#3f# then + Incr := -1; + end if; + if Incr /= 0 then + for I in Self.ADSR_Config'Range loop + if Self.ADSR_Config (I).Controller_Index = Controller_Number then + Self.ADSR_Config (I).Value := Self.ADSR_Config (I).Value + Incr; + if Self.ADSR_Config (I).Value < + Self.ADSR_Config (I).Min_Value + then + Self.ADSR_Config (I).Value := Self.ADSR_Config (I).Min_Value; + end if; + if Self.ADSR_Config (I).Value > + Self.ADSR_Config (I).Max_Value + then + Self.ADSR_Config (I).Value := Self.ADSR_Config (I).Max_Value; + end if; + Self.Update_ADSR; + exit; + end if; + end loop; + end if; + end Control_Change; + +end MIDI_Synthesizer; diff --git a/examples/midi_synthesizer.ads b/examples/midi_synthesizer.ads new file mode 100644 index 0000000..1879307 --- /dev/null +++ b/examples/midi_synthesizer.ads @@ -0,0 +1,51 @@ +with Interfaces; use Interfaces; +with Waves; use Waves; +with Effects; use Effects; +with MIDI; use MIDI; + +package MIDI_Synthesizer is + + type Freq_Table is array (0 .. 127) of Float; + + type ADSR_Config_Entry is record + Controller_Index : Unsigned_8; + Value : Integer; + Min_Value : Integer; + Max_Value : Integer; + Factor : Float; + end record; + + type ADSR_Config_Array is array (0 .. 3) of ADSR_Config_Entry; + + type Synthesizer is new I_Event_Listener with record + MIDI_Parser : access Parser'Class; + MIDI_Notes : Freq_Table; + Freq0 : access Fixed_Gen; + Env0 : access ADSR; + Mixer0 : access Mixer; + ADSR_Config : ADSR_Config_Array; + end record; + + function Create_Synthesizer return access Synthesizer; + + procedure Parse_MIDI_Byte + (Self : in out Synthesizer; + Received : in Unsigned_8); + + overriding procedure Note_On + (Self : in out Synthesizer; + Channel : in Unsigned_8; + Note : in Unsigned_8; + Velocity : in Unsigned_8); + overriding procedure Note_Off + (Self : in out Synthesizer; + Channel : in Unsigned_8; + Note : in Unsigned_8; + Velocity : in Unsigned_8); + overriding procedure Control_Change + (Self : in out Synthesizer; + Channel : in Unsigned_8; + Controller_Number : in Unsigned_8; + Controller_Value : in Unsigned_8); + +end MIDI_Synthesizer; diff --git a/examples/midi_test.adb b/examples/midi_test.adb new file mode 100644 index 0000000..df08c6c --- /dev/null +++ b/examples/midi_test.adb @@ -0,0 +1,59 @@ +-- This sample program receives MIDI events from a keyboard and from +-- rotary encoders and outputs audio, like a synthesizer. +-- Call it like this from Linux: +-- +-- amidi -p "hw:1,0,0" -r >(./obj/midi_test | aplay -f S16_LE -c1 -r44100 --buffer-size=4096) +-- +-- The BASH syntax ">(program)" creates a temporary FIFO file, because amidi +-- needs a file where it writes the received MIDI events. In case of problems, +-- you can also create a named FIFO with "mkfifo", then start amidi in the +-- background writing to this file, and then the midi_test program like this: +-- +-- cat midi | ./obj/midi_test | aplay -f S16_LE -c1 -r44100 --buffer-size=4096) +-- +-- where "midi" is the named FIFO file. If it keeps playing a tone when you stop +-- the program with ctrl-c, try this command: +-- +-- killall amidi aplay +-- +-- You can see the list of available MIDI devices with "amidi -l". +-- For testing it is useful to use the AMIDI "--dump" option. +-- For lower latency, you might need to change the Linux pipe size: +-- +-- sudo sysctl fs.pipe-max-size=4096 + +with GNAT.OS_Lib; +with Interfaces; use Interfaces; +with MIDI_Synthesizer; use MIDI_Synthesizer; +with Write_To_Stdout_Once; + +procedure MIDI_Test is + Data : Unsigned_8; + Ignore : Integer; + + Main_Synthesizer : access Synthesizer'Class := Create_Synthesizer; + + task Main_Task is + entry Data_Received (Data : in Unsigned_8); + end Main_Task; + + task body Main_Task is + begin + loop + select + accept Data_Received (Data : in Unsigned_8) do + Main_Synthesizer.Parse_MIDI_Byte (Data); + end Data_Received; + else + Write_To_Stdout_Once (Main_Synthesizer.Mixer0); + end select; + end loop; + end Main_Task; + +begin + loop + Ignore := + GNAT.OS_Lib.Read (GNAT.OS_Lib.Standin, Data'Address, Data'Size / 8); + Main_Task.Data_Received (Data); + end loop; +end MIDI_Test; diff --git a/src/blit.adb b/src/blit.adb index ac8f1ef..d448dc9 100644 --- a/src/blit.adb +++ b/src/blit.adb @@ -142,7 +142,8 @@ package body BLIT is Impulse_Time := Self.Next_Impulse_Time; Impulse_Phase := Self.Next_Impulse_Phase; - Delta_Time := Float (Self.P_Buffer (I)) / 2.0 + Self.Next_Impulse_Phase; + Delta_Time := Float (Self.P_Buffer (I)) / 2.0 + + Self.Next_Impulse_Phase; Self.Next_Impulse_Time := Self.Next_Impulse_Time + Natural (Float'Floor (Delta_Time)); diff --git a/src/midi.adb b/src/midi.adb new file mode 100644 index 0000000..7bd535c --- /dev/null +++ b/src/midi.adb @@ -0,0 +1,80 @@ +package body MIDI is + + procedure Test_Note_Command (Self : in out Parser; Received : Unsigned_8) is + begin + if ((Received and 16#80#) = 16#80#) or + ((Received and 16#f0#) = 16#90#) or + ((Received and 16#f0#) = 16#b0#) + then + Self.Wait_For_Event := False; + Self.Byte_Counter := 0; + Self.Last_Command := Received; + else + Self.Wait_For_Event := True; + end if; + end Test_Note_Command; + + function Create_Parser + (Event_Listener : access I_Event_Listener'Class) return access Parser + is + Ret : constant access Parser := + new Parser' + (Event_Listener => Event_Listener, + Wait_For_Event => True, + others => 0); + begin + return Ret; + end Create_Parser; + + procedure Parse (Self : in out Parser; Received : Unsigned_8) is + begin + -- ignore system real time messages + if Received >= 16#f8# then + return; + end if; + + -- parse note-on or note-off message + if Self.Wait_For_Event then + Test_Note_Command (Self, Received); + else + -- if a command byte is received, test for note command + if (Received and 16#80#) > 0 then + Test_Note_Command (Self, Received); + return; + end if; + + -- otherwise read the next 2 bytes + Self.Byte0 := Self.Byte1; + Self.Byte1 := Received; + Self.Byte_Counter := Self.Byte_Counter + 1; + if Self.Byte_Counter = 2 then + if (Self.Last_Command and 16#f0#) = 16#90# then + -- test for note-on message + if Self.Byte1 = 0 then + -- special case: note-on message with velocity 0 is used + -- as noteOff by some instruments + Self.Event_Listener.Note_Off + (Self.Last_Command and 16#f#, Self.Byte0, 0); + else + Self.Event_Listener.Note_On + (Self.Last_Command and 16#f#, Self.Byte0, Self.Byte1); + end if; + elsif (Self.Last_Command and 16#f0#) = 16#80# then + -- test for note-off message + Self.Event_Listener.Note_Off + (Self.Last_Command and 16#f#, Self.Byte0, Self.Byte1); + elsif (Self.Last_Command and 16#f0#) = 16#b0# then + -- test for control change message + Self.Event_Listener.Control_Change + (Self.Last_Command and 16#f#, Self.Byte0, Self.Byte1); + end if; + -- otherwise it is an unknown message and we ignore it + + -- reset byte counter only, not the waitForNoteCommand flag, + -- to handle "running status" + Self.Byte_Counter := 0; + end if; + end if; + end Parse; + +end MIDI; diff --git a/src/midi.ads b/src/midi.ads new file mode 100644 index 0000000..5a26769 --- /dev/null +++ b/src/midi.ads @@ -0,0 +1,43 @@ +with Interfaces; use Interfaces; + +package MIDI is + + type Parser is tagged private; + + type I_Event_Listener is interface; + procedure Note_On + (Self : in out I_Event_Listener; + Channel : Unsigned_8; + Note : Unsigned_8; + Velocity : Unsigned_8) is abstract; + procedure Note_Off + (Self : in out I_Event_Listener; + Channel : Unsigned_8; + Note : Unsigned_8; + Velocity : Unsigned_8) is abstract; + procedure Control_Change + (Self : in out I_Event_Listener; + Channel : Unsigned_8; + Controller_Number : Unsigned_8; + Controller_Value : Unsigned_8) is abstract; + + -- creates a new parser with a listener for receiving MIDI events + function Create_Parser + (Event_Listener : access I_Event_Listener'Class) return access Parser; + + -- parses one incoming byte and calls the listener for received MIDI events + procedure Parse (Self : in out Parser; Received : Unsigned_8); + +private + procedure Test_Note_Command (Self : in out Parser; Received : Unsigned_8); + + type Parser is tagged record + Event_Listener : access I_Event_Listener'Class; + Wait_For_Event : Boolean; + Last_Command : Unsigned_8; + Byte_Counter : Unsigned_8; + Byte0 : Unsigned_8; + Byte1 : Unsigned_8; + end record; + +end MIDI; diff --git a/src/waves.adb b/src/waves.adb index 05347c3..870ceba 100644 --- a/src/waves.adb +++ b/src/waves.adb @@ -97,12 +97,10 @@ package body Waves is Ret : constant access Sine_Generator := new Sine_Generator'(Frequency_Provider => Generator_Access (Freq_Provider), - Current_Sample => 0, - Current_P => 0.0, + Phase => 0.0, others => <>); begin Update_Period (Ret.all); - Ret.Current_P := 0.0; return Ret; end Create_Sine; @@ -112,19 +110,19 @@ package body Waves is overriding procedure Next_Samples (Self : in out Sine_Generator) is + Phase_Increment : Float; begin Update_Period (Self); for I in B_Range_T'Range loop - Self.Current_Sample := Self.Current_Sample + 1; - if Period (Self.Current_Sample) >= Self.Current_P then - Self.Current_P := Self.P_Buffer (I) * 2.0; - Self.Current_Sample := 0; + Phase_Increment := Float (Self.P_Buffer (I)); + if Phase_Increment /= 0.0 then + Phase_Increment := 2.0 * Pi / Phase_Increment; + end if; + Self.Phase := Self.Phase + Phase_Increment; + if Self.Phase >= 2.0 * Pi then + Self.Phase := Self.Phase - 2.0 * Pi; end if; - Self.Buffer (I) := - Sample - (Sin - (Float (Self.Current_Sample) - / Float (Self.Current_P) * Pi * 2.0)); + Self.Buffer (I) := Sample (Sin (Self.Phase)); end loop; end Next_Samples; @@ -135,7 +133,7 @@ package body Waves is function Create_Chain (Gen : access Generator'Class; Sig_Procs : Signal_Processors - := No_Signal_Processors) return access Chain + := No_Signal_Processors) return access Chain is Ret : constant access Chain := new Chain'(Gen => Generator_Access (Gen), others => <>); @@ -217,16 +215,15 @@ package body Waves is Ret : Sample; begin for I in B_Range_T'Range loop - case Self.Source.Buffer (I).Kind is - when On => - Self.Current_P := 0; - Self.State := Running; - when Off => - Self.State := Release; - Self.Cur_Sustain := Scale (Self.Memo_Sample); - Self.Current_P := 0; - when No_Signal => null; - end case; + if Self.Source /= null then + case Self.Source.Buffer (I).Kind is + when On => + Self.Gate_On; + when Off => + Self.Gate_Off; + when No_Signal => null; + end case; + end if; Self.Current_P := Self.Current_P + 1; @@ -244,7 +241,7 @@ package body Waves is / Float (Self.Decay)); Ret := Ret - * Sample (1.0 - Self.Sustain) + * Sample (1.0 - Self.Sustain) + Sample (Self.Sustain); else Ret := Sample (Self.Sustain); @@ -256,7 +253,7 @@ package body Waves is Exp8_Transfer (Sample (Self.Release - Self.Current_P) / Sample (Self.Release)) - * Sample (Self.Cur_Sustain); + * Sample (Self.Cur_Sustain); else Self.State := Off; Ret := 0.0; @@ -268,6 +265,21 @@ package body Waves is end loop; end Next_Samples; + procedure Gate_On (Self : in out ADSR) + is + begin + Self.Current_P := 0; + Self.State := Running; + end Gate_On; + + procedure Gate_Off (Self : in out ADSR) + is + begin + Self.State := Release; + Self.Cur_Sustain := Scale (Self.Memo_Sample); + Self.Current_P := 0; + end Gate_Off; + ---------------------- -- Next_Sample -- ---------------------- @@ -412,7 +424,7 @@ package body Waves is Base_Reset (Self); Reset_Not_Null (Self.Frequency_Provider); Self.P_Buffer := (others => 0.0); - Self.Current_Sample := 0; + Self.Phase := 0.0; end Reset; ----------- @@ -462,7 +474,7 @@ package body Waves is ----------- function Fixed - (Freq : Frequency; + (Freq : Frequency := 0.0; Modulator : Generator_Access := null; Name : String := ""; Min : Float := 0.0; diff --git a/src/waves.ads b/src/waves.ads index 95bf072..d6ddc98 100644 --- a/src/waves.ads +++ b/src/waves.ads @@ -52,8 +52,7 @@ package Waves is -------------------- type Sine_Generator is new Wave_Generator with record - Current_Sample : Sample_Period; - Current_P : Period; + Phase : Float; end record; function Create_Sine @@ -120,7 +119,7 @@ package Waves is type Fixed_Generator is access all Fixed_Gen; function Fixed - (Freq : Frequency; + (Freq : Frequency := 0.0; Modulator : Generator_Access := null; Name : String := ""; Min : Float := 0.0; @@ -282,4 +281,8 @@ package Waves is (case I is when 0 | 1 | 3 => Exp, when others => Linear); + procedure Gate_On (Self : in out ADSR); + + procedure Gate_Off (Self : in out ADSR); + end Waves; diff --git a/src/write_to_stdout.adb b/src/write_to_stdout.adb index 3931492..14fcf5e 100644 --- a/src/write_to_stdout.adb +++ b/src/write_to_stdout.adb @@ -1,28 +1,13 @@ with Utils; use Utils; -with GNAT.OS_Lib; -with Interfaces; use Interfaces; +with Write_To_Stdout_Once; -procedure Write_To_Stdout (G : access Generator'Class) -is - function Sample_To_Int16 is new Sample_To_Int (Short_Integer); - function Sample_To_UInt16 is new Sample_To_UInt (Unsigned_16); - Int_Smp : Short_Integer := 0; - Ignore : Integer; +procedure Write_To_Stdout (G : access Generator'Class) is begin loop - Next_Steps; - G.Next_Samples; - - for I in B_Range_T'Range loop - Int_Smp := Sample_To_Int16 (G.Buffer (I)); - Ignore := GNAT.OS_Lib.Write - (GNAT.OS_Lib.Standout, Int_Smp'Address, Int_Smp'Size / 8); - end loop; - + Write_To_Stdout_Once (G); exit when Sample_Nb > 10_000_000; Sample_Nb := Sample_Nb + Generator_Buffer_Length; end loop; - end Write_To_Stdout; diff --git a/src/write_to_stdout_once.adb b/src/write_to_stdout_once.adb new file mode 100644 index 0000000..dc1f723 --- /dev/null +++ b/src/write_to_stdout_once.adb @@ -0,0 +1,20 @@ +with Utils; use Utils; +with GNAT.OS_Lib; + +procedure Write_To_Stdout_Once (G : access Generator'Class) +is + function Sample_To_Int16 is new Sample_To_Int (Short_Integer); + Int_Smp : Short_Integer := 0; + Ignore : Integer; +begin + + Next_Steps; + G.Next_Samples; + + for I in B_Range_T'Range loop + Int_Smp := Sample_To_Int16 (G.Buffer (I)); + Ignore := GNAT.OS_Lib.Write + (GNAT.OS_Lib.Standout, Int_Smp'Address, Int_Smp'Size / 8); + end loop; + +end Write_To_Stdout_Once; diff --git a/src/write_to_stdout_once.ads b/src/write_to_stdout_once.ads new file mode 100644 index 0000000..a25b5e9 --- /dev/null +++ b/src/write_to_stdout_once.ads @@ -0,0 +1,3 @@ +with Sound_Gen_Interfaces; use Sound_Gen_Interfaces; + +procedure Write_To_Stdout_Once (G : access Generator'Class); From 715fd10bdcaf69f25569191d07d83730a93bd65f Mon Sep 17 00:00:00 2001 From: Frank Buss Date: Thu, 14 Sep 2017 19:38:30 +0200 Subject: [PATCH 2/3] fixed No_Task_Hierarchy error for native compilation --- ada_synth_lib.gpr | 9 +++++++-- gnat-ravenscar.adc | 2 ++ gnat.adc | 1 - 3 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 gnat-ravenscar.adc diff --git a/ada_synth_lib.gpr b/ada_synth_lib.gpr index 4452c0c..9823b00 100644 --- a/ada_synth_lib.gpr +++ b/ada_synth_lib.gpr @@ -21,9 +21,14 @@ project Ada_Synth_Lib is end case; package Builder is - for Global_Configuration_Pragmas use "./gnat.adc"; + case Build_Type is + when "native" => + for Global_Configuration_Pragmas use "./gnat.adc"; + when "bareboard" => + for Global_Configuration_Pragmas use "./gnat-ravenscar.adc"; + end case; end Builder; - + package Compiler is case Build is when "Debug" => diff --git a/gnat-ravenscar.adc b/gnat-ravenscar.adc new file mode 100644 index 0000000..6bbf56b --- /dev/null +++ b/gnat-ravenscar.adc @@ -0,0 +1,2 @@ +pragma Profile (Ravenscar); +pragma Warnings (Off, "pragma Restrictions (No_Exception_Propagation) in effect"); diff --git a/gnat.adc b/gnat.adc index 6bbf56b..1141275 100644 --- a/gnat.adc +++ b/gnat.adc @@ -1,2 +1 @@ -pragma Profile (Ravenscar); pragma Warnings (Off, "pragma Restrictions (No_Exception_Propagation) in effect"); From 9f4acb4406d4b1727eb37c0d42e3b88160c3aa1e Mon Sep 17 00:00:00 2001 From: Frank Buss Date: Fri, 15 Sep 2017 13:35:27 +0200 Subject: [PATCH 3/3] removing Float_Text_IO, because it is not supported for ravenscar and fixing a warning --- src/utils.adb | 15 +++++++-------- src/utils.ads | 2 +- src/waves.adb | 4 ++-- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/utils.adb b/src/utils.adb index a168155..db4acbf 100644 --- a/src/utils.adb +++ b/src/utils.adb @@ -1,5 +1,4 @@ with Utils.Tan_Table; use Utils.Tan_Table; -with Ada.Float_Text_IO; package body Utils is @@ -284,12 +283,12 @@ package body Utils is -- Img -- --------- - function Img (F : Float) return String - is - S : String (1 .. 10); - begin - Ada.Float_Text_IO.Put (S, F, 2, 0); - return S; - end Img; +-- function Img (F : Float) return String +-- is +-- S : String (1 .. 10); +-- begin +-- Ada.Float_IO.Put (S, F, 2, 0); +-- return S; +-- end Img; end Utils; diff --git a/src/utils.ads b/src/utils.ads index 1971d78..e5ed5ab 100644 --- a/src/utils.ads +++ b/src/utils.ads @@ -84,6 +84,6 @@ package Utils is is (Exp ((F - 1.0) * 8.0)); - function Img (F : Float) return String; + -- function Img (F : Float) return String; end Utils; diff --git a/src/waves.adb b/src/waves.adb index 870ceba..33b6946 100644 --- a/src/waves.adb +++ b/src/waves.adb @@ -229,7 +229,7 @@ package body Waves is case Self.State is when Running => - if Self.Current_P in 0 .. Self.Attack then + if Self.Current_P <= Self.Attack then Ret := Exp8_Transfer (Sample (Self.Current_P) / Sample (Self.Attack)); elsif @@ -248,7 +248,7 @@ package body Waves is end if; Self.Memo_Sample := Ret; when Release => - if Self.Current_P in 0 .. Self.Release then + if Self.Current_P <= Self.Release then Ret := Exp8_Transfer (Sample (Self.Release - Self.Current_P)