Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

How do I import midi files into Mathematica?

124 views
Skip to first unread message

Roger Bagula

unread,
Mar 23, 2012, 2:34:57 AM3/23/12
to
I asked this in a Mathematica help group
and came up with an xml conversion of midi
that Mathematica will read, but stripping down the xml tree
is hard and I missed the pauses on the first successful track.
Do anyone else know of a better method to
get a midi track into a times series in Mathematica?

markholtuk

unread,
Mar 24, 2012, 3:03:41 AM3/24/12
to
This is something that I've been working on recently, so the code is
pretty nascent and has only been used on simple one track midi files
consisting of four bars of notes. It might be a useful spring for you
though.

midiIn=Import["file.mid","Byte"];

midiIn={77,84,104,100,0,0,0,6,0,0,0,1,0,96,77,84,114,107,0,0,0,255,0,255,3,7,66,76,95,52,95,67,0,0,255,88,4,4,2,36,8,0,255,88,4,4,2,36,8,0,144,60,95,36,128,60,32,13,144,36,95,11,128,36,32,13,144,48,95,10,128,48,32,13,144,48,127,13,128,48,32,11,144,60,95,13,128,60,32,13,144,48,95,10,128,48,32,12,144,72,95,25,144,48,95,9,128,72,32,27,128,48,32,11,144,48,127,13,128,48,32,11,144,48,95,12,128,48,32,12,144,60,95,12,128,60,32,13,144,48,127,10,128,48,32,13,144,48,127,11,128,48,32,13,144,48,95,13,128,48,32,11,144,60,95,37,128,60,32,12,144,36,95,11,128,36,32,13,144,48,95,12,128,48,32,11,144,48,127,13,128,48,32,11,144,60,95,12,128,60,32,13,144,48,95,11,128,48,32,11,144,72,95,26,144,48,95,9,128,72,32,26,128,48,32,12,144,48,127,13,128,48,32,10,144,48,95,13,128,48,32,12,144,60,95,12,128,60,32,13,144,48,127,10,128,48,32,14,144,48,127,10,128,48,32,13,144,48,95,13,128,48,32,0,255,47,0};
(*This is the imported data from a MIDI file that I have. Execute this
rather than the line above so that you can see how it should work. You
can also export this to see how it should sound. It's a modern
electronic bassline!*)

ch=1; (*assuming a range of 1-16*)

noteonPos=Position[midiIn,143+ch];

noteoffPos=Position[midiIn,127+ch];

timeEvents=Flatten[Union[noteonPos,noteoffPos]-1]; (*These are
timing events since the last timing event.*)

realTimeEvents=Accumulate[midiIn[[timeEvents]]]; (*Time events are
accumulated to give actual timing events.*)

midi=ReplacePart[midiIn,Table[timeEvents[[i]]->realTimeEvents[[i]],
{i,Length[timeEvents]}]]; (*The original timing events are replaced
by the real timing events*)

extractEvents[data_List,{x_}]:=Extract[data,{{x-1},{x},{x+1},{x
+2}}]; (*This function is for extracting timing, note event, note
number, and velocity for each note on/off evnt*)

