Natooz / MidiTok

MIDI / symbolic music tokenizers for Deep Learning models 🎶
https://miditok.readthedocs.io/
MIT License
665 stars 81 forks source link

Add a faster midi parsing backend #112

Closed Yikai-Liao closed 8 months ago

Yikai-Liao commented 10 months ago

Recently, I have writen a lightweight midi parsing library (writen in c++ with pybind11), wich is significantly faster than miditoolkit.

https://github.com/Yikai-Liao/symusic

Midi parsing speed has been the bottleneck of preprocessing for a long time, and I want to deal with this problem.

Please tell me if you need any help or if i need add new features to the library. (Note that not all MIDI events are supported currently. For example, we ignored lyrics and pitch bend. Wel will add these features in the future if you need them. )

Here is the midi parsing benchmark:

libarary time
symusic 21.8 ms ± 11.7 ms
MIDI.jl 128.024 ms
mido 5.68 s ± 2 s
pretty_midi 5.59 s ± 844 ms
miditoolkit 6.27 s ± 1.79 s
music21 8.59 s ± 1.2 s
Natooz commented 10 months ago

This looks promising! I'll work on a current PR right now and dig deeper into this when it's done. :) Now from what I just red, I think to use it in MidiTok it would require to support writing files, and load other event types. MidiTok supports Pedals, Pitch Bends, Tempo changes, Time Signature changes. Ultimately we plan to also include Control Changes, and it would be great if one day we can also (allow users to) embed everything else such as Key Signature or Lyrics / Markers somehow.

Also, the way I saw the switch was to use the new c++ backend by default when reading / loading files, but still supporting miditoolkit.MidiFile objects for a while by converting them to the new backend Python class. The conversion would be done in MidiTok, but that's just to say to the new backend should allow to manipulate the data and create an object from scratch

Yikai-Liao commented 10 months ago

That's great. It's easier to support other types of midi events, while writing back will lead to heavy workload. I'll try to implement these functions.

And is there any high level interface you need for better integration? Currently, we merely add some functions like time_shift, clip, sort, to meet our own needs, and the pianoroll conversion function is quite casual.

We are trying to make a more friendly interface in python level.

Natooz commented 10 months ago

It's easier to support other types of midi events, while writing back will lead to heavy workload. I'll try to implement these functions.

I totally get it. :) I didn't touch c/c++ in a long time, but if there is a way I can help don't hesitate to tell me! Until writing is implemented, I can open a PR here to first handle the loading/parsing, and mock writing with miditoolkit (which is a very small part).

And is there any high level interface you need for better integration? Currently, we merely add some functions like time_shift, clip, sort, to meet our own needs, and the pianoroll conversion function is quite casual.

For MidiTok, the needs are mainly to parse the MIDI data with respect to the General MIDI 2 specifications, so I guess you already cover them well. 🙌 I'll give it a try soon, and compare it with miditoolkit (make sure the content is loaded exactly the same).

Natooz commented 10 months ago

I tried Symusic, it would be great if MidiTok can benefit from such speed!

Now I have some questions, more towards how you envision Symusic, to know if it can fit with MidiTok. For the time unit, I saw that you define times in "quarter note". I suppose this is because you must have some use for this. But would you consider to use ticks (integers) as the default time unit ? And maybe add some method to convert the time unit across all the events of a MIDI into quarter notes / beat / seconds? As the tick is the default unit, it would allow users to have a greater flexibility over the time dimension, for any downstream usage.

I checks the note loading, they seems to be identical to what is loaded with miditoolkit, at least I found the same number of notes, and the notes I manually checks contained the same data except when they are not sorted exactly the same but that's not an issue.

A few suggestions/comments:

Capture d’écran 2023-11-28 à 15 39 47

Overall I think this is promising, the speed gain are amazing. I don't know if I can help much on the C++ side, but maybe I can do something for the Python binding part.

Yikai-Liao commented 10 months ago

