# Generating MIDI files

**URL:** <https://forum.juce.com/t/generating-midi-files/1558>\
**Category:** General JUCE discussion\
**Created:** [March 15, 2007, 8:44pm UTC](https://forum.juce.com/t/generating-midi-files/1558 "2007-03-15T20:44:51Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![imekon](https://avatars.discourse-cdn.com/v4/letter/i/3da27b/32.png) [@imekon](https://forum.juce.com/u/imekon)\
**Post date:** [March 15, 2007, 8:44pm UTC](https://forum.juce.com/t/generating-midi-files/1558/1 "2007-03-15T20:44:51Z")

</div>

I’m trying to work out how to write a MIDI file but getting problems:

```auto
FileChooser chooser( "Select file to save",
    File::getCurrentWorkingDirectory(), "*.mid" );

if ( chooser.browseForFileToSave( true ) )
{
    MidiMessageSequence sequence;

    sequence.addEvent( MidiMessage( 0x90, 48, 96, 0.0 ) );
    sequence.addEvent( MidiMessage( 0x80, 48, 0, 24.0 ) );

    sequence.updateMatchedPairs();

    MidiFile file;

    file.setTicksPerQuarterNote( 96 );

    FileOutputStream stream( chooser.getResult() );

    file.addTrack( sequence );

    file.writeTo( stream );
}
```

It doesn’t seem to work - what it outputs doesn’t seem to be a valid MIDI file.

:?:

---

<div class="post-metadata">

**Author:** ![imekon](https://avatars.discourse-cdn.com/v4/letter/i/3da27b/32.png) [@imekon](https://forum.juce.com/u/imekon)\
**Post date:** [March 15, 2007, 8:46pm UTC](https://forum.juce.com/t/generating-midi-files/1558/2 "2007-03-15T20:46:44Z")

</div>

I did try this as well:

```auto

sequence.addEvent( MidiMessage::noteOn( 1, 48, 96 ), 0.0 );
sequence.addEvent( MidiMessage::noteOff( 1, 48 ), 24.0 );

```

---

<div class="post-metadata">

**Author:** ![jules](https://avatars.discourse-cdn.com/v4/letter/j/41988e/32.png) [@jules](https://forum.juce.com/u/jules)\
**Post date:** [March 16, 2007, 9:02am UTC](https://forum.juce.com/t/generating-midi-files/1558/3 "2007-03-16T09:02:49Z")

</div>

I assume you flushed and/or deleted the stream, right? Otherwise you’d have a half-written file…

---

<div class="post-metadata">

**Author:** ![imekon](https://avatars.discourse-cdn.com/v4/letter/i/3da27b/32.png) [@imekon](https://forum.juce.com/u/imekon)\
**Post date:** [March 16, 2007, 1:40pm UTC](https://forum.juce.com/t/generating-midi-files/1558/4 "2007-03-16T13:40:27Z")

</div>

> [@jules](#):
>
> I assume you flushed and/or deleted the stream, right? Otherwise you’d have a half-written file…

Didn’t make any difference.

---

<div class="post-metadata">

**Author:** ![jules](https://avatars.discourse-cdn.com/v4/letter/j/41988e/32.png) [@jules](https://forum.juce.com/u/jules)\
**Post date:** [March 16, 2007, 1:52pm UTC](https://forum.juce.com/t/generating-midi-files/1558/5 "2007-03-16T13:52:12Z")

</div>

Ok, well it’s been a while since I used that class, so my memory’s a bit flakey, but tracktion uses it to create midi files, so it must work.

Did you try setting the ticks and time format stuff?

---

<div class="post-metadata">

**Author:** ![jules](https://avatars.discourse-cdn.com/v4/letter/j/41988e/32.png) [@jules](https://forum.juce.com/u/jules)\
**Post date:** [March 16, 2007, 1:55pm UTC](https://forum.juce.com/t/generating-midi-files/1558/6 "2007-03-16T13:55:20Z")

</div>

Sorry, yes, I see that you did set the ticks… I’m a bit puzzled then. What’s the output like? Can the MidiFile class load its own output back in ok?

---

<div class="post-metadata">

**Author:** ![imekon](https://avatars.discourse-cdn.com/v4/letter/i/3da27b/32.png) [@imekon](https://forum.juce.com/u/imekon)\
**Post date:** [March 16, 2007, 4:20pm UTC](https://forum.juce.com/t/generating-midi-files/1558/7 "2007-03-16T16:20:17Z")

</div>

It can read it back - however it doesn’t match what was written. I did the following:

```auto
FileChooser chooser( T("Select file to open"),
    File::getCurrentWorkingDirectory(), "*.mid" );

if ( chooser.browseForFileToOpen( ) )
{
    MidiFile file;
    FileInputStream stream( chooser.getResult() );

    file.readFrom( stream );

    for ( int i = 0; i < file.getNumTracks(); i++ )
    {
        const MidiMessageSequence *sequence = file.getTrack( i );

        for ( int j = 0; j < sequence->getNumEvents(); j++ )
       {
            MidiMessageSequence::MidiEventHolder *holder = 
                sequence->getEventPointer( j );

            Logger::outputDebugPrintf( T("Message: %d %d %d %f"),
                holder->message.getChannel(),
                holder->message.getNoteNumber(),
                holder->message.getVelocity(),
                holder->message.getTimeStamp() );
       }
    }
}
```

The results are:

Message: 1 48 0 0.000000  
Message: 0 47 0 0.000000  
Message: 1 48 127 0.000000

They appear to be in the wrong order and with the wrong velocity - I wrote out 96, it comes back as 127? Also, the timestamp is zero.

---

<div class="post-metadata">

**Author:** ![jules](https://avatars.discourse-cdn.com/v4/letter/j/41988e/32.png) [@jules](https://forum.juce.com/u/jules)\
**Post date:** [March 16, 2007, 4:40pm UTC](https://forum.juce.com/t/generating-midi-files/1558/8 "2007-03-16T16:40:08Z")

</div>

The velocity parameter is floating point, not a byte.

I’m too snowed under to debug this at the moment - I’ll take a look when I eventually get time, but it can’t be too hard to trace what’s happening if you want to take a look.

---

<div class="post-metadata">

**Author:** ![imekon](https://avatars.discourse-cdn.com/v4/letter/i/3da27b/32.png) [@imekon](https://forum.juce.com/u/imekon)\
**Post date:** [March 16, 2007, 4:43pm UTC](https://forum.juce.com/t/generating-midi-files/1558/9 "2007-03-16T16:43:42Z")

</div>

I’ll take a look. I’m a bit baffled by the timestamp at the moment. I’m used to writing out delta times (yuk!) so I can’t quite figure out how timestamp works in Juce.

Loading the MIDI file into FLStudio, it can see one note with zero length and velocity of 127, which seems consistent with what I read back.

---

<div class="post-metadata">

**Author:** ![imekon](https://avatars.discourse-cdn.com/v4/letter/i/3da27b/32.png) [@imekon](https://forum.juce.com/u/imekon)\
**Post date:** [March 18, 2007, 7:51pm UTC](https://forum.juce.com/t/generating-midi-files/1558/10 "2007-03-18T19:51:33Z")

</div>

I’m using GNMIDI tools to examine the output I’m getting:

mid2txt shows the following:

```auto
MThd 00000006
  0001 0001 E728 // format, trackcount, resolution

MTrk 0000000C
0016: 00 90 30 7F // note on
001A: 00 80 30 00 // note off
001E: 00 FF 2F 00 // end of track
```

If I use another MIDI editor I get the following:

```auto
MTrk 00000017
0044: 00 FF 03 04 74 65 73 74 // trackname "test"
004C: 00 C0 00 // program
004F: 00 90 60 64 // note on
0053: 08 80 60 00 // note off
0057: 00 FF 2F 00 // end of track
```

The important bits are interesting - juce is writing

90 30 7F

as the first note, which is wrong - I didn’t select 7F as the velocity.

If I dump the MidiMessage as a trace statement, I get:

90 30 58

which is more like it.

The second note looks correct:

80 30 00

What’s wrong is the timestamp of 00, which means the note is of zero duration.

I’ll keep looking into this.

---

<div class="post-metadata">

**Author:** ![imekon](https://avatars.discourse-cdn.com/v4/letter/i/3da27b/32.png) [@imekon](https://forum.juce.com/u/imekon)\
**Post date:** [March 18, 2007, 9:05pm UTC](https://forum.juce.com/t/generating-midi-files/1558/11 "2007-03-18T21:05:38Z")

</div>

Ok, it’s Visual Studio Express

This is the result if I move the code onto my MacBook:

```auto
MThd 00000006
  0001 0001 0060 // format, trackcount, resolution

MTrk 0000000C
0016: 00 90 30 58 // note on
001A: 18 80 30 00 // note off
001E: 00 FF 2F 00 // end of track
```

It would appear that somehow Visual Studio C++ Express is mangling the data?

---

<div class="post-metadata">

**Author:** ![jules](https://avatars.discourse-cdn.com/v4/letter/j/41988e/32.png) [@jules](https://forum.juce.com/u/jules)\
**Post date:** [March 19, 2007, 10:21am UTC](https://forum.juce.com/t/generating-midi-files/1558/12 "2007-03-19T10:21:46Z")

</div>

Don’t think VCExpress could be to blame… Like I said above, did you check that the velocity param is a float, not an integer?

---

<div class="post-metadata">

**Author:** ![imekon](https://avatars.discourse-cdn.com/v4/letter/i/3da27b/32.png) [@imekon](https://forum.juce.com/u/imekon)\
**Post date:** [March 19, 2007, 12:31pm UTC](https://forum.juce.com/t/generating-midi-files/1558/13 "2007-03-19T12:31:01Z")

</div>

I tried this with VC 2005 Pro:

```auto
MidiMessageSequence sequence;

sequence.addEvent( MidiMessage::noteOn( 1, 48, 0.7f ), 0.0 );
sequence.addEvent( MidiMessage::noteOff( 1, 48 ), 24.0 );

FileChooser chooser( T("Select MIDI file to save"),
    File::getCurrentWorkingDirectory(), T("*.mid") );

if ( chooser.browseForFileToSave( true ) )
{
    MidiFile file;

    FileOutputStream stream( chooser.getResult() );

    file.setTicksPerQuarterNote( 96 );

    file.addTrack( sequence );

    file.writeTo( stream );
}
```

Same result, note of zero duration and velocity 127 - I did use 0.7 in the above example.

---

<div class="post-metadata">

**Author:** ![ShareCommunity](https://sea2.discourse-cdn.com/flex026/user_avatar/forum.juce.com/sharecommunity/32/1748_2.png) [@ShareCommunity](https://forum.juce.com/u/ShareCommunity)\
**Post date:** [March 20, 2007, 8:21pm UTC](https://forum.juce.com/t/generating-midi-files/1558/14 "2007-03-20T20:21:59Z")

</div>

Combining the snip-its of code from above, but changing the MIDI slightly (now it’s nonsensical and non-musical):  
i.e.

```auto
		{//hacky test
		MidiMessageSequence sequence; 

		sequence.addEvent( MidiMessage::noteOn( 1, 38, (juce::uint8)0x3f ), 0.0 );
		sequence.addEvent( MidiMessage::noteOn( 1, 38, (juce::uint8)0x3f ), 0.0 );
			sequence.addEvent( MidiMessage::noteOn( 2, 39, (juce::uint8)0x7f ), 96.0 );
			sequence.addEvent( MidiMessage::noteOn( 2, 39, (juce::uint8)0x7f ), 96.0 );
			sequence.addEvent( MidiMessage::noteOff( 3, 40 ), 124.0 );
			sequence.addEvent( MidiMessage::noteOff( 3, 40 ), 124.0 );

		FileChooser chooser( T("Select MIDI file to save"), File::getCurrentWorkingDirectory(), T("*.mid") ); 

			if ( chooser.browseForFileToSave( true ) ) 
			{ 
			MidiFile file; 

				chooser.getResult().File::deleteFile();
				FileOutputStream stream( chooser.getResult() );
				//stream.setPosition(0);

				file.setTicksPerQuarterNote( 96 ); 

				file.addTrack( sequence ); 

				file.writeTo( stream ); 

				//stream.flush();
			} 	
			{
		    MidiFile file; 
			FileInputStream stream( chooser.getResult() ); 

			    file.readFrom( stream ); 

				for ( int i = 0; i < file.getNumTracks(); i++ ) 
				{ 
					const MidiMessageSequence *sequence = file.getTrack( i ); 

					for ( int j = 0; j < sequence->getNumEvents(); j++ ) 
					{ 
						MidiMessageSequence::MidiEventHolder *holder = 
							sequence->getEventPointer( j ); 

						Logger::outputDebugPrintf( T("Message: chn:%d Note:%d Vel:%d T:%f"), 
														holder->message.getChannel(), 
														holder->message.getNoteNumber(), 
														holder->message.getVelocity(), 
														holder->message.getTimeStamp() ); 
					} 
				} 
			}
			
			
		}//Hacky test

```

Here’s the output I get: (same on both XP and OSX machines), which leaves me thinking there’s an OBOE somewhere!

[quote]Message: chn:1 Note:38 Vel:63 T:0.000000  
Message: chn:1 Note:38 Vel:0 T:0.000000  
Message: chn:1 Note:38 Vel:63 T:0.000000  
Message: chn:2 Note:39 Vel:127 T:96.000000  
Message: chn:2 Note:39 Vel:0 T:96.000000  
Message: chn:2 Note:39 Vel:127 T:96.000000  
Message: chn:3 Note:40 Vel:0 T:124.000000  
Message: chn:0 Note:47 Vel:0 T:124.000000  
Message: chn:3 Note:40 Vel:0 T:124.000000[/quote]

Those bolded items are interesting…

I haven’t had a chance to step through everything, but that’s where I’m off to next…

EDIT Thurs PM

A cruel, but valid test tonight… (only tested on Windows)  
Creating an empty track, and adding that, saving and reloading it generates one “Note”

> [@](#):
>
> Message: chn:0 Note:47 Vel:0 T:124.000000

which is the endOfTrack Message. Which is OK.

but I don’t understand this line (in void MidiFile::readNextTrack)

[quote] // use a sort that puts all the note-offs before note-ons that have the same time  
result.list.sort (\*this);[/quote]

But that seems that the endOfTrack’s cropping up in odd places (IMHO) -

Should the comparator ignore comparisons (return 0) ie either message is an endOfTrack message, or something?

Actually, that might be wrong- I tried removing the sort, and I got this:  
(after I added debugs to writeTrack/nextTrack)

[quote]MidiFile::writeTrack - Message: chn:1 Note:38 Vel:63 T:0.000000  
MidiFile::writeTrack - Message: chn:1 Note:38 Vel:63 T:1.000000  
MidiFile::writeTrack - Message: chn:2 Note:39 Vel:127 T:96.000000  
MidiFile::writeTrack - Message: chn:2 Note:39 Vel:127 T:97.000000  
MidiFile::writeTrack - Message: chn:3 Note:40 Vel:0 T:124.000000  
MidiFile::writeTrack - Message: chn:3 Note:40 Vel:0 T:125.000000  
MidiFile:readNextTrack - Message: chn:1 Note:38 Vel:63 T:0.000000  
MidiFile:readNextTrack - Message: chn:1 Note:38 Vel:63 T:1.000000  
MidiFile:readNextTrack - Message: chn:2 Note:39 Vel:127 T:96.000000  
MidiFile:readNextTrack - Message: chn:2 Note:39 Vel:127 T:97.000000  
MidiFile:readNextTrack - Message: chn:3 Note:40 Vel:0 T:124.000000  
MidiFile:readNextTrack - Message: chn:3 Note:40 Vel:0 T:125.000000  
MidiFile:readNextTrack - Message: chn:0 Note:47 Vel:0 T:125.000000  
final Message: chn:1 Note:38 Vel:63 T:0.000000  
final Message: chn:1 Note:38 Vel:0 T:1.000000  
final Message: chn:1 Note:38 Vel:63 T:1.000000  
final Message: chn:2 Note:39 Vel:127 T:96.000000  
final Message: chn:2 Note:39 Vel:0 T:97.000000  
final Message: chn:2 Note:39 Vel:127 T:97.000000  
final Message: chn:3 Note:40 Vel:0 T:124.000000  
final Message: chn:3 Note:40 Vel:0 T:125.000000  
final Message: chn:0 Note:47 Vel:0 T:125.000000[/quote]

It looks like it’s happening after the MidiFile is loaded (even without the sort in place, I think)

Additional: Running the above on OSX somehow manages to pop-up 2 “File Open” windows?

---

<div class="post-metadata">

**Author:** ![jules](https://avatars.discourse-cdn.com/v4/letter/j/41988e/32.png) [@jules](https://forum.juce.com/u/jules)\
**Post date:** [March 23, 2007, 9:28am UTC](https://forum.juce.com/t/generating-midi-files/1558/15 "2007-03-23T09:28:39Z")

</div>

Why not try it with a sequence that doesn’t leave midi notes left on? It’s probably putting in the extra note-offs to stop the hanging notes that are there.

---

<div class="post-metadata">

**Author:** ![jules](https://avatars.discourse-cdn.com/v4/letter/j/41988e/32.png) [@jules](https://forum.juce.com/u/jules)\
**Post date:** [March 23, 2007, 10:13am UTC](https://forum.juce.com/t/generating-midi-files/1558/16 "2007-03-23T10:13:55Z")

</div>

Actually, just looked at it again, and the extra note-offs are perfectly legitimate, because it adds a note-off before a repeated note-on if there’s not already one there. Nothing wrong with that.

The sorting callback is correctly returning 0 for the end-of-track, but I’m not sure if I trust the sort method to leave the order unchanged. Hmm. Will have to look at that one.

---

<div class="post-metadata">

**Author:** ![mrblasto](https://avatars.discourse-cdn.com/v4/letter/m/5f9b8f/32.png) [@mrblasto](https://forum.juce.com/u/mrblasto)\
**Post date:** [June 21, 2009, 4:52am UTC](https://forum.juce.com/t/generating-midi-files/1558/17 "2009-06-21T04:52:18Z")

</div>

In the 718 version, MidiFile.writeTo is writing an extra 0xFF 0x2f 0x00 0x00 before writing the normal 0xFF 0x2F 0x00. That’s an extra MIDI end of track with a null byte separating them. MidiFile.readFrom reads the resulting file ok as does Tracktion, but Cakewalk Pro Audio 9 doesn’t.

Commenting out these lines in MidiFile::writeTrack corrects the problem:

` out.writeByte (0);
    const MidiMessage m (MidiMessage::endOfTrack());
    out.write (m.getRawData(),
               m.getRawDataSize());`

---

<div class="post-metadata">

**Author:** ![jules](https://avatars.discourse-cdn.com/v4/letter/j/41988e/32.png) [@jules](https://forum.juce.com/u/jules)\
**Post date:** [June 22, 2009, 3:47pm UTC](https://forum.juce.com/t/generating-midi-files/1558/18 "2009-06-22T15:47:47Z")

</div>

Although I can’t remember writing that, it seems strange that I’d have put it in there without a good reason, and tracktion will have been using that code to pump out midi files for years… Are you sure that without those lines the files still load in a range of hosts?

---

<div class="post-metadata">

**Author:** ![imekon](https://avatars.discourse-cdn.com/v4/letter/i/3da27b/32.png) [@imekon](https://forum.juce.com/u/imekon)\
**Post date:** [February 14, 2014, 5:24pm UTC](https://forum.juce.com/t/generating-midi-files/1558/19 "2014-02-14T17:24:35Z")

</div>

Coming back to this (after a few years), I still couldn't get it to work:

```
    MidiFile midiFile;
    midiFile.setTicksPerQuarterNote(96);
    createEmptyTrack(midiFile, 0.7f);
    createTrack(midiFile, 1);
    createTrack(midiFile, 2);
    createTrack(midiFile, 10);
    File file("D:\\tools\\midi2txt\\test.mid");
    FileOutputStream stream(file);
    midiFile.writeTo(stream);
```

I got an emtpy midi file with garbage at the end of the file (according to the GNMIDI tool MIDI2TXT). So I started debugging, and realised that:

```
File file("D:\\tools\\midi2txt\\test.mid");
FileOutputStream stream(file);
midiFile.writeTo(stream);
```

has a problem. FileOutputStream keeps writing to the end of the file. What's missing is stream.setPosition(0) to ensure the file is written correctly, and new data is not just appended to the end of the file.

So corrected it looks like the following:

```
    MidiFile midiFile;
    midiFile.setTicksPerQuarterNote(96);
    createEmptyTrack(midiFile, 0.7f);
    createTrack(midiFile, 1);
    createTrack(midiFile, 2);
    createTrack(midiFile, 10);
    File file("D:\\tools\\midi2txt\\test.mid");
    FileOutputStream stream(file);
    stream.setPosition(0); // <-- here lies dragons!
    midiFile.writeTo(stream);
```

---

<div class="post-metadata">

**Author:** ![jules](https://avatars.discourse-cdn.com/v4/letter/j/41988e/32.png) [@jules](https://forum.juce.com/u/jules)\
**Post date:** [February 14, 2014, 5:32pm UTC](https://forum.juce.com/t/generating-midi-files/1558/20 "2014-02-14T17:32:27Z")

</div>

I think you probably want to delete the file first.. Just skipping back to the start won't truncate it, so you could end up leaving garbage at the end.

[Next page](https://forum.juce.com/t/generating-midi-files/1558.md?page=2)
