Struggling with MIDIFIle creation and velocity #255
Replies: 11 comments 7 replies
-
Hi, First of all, you don't need to use constructions like this: TimedObjectUtilities.AddObjects(midiTrackClick.Events, click); There is a much simple way: var midiTrackClick = click.ToTrackChunk(); As for velocity. Just open please documentation on the Note class. As you can see there is the Velocity property there: new Note((SevenBitNumber)75, (SevenBitNumber)100)
{
Time = tickCalc(2, 1, 0),
Length = 480,
Channel = (FourBitNumber)9,
Velocity = (SevenBitNumber)70
} Please use the documentation. There are cases where a task is complex and there is a little help from the docs, but that's not your case. Answer on your question can be easily found there. Maybe there are problems with the documentation? If so, I'll be glad to improve it. Also DryWetMIDI already provides a way to convert bars/beats to ticks, more info here: https://melanchall.github.io/drywetmidi/articles/high-level-managing/Time-and-length.html. Thanks, |
Beta Was this translation helpful? Give feedback.
-
Thanks. No, I'm a total noob! But I'm a musician and sound engineer, so I've been working with midi for years and also making projects on MAX MSP which has some similarities to coding. 1, My first question is a general one regarding MIDI file playback timing. I am still noticing some minor wobbles. I'm hoping that this is due to the fact I need to have MAX open as a GM playback device, and my compiled game would either use sound fonts or MIDI chips on phones (If such things are easy to access - I'll cross that bridge later). However, I just wondered if you have any recommends for getting playback as solid as possible at the code point. 2, Secondly, I've inserted noteOn(60, 0) messages on every beat on channel 16 as a way to get the time from the midi file and use that to trigger events in game. Possibly there is a better way to do that? Like just getting the current beats position of the Midifile in playback? Either way, I've not been able to make it work. I set the channel to 15, assuming it's 0-15, but I've also tried 16 and other channels which definitely have events on. Code below, I'd be really grateful if you could take a look: using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using Melanchall.DryWetMidi.Multimedia;
using Melanchall.DryWetMidi.Core;
using Melanchall.DryWetMidi.Interaction;
using Melanchall.DryWetMidi.Common;
using RomansNumerals.Transport;
using System.Threading;
using System.Linq;
public class GameLevel : MonoBehaviour
{
private Playback playback;
private void Start()
{
MidiFile midiFile = MidiFile.Read(Application.dataPath + "/RN_play.mid");
playback = midiFile.GetPlayback();
// Subscribe to the NoteOn events
playback.EventPlayed += OnNotePlayed;
Debug.Log("note callback set");
}
private void OnNotePlayed(object sender, MidiEventPlayedEventArgs e)
{
if (e.Event.EventType == MidiEventType.NoteOn)
{
NoteOnEvent noteOn = (NoteOnEvent)e.Event;
if (noteOn.Channel == 15)
{
// Go
Debug.Log("Note channel 16 was played!");
}
else
{
Debug.Log("anything at all??");
}
}
}
public void play()
{
MidiFileMaker.MakeFile(1, 1, 1);
string filePath = Path.Combine(Application.dataPath + "/RN_play.mid");
MidiFilePlayer.StartPlaying(filePath);
}
public void stop()
{
MidiFilePlayer.StopPlaying();
}
void OnApplicationQuit()
{
MidiFilePlayer.StopPlaying();
}
} |
Beta Was this translation helpful? Give feedback.
-
Hi Max. I've been trying variations around this the last couple of days but I haven't found a solution yet. Perhaps if you could give me a nudge in the right direction? I went to chat GPT for help but it writes stuff I don't understand so it stops being a learning exercise for me because I don't understand what it's doing, and then it doesn't work anyway. I'm declaring midiFile and playback variables public class GameLevel : MonoBehaviour
{
private MidiFile midiFile;
private Playback playback; then in start I locate the midiFile and create the playback object private void Start()
{
MidiFile midiFile = MidiFile.Read(Application.dataPath + "/RN_play.mid");
playback = midiFile.GetPlayback();
} And then in update I try to get the CurrentTime with a spacebar press. I think this is where it's going wrong. private void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
// Get the current time in seconds
var currentTime = playback.GetCurrentTime(TimeSpanType.Midi);
Debug.Log("Current time in MIDI ticks: " + currentTime);
}
} This particular variation on the things I've tried doesn't create errors, but it always returns 0 and/or crashes. I think I need to convert the time to ticks right? I'm not sure what format comes back just from TimeSpanType.Midi? var ticks = TimeConverter.ConvertTo(midiFile.GetTimeDivision(), currentTime, TimeSpanType.Midi, TimeConversionTarget.Ticks); But it doesn't work. I can't find this TimeConversionTarget in your docs and I'm unsure what to replace it with. As ever, sorry about the nooby questions, but at least my markdown seems to be working ok, so that's something. p.s regards the actual playback, that's working fine. I'm successfully creating a new midiFile based on code iterating through loops, and then it plays back. Now I just need game events to respond to things based on the time position. |
Beta Was this translation helpful? Give feedback.
-
Aha! Ok from what you're saying I think I've worked out my problem. I had the actual playback in another script. I was thinking of the playback as a kind of static thing that could only exist in a singular state across scripts, but from what you're saying, I infer that actually multiple playbacks could be created around the same midFile. That would explain why it's returning 0 - because it's actually a different playback to the one I'm listening to and it hasn't been started. using UnityEngine;
using Melanchall.DryWetMidi.Core;
//using Melanchall.DryWetMidi.Common;
//using Melanchall.DryWetMidi.Composing;
//using Melanchall.DryWetMidi.Interaction;
using Melanchall.DryWetMidi.Multimedia;
//using Melanchall.DryWetMidi.Standards;
//using Melanchall.DryWetMidi.Tools;
using System;
using System.IO;
using RomansNumerals.Transport;
namespace RomansNumerals.Transport
{
public static class MidiFilePlayer
/* Call it by:
string filePath = Path.Combine(Application.streamingAssetsPath, "RN_play.mid");
MidiFilePlayer.StartPlaying(filePath);
*/
{
private static OutputDevice outputDevice;
private static Playback playback;
private static string port = "IAC Driver Bus 1";
public static void StartPlaying(string filePath)
{
MidiReset();
var midiFile = MidiFile.Read(filePath);
playback = midiFile.GetPlayback();
playback.Start();
outputDevice = OutputDevice.GetByName(port);
playback.EventPlayed += (_, e) =>
{
outputDevice.SendEvent(e.Event);
};
} etc. Thanks. I'll have to experiment a bit with referencing it, but I'm hoping this has put me in the right direction to proceed. |
Beta Was this translation helpful? Give feedback.
-
Got it all working :D // Get the current time in ticks
MidiFile midiFile = MidiFile.Read(Application.dataPath + "/RN_play.mid");
var tempoMap = midiFile.GetTempoMap();
var currentTime = MidiFilePlayer.playback.GetCurrentTime(TimeSpanType.Midi);
// Convert the time to int
var tickInts = TimeConverter.ConvertFrom(currentTime, tempoMap);
Debug.Log("Current beat time: " + ((tickInts / 480) - 12)); |
Beta Was this translation helpful? Give feedback.
-
Hi Max. using UnityEngine;
using Melanchall.DryWetMidi.Multimedia;
using Melanchall.DryWetMidi.Core;
using RomansNumerals.Transport;
using Melanchall.DryWetMidi.Interaction;
public class GetBeats : MonoBehaviour
{
private void Update()
{
// Get the current time in ticks
var currentTime = MidiFilePlayer.playback.GetCurrentTime<MidiTimeSpan>();
//absolute ints
var tickLong = currentTime.TimeSpan; //this is a long
var beatInts = (int)tickLong / 480 - 12;
var barInts = (int)tickLong / 1920 - 3;
if (tickLong % 480 == 0)
{
Debug.Log("beat");
//perform action
}
}
} |
Beta Was this translation helpful? Give feedback.
-
Oh I just found the PlaybackCurrentTimeWatcher. That sound like just the thing :) |
Beta Was this translation helpful? Give feedback.
-
where am I going wrong with this? It doesn't catch hardly any of the beats. using UnityEngine;
using Melanchall.DryWetMidi.Multimedia;
using Melanchall.DryWetMidi.Core;
using RomansNumerals.Transport;
using Melanchall.DryWetMidi.Interaction;
using System;
public class GetBeats : MonoBehaviour
{
public static void PlaybackListen()
{
// Add playback to the watcher
PlaybackCurrentTimeWatcher.Instance.AddPlayback(MidiFilePlayer.playback, TimeSpanType.Midi);
// Subscribe to CurrentTimeChanged event
PlaybackCurrentTimeWatcher.Instance.CurrentTimeChanged += OnCurrentTimeChanged;
// Start the watcher
PlaybackCurrentTimeWatcher.Instance.Start();
}
public static void OnCurrentTimeChanged(object sender, PlaybackCurrentTimeChangedEventArgs e)
{
foreach (var playbackTime in e.Times)
{
var playback = playbackTime.Playback;
var time = playbackTime.Time;
long ticks = TimeConverter.ConvertTo<MidiTimeSpan>(time, playback.TempoMap);
int beats = (int)ticks % 480;
if (beats == 0)
{
print("beat!");
}
}
}
} The playback is started here, which also calls the methods above using UnityEngine;
using Melanchall.DryWetMidi.Core;
//using Melanchall.DryWetMidi.Common;
//using Melanchall.DryWetMidi.Composing;
//using Melanchall.DryWetMidi.Interaction;
using Melanchall.DryWetMidi.Multimedia;
//using Melanchall.DryWetMidi.Standards;
//using Melanchall.DryWetMidi.Tools;
using System;
using System.IO;
using RomansNumerals.Transport;
using RomansNumerals.Midi;
namespace RomansNumerals.Transport
{
public static class MidiFilePlayer
/* Call it by:
string filePath = Path.Combine(Application.streamingAssetsPath, "RN_play.mid");
MidiFilePlayer.StartPlaying(filePath);
*/
{
private static string port;
private static OutputDevice outputDevice;
public static Playback playback;
public static void StartPlaying(string filePath)
{
MidiReset();
var midiFile = MidiFile.Read(filePath);
playback = midiFile.GetPlayback();
GetBeats.PlaybackListen();
//get the port from MidiDropdown
port = MidiDropdown.GetPort();
outputDevice = OutputDevice.GetByName(port);
playback.Start();
playback.EventPlayed += (_, e) =>
{
outputDevice.SendEvent(e.Event);
};
}
public static void StopPlaying()
{
if (playback != null)
{
playback.Stop();
}
playback.Dispose();
MidiReset();
}
public static bool IsPlaying()
{
return playback != null && playback.IsRunning;
}
private static void MidiReset()
{
AllNotesOff.TurnOffAllNotes();
AllNotesOff.ResetControllers();
}
}
} |
Beta Was this translation helpful? Give feedback.
-
Thanks Max. I find the time conversion stuff really confusing. I've managed to get this working, markers are printing to the midiFile as it's made dynamically, and those markers are being read. All the playback is being handled elsewhere and is working. The only issue (and it's a dealbreaker) is I can only get them to write onto the midifile in delta time like so: //markerTrackChunk.Events.Add(new MarkerEvent { Text = $"BAR {i}", DeltaTime = SOMETIME }); ^ this is done in an iterating loop, so the next bit below has to be outside the loop: midiFile.Chunks.Add(markerTrackChunk); I think this is almost there, but apparently it uses obsolete code and I got lost trying to change it //new TimedEvent(new MarkerEvent($"BAR {i}")).SetTime(new BarBeatTicksTimeSpan(tickCalc(i, 1, 0)), tempoMap); here's the whole script where I try to dynamically add markers to a midifile being created. It's a commented out graveyard of failed attempts to use time conversion. using UnityEngine;
using System.Collections.Generic;
using Melanchall.DryWetMidi.Common;
using Melanchall.DryWetMidi.Interaction;
using Melanchall.DryWetMidi.Core;
using System.IO;
using System;
namespace RomansNumerals.Transport
{
public static class MidiFileMaker
{
public static void MakeFile(int freq, int key, int BPM)
{
string filePath = Application.dataPath + "/RN_play.mid";
if (File.Exists(filePath))
{
try
{
File.Delete(filePath);
}
catch (System.Exception ex)
{
Debug.Log("An error occurred while deleting the file: " + ex.Message);
}
}
var midiFile = new MidiFile();
TempoMap tempoMap = TempoMap.Create(new TicksPerQuarterNoteTimeDivision(480), Tempo.FromBeatsPerMinute(BPM));
midiFile.ReplaceTempoMap(tempoMap);
//keep just in case new way above doesn't work
//midiFile.ReplaceTempoMap(TempoMap.Create(new TicksPerQuarterNoteTimeDivision(480), Tempo.FromBeatsPerMinute(BPM)));
var allKickNotes = new List<Note>();
var markerTrackChunk = new TrackChunk();
for (int i = 4, j = 0; i <= GameLevel.runSize; i += 2, j += 2)
{
var kick = new List<Note>
{
new Note((SevenBitNumber)35)
{
Time = tickCalc(i, 2, 0),
Length = 120,
Channel = (FourBitNumber)9,
Velocity = (SevenBitNumber)127
},
new Note((SevenBitNumber)35)
{
Time = tickCalc(i, 4, 0),
Length = 120,
Channel = (FourBitNumber)9,
Velocity = (SevenBitNumber)127
},
new Note((SevenBitNumber)35)
{
Time = tickCalc((i + 1), 2, 0),
Length = 120,
Channel = (FourBitNumber)9,
Velocity = (SevenBitNumber)127
},
new Note((SevenBitNumber)35)
{
Time = tickCalc((i + 1), 4, 0),
Length = 120,
Channel = (FourBitNumber)9,
Velocity = (SevenBitNumber)127
}
};
allKickNotes.AddRange(kick);
//this works but it's delta time not ticks... AARRRGGGHHHH SO CLOSE
//markerTrackChunk.Events.Add(new MarkerEvent { Text = $"BAR {i}", DeltaTime = (int)tickCalc(i, 1, 0) });
//markerTrackChunk.Events.Add(new MarkerEvent { Text = $"BAR {i + 1}", DeltaTime = (int)tickCalc((i + 1), 1, 0) });
//this creates markers but just at beginning
//markerTrackChunk.Events.Add(new MarkerEvent($"BAR {i}"));
//markerTrackChunk.Events.Add(new MarkerEvent($"BAR {i + 1}"));
//is this it????? UPDATE - NO - produces nothing
//var timedEvent = new TimedEvent(new MarkerEvent($"BAR {i}"));
//TimedObjectUtilities.SetTime(timedEvent, new BarBeatTicksTimeSpan(i, 1, 0), tempoMap);
//var timedEvent2 = new TimedEvent(new MarkerEvent($"BAR {(i + 1)}"));
//TimedObjectUtilities.SetTime(timedEvent, new BarBeatTicksTimeSpan((i + 1), 1, 0), tempoMap);
//test this too
//new TimedEvent (new MarkerEvent($"BAR {i}"));
//TimedObjectUtilities.SetTime(timedEvent, new BarBeatTicksTimeSpan(i, 1, 0), tempoMap);
//errrors
//markerTrackChunk.Events.Add(new TimedEvent(new MarkerEvent($"BAR {i}"))
//.SetTime(new BarBeatTicksTimeSpan(i, 1, 0), tempoMap));
//errors
//markerTrackChunk.Events.Add(new TimedEvent<MarkerEvent>(new MarkerEvent($"BAR {i}"))
//.SetTime(new BarBeatTicksTimeSpan(i, 1, 0), tempoMap));
//no errors
//markerTrackChunk.Events.Add(
//new TimedEvent(new MarkerEvent($"BAR {i}"))
//.SetTime(new BarBeatTicksTimeSpan(i, 1, 0), tempoMap) as MidiEvent);
//this dumps out all the markers on start
//var timedEvent = new TimedEvent(new MarkerEvent($"BAR {i}"));
//TimedObjectUtilities.SetTime(timedEvent, new BarBeatTicksTimeSpan(i, 1, 0), tempoMap);
//markerTrackChunk.Events.Add(timedEvent.Event as MidiEvent);
//var timedEvent2 = new TimedEvent(new MarkerEvent($"BAR {(i + 1)}"));
//TimedObjectUtilities.SetTime(timedEvent2, new BarBeatTicksTimeSpan(i + 1, 1, 0), tempoMap);
//markerTrackChunk.Events.Add(timedEvent2.Event as MidiEvent);
//This one is Close but uses obsolete code & thus doesn't work (produces no Markers)
//new TimedEvent(new MarkerEvent($"BAR {i}")).SetTime(new BarBeatTicksTimeSpan(tickCalc(i, 1, 0)), tempoMap);
//new TimedEvent(new MarkerEvent($"BAR {(i + 1)}")).SetTime(new BarBeatTicksTimeSpan(tickCalc((i + 1), 1, 0)), tempoMap);
//nope
//var timedEvent = new TimedEvent(new MarkerEvent($"BAR {i}"));
//TimedObjectUtilities.SetTime(timedEvent, new BarBeatTicksTimeSpan(i, 1, 0), tempoMap);
//markerTrackChunk.Events.Add(timedEvent);
//this dumps out all the markers on start instead of at time points
//var timedEvent = new TimedEvent(new MarkerEvent($"BAR {i}"));
//TimedObjectUtilities.SetTime(timedEvent, new BarBeatTicksTimeSpan(i, 1, 0), tempoMap);
//markerTrackChunk.Events.Add(timedEvent.Event);
//var timedEvent2 = new TimedEvent(new MarkerEvent($"BAR {i + 1}"));
//TimedObjectUtilities.SetTime(timedEvent, new BarBeatTicksTimeSpan(i + 1, 1, 0), tempoMap);
//markerTrackChunk.Events.Add(timedEvent.Event);
}
var midiTrackKick = allKickNotes.ToTrackChunk();
midiFile.Chunks.Add(midiTrackKick);
midiFile.Chunks.Add(markerTrackChunk);
midiFile.Write(Application.dataPath + "/RN_play.mid");
long tickCalc(int bar, int beat, int tick)
{
return ((bar - 1) * 1920) + ((beat - 1) * 480) + tick;
}
}
}
} I really appreciate your help. If you have a patreon or something I'd be happy to chip in a bit. |
Beta Was this translation helpful? Give feedback.
-
Ahhh. Thanks so much. I've been fighting with that one line of code for 2 days straight. It's now 4AM here and I've got it working. Your code gave me enough to go on. using UnityEngine;
using System.Collections.Generic;
using Melanchall.DryWetMidi.Common;
using Melanchall.DryWetMidi.Interaction;
using Melanchall.DryWetMidi.Core;
using System.IO;
//using System;
//using Unity.VisualScripting;
using System.Linq;
using RomansNumerals.noteslist;
namespace RomansNumerals.Transport
{
public static class MidiFileMaker
{
public static void MakeFile(int freq, int key, int BPM)
{
string filePath = Application.dataPath + "/RN_play.mid";
if (File.Exists(filePath))
{
try
{
File.Delete(filePath);
}
catch (System.Exception ex)
{
Debug.Log("An error occurred while deleting the file: " + ex.Message);
}
}
var midiFile = new MidiFile();
TempoMap tempoMap = TempoMap.Create(new TicksPerQuarterNoteTimeDivision(480), Tempo.FromBeatsPerMinute(BPM));
midiFile.ReplaceTempoMap(tempoMap);
var allKickNotes = new List<Note>();
//loop start
for (int i = 4, j = 0; i <= GameLevel.runSize; i += 2, j += 2)
{
var kickList = NotesListReggae.GetKickDrums(i);
allKickNotes.AddRange(kickList);
midiFile.Chunks.Add(new[] { i }
.Select(i => new TimedEvent(new MarkerEvent($"BAR {i}"))
.SetTime(new MidiTimeSpan(MidiFileMaker.tickCalc(i, 1, 0)), tempoMap)).ToTrackChunk());
midiFile.Chunks.Add(new[] { i }
.Select(i => new TimedEvent(new MarkerEvent($"BAR {i + 1}"))
.SetTime(new MidiTimeSpan(tickCalc(i + 1, 1, 0)), tempoMap)).ToTrackChunk());
};
var midiTrackKick = allKickNotes.ToTrackChunk();
midiFile.Chunks.Add(midiTrackKick);
midiFile.Write(Application.dataPath + "/RN_play.mid");
}
public static long tickCalc(int bar, int beat, int tick)
{
return ((bar - 1) * 1920) + ((beat - 1) * 480) + tick;
}
}
} |
Beta Was this translation helpful? Give feedback.
-
I've tidied up the script and moved all the notelists to separate static objects so it's much easier to read. The functionality of it is great now. I'm going to get to work on using those markers to generate game events :-) using UnityEngine;
using System.Collections.Generic;
using Melanchall.DryWetMidi.Common;
using Melanchall.DryWetMidi.Interaction;
using Melanchall.DryWetMidi.Core;
using System.IO;
//using System;
//using Unity.VisualScripting;
using System.Linq;
using RomansNumerals.noteslist;
namespace RomansNumerals.Transport
{
public static class MidiFileMaker
{
public static void MakeFile(int freq, int key, int BPM)
{
string filePath = Application.dataPath + "/RN_play.mid";
if (File.Exists(filePath))
{
try
{
File.Delete(filePath);
}
catch (System.Exception ex)
{
Debug.Log("An error occurred while deleting the file: " + ex.Message);
}
}
var midiFile = new MidiFile();
TempoMap tempoMap = TempoMap.Create(new TicksPerQuarterNoteTimeDivision(480), Tempo.FromBeatsPerMinute(BPM));
midiFile.ReplaceTempoMap(tempoMap);
//declare lists
var allClickNotes = new List<Note>();
var allKickNotes = new List<Note>();
var allRimNotes = new List<Note>();
var allHatNotes = new List<Note>();
var allSnareNotes = new List<Note>();
var allBassNotes = new List<Note>();
var allPianoNotes = new List<Note>();
var allGuitarNotes = new List<Note>();
var allMutedGuitarNotes = new List<Note>();
var allOrganNotes = new List<Note>();
//Get click and add to chunks
allClickNotes.AddRange(NotesListClick.GetClick(key));
//loop start
for (int i = 4, j = 0, b = 1; i <= GameLevel.runSize; i += 2, j += 2, b +=1) // i is bar count after count in, j is pickIndex, b is bar numbers - End
{
allKickNotes.AddRange(NotesListReggaeKick.GetKickList(i));
allRimNotes.AddRange(NotesListReggaeRim.GetRim(i));
allRimNotes.AddRange(NotesListReggaeHat.GetHat(i));
allSnareNotes.AddRange(NotesListReggaeSnare.GetSnare(i));
allBassNotes.AddRange(NotesListReggaeBass.GetBass(key, i, j));
allPianoNotes.AddRange(NotesListReggaePiano.GetPiano(key, i, j));
allGuitarNotes.AddRange(NotesListReggaeGuitar.GetGuitar(key, i, j));
allMutedGuitarNotes.AddRange(NotesListReggaeMutedGuitar.GetMutedGuitar(key, i, j));
allOrganNotes.AddRange(NotesListReggaeOrgan.GetOrgan(key, i, j));
//add markers for bars
midiFile.Chunks.Add(new[] { b }
.Select(b => new TimedEvent(new MarkerEvent($"BAR {b}"))
.SetTime(new MidiTimeSpan(TickCalc.tickCalc(b, 1, 0)), tempoMap)).ToTrackChunk());
};
// add click to midifile chunks
midiFile.Chunks.Add(allClickNotes.ToTrackChunk());
//add instruments ro midifile chunks
midiFile.Chunks.Add(allKickNotes.ToTrackChunk());
midiFile.Chunks.Add(allRimNotes.ToTrackChunk());
midiFile.Chunks.Add(allHatNotes.ToTrackChunk());
midiFile.Chunks.Add(allSnareNotes.ToTrackChunk());
midiFile.Chunks.Add(allBassNotes.ToTrackChunk());
midiFile.Chunks.Add(allPianoNotes.ToTrackChunk());
midiFile.Chunks.Add(allGuitarNotes.ToTrackChunk());
midiFile.Chunks.Add(allMutedGuitarNotes.ToTrackChunk());
midiFile.Chunks.Add(allOrganNotes.ToTrackChunk());
//write the midi file
midiFile.Write(Application.dataPath + "/RN_play.mid");
}
}
} |
Beta Was this translation helpful? Give feedback.
-
I created this script to generate a MIDI file in game. However, It doesn't seem to respect velocity info because of the Note obj. I've tried NoteOnEvent instead, but I can't get the syntax right for the time parameters. It has to be ticks as I want flam, swings etc. So I can't work with seconds or with big divisions like 16ths. As you can see I made a calculator to convert bars/beats/ticks to raw ticks anyway, so raw ticks is perfect for what I want. But I don't get the syntax.
Below is the file I made which actually works fine apart from the velocity problem, how can I do something very similar to this but with velocity data byte 2 as well?:
using UnityEngine;
using System.Collections.Generic;
using Melanchall.DryWetMidi.Common;
using Melanchall.DryWetMidi.Interaction;
using Melanchall.DryWetMidi.Core;
using System.IO;
using RomansNumerals.Transport;
using System;
namespace RomansNumerals.Transport
{
public static class MidiFileMaker
{
public static void MakeFile(int index, int pick, int freq)
//call with: MidiFileMaker.MakeFile(1, 2, 3);
{
//delete the last iteration of file
string filePath = Application.dataPath + "/RN_play.mid";
}
Beta Was this translation helpful? Give feedback.
All reactions