Thanks for your feedback. I'll answer some of the questions, while @lzqlzzq (the author of minimidi, also the co-author of symusic) will answer the others, especially those about architecture.

  1. For Note.duration, I actually store duration in the class, not the end time. So end could not be modified. Here, I just think both choices (store duration or end time) are acceptable, and I choose one of them casually. Is there any advantages for choosing a specific one?

    class Note {
    public:
        float start;
        float duration;
        int8_t pitch;
        int8_t velocity;
    }
  2. I'll try to fix the errors about empty track list. We have met many errors caused by empty during developing this library. Many thanks for the test.

  3. For the type hints and debugging information (actually it is the string got from repr functions)

    • I have generated a symusic.pyi file for type hints. And I have noticed that it can't work well with ipython. So is there any methods for offering better type hints?
    • As for the repr function, I found it too verbose especially when the List is long, like[Note(start=xxx, end=xxx, pitch=xxx, velocity=xxx), Note(start=xxx, end=xxx, pitch=xxx, velocity=xxx)...] . However, just like what you thought, just offering length information is also not a good design for debugging. I think the idealized implementation might be that in pandas, which is hard to achieve. So I wonder if there is a more suitable design (if not, I'll just rollback to the format in miditoolkit) ?

By the way, I have added Marker, Lyrics, and Pitch Bend to symusic (haven't upload to pypi) and minimid, but I have some problems with Pedal. I have opened an issue in miditoolkit. Do you have any ideas about this?

lzqlzzq commented 10 months ago

Thanks for your attention and feedback on symusic~ Here are some of our design considerations and developing processing:

  1. For the time unit, we think "quarter note" is a unified unit compared to MIDI tick. In MIDI files, I have seen 960, 320, 192 ticks per quarter as far as I know. A unified unit is more consistent in semantics, quarter in float is suitable. For the flexibility and usability, we will consider add MIDI tick and second maybe as attributes.
  2. The name Score is the alias of sheet music. We consider the Score as a intermediate representation of symbolic music rather than just MIDI, because musicxml, ABC and other format can be converted into the Score while retaining most of original information.
  3. There is an advantage using start, duration rather than start, end to store the time information of a note: when performing a time_shift on N notes, using start, duration only needs N additions, while using start, end needs 2N. And I didn' t see any visible advantage of using start, end.
  4. Would you please provide the MIDI file you used to reproduce the TrackList is empty problem? That will help us locate the problem.
  5. We are still looking into the type hint issue, because making type hint in a pybind project is tricky. :(
Yikai-Liao commented 10 months ago

Additionally, the conversion between "quater note" and MIDI tick is quite simple, while the conversion between seconds and MIDI tick is quite complex (also, using second as time unit will make it hard to reset bpm for a specific range of time).

Of course, this kind of conversion will cause a certain degree of precision loss.

from miditoolkit import MidiFile
midi = MidiFile("midi file path")
ticks_per_quarter = midi.ticks_per_beat
# conversion
start_midi_tick: int = midi.instruments[0].notes[0]  # start time of an example note
start_quarter: float = start_midi_tick / ticks_per_quarter 
# For writing back, we just need to set a new quantization factor, the same meaning as ticks per quarter
q = 960 # for example
start_quantized = int(start_quarter * q)
Natooz commented 10 months ago

Hi guys, thank you for the quick feedback!

I finally got time to run a more complete "benchmark", here is the script: https://gist.github.com/Natooz/1a39413220d038e0ac3981f864039370 I run it on the test cases of MidiTok. Here are the results, run on a i7 6700k:

POP909_191.mid: 105 errors (0.05)
POP909_008.mid: 156 errors (0.09)
POP909_022.mid: 13 errors (0.01)
POP909_010.mid: 127 errors (0.08)
+----------------------------------+
|  MIDI loading speed comparison   |
+-------------+--------------------+
|     Lib     |       Speed        |
+-------------+--------------------+
| MidiToolkit | 0.7407 ± 0.750 sec |
|   SyMusic   | 0.0014 ± 0.001 sec |
+-------------+--------------------+

The good news is that almost all the notes are retrieved exactly the same, with a few exception after time signature changes. Here are the saved files. failed_midis.zip And of course, the speed boost, 529 times faster in average here 🥲🥲

I'll answer to each points respectively now.

@Yikai-Liao

  1. Note.duration: Great, I though the two were "stored" as attributes. I actually don't see any advantage of storing one over the other, I think both are convenient, as long as one is stored and the other computed on the fly. :)
  2. If I have time, I'll try more elaborate tests;
  3. Type hints: I don't know methods that can provide such "interface", I which I had some knowledge on Python bindings. But this might be a good start, I'll maybe have some more free time from the period from January to March to look into it. I put this suggestion, but I don't think this is something urgent or of high priority, it's just debugging convenience for users.
  4. Marker, Lyrics, and Pitch Bend: Great, I'll inspect the PR very soon (maybe tonight, otherwise tomorrow I hope).

@lzqlzzq

  1. Noted. I still suggest to adopt the tick as default unit, and add attributes for the beat equivalent, that could be chosen to be computed or not when parsing the MIDI. The reasons are that the tick remains the native unit, and can be useful/required in many use cases. It keeps 100% of the original time information with no approximations, which is quite useful in the case of MidiTok as we use it to downsample the starting and ending time of the events. It also allows to performs computations with integers instead of floats, which can be significant in Python with large files. Even if we adopt Symusic, we would have to reconvert back all "beat" times into ticks, in Python. I assume other users of Symusic (for any task) would be in a similar situation. Now for the seconds, that's another story. I personally don't have usage of this, but users of pretty_midi do, for transcription for example. I guess supporting conversions to seconds in Symusic would attract them :) ;
  2. I got your point;
  3. I agree!;
  4. Its the empty.mid files in the test cases;
  5. That's great! For my own usage, I don't think this would be a big issue, but I guess that having appealing interfaces would convince users to adopt Symusic and minimidi.

Another thing, while writing the benchmark I had to manually recreate the lists of notes from the tracks in order to sort them. I guess the *List classes could benefit from a .sort method call.

Again, I want to thank you for developing this and getting to MidiTok. I truly think that once achieved (i.e. all MIDI data parsed and written back as original), Symusic + minimidi can durably replace miditoolkit / pretty_midi and maybe even Mido for some usages. In the case of MidiTok, it would allow to entirely get rid the current json conversion step which is performed before training models, as loading MIDIs on the fly can be a bottleneck.

Yikai-Liao commented 10 months ago
Yikai-Liao commented 10 months ago

@Natooz I have refactored almost all the code using template, which allows users to choose the time_unit by themselves.

The new version of symusic v0.1.0 have been uploaded to pypi, although I haven't update the README file, tutorial.ipynb and symusic.pyi. I fill fix these problems as soon as possible.

However, it might be easy for you to explore the new version:

from symusic import Score
score = Score("path to your midi", time_unit="tick")
for track in score.tracks:
    for note in track.notes:
        cur_tick = note.time  # Here, for all the event, I use time attribute to represent the time stamp (instead of start for Note)
        duration = note.duration
Natooz commented 10 months ago

@Yikai-Liao This is great! I got some time this morning to test it. I managed to make more comprehensive tests. I updated the gist, you can take a look, almost every MIDI content is tested. This can serve as a reference to continue to fix the bugs until having a version ready to be integrated 🙌

I added "TODOs" notes in the code where I found things that could be improved, namely:

When I run it on the same MIDI files, I get the same errors for the notes, and one file has errors with tempos, and the last one has a key signature mismatch, its key is -4 whereas the one loaded with MIDIToolkit has key_number=8.

POP909_191.mid: 105 errors (0.05)
POP909_008.mid: 156 errors (0.09)
6338816_Etude No. 4.mid: expected 46 tempos, got 49
6338816_Etude No. 4.mid: 3 errors (0.00)
POP909_022.mid: 13 errors (0.01)
POP909_010.mid: 127 errors (0.07)
6354774_Macabre Waltz.mid: 1 errors (0.00)

I also tested on the multitrack files, and got more errors but I think we can keep on working the "one_track" ones for the moment.

I did not have time to really explore the code of Symusic, I'll try to do so and maybe send PR if I find things where I can help. And for the two following weeks, I will not be 100% available (conference + vacation) but I'll try my best to answer in the best delay :)

Yikai-Liao commented 10 months ago

Thanks for your reply. We are grateful for your help in testing it!

Maybe there's something wrong with my expression, I mean that even if I tweak some of the bindings, debugging tool might be able to help you to see the new interface.

Also, after the refactoring, the code is a lot more abstract and indeed harder to read.

Btw, I think I should also prepare more for the my 12.10 gre test these days

Natooz commented 10 months ago

@Yikai-Liao @lzqlzzq thank you for the active development of symusic and minimidi! I run the tests with the new update, on both one track and multitrack MIDI test files, all the content is parsed exactly as it should, congratulations! 🎉 There are still a few tests not passing, but that's only edge cases up to debate, and not problematic in my opinion to begin integrate symusic in miditok. Here are the test logs, with the explanations of each fail commented:

/Users/nathan/git/MidiTok/venv/bin/python /Users/nathan/git/MidiTok/tests/benchmark_symusic.py 
Loading MIDIs:  59%|█████▉    | 16/27 [00:16<00:09,  1.13it/s]
6354774_Macabre Waltz.mid: 1 errors (0.00)  # key signature number mismatch
Loading MIDIs:  67%|██████▋   | 18/27 [00:17<00:05,  1.68it/s]
Mr. Blue Sky.mid: 1 errors (0.00)  # same as above
Loading MIDIs:  70%|███████   | 19/27 [00:17<00:04,  1.70it/s]
Les Yeux Revolvers.mid: 1 errors (0.00)  # same again
Loading MIDIs:  74%|███████▍  | 20/27 [00:18<00:03,  1.79it/s]
Funkytown.mid: expected 1time signatures, got 3  # 2 last time sigs are likely to be filterd by mido
Funkytown.mid: 2 errors (0.00)
Loading MIDIs:  78%|███████▊  | 21/27 [00:18<00:03,  1.72it/s]
Aicha.mid: expected 17 markers, got 30  # all extras markers feature empty text values
Aicha.mid: 13 errors (0.00)
Loading MIDIs:  96%|█████████▋| 26/27 [00:22<00:00,  1.50it/s]
Girls Just Want to Have Fun.mid: 1 errors (0.00)  # key sig mismatch
Loading MIDIs: 100%|██████████| 27/27 [00:23<00:00,  1.17it/s]
+----------------------------------+
|  MIDI loading speed comparison   |
+-------------+--------------------+
|     Lib     |       Speed        |
+-------------+--------------------+
| MidiToolkit | 0.5160 ± 0.516 sec |
|   SyMusic   | 0.0014 ± 0.001 sec |
+-------------+--------------------+

I updated the test script to handle the symusic update, and left some todo notes for things that could benefit from some improvement (in symusic and/or just the test script itself). In summary, they are:

For the markers and time signatures, I don't know if we should filter them as mido might do. As the library plays the role of a parser, it is intended to retrieve the data exactly as it is within the file without altering (filtering) it even if it is invalid our outside the recommended values, leaving this last task up to the user. But maybe a nice feature would be to offer users a ‘filter_unrecommended_values‘ argument when loading the MIDI, that would make checks when parsing time signatures, markers, lyrics or other concerned events, and not parse those containing values outside the recommended values in the General MIDI specifications.

In the coming days I'll open a PR here for the integration of Symusic. This will mark either the 2.2.0 or 3.0.0 version, depending on the amount of changes. The plan is to remove miditoolkit for the requirements, replace it with symusic, but still support miditoolkit.MidiFile objects for the tokenizer.midi_to_tokens() method for retrocompatibility by converting it to a symusic.Score object. For the writing process, I'll probably begin by making a method calling mido. Ultimately it would be great for users if symusic could do it. I don't realize the work load for this, do you have any clue/directions?

Yikai-Liao commented 10 months ago

Thanks for your test.

Simple Parsing Error and Design Problems


@dataclass(frozen=True)
class ScoreFactory:
    __core_classes = CoreClasses(core.ScoreTick, core.ScoreQuarter, smt.ScoreSecond)

    def __call__(self, file: Union[str, Path], ttype: smt.GeneralTimeUnit = TimeUnit.tick):
        if isinstance(file, str) or isinstance(file, Path):
            return self.from_file(file, ttype)

    def from_file(self, path: Union[str, Path], ttype: smt.GeneralTimeUnit = TimeUnit.tick) -> smt.Score:
        a = self.__core_classes.dispatch(ttype)
        return a.from_file(path)

Hard Problems

Future

Natooz commented 10 months ago
Natooz commented 10 months ago

I have begun the switch to symidi, it's going well but there are still a few things blocking its full completion:

Additionally, the PedalTick class yields a "Expected type 'bool', got 'int' instead" warning for the duration argument;

I'll take a look at those if I have time before you do. Right now, the tokenization step is working, only the detokenization is blocked (first bullet point above). Didn't try the other tests. I'll soon open the PR, I just wanted to do as much as possible before. The tests will fail first as there are still a lot of things to fix / improve.

Natooz commented 10 months ago

Also it would be great to implement the __eq__ magic methods for the "container" (Note...) and Score classes. They are somehow implemented as they are dataclasses, but only work when comparing the exact same object:

midi = Score(midi_path)
midi2 = Score(midi_path)

midi.tracks[0].notes[0] == midi.tracks[0].notes[0]
# True

midi.tracks[0].notes[0] == midi2.tracks[0].notes[0]
# False
Yikai-Liao commented 10 months ago
Natooz commented 10 months ago

Great, thank you for the very prompt reaction! No worry for the current compilation issue :)

