Forum > Audio and Video

MIDI file generator - work in progress

(1/6) > >>

finlazarus:
Hi everybody!

I'm planning to program a MIDI  software which generates MIDI data to arrays and then saves the data to the MIDI file - kinda MIDI kaleidoscope software. I've all ready programmed my own MIDI sequencer solution with hard work (with Delphi MIDI libraries), but I think that it would just  easier to generate the data with the code and then save it to the MIDI file. After that any media player software could play the result.
The MIDI file structure seemed to be a strange beast at first to me, but after little studying I understand something about it.

Here is a sample code, which generates a short MIDI file. It works for me in Windows.
I'm curious to know if it works in other systems.
I understood that the MIDI file format is standard in every system.

Is anyone there familiar with the MIDI file structure or interested in MIDI file generation solutions?

Sample code:

Just place two buttons to the form. Button1 caption could be 'Generate the MIDI file' and Button2 caption could be 'Listen the MIDI file'.

Does this code work for you Linux and Mac OS X folks, I'm just curious to know.

==============

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---program onetrackmidigeneratorProject; {$mode objfpc}{$H+} uses  {$IFDEF UNIX}{$IFDEF UseCThreads}  cthreads,  {$ENDIF}{$ENDIF}  Interfaces, // this includes the LCL widgetset  Forms, onetrackmidigenerator  { you can add units after this }; {$R *.res} begin  RequireDerivedFormResource:=True;  Application.Initialize;  Application.CreateForm(TForm1, Form1);  Application.Run;end.==============unit onetrackmidigenerator; {$mode objfpc}{$H+} interface uses  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,   LCLIntf; type   { TForm1 }   TForm1 = class(TForm)    Button1: TButton;    Button2: TButton;    procedure Button1Click(Sender: TObject);    procedure Button2Click(Sender: TObject);  private   public   end; var  Form1: TForm1; implementation {$R *.lfm} { TForm1 } procedure TForm1.Button1Click(Sender: TObject);var   strMididata: string;var   n : integer;var   myFile : TextFile;var midinotes : array of integer;var notelengths : array of integer; begin  //Remember to close media player every time before generating the new file  //otherwise error rises, because the midi file is locked by the system  //Of course  you can build an error handling for this ;)  AssignFile(myFile, 'mymidifile.mid');  ReWrite(myFile);   // HEADER CHUNK  -----------------------------  strMididata := 'MThd'; //CHUNK id, it's always the same [4 bytes]  Write(myFile, strMididata);   //chunk-size: [4 bytes], value always 6  strMididata := chr($00) + chr($00) + chr($00) + chr($06);  Write(myFile, strMididata);   //midi-file format type = 0/1/2: [2 bytes]  //type 1 midi-file can use several tracks, type 0 only one track  strMididata := chr($00) + chr($00); // midi file type 0  in use  Write(myFile, strMididata);   //number of tracks = 1: [2 bytes]  strMididata := chr($00) + chr($01); // one track  Write(myFile, strMididata);   // Time division $60 = 96 ticks per 1/4 note: [2 bytes]  strMididata := chr($00) + chr(96);  Write(myFile, strMididata);    //TRACK HEADER   ----------------------------------  strMididata := 'MTrk'; // Track header, always the same    // [4 bytes]  Write(myFile, strMididata);   // Track CHUNK-SIZE:   [4bytes]  strMididata := chr($00) + chr($00) + chr($00) + chr(46); // chunk size 46 bytes  Write(myFile, strMididata);   //Midi-play commands ---------------->   //Keep track of byte count and add the final value to the Chunk size value   //TEMPO [7 bytes]  //Delta time $00 one byte | Tempo-id (3 bytes): $FF $51 $03 | Tempo value 3 bytes (value in // microseconds per MIDI quarter-note)  //120 bpm would be 500000 microseconds per quarter note :Hex 07 A1 20  //100 bpm would be 600000 microseconds per quarter note :Hex 09 27 C0  //60 bpm would be 1000000 microseconds per quarter note: Hex 0F 42 40  //50 bpm would be 1200000 microseconds per quarter note:  Hex 12 4F 80  //30 bpm would be 2 000 000 microseconds per quarter note: Hex 1E 84 80  strMididata := chr($00) + chr($FF) + chr($51) + chr($03)+ chr($1E) + chr($84) + chr($80); // 30 bpm  Write(myFile, strMididata);   // Instrument change for the Channel 0: [3 bytes]  // delta time | instr change C+ch0 | GM instrument 10  strMididata := chr($00) + chr($C0)+ chr(10);  Write(myFile, strMididata);   //------------  SetLength (midinotes, 4);     midinotes[0] := 60;     midinotes[1] := 62;     midinotes[2] := 64;     midinotes[3] := 66;   SetLength (notelengths, 4);     notelengths[0] := 60;     notelengths[1] := 30;     notelengths[2] := 30;     notelengths[3] := 108;   //Let's have some MIDI notes  for n := 0 to 3 do  // [4x8 = 32 bytes]  begin      //Note on: [4 bytes]     // delta | note on $9 +ch0 | midinote (0-127) | vol (0-127)     strMididata := chr($0) + chr($90) + chr(midinotes[n]) + chr(100);     Write(myFile, strMididata);      //Note off: [4 bytes]     // delta=note length | note off $8 + ch0 | midinote | vol     strMididata := chr(notelengths[n]) + chr($80) + chr(midinotes[n]) + chr(100);     Write(myFile, strMididata);   end;  //----------------   //Track end: delta(1 byte) + track end id (3 bytes): [4 bytes]  strMididata := chr($00) + chr($FF) + chr($2F)+ chr($00) ; //Always the same  Write(myFile, strMididata);   //=================   CloseFile(myFile); end; procedure TForm1.Button2Click(Sender: TObject);begin  OpenDocument('mymidifile.mid');  //Works only in Windows...?end; end. 

Ñuño_Martínez:
I recommend you to separate logic from GUI.  I mean: create an UNIT with classes, records and procedures to create, manage, reproduce, load and save MIDI files. Then USE that unit in the FORM unit.  That will make it more easy to understand and to improve.

I have some interest about MIDI as I want to add MIDI support to my game engine (and Allegro too). Unfortunately I haven't time now for such project. :(

finlazarus:
Thanks for the hint! It really would be more practical  to keep data processing in a separate unit.
I guess there is a two ways in programming the midi: Driving midi hardware directly from the code or/and saving data to the midi file.
At the moment I'm interest in generating random kind of midi music based on the different kind of scales (whole tone, dodecaphonic twelvetone etc.) and save the result in the midi file. Some experimental stuff I mean.

Ñuño_Martínez:
Interesting. "Random Music".

finlazarus:
It's already done here  http://tones.wolfram.com/generate/    :D but I would like to recreate this kind of stuff with Lazarus code.

Navigation

[0] Message Index

[#] Next page

Go to full version