Rainbow-Dreamer / sf2_loader

This is an easy-to-use soundfonts loader, player and audio renderer in python
GNU Lesser General Public License v2.1
35 stars 8 forks source link

sf2.export_midi_file fails #9

Closed gildurao closed 2 years ago

gildurao commented 2 years ago

Hello 👋 ,

Today I tried to export a piece to a midi file, but I am facing the following errors from musicpy and midifiles.

Traceback (most recent call last):
  File "C:\Users\GilDurao\AppData\Local\Programs\Python\Python39\lib\site-packages\musicpy\musicpy.py", line 317, in read
    current_midi = midi(name)
  File "C:\Users\GilDurao\AppData\Local\Programs\Python\Python39\lib\site-packages\mido\midifiles\midifiles.py", line 323, in __init__
    with io.open(filename, 'rb') as file:
TypeError: expected str, bytes or os.PathLike object, not piece

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\GilDurao\Documents\GitHub\flutterpy-lmusic\flutterpy_lmusic\lib\yourscript.py", line 176, in <module>
    generate_music(lSystem, iterations, musicalParsing, scale, musicKey, bpm,
  File "C:\Users\GilDurao\Documents\GitHub\flutterpy-lmusic\flutterpy_lmusic\lib\yourscript.py", line 145, in generate_music
    sf2.export_midi_file(
  File "C:\Users\GilDurao\AppData\Local\Programs\Python\Python39\lib\site-packages\sf2_loader\read_sf2\read_sf2.py", line 739, in export_midi_file
    current_chord = mp.read(current_chord,
  File "C:\Users\GilDurao\AppData\Local\Programs\Python\Python39\lib\site-packages\musicpy\musicpy.py", line 319, in read
    current_midi = midi(file=riff_to_midi(name))
  File "C:\Users\GilDurao\AppData\Local\Programs\Python\Python39\lib\site-packages\musicpy\musicpy.py", line 3598, in riff_to_midi
    root = chunk.Chunk(riff_name, bigendian=False)
  File "C:\Users\GilDurao\AppData\Local\Programs\Python\Python39\lib\chunk.py", line 61, in __init__
    self.chunkname = file.read(4)
AttributeError: 'piece' object has no attribute 'read'

Here is the python code with the export midi call

sf2.export_midi_file(
        piece, name=rf"{defaultPath}\generated_music.mid")

The variable piece is correct because I can generate .wav files correctly.

Output of pip list:

PS C:\Users\GilDurao\Documents\GitHub\flutterpy-lmusic\flutterpy_lmusic> pip list
Package     Version
----------- -------
autopep8    1.5.7
keyboard    0.13.5
MIDIUtil    1.2.1
mido        1.2.10
mpu         0.23.1
musicpy     4.24
numpy       1.21.2
Pillow      8.3.2
pip         21.2.3
py          1.10.0
pycodestyle 2.7.0
pydub       0.25.1
pygame      2.0.1
pyglet      1.5.11
setuptools  57.4.0
sf2-loader  0.59
simpleaudio 1.0.4
toml        0.10.2

Output of python --version

PS C:\Users\GilDurao\Documents\GitHub\flutterpy-lmusic\flutterpy_lmusic> python --version
Python 3.9.7
Rainbow-Dreamer commented 2 years ago

I see what is causing this issue here. Let me clarify the parameters of the function export_midi_file, the first parameter current_chord is the name of the midi file you want to export as audio files using loaded soundfont files, and the name parameter is the name of the exported audio file. Here your first parameter should be rf"{defaultPath}\generated_music.mid" instead of piece, and if the variable piece is the name of the exported audio file you want, you can set name=piece, but actually piece is a defined class in musicpy, so it is better to use another variable name to avoid naming conflicts. Or if you actually want to export a piece instance to a midi file, just simply use musicpy's write function, like this:

write(piece1, name=rf"{defaultPath}\generated_music.mid")
gildurao commented 2 years ago

I see what is causing this issue here. Let me clarify the parameters of the function export_midi_file, the first parameter current_chord is the name of the midi file you want to export as audio files using loaded soundfont files, and the name parameter is the name of the exported audio file. Here your first parameter should be rf"{defaultPath}\generated_music.mid" instead of piece, and if the variable piece is the name of the exported audio file you want, you can set name=piece, but actually piece is a defined class in musicpy, so it is better to use another variable name to avoid naming conflicts.

Does this mean I first have to generate an audio file, using export_piece and then I can use export_midi_file ?

The variable piece here is the output of calling piece from `musicpy```, I can share a bitmore of my code:

 if(musicalParsing == "random"):
       # this is a musicpy chord object
        musicalOutput = lmusic_parser.random_parsing(output)
        piece = sf.mp.piece(
            [musicalOutput], [sf2.current_preset_num], random.randint(40, 220), [0], channels=[1]
        )

    now = datetime.datetime.now()
    now = now.strftime('%Y_%m_%d_%H_%M_%S')
    sf2.export_midi_file(
      piece, name=rf"{defaultPath}\generated_music_{now}.mid")

I will rename the variable, thanks for the tip

Rainbow-Dreamer commented 2 years ago

No, you can use musicpy's write function, which can directly output any musicpy data structures to a midi file. The export_midi_file function of sf2_loader is to read a midi file, and render it to an audio file using loaded soundfont files, and then export the audio file. The write function of musicpy is to convert a musicpy data structure (such as note, chord, track, piece) to a midi file.

gildurao commented 2 years ago

No, you can use musicpy's write function, which can directly output any musicpy data structures to a midi file. The export_midi_file function of sf2_loader is to read a midi file, and render it to an audio file using loaded soundfont files, and then export the audio file. The write function of musicpy is to convert a musicpy data structure (such as note, chord, track, piece) to a midi file.

Thank you for the clarification! Feel free to close the issue.

Rainbow-Dreamer commented 2 years ago

You're welcome :)