improvements to MIDI data format
This commit is contained in:
parent
fd74e7a36e
commit
f3df76ed40
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UniversalEditor.Accessors;
|
||||
using UniversalEditor.IO;
|
||||
using UniversalEditor.ObjectModels.Multimedia.Audio.Synthesized;
|
||||
namespace UniversalEditor.DataFormats.Multimedia.Audio.Synthesized.MIDI
|
||||
@ -34,76 +35,61 @@ namespace UniversalEditor.DataFormats.Multimedia.Audio.Synthesized.MIDI
|
||||
long position = br.Accessor.Position;
|
||||
while (br.Accessor.Position - position < (long)trackLength)
|
||||
{
|
||||
try
|
||||
int deltaTime = br.ReadVariableLengthInt32();
|
||||
MIDIEventType command = (MIDIEventType) br.ReadByte();
|
||||
if (command == MIDIEventType.Meta)
|
||||
{
|
||||
int deltaTime = br.ReadVariableLengthInt32();
|
||||
byte command = br.ReadByte();
|
||||
if (command == 255)
|
||||
MIDIMetaEventType metaEventType = (MIDIMetaEventType) br.ReadByte();
|
||||
byte length = br.ReadByte();
|
||||
switch (metaEventType)
|
||||
{
|
||||
byte metaEventType = br.ReadByte();
|
||||
byte b = metaEventType;
|
||||
if (b <= 47)
|
||||
case MIDIMetaEventType.Text:
|
||||
{
|
||||
switch (b)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
byte length = br.ReadByte();
|
||||
string text = br.ReadFixedLengthString(length);
|
||||
track.Commands.Add(new SynthesizedAudioCommandText(text));
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
byte length = br.ReadByte();
|
||||
string text = br.ReadFixedLengthString(length);
|
||||
track.Name = text;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
if (b == 47)
|
||||
{
|
||||
byte endOfTrackMarker = br.ReadByte();
|
||||
syn.Tracks.Add(track);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
string text = br.ReadFixedLengthString(length);
|
||||
track.Commands.Add(new SynthesizedAudioCommandText(text));
|
||||
break;
|
||||
}
|
||||
else
|
||||
case MIDIMetaEventType.CopyrightNotice:
|
||||
{
|
||||
if (b != 81)
|
||||
{
|
||||
if (b == 88)
|
||||
{
|
||||
byte zero4 = br.ReadByte();
|
||||
byte numerator = br.ReadByte();
|
||||
byte denominator = (byte)Math.Pow(2.0, (double)br.ReadByte());
|
||||
byte ticksPerMetronomeClick = br.ReadByte();
|
||||
byte numberOf32ndNotesPerQuarterNote = br.ReadByte();
|
||||
track.Commands.Add(new SynthesizedAudioCommandTimeSignature(numerator, denominator, ticksPerMetronomeClick, numberOf32ndNotesPerQuarterNote));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
byte zero5 = br.ReadByte();
|
||||
int tempo = (int)br.ReadInt16();
|
||||
int tempo2 = (int)br.ReadByte();
|
||||
int tempo3 = tempo + tempo2;
|
||||
track.Commands.Add(new SynthesizedAudioCommandTempo((double)tempo3));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MIDIMetaEventType.SequenceName:
|
||||
{
|
||||
string text = br.ReadFixedLengthString(length);
|
||||
track.Name = text;
|
||||
break;
|
||||
}
|
||||
case MIDIMetaEventType.EndOfTrack:
|
||||
{
|
||||
syn.Tracks.Add(track);
|
||||
break;
|
||||
}
|
||||
case MIDIMetaEventType.TimeSignature:
|
||||
{
|
||||
byte zero4 = br.ReadByte();
|
||||
byte numerator = br.ReadByte();
|
||||
byte denominator = (byte)Math.Pow(2.0, (double)br.ReadByte());
|
||||
byte ticksPerMetronomeClick = br.ReadByte();
|
||||
byte numberOf32ndNotesPerQuarterNote = br.ReadByte();
|
||||
track.Commands.Add(new SynthesizedAudioCommandTimeSignature(numerator, denominator, ticksPerMetronomeClick, numberOf32ndNotesPerQuarterNote));
|
||||
break;
|
||||
}
|
||||
case MIDIMetaEventType.SetTempo:
|
||||
{
|
||||
byte zero5 = br.ReadByte();
|
||||
int tempo = (int)br.ReadInt16();
|
||||
int tempo2 = (int)br.ReadByte();
|
||||
int tempo3 = tempo + tempo2;
|
||||
track.Commands.Add(new SynthesizedAudioCommandTempo((double)tempo3));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
Console.WriteLine("ue: MIDI: warning: meta event type {0} ({1}) [{2} bytes] unhandled", metaEventType, (byte)metaEventType, length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (System.IO.EndOfStreamException ex)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -154,7 +140,91 @@ namespace UniversalEditor.DataFormats.Multimedia.Audio.Synthesized.MIDI
|
||||
|
||||
protected override void SaveInternal(ObjectModel objectModel)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
Writer bw = Accessor.Writer;
|
||||
bw.Endianness = Endianness.BigEndian;
|
||||
|
||||
SynthesizedAudioObjectModel syn = objectModel as SynthesizedAudioObjectModel;
|
||||
bw.WriteFixedLengthString("MThd");
|
||||
|
||||
int headerSize = 6;
|
||||
bw.WriteInt32(headerSize);
|
||||
|
||||
MIDIFileFormatType fileFormat = MIDIFileFormatType.SimultaneousMultitrack;
|
||||
bw.WriteInt16((short)fileFormat);
|
||||
|
||||
bw.WriteInt16((short)syn.Tracks.Count);
|
||||
|
||||
short ticksPerQuarterNote = 0; // if MSB is 1, bits 0-7 are ticks / frame and bits 8-14 are frames / second
|
||||
bw.WriteInt16(ticksPerQuarterNote);
|
||||
|
||||
for (short i = 0; i < syn.Tracks.Count; i += 1)
|
||||
{
|
||||
bw.WriteFixedLengthString("MTrk");
|
||||
|
||||
int trackLength = 0;
|
||||
bw.WriteInt32(trackLength);
|
||||
|
||||
SynthesizedAudioTrack track = syn.Tracks[i];
|
||||
|
||||
// track name
|
||||
WriteMIDIEvent(bw, 0, MIDIMetaEventType.SequenceName, track.Name);
|
||||
|
||||
for (int j = 0; j < syn.Tracks[i].Commands.Count; j++)
|
||||
{
|
||||
int deltaTime = 0;
|
||||
|
||||
if (syn.Tracks[i].Commands[j] is SynthesizedAudioCommandText)
|
||||
{
|
||||
SynthesizedAudioCommandText cmd = (syn.Tracks[i].Commands[j] as SynthesizedAudioCommandText);
|
||||
WriteMIDIEvent(bw, deltaTime, MIDIMetaEventType.Text, cmd.Text);
|
||||
}
|
||||
else if (syn.Tracks[i].Commands[j] is SynthesizedAudioCommandTimeSignature)
|
||||
{
|
||||
SynthesizedAudioCommandTimeSignature cmd = (syn.Tracks[i].Commands[j] as SynthesizedAudioCommandTimeSignature);
|
||||
|
||||
byte numerator = 4;
|
||||
byte denom = 4;
|
||||
byte denominator = (byte)(Math.Sqrt(denom) / Math.Sqrt(2));
|
||||
byte ticksPerMetronomeClick = 0;
|
||||
byte numberOf32ndNotesPerQuarterNote = 0;
|
||||
WriteMIDIEvent(bw, deltaTime, MIDIMetaEventType.TimeSignature, new byte[] { numerator, denominator, ticksPerMetronomeClick, numberOf32ndNotesPerQuarterNote });
|
||||
}
|
||||
else if (syn.Tracks[i].Commands[j] is SynthesizedAudioCommandTempo)
|
||||
{
|
||||
SynthesizedAudioCommandTempo cmd = (syn.Tracks[i].Commands[j] as SynthesizedAudioCommandTempo);
|
||||
MemoryAccessor ma = new MemoryAccessor();
|
||||
ma.Writer.WriteInt24((int)cmd.Tempo);
|
||||
WriteMIDIEvent(bw, deltaTime, MIDIMetaEventType.SetTempo, ma.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
WriteMIDIEvent(bw, 0, MIDIMetaEventType.EndOfTrack, new byte[0]);
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteMIDIEvent(Writer bw, int deltaTime, MIDIMetaEventType midiEventType, string data)
|
||||
{
|
||||
WriteMIDIEvent(bw, deltaTime, midiEventType, System.Text.Encoding.UTF8.GetBytes(data));
|
||||
}
|
||||
private void WriteMIDIEvent(Writer bw, int deltaTime, MIDIMetaEventType midiEventType, byte[] data)
|
||||
{
|
||||
bw.WriteVariableLengthInt32(deltaTime);
|
||||
bw.WriteByte((byte)MIDIEventType.Meta);
|
||||
bw.WriteByte((byte)midiEventType);
|
||||
bw.WriteVariableLengthInt32(data.Length);
|
||||
bw.WriteBytes(data);
|
||||
}
|
||||
|
||||
private void WriteMIDIEvent(Writer bw, int deltaTime, MIDIEventType midiEventType, string data)
|
||||
{
|
||||
WriteMIDIEvent(bw, deltaTime, midiEventType, System.Text.Encoding.UTF8.GetBytes(data));
|
||||
}
|
||||
private void WriteMIDIEvent(Writer bw, int deltaTime, MIDIEventType midiEventType, byte[] data)
|
||||
{
|
||||
bw.WriteVariableLengthInt32(deltaTime);
|
||||
bw.WriteByte((byte)midiEventType);
|
||||
bw.WriteVariableLengthInt32(data.Length);
|
||||
bw.WriteBytes(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,30 @@
|
||||
//
|
||||
// MIDIEventType.cs
|
||||
//
|
||||
// Author:
|
||||
// Mike Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// Copyright (c) 2019 Mike Becker
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
using System;
|
||||
namespace UniversalEditor.DataFormats.Multimedia.Audio.Synthesized.MIDI
|
||||
{
|
||||
public enum MIDIEventType : byte
|
||||
{
|
||||
SysEx = 0xF0,
|
||||
Escape = 0xF7,
|
||||
Meta = 0xFF
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
//
|
||||
// MIDIFileFormatType.cs
|
||||
//
|
||||
// Author:
|
||||
// Mike Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// Copyright (c) 2019 Mike Becker
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
using System;
|
||||
namespace UniversalEditor.DataFormats.Multimedia.Audio.Synthesized.MIDI
|
||||
{
|
||||
public enum MIDIFileFormatType : short
|
||||
{
|
||||
/// <summary>
|
||||
/// Single track (format 0). Consists of a header-chunk and a single track-chunk. The single track chunk will contain all the
|
||||
/// note and tempo information.
|
||||
/// </summary>
|
||||
SingleTrack = 0,
|
||||
/// <summary>
|
||||
/// Simultaneous multi-track (format 1). Consists of a header-chunk and one or more track-chunks, with all tracks being played
|
||||
/// simultaneously. The first track of a Format 1 file is special, and is also known as the 'Tempo Map'. It should contain all
|
||||
/// meta-events of the types Time Signature, and Set Tempo. The meta-events Sequence/Track Name, Sequence Number, Marker, and
|
||||
/// SMTPE Offset. should also be on the first track of a Format 1 file.
|
||||
/// </summary>
|
||||
SimultaneousMultitrack = 1,
|
||||
/// <summary>
|
||||
/// Independent multi-track (format 2). Consists of a header-chunk and one or more track-chunks, where each track represents an
|
||||
/// independent sequence.
|
||||
/// </summary>
|
||||
IndependentMultitrack = 2
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
//
|
||||
// MIDIMetaEventType.cs
|
||||
//
|
||||
// Author:
|
||||
// Mike Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// Copyright (c) 2019 Mike Becker
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
using System;
|
||||
namespace UniversalEditor.DataFormats.Multimedia.Audio.Synthesized.MIDI
|
||||
{
|
||||
public enum MIDIMetaEventType : byte
|
||||
{
|
||||
SequenceNumber = 0x00,
|
||||
Text = 0x01,
|
||||
CopyrightNotice = 0x02,
|
||||
SequenceName = 0x03,
|
||||
InstrumentName = 0x04,
|
||||
Lyric = 0x05,
|
||||
Marker = 0x06,
|
||||
CuePoint = 0x07,
|
||||
ChannelPrefix = 0x20,
|
||||
EndOfTrack = 0x2F,
|
||||
SetTempo = 0x51,
|
||||
SMPTEOffset = 0x54,
|
||||
TimeSignature = 0x58,
|
||||
KeySignature = 0x59,
|
||||
SequencerSpecific = 0x7F
|
||||
}
|
||||
}
|
||||
@ -324,6 +324,9 @@
|
||||
<Compile Include="DataFormats\Multimedia\Audio\Synthesized\EdLib\D00DataFormat.cs" />
|
||||
<Compile Include="DataFormats\Multimedia\Audio\Synthesized\EdLib\AdlibSoundcard.cs" />
|
||||
<Compile Include="DataFormats\Multimedia\Audio\Synthesized\EdLib\AdlibBlockType.cs" />
|
||||
<Compile Include="DataFormats\Multimedia\Audio\Synthesized\MIDI\MIDIFileFormatType.cs" />
|
||||
<Compile Include="DataFormats\Multimedia\Audio\Synthesized\MIDI\MIDIEventType.cs" />
|
||||
<Compile Include="DataFormats\Multimedia\Audio\Synthesized\MIDI\MIDIMetaEventType.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Libraries\UniversalEditor.Compression\UniversalEditor.Compression.csproj">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user