melanchall / drywetmidi

.NET library to read, write, process MIDI files and to work with MIDI devices
https://melanchall.github.io/drywetmidi
MIT License
540 stars 75 forks source link

Cannot get track name to change #292

Closed breezy876 closed 5 months ago

breezy876 commented 6 months ago

Hello! Firstly, fantastic work on the library! I have been using it extensively in a project of mine (which can be found in my GitHub page) with mostly success.

Now my problem.
I have written the following code, according to the documentation.

var midiFile = new MidiFile();

        var trackChunk = new TrackChunk(new SequenceTrackNameEvent("Test"));

        using (var notesManager = trackChunk.ManageNotes())
        {
            code to add notes goes here and is working...
        }

        midiFile.Chunks.Add(trackChunk);
        midiFile.Write("My Future Great Song.mid");

I have also tried to the following code to get the track name to change with no success.

        using (var evManager = trackChunk.ManageTimedEvents())
        {
            evManager.Objects.Add(new TimedEvent(new SequenceTrackNameEvent() { Text = "Test" }, 0));
        }

The track is added when opening the MIDI with the notes, but its name has not changed . It is showing as "001 Acoustic Piano". From above code I am expecting that the track name is "Test".

How can I add a new track to a MIDI programmatically with the library and get its name to change?

melanchall commented 6 months ago

Hi,

It's not possible for first code sample to have a name other than "Test". You're obviously showing me incomplete code. Because your new track chunk will have the "Test" name.

Please show complete code and how you check the name of a track chunk.

Also please note that I'm on vacation right now so delays in responses are possible.

Thanks, Max

breezy876 commented 6 months ago

This is the complete code. It's opening an existing MIDI then writing the 1st note of the 4th track to a new MIDI.

void BuildFile()
{
    string path = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), Configuration.config.midiFilePath, "EDM", "alicedeejay.mid");

    using (var f = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
    {
        var midiData = MidiFile.Read(f, new ReadingSettings
        {
            NoHeaderChunkPolicy = NoHeaderChunkPolicy.Ignore,
            NotEnoughBytesPolicy = NotEnoughBytesPolicy.Ignore,
            InvalidChannelEventParameterValuePolicy = InvalidChannelEventParameterValuePolicy.ReadValid,
            InvalidChunkSizePolicy = InvalidChunkSizePolicy.Ignore,
            InvalidMetaEventParameterValuePolicy = InvalidMetaEventParameterValuePolicy.SnapToLimits,
            MissedEndOfTrackPolicy = MissedEndOfTrackPolicy.Ignore,
            UnexpectedTrackChunksCountPolicy = UnexpectedTrackChunksCountPolicy.Ignore,
            ExtraTrackChunkPolicy = ExtraTrackChunkPolicy.Read,
            UnknownChunkIdPolicy = UnknownChunkIdPolicy.ReadAsUnknownChunk,
            SilentNoteOnPolicy = SilentNoteOnPolicy.NoteOff,
            TextEncoding = Encoding.Default
        });

        var track = midiData.GetTrackChunks().ToArray()[3];
        var notes = NotesManagingUtilities.GetNotes(track).ToArray();

        var midiFile = new MidiFile();
        var trackChunk = new TrackChunk(new SequenceTrackNameEvent() { Text = "Test" });

        using (var notesManager = trackChunk.ManageNotes())
        {
            notesManager.Objects.Add(notes[0]);
        }

        midiFile.Chunks.Add(trackChunk);
        midiFile.Write("My Future Great Song.mid");

    }
melanchall commented 6 months ago

It's opening an existing MIDI

It changes everything. Reading an existing file is not the same case as creation of a new one.

So you add SequenceTrackNameEvent("Test")) to the existing track chunk and expect this event will magically replace existing one? Obviously it won't. You just add another one event at the end of a track chunk.

To do what you want you have two choices:

  1. Find existing SequenceTrackNameEvent and change its Text property.
  2. Remove existing event and add a new one at the start of a track chunk (see Insert method on TrackChunk.Events).

But you again didn't show me complete code:

  1. using (var notesManager = trackChunk.ManageNotes()). What is trackChunk and where it is defined? There is no this field/var declaration in your code.
  2. There is no code for SequenceTrackNameEvent in your snippet.

Anyway now I believe you see what was wrong in your algorithm.

Thanks, Max

breezy876 commented 6 months ago

Hello. I am aware of what is required in a MIDI file as Iv'e been working with them for years- that adding a new event won't change the track name as the first one gets selected by any MIDI player/editor. That is why in the code I create a new MidiFile instance, a new track (with the expected name), add a note (taken from the existing file) to the track and then write to it. The code still was not complete, I have added the 2 missing lines. Please review.

breezy876 commented 6 months ago

I just discovered that for some reason when I open it in MIDIEditor the track name of the first track is "Test", but in others (FL Studio) it is "Acoustic Piano 001". I am very confused. There are also two tracks, the first I added and a second that gets added presumably automatically by the library that has a tempo change and time signature event, possibly by the note from the other MIDI that gets read and added to it. Is it possible a program change event for note tracks is also required for compatibility with certain software to resolve this??

melanchall commented 6 months ago

Well, if we look at this example:

var midiFile = new MidiFile();

var trackChunk = new TrackChunk(new SequenceTrackNameEvent("Test"));

using (var notesManager = trackChunk.ManageNotes())
{
    // code to add notes goes here and is working...
}

midiFile.Chunks.Add(trackChunk);
midiFile.Write("My Future Great Song.mid");

it's guaranteed the track chunk will have Test name.

Looks like some DAWs (FL in your example) use a program to name a track chunk. But there is an interesting thing here. In our example we don't specify a program. In this case the MIDI spec says nothing about what program is default one. Many software use zero program (which is Acoustic Piano according to GM) but that just a way these software operate, it's not by the MIDI spec.

Is it possible a program change event for note tracks is also required for compatibility with certain software to resolve this??

Definitely no. More than that program is a number, we can't specify text there.

My conclusion is: it's wrong to use a program to name a track, especially if Sequence/Track Name event is present there. Maybe FL has an option what to use for tracks names?

In any case you're doing all correct, and there are no bugs in the library. I recommend to contact FL forum or tech support to get clarification why they use program for naming.

breezy876 commented 6 months ago

I don't understand. I open other MIDIs in FL Studio and they have the correct track names (same as in MidiEditor), but when I use your code to create a new MIDI from scratch as above. Only MidiEditor software has the correct track name- "Test".

breezy876 commented 6 months ago

Off-topic- are you interested in Collaborating on a project that would utilise your library for the MIDI processing? There is potential profit to be made for both of us. I will provide further details of the project after your answer- if yes.

melanchall commented 6 months ago

Well, send me two files please:

  1. One created with DryWetMIDI.
  2. An arbitrary one FL shows correct track names for.

are you interested in Collaborating on a project that would utilise your library for the MIDI processing?

Thank you for the offer, but no. Unfortunately I have small amount of free time so I just want to have some rest, hope you understand :-)

melanchall commented 6 months ago

@breezy876 Any news?