spotify / basic-pitch

A lightweight yet powerful audio-to-MIDI converter with pitch bend detection
https://basicpitch.io
Apache License 2.0
3.45k stars 273 forks source link

Doc error in README re predict_and_save() usage #141

Open drscotthawley opened 1 month ago

drscotthawley commented 1 month ago

Describe the bug Try to use predict_and_save as listed on the README and you get

TypeError: predict_and_save() missing 1 required positional argument: 'model_or_model_path'

To Reproduce Steps to reproduce the behavior:

  1. Go to README.me
  2. Create code using the template provided,
    
    from basic_pitch.inference import predict_and_save

predict_and_save(

, , , , , , ) ``` e.g. ```python predict_and_save( audio_file, '/content', True, True, True, True, ) ``` 4. Run that 5. See error **Expected behavior** Either the README code should work as is, or README should be modified to include specification of required positional argument: 'model_or_model_path' **Screenshots** If applicable, add screenshots to help explain your problem. Screenshot 2024-09-20 at 1 18 44 PM **Desktop (please complete the following information):** - OS: Linux -- Google Colab - Browser: Crhome - Version: 1.69.168 **Additional context** Add any other context about the problem here.
drscotthawley commented 1 month ago

Trying to add model path as positional argument, e.g.

from basic_pitch.inference import predict_and_save
from basic_pitch import ICASSP_2022_MODEL_PATH

predict_and_save(
    audio_file,
    '/content',
    True,
    True,
    True,
    True,
    ICASSP_2022_MODEL_PATH
)

results in this error:

Predicting MIDI for /...
/usr/local/lib/python3.10/dist-packages/basic_pitch/inference.py:229: UserWarning: PySoundFile failed. Trying audioread instead.
  audio_original, _ = librosa.load(str(audio_path), sr=AUDIO_SAMPLE_RATE, mono=True)
/usr/local/lib/python3.10/dist-packages/librosa/core/audio.py:184: FutureWarning: librosa.core.audio.__audioread_load
    Deprecated as of librosa version 0.10.0.
    It will be removed in librosa version 1.0.
  y, sr_native = __audioread_load(path, offset, duration, dtype)
---------------------------------------------------------------------------
LibsndfileError                           Traceback (most recent call last)
[/usr/local/lib/python3.10/dist-packages/librosa/core/audio.py](https://localhost:8080/#) in load(path, sr, mono, offset, duration, dtype, res_type)
    175         try:
--> 176             y, sr_native = __soundfile_load(path, offset, duration, dtype)
    177 

13 frames
[/usr/local/lib/python3.10/dist-packages/librosa/core/audio.py](https://localhost:8080/#) in __soundfile_load(path, offset, duration, dtype)
    208         # Otherwise, create the soundfile object
--> 209         context = sf.SoundFile(path)
    210 

[/usr/local/lib/python3.10/dist-packages/soundfile.py](https://localhost:8080/#) in __init__(self, file, mode, samplerate, channels, subtype, endian, format, closefd)
    657                                          format, subtype, endian)
--> 658         self._file = self._open(file, mode_int, closefd)
    659         if set(mode).issuperset('r+') and self.seekable():

[/usr/local/lib/python3.10/dist-packages/soundfile.py](https://localhost:8080/#) in _open(self, file, mode_int, closefd)
   1215             err = _snd.sf_error(file_ptr)
-> 1216             raise LibsndfileError(err, prefix="Error opening {0!r}: ".format(self.name))
   1217         if mode_int == _snd.SFM_WRITE:

LibsndfileError: Error opening '/': Format not recognised.

During handling of the above exception, another exception occurred:

IsADirectoryError                         Traceback (most recent call last)
[<ipython-input-50-2a564e710ae5>](https://localhost:8080/#) in <cell line: 1>()
----> 1 predict_and_save(
      2     audio_file,
      3     '/content',
      4     True,
      5     True,

[/usr/local/lib/python3.10/dist-packages/basic_pitch/inference.py](https://localhost:8080/#) in predict_and_save(audio_path_list, output_directory, save_midi, sonify_midi, save_model_outputs, save_notes, model_or_model_path, onset_threshold, frame_threshold, minimum_note_length, minimum_frequency, maximum_frequency, multiple_pitch_bends, melodia_trick, debug_file, sonification_samplerate, midi_tempo)
    585                     raise e
    586         except Exception as e:
--> 587             raise e

[/usr/local/lib/python3.10/dist-packages/basic_pitch/inference.py](https://localhost:8080/#) in predict_and_save(audio_path_list, output_directory, save_midi, sonify_midi, save_model_outputs, save_notes, model_or_model_path, onset_threshold, frame_threshold, minimum_note_length, minimum_frequency, maximum_frequency, multiple_pitch_bends, melodia_trick, debug_file, sonification_samplerate, midi_tempo)
    532         print("")
    533         try:
--> 534             model_output, midi_data, note_events = predict(
    535                 pathlib.Path(audio_path),
    536                 model_or_model_path,

[/usr/local/lib/python3.10/dist-packages/basic_pitch/inference.py](https://localhost:8080/#) in predict(audio_path, model_or_model_path, onset_threshold, frame_threshold, minimum_note_length, minimum_frequency, maximum_frequency, multiple_pitch_bends, melodia_trick, debug_file, midi_tempo)
    449         print(f"Predicting MIDI for {audio_path}...")
    450 
--> 451         model_output = run_inference(audio_path, model_or_model_path, debug_file)
    452         min_note_len = int(np.round(minimum_note_length / 1000 * (AUDIO_SAMPLE_RATE / FFT_HOP)))
    453         midi_data, note_events = infer.model_output_to_notes(

[/usr/local/lib/python3.10/dist-packages/basic_pitch/inference.py](https://localhost:8080/#) in run_inference(audio_path, model_or_model_path, debug_file)
    290 
    291     output: Dict[str, Any] = {"note": [], "onset": [], "contour": []}
--> 292     for audio_windowed, _, audio_original_length in get_audio_input(audio_path, overlap_len, hop_size):
    293         for k, v in model.predict(audio_windowed).items():
    294             output[k].append(v)

[/usr/local/lib/python3.10/dist-packages/basic_pitch/inference.py](https://localhost:8080/#) in get_audio_input(audio_path, overlap_len, hop_size)
    227     assert overlap_len % 2 == 0, f"overlap_length must be even, got {overlap_len}"
    228 
--> 229     audio_original, _ = librosa.load(str(audio_path), sr=AUDIO_SAMPLE_RATE, mono=True)
    230 
    231     original_length = audio_original.shape[0]

[/usr/local/lib/python3.10/dist-packages/librosa/core/audio.py](https://localhost:8080/#) in load(path, sr, mono, offset, duration, dtype, res_type)
    182                     "PySoundFile failed. Trying audioread instead.", stacklevel=2
    183                 )
--> 184                 y, sr_native = __audioread_load(path, offset, duration, dtype)
    185             else:
    186                 raise exc

<decorator-gen-157> in __audioread_load(path, offset, duration, dtype)

[/usr/local/lib/python3.10/dist-packages/librosa/util/decorators.py](https://localhost:8080/#) in __wrapper(func, *args, **kwargs)
     57             stacklevel=3,  # Would be 2, but the decorator adds a level
     58         )
---> 59         return func(*args, **kwargs)
     60 
     61     return decorator(__wrapper)

[/usr/local/lib/python3.10/dist-packages/librosa/core/audio.py](https://localhost:8080/#) in __audioread_load(path, offset, duration, dtype)
    238     else:
    239         # If the input was not an audioread object, try to open it
--> 240         reader = audioread.audio_open(path)
    241 
    242     with reader as input_file:

[/usr/local/lib/python3.10/dist-packages/audioread/__init__.py](https://localhost:8080/#) in audio_open(path, backends)
    125     for BackendClass in backends:
    126         try:
--> 127             return BackendClass(path)
    128         except DecodeError:
    129             pass

[/usr/local/lib/python3.10/dist-packages/audioread/rawread.py](https://localhost:8080/#) in __init__(self, filename)
     57     """
     58     def __init__(self, filename):
---> 59         self._fh = open(filename, 'rb')
     60 
     61         try:

IsADirectoryError: [Errno 21] Is a directory: '/'
drscotthawley commented 1 month ago

Ok, so correct syntax is to supply a list where it says to supply a list. ;-) But still positional requirement of model path remains:

predict_and_save(
    [audio_file],
    '/content',
    True,
    True,
    True,
    True,
    ICASSP_2022_MODEL_PATH
)

README example should read as follows:

predict_and_save(
    <input-audio-path-list>,
    <output-directory>,
    <save-midi>,
    <sonify-midi>,
    <save-model-outputs>,
    <save-notes>,
    <model-path>
)