For the first bullet point, I was referring to the base type of the objects. It seems to need to be addressed when using pybind: https://stackoverflow.com/questions/76239369/in-pybind11-is-it-possible-to-determine-whether-a-pyobject-is-a-pydict

Also for the __eq__ method, you can take a look at how it is done in miditoolkit. Normally the __eq__ of dataclasses will automatically compare the values of their attributes, however here it seems to check if the compared element is the exact same object (id).

Yikai-Liao commented 9 months ago

Well, I do understand the part about __eq__. But I still don't get what you do you mean, about isinstance(). Could you give me a few examples about the usage?

Natooz commented 9 months ago

Of course, here is an example in MidiTok, in the __call__ magic method where MidiFile is replaced by Score, that's allows to directly route to the expected method.

Yikai-Liao commented 9 months ago

Like this?

from symusic import Score
from symusic.core import ScoreTick
s = Score("path")
assert isinstance(s, Score)  # fail, but you want to make is pass
assert isinstance(s, ScoreTick)  # pass
Natooz commented 9 months ago

Yes that's it! I just assumed that there would be some inheritance here making it work with Score, I'll just use ScoreTick as we are working in ticks anyway. Apologies for bothering you with this :)

Yikai-Liao commented 9 months ago

Well, it's hard to make isinstance(s, Score) work, because Score is not a class. it is an instance of ScoreFactory.

