alphaTab icon indicating copy to clipboard operation
alphaTab copied to clipboard

MIDI file generated by MidiFileGenerator is unusable

Open kyledecot opened this issue 3 years ago • 2 comments

Is there an existing issue for this?

  • [X] I have searched the existing issues

Current Behavior

Screen Shot 2022-08-20 at 12 01 58 PM

Attempting to load the .mid file generated by code similar that found at Generating Midi Files via MidiFileGenerator into a 3rd party synthesizer yields an error.

For example, attempting to load the midi file into FluidSynth yields fluidsynth: error: Unrecognized MIDI event:

Screen Shot 2022-08-20 at 11 35 44 AM

Attempting to load the midi file into Online Sequencer yields Could not parse MIDI file: Invalid event: first byte must be greater than or equal to 0x80:

Screen Shot 2022-08-20 at 11 18 07 AM

Finally, attempting to load the midi file into Guitar Pro will load the file however the notes are incorrect and the instrument has changed from Guitar to Piano.

Screen Shot 2022-08-20 at 11 47 21 AM

Expected Behavior

Generated .mid file should play in 3rd party synthesizers without issue.

Steps To Reproduce


# NodeJS Application

export class MidiService implements IMidiService {
  async generate(buffer: Buffer): Promise<Uint8Array> {
    const settings = await this.createSettings();
    const score = await this.createScore(buffer, settings);
    const midiFile = await this.createMidiFile();
    const handler = await this.createMidiFileHandler(midiFile);
    const generator = await this.createMidiGenerator(score, null, handler);

    generator.generate();

    return midiFile.toBinary();
  }

const midiService = new MidiService();
const response = await fetch(URL);
const arrayBuffer = await response.arrayBuffer();
const buffer = Buffer.from(arrayBuffer);
const generatedMidi = await this.midiService.generate(buffer);

writeFileSync(PATH, generatedMidi);

Link to jsFiddle, CodePen, Project

No response

Found in Version

1.3-alpha

Platform

Node.js

Environment

- **OS**: MacOS 12.5
- **Browser**: Chrome
- **.net Version**: N/A

Anything else?

sample.zip

kyledecot avatar Aug 20 '22 15:08 kyledecot

Generally the writing seems to be fine beside one key problem. I think the bytes in this method have to be reversed: https://github.com/CoderLine/alphaTab/blob/43cbd1f78bf16a4d8a8d086592fd9f278a23d14a/src/midi/MidiEvent.ts#L128-L132

Danielku15 avatar Aug 27 '22 09:08 Danielku15

I had a further look. I think alphaTab cannot provide a midi file export to external software at this point. Reason behind this is: alphaTab already uses the "4.2.11 MIDI 2.0 Pitch Bend Message" from the MIDI 2.0 specification to provide bends on an individual note level instead of midi channel wide pitch bends.

Unfortunately the MIDI Associateion did not publish any proper specification on how to write MIDI 2.0 files (SMF2) leaving us with a gap there. Due to this also hardly any software supports midi 2.0 events.

https://www.midi.org/midi/forum/7958-midi-files-and-midi-2-0

I could try to restore at least some basics on the SMF1 export to work as expected, but I am not sure if this would actually help you because it would not be the file you likely need to bring it further e.g. into Fluidsynth.

Danielku15 avatar Aug 27 '22 19:08 Danielku15

Seems there is a bit progress on the TMA side and hopefully soon this month there will be a first presentation about the SMF2 fileformat https://www.midi.org/midi/forum/16368-midi-2-0-specs

Danielku15 avatar Nov 01 '22 12:11 Danielku15

I am using this lib to import/export/edit midi files, it may help to solve this issue, it has a option to import a midi from a json, I am thinking of converting the alphatab Score object to this json format to export the midi, the opposite should work too to import midis https://github.com/CoderLine/alphaTab/issues/150

https://github.com/Tonejs/Midi

allandiego avatar Jun 06 '23 11:06 allandiego

Generally the writing seems to be fine beside one key problem. I think the bytes in this method have to be reversed:

https://github.com/CoderLine/alphaTab/blob/43cbd1f78bf16a4d8a8d086592fd9f278a23d14a/src/midi/MidiEvent.ts#L128-L132

I quickly checked the output and reversing the bytes in this place alone doesn't help, this still results in malformed midi. I'm interested in mid file IO and would be happy to collaborate on the issue. Was it working at some point, can this be tracked to the earlier commits? I guess a fallback for per-note bends is essential, SMF1 will be there for a long time. This is how GP midi export works after all.

bisubus avatar Jul 09 '23 10:07 bisubus

As already reported: The key problem is in the fact that we generate a MIDI 2.0 structure. We heavily rely on the per-note pitch bend messages to generate the correct note heights and bend effects. This was introduced to solve the problem on combined bends like: image

In this example with the SMF1 events both notes would be affected by the pitch wheel and resulting in a full note bend of both strings instead of only one. The MidiFileGenerator would need an option to not generate those per-note events but the channel wide pitch wheel.

I would need to check again how GP exports the midi in this case. It could be that dynamically a new channel is created or simply it suffers from the same problem that the bend will be wrong.

The related issue and PR on this topic were:
https://github.com/CoderLine/alphaTab/issues/367 https://github.com/CoderLine/alphaTab/pull/378

We could add an option for the old generation and then ensure again that the binary writing of the midi files is fully correct.

Danielku15 avatar Jul 11 '23 04:07 Danielku15

@Danielku15 Yes, that's the case, thanks for confirming. Per-note bends were a problem in early GP. I expected too that MIDI export was changed at some point and currently works on multiple channels in GP the same way it does on playback, but no, it's still the same in 8.

bisubus avatar Jul 11 '23 08:07 bisubus

https://next.alphatab.net/docs/reference/api/downloadmidi/ SMF1.0 compliant output restored. You can also choose between format 0 (single track) and format 1 (multi track) depending on your needs. The problem with bends remains though. But better than something which doesn't work at all. 😉

Danielku15 avatar Aug 05 '23 20:08 Danielku15

Oh thank you Daniel! so far working like a charm 😄

allandiego avatar Aug 07 '23 13:08 allandiego