noteOn=Flatten[{#,extractEvents[midi,#]}]&/@noteonPos; (*This
extracts each note on event and prepends with the events position in
the imported list*)

noteOff=Flatten[{#,extractEvents[midi,#]}]&/@noteoffPos; (*This
extracts each note off event and prepends with the events position in
the imported list*)

notes=Table[{noteOn[[i]],First[Select[noteOff,#[[1]]>noteOn[[i,
1]]&&#[[4]]==noteOn[[i,4]]&]]},{i,Length[noteOn]}]; (*Pair the
correct note off messages with each note on message*)

Graphics[{Hue[0.8#[[1,5]]/127],Rectangle[#[[1,{2,4}]]+{0,-0.5},#[[2,
{2,4}]]+{0,0.5}]}&/@notes,Frame->True,AspectRatio->0.5] (*Show the
notes on a grid*)

I should point out that the MIDI file that I have used here only
contains note on and off events and consists of only one MIDI channel.
There are no controller events present. If you want to work with MIDI
files that contain more than one channel and events other than note on
or off then you will need to significantly change some of this code.

Hopefully, this is a start to go on to bigger, better things! I will
try to incorporate controller events and multi-channel data myself at
some point.

Good Luck!

Mark

Roger Bagula

unread,
Mar 25, 2012, 1:18:19 AM3/25/12
to
On Mar 24, 12:03 am, markholtuk <markhol...@googlemail.com> wrote:
> On Mar 23, 6:34 am, Roger Bagula <roger.bag...@gmail.com> wrote:

>
> Good Luck!
>
> Mark

Mark Holtuk
Thanks a lot for this:
it works better than the xml tree method.
With your notes array from my midi file, I was able to extract
something that sounded kind of like my tune, but only when taking out
6 repeats, but I put in a four track/ channel midi
to start ( no controller events).
At least I'm learning.
You have put in a lot of good work on this!
Roger Bagula
Although I can't get Musica to export in version 8 because it
needs:Utilities`BinaryFiles`
it still imports a version of a midi file. His method may help you
with yours.
>From Musica.m by the great Greek fellow:
Musica had:
ImportMIDI::usage = "ImportMIDI[Filename,options] returns an eventlist
\
containing all the events of the format 0 MIDI file named Filename.
Posible \
options are NoteConversion, ToCustomEvents and FinalFunction."
ImportMIDI::format = "File in unsupported format."
ImportMIDI::filetype = "Not a MIDI file."
ImportMIDI::filenotfound = "File not found."
Options[ImportMIDI] = {NoteConversion -> True, ToCustomEvents -> {},
FinalFunction -> Identity}
ImportMIDI[filename_, options___] :=
Module[{l, e, b, status, t, len, type, data, format, hd, nc, tce,
ff}, ({nc, tce,
ff} = {If[NoteConversion, NoteOn2Note, Identity],
ToCustomEvents,
FinalFunction} /. {options} /. Options[ImportMIDI]);
l = OpenRead[filename, DOSTextFormat -> False]; hd = {};
If[l=!=$Failed,
Do[AppendTo[hd, Read[l, Byte]], {4}];
If[hd == {77, 84, 104, 100}, Skip[l, Byte, 5];
Print["Format: ", format = Read[l, Byte]];
Print["Tracks: ", 256Read[l, Byte] + Read[l, Byte]];
Print["PPQ: ", 256Read[l, Byte] + Read[l, Byte]]; Skip[l, Byte,
8];
If[format == 0, e = {};
While[(b = Read[l, Byte]) =!= EndOfFile, t = {}; len = {}=
;
While[b > 128, AppendTo[t, b]; (b = Read[l, Byte])];
AppendTo[t, b]; (status = Read[l, Byte]);
AppendTo[e,

Which[status < 128, {FromVariableLength[t],
Head[e[[-1, 2]]] @@
Join[{status}, (ReadList[l, Byte,
Length[e[[-1, 2]]] -
2] /. (ReadList[l, Byte, 0] :> {})),
{e[[-1,
2, -1]]}]},
143 < status < 160,
{FromVariableLength[t],
NoteOn[Read[l, Byte], Read[l, Byte], status - 143]},
175 < status < 192,
{FromVariableLength[t],
Controller[Read[l, Byte], Read[l, Byte], status -
175]},

223 < status < 240, {FromVariableLength[t],
PitchBend[Read[l, Byte], Read[l, Byte], status -
223]},
207 < status < 224, {FromVariableLength[t],
ChannelAftertouch[Read[l, Byte], status - 207]},
191 < status < 208, {FromVariableLength[t],
ProgramChange[Read[l, Byte], status - 191]},

127 < status < 144, {FromVariableLength[t],
NoteOff[Read[l, Byte], Read[l, Byte], status - 127]},
159 < status < 176, {FromVariableLength[t],
KeyAftertouch[Read[l, Byte], Read[l, Byte], status -
159]},
status == 255, (type = Read[l, Byte]); b = Read[l,
Byte];
While[b > 128, AppendTo[len, b]; (b = Read[l, Byte])];
AppendTo[len, b];
data = (ReadList[l, Byte,
FromVariableLength[
len]] /. (ReadList[l, Byte, 0] :> {}));
{FromVariableLength[t], MetaEvent[type, data]},
status == 240, b = Read[l, Byte];
While[b > 128, AppendTo[len, b]; (b = Read[l, Byte])];
AppendTo[len, b];
data = ReadList[l, Byte, FromVariableLength[len]];
{FromVariableLength[t], Sysex[Prepend[data, 240]]},
status == 247, b = Read[l, Byte];
While[b > 128, AppendTo[len, b]; (b = Read[l, Byte])];
AppendTo[len, b];
data = ReadList[l, Byte, FromVariableLength[len]];
{FromVariableLength[t], Sysex[Prepend[data, 247]]}
]
];
]; Close[l];
ff[nc[Transpose[{FromDeltaTime[e[[All, 1]]],
e[[All, 2]]}] /. {PitchBend[fine_, coarse_,
channel_] ->
PitchBend[fine + 128coarse - 8192, channel],
ProgramChange[num_, channel_] ->
ProgramChange[num + 1, channel]} /.
Dispatch[{MetaEvent[81, micros_] :>
Tempo[N[6/Plus @@ ({65536, 256, 1}micros)
10^7]],
MetaEvent[1, text_] :>
TextEvent[FromCharacterCode[text]],
MetaEvent[2, text_] :>
Copyright[FromCharacterCode[text]],
MetaEvent[3, text_] :>
TrackName[FromCharacterCode[text]],
MetaEvent[4, text_] :>
InstrumentName[FromCharacterCode[text]],
MetaEvent[5, text_] :>
Lyric[FromCharacterCode[text]],
MetaEvent[6, text_] :>
Marker[FromCharacterCode[text]],
MetaEvent[7, text_] :>
CuePoint[FromCharacterCode[text]],
MetaEvent[88, {n_, d_, x_, y_}] :>
TimeSignature[n, 2^d, x, y],
MetaEvent[47, {}] :> EndOfTrack,
MetaEvent[89, {sf_, mm_}] :>
KeySignature[
Which[0 <= sf <= 7, sf, sf >= 249, sf - 256],
mm]}]] //. tce]
, Message[ImportMIDI::format]],
Message[ImportMIDI::filetype]],Message[ImportMIDI::filenotfound]]]
GenerateEventList[x__ /; (Equal @@ (Prepend[Length /@ {x}, 2])),
i__ /; (Equal @@ (Prepend[Length /@ {i}, 3])), PPQ_Integer:
192] :=
SortEventList[Flatten[Table[{x}, i], Length[{i}] + Length[{x}] - 2],
PPQ]
SetAttributes[GenerateEventList, HoldAll]
SumList[f_, i_] := Module[{l = {}, s = 0}, Do[AppendTo[l, s]; s += =
f,
i]; l]
SetAttributes[SumList, HoldAll]
EventSequence[x_, step_, i_, PPQ_:192] :=
Module[{s, j}, s = SumList[step, i];
SortEventList[Transpose[{s, Flatten[Table[x, i], Length[{i}] -
1]}],
PPQ]]
SetAttributes[EventSequence, HoldAll]
End[]

David Bailey

unread,
Mar 26, 2012, 2:46:33 AM3/26/12
to
On 25/03/2012 06:18, Roger Bagula wrote:
> On Mar 24, 12:03 am, markholtuk<markhol...@googlemail.com> wrote:
>> On Mar 23, 6:34 am, Roger Bagula<roger.bag...@gmail.com> wrote:
>
>>

A simpler approach might be to use a program, such as midicsv to
pre-process the midi file into textual (CSV) format.
After that, it would be easy to import the result and select the midi
records that you wished to keep.

Of course the conversion program could be driven from within Mathematica
using Run, etc.

Alternatively, I would write the low level reader in Java, and drive it
via J/Link (an immensely useful tool!).

I have never understood why WRI doesn't support Import of midi files.

David Bailey
http://www.dbaileyconsultancy.co.uk

0 new messages