On the other hand, the purpose of putting all the things from c++ to core is that, I don't want to expose those annoying NoteTick, NoteQuarter, NoteSecond to users. Directly checking ScoreTick may lead to another 2 if-else statement if you want to support other time-unit (although they all will have simple convertion methods among each other)

Maybe there is a better encapsulation method?

What about writing a isinstance(alignwith maybe a better name) method for all those factory class?

Natooz commented 9 months ago

I agree with you that it's nicer to not expose *Tick, *Quarter *Second classes.

Maybe an isinstance for Score covering these subclasses could have some interest/use cases? That's ok for me to not have it.

Now, do you think we could have interfaces to create Note, Track, Tempo etc classes from there attributes, such as note = Note(my_time, dur, pitch, vel)?

Yikai-Liao commented 9 months ago

Doesn't the current version work?


@dataclass(frozen=True) 
 class NoteFactory: 
     __core_classes = CoreClasses(core.NoteTick, core.NoteQuarter, core.NoteSecond) 

     def __call__( 
         self, 
         time: smt.TimeDtype, 
         duration: smt.TimeDtype, 
         pitch: int, 
         velocity: int, 
         ttype: smt.GeneralTimeUnit = TimeUnit.tick, 
     ) -> smt.Note: 
         """ 
         Note that `smt.TimeDtype = Union[int, float]`, and Note constructor requires `int` or `float` as time. 
         So Type Checker like MyPy will complain about the type of `time` argument. 
         However, float and int can be converted to each other implicitly. 
         So I just add a `# type: ignore` to ignore the type checking. 
         """ 
         return self.__core_classes.dispatch(ttype)(time, duration, pitch, velocity)  # type: ignore
Natooz commented 9 months ago

It's working for notes, tempos etc Just missing for Track

Yikai-Liao commented 9 months ago

It's working for notes, tempos etc Just missing for Track

I'll fix it.

And in your test script, all the bugs are fixed now 🤗 (our definition of key signature is different from miditoolkit, and we just follows midi standard.@lzqlzzq )

I release this version later.

Thank you again for your test. We can't find thouse bugs without them.

Then, we will move on to midi writing.

Natooz commented 9 months ago

You're welcome, it's the least I can do :)

Also found an error on PedalFactory (mine actually, I'll just open a PR to fix it)

Natooz commented 9 months ago

Also TrackTickList doesn't have the new .sort method with inplace argument. If I have time tomorrow I'll take a look and send a PR for this.

Yikai-Liao commented 9 months ago

Also TrackTickList doesn't have the new .sort method with inplace argument. If I have time tomorrow I'll take a look and send a PR for this.

This have been fixed. You could try it by installing from the source code.

And this is why there were so many miss matches on note and control change in your benchmark before.

I'll release this version with new track factory tomorrow.

Yikai-Liao commented 9 months ago

I have created three projects as symusic's todo list.

Natooz commented 9 months ago

Great idea!

I'm currently in vacation abroad til next Monday, so I'll try to take time to enjoy it, but I'll still be able to work a bit in the evening. I'll first focus on integrating symusic within MidiTok, and then if I can to contribute to symusic.

Yikai-Liao commented 9 months ago

midi writing is now supported 🤗

Natooz commented 9 months ago

Wonderful! 🤩 I'll test it in the coming days!

Natooz commented 9 months ago

I have begun the new tests, right now 50 out of 136 tests on single track tokenization - detokenization are passing! 🙌 A lot of things need to be fix/adapted within MidiTok, which I'm taking care of. I'll keep you updated.

On the meantime, would it be possible to have a .end property for the Pedal class, similarly to Note?

Yikai-Liao commented 9 months ago

ok,I'll add it

Natooz commented 9 months ago

@Yikai-Liao there is still one test not passing, that might come from symusic's writing - loading (but not sure 100%). I'll try to add tests to the library now in order to make sure.

Also, while when I load back a midi saved with symusic I can retrieve all the tracks with their programs, when I open it with a DAW (Logic Pro), all the tracks have the same program, being the same as the first one, except for drums tracks. There may be a channel mismatch or something else in the writing process?

Here are two examples: Archive.zip The originals are in the miditok tests repo

Yikai-Liao commented 9 months ago

@Yikai-Liao there is still one test not passing, that might come from symusic's writing - loading (but not sure 100%). I'll try to add tests to the library now in order to make sure.

Also, while when I load back a midi saved with symusic I can retrieve all the tracks with their programs, when I open it with a DAW (Logic Pro), all the tracks have the same program, being the same as the first one, except for drums tracks. There may be a channel mismatch or something else in the writing process?

Here are two examples: Archive.zip The originals are in the miditok tests repo

well,in symusic, there is no channel information. there is only program currently. So for convenience, when writing midi back, I just set all track except for drum to channel 0. I'm not sure will this cause a problem.

Yikai-Liao commented 9 months ago

@Yikai-Liao there is still one test not passing, that might come from symusic's writing - loading (but not sure 100%). I'll try to add tests to the library now in order to make sure.

Also, while when I load back a midi saved with symusic I can retrieve all the tracks with their programs, when I open it with a DAW (Logic Pro), all the tracks have the same program, being the same as the first one, except for drums tracks. There may be a channel mismatch or something else in the writing process?

Here are two examples: Archive.zip The originals are in the miditok tests repo

I dumped those two midi files with symusic, and asked someone else to load them to Logic Pro and FL studio. He said that, they are loaded correctly.

Natooz commented 9 months ago

Here is what it looks like with Logic Pro

Capture d’écran 2023-12-18 à 09 40 25

I haven't had time to figure what's wrong, but I can tell something is wrong (I never encountered this with other libs) as I just tested it again.

Yikai-Liao commented 9 months ago

cca3162c7140c74fff3bcdcf72e6ac0 We can't reproduce this problem. There is no problem loading midis both in you zip or dumped by myself

Natooz commented 9 months ago

Great that's good news! I prefer something being wrong just on my machine, now time to find what. Could you share the MIDI you dumped? Was it dumped on macOS (arm/intel) or something else?

FYI I encounter this bug with symusic 0.2.1, macOS 12.7 intel

Yikai-Liao commented 9 months ago

Great that's good news! I prefer something being wrong just on my machine, now time to find what. Could you share the MIDI you dumped? Was it dumped on macOS (arm/intel) or something else?

FYI I encounter this bug with symusic 0.2.1, macOS 12.7 intel

The midi is exactly the same as yours. And we test it on m1 and m2 mmexport1702891431946.jpg mmexport1702891958319.png

Natooz commented 9 months ago

I meant that maybe the MIDI might have been dumped differently from the different platforms? Or maybe this might just come from Logic Pro? I can't find where to see the version. I'll be able to test on my second machine in a few hours, which has an other version installed.

Yikai-Liao commented 9 months ago

Music.zip well, I just read your midi files and re-dump them (on Linux). I thought they were same. However, when looking at there md5, they are different. But both these two version can be loaded correctly.

Natooz commented 9 months ago

I have the same problem with these files, so the issue must come from my installation (Logic 10.7.4). I'll test on my desktop when I'll get home. But I assume there is no issue/bug within symusic then. Thank you for sharing the files!

Natooz commented 9 months ago

I have the same issue on my other machine, with Logic Pro 10.6.0. I doubt that the issue comes from my two machines.

Yikai-Liao commented 9 months ago

I have the same issue on my other machine, with Logic Pro 10.6.0. I doubt that the issue comes from my two machines.

Maybe you could try fl studio, which also works as excepted in my test.

Natooz commented 9 months ago

It can be opened normally with signal. I don't know what to think about it, I'll just leave it for later maybe and focus on more important matters (tests, integration...)