Skip to content

feat: replace scipy.wavfile with soundfile for WAV handling#22

Open
ksylvan wants to merge 1 commit intottm:masterfrom
ksylvan:feature/replace_wavfile_with_soundfile
Open

feat: replace scipy.wavfile with soundfile for WAV handling#22
ksylvan wants to merge 1 commit intottm:masterfrom
ksylvan:feature/replace_wavfile_with_soundfile

Conversation

@ksylvan
Copy link

@ksylvan ksylvan commented Apr 17, 2025

CHANGES

  • Replace scipy.io.wavfile with soundfile for WAV file operations.
  • Update WAV reading logic to handle integer PCM data.
  • Adjust WAV writing to use soundfile library.
  • Bump version to 1.0.0-b6 in pyproject.toml.
  • Add soundfile dependency to project requirements.
  • Update installation instructions to use uv sync.
  • Restructure example files into music/examples directory.

Summary

This PR introduces several improvements to the Music package:

  • Switches WAV file IO from scipy.io.wavfile to soundfile (pysoundfile) for improved compatibility, feature set, and reliability.
  • Moves all example scripts into the package directory (music/examples/) and configures them to be included as package data.
  • Updates the build system and dependency management
    • Adds soundfile as an explicit dependency.
    • Switches dependency management instructions in the README to use uv and adds a generated uv.lock file.
    • Updates pyproject.toml to reflect these changes and properly include example scripts in distributions.
  • README improvements
    • Clarifies installation and development workflow.
    • Updates example paths.
  • Minor .gitignore update to include .vscode/.

Files Changed

Added

  • uv.lock: Lockfile for uv-based dependency management.

Modified

  • .gitignore: Adds .vscode/ to ignored files.
  • README.md:
    • Updates installation and development instructions to recommend uv.
    • Updates references to example script locations.
    • Clarifies dependency management and points to uv.lock.
  • music/core/io.py:
    • Replaces all scipy.io.wavfile usage with soundfile (sf).
    • Updates WAV reading/writing logic and comments for clarity.
  • music/singing/perform.py:
    • Replaces scipy.io.wavfile with soundfile for reading output from ecantorix.
  • pyproject.toml:
    • Updates version to 1.0.0-b6.
    • Switches license to string "MIT" and updates Python requirement to >=3.10.
    • Adds soundfile as a dependency.
    • Adds setuptools config to ensure music/examples/ is included as package data.
  • Renamed/Moved example scripts:
    • All files from examples/ are now under music/examples/ and set as package data:
      • campanology.py, chromatic_scale.py, geometric_music.py, isynth.py, noisy.py, penta_effects.py, thirty_notes.py, thirty_numpy_notes.py.

Removed

  • None.

Code Changes

music/core/io.py

  • Imports
    -from scipy.io import wavfile
    +import soundfile as sf
  • read_wav: Now uses sf.read instead of wavfile.read, always requests int16 PCM, and transposes stereo files to (channels, nsamples) for consistency.
  • write_wav_mono / write_wav_stereo: Now use sf.write instead of wavfile.write, with a comment clarifying the change.

music/singing/perform.py

  • Imports
    -from scipy.io import wavfile
    +import soundfile as sf
  • sing(): Now reads achant.wav using sf.read and checks for 44.1kHz sample rate.

pyproject.toml

  • Adds soundfile to dependencies.
  • Updates license, Python version, and package data section for setuptools to include music/examples/*.

README.md

  • Updates install and development instructions to recommend uv.
  • Updates example script paths.
  • Updates dependency management section to mention uv.lock.

.gitignore

  • Adds .vscode/ directory.

Example Scripts

  • All example scripts are moved to music/examples/ and included as package data.

uv.lock

  • New file: generated lockfile for uv, capturing the full dependency tree.

Reason for Changes

  • Switch to soundfile: soundfile offers broader file format support, better handling of non-16bit data, and improved reliability over scipy.io.wavfile. This future-proofs the codebase and aligns with modern Python audio practices.
  • Package data for examples: Moving example scripts into the package and explicitly marking them as package data ensures users (and test suites) always have access to up-to-date examples, and that they are included in both source and binary distributions.
  • Modern dependency management: uv is a fast, modern Python package/dependency manager. By switching to uv and including a lockfile, installation is reproducible and less error-prone.
  • README and install improvements: Ensures new contributors and users have clear, accurate, up-to-date instructions.
  • .gitignore update: Avoids accidental check-in of VSCode IDE config.

Impact of Changes

  • Backward compatibility: Reading/writing WAV files should remain compatible, but now supports a broader range of file types and sample formats (with future potential for non-16bit support).
  • Packaging: Example scripts will now be available in installed packages and wheels.
  • Development workflow: Encourages use of uv and a lockfile for consistent dependency management.
  • No breaking API changes are introduced in core music functionality.

Test Plan

  • Manual test: Run all example scripts in music/examples/ to verify they work as expected.
  • Confirm that WAV reading/writing (including mono and stereo) produces the same results as before.
  • Test installation with uv and verify that the lockfile ensures reproducible installs.
  • Verify that example scripts are included in both sdist and wheel builds.
  • Confirm that the package still works with the latest versions of its dependencies (see uv.lock).

Additional Notes

  • The code currently only supports 16-bit PCM WAV files for reading; a warning is printed for other formats. Extending this to handle other sample widths is a future enhancement.
  • The example scripts themselves are unchanged except for their new location; any references in documentation or other code should now point to music/examples/.
  • The move to soundfile may require users to have the underlying libsndfile library installed (typically handled automatically via pip wheels, but may require system packages on some platforms).
  • The .vscode/ ignore is a convenience for developers using Visual Studio Code and does not affect the package or build process.

### CHANGES
- Replace `scipy.io.wavfile` with `soundfile` for WAV file operations.
- Update WAV reading logic to handle integer PCM data.
- Adjust WAV writing to use `soundfile` library.
- Bump version to `1.0.0-b6` in `pyproject.toml`.
- Add `soundfile` dependency to project requirements.
- Update installation instructions to use `uv sync`.
- Restructure example files into `music/examples` directory.
Copy link
Owner

@ttm ttm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for your contributions. Any thoughts on my comments?

* [penta_effects](https://github.com/ttm/music/tree/master/music/examples/chromatic_scale.py): writes a pentatonic scale repeated once clean, once with pitch, one with vibrato, one with Doppler, and one with FM, into a WAV stereo file.
* [noisy](https://github.com/ttm/music/tree/master/music/examples/noisy.py): writes into a WAV file a sequence of different noises.
* [thirty_notes](https://github.com/ttm/music/tree/master/music/examples/thirty_notes.py) and [thirty_numpy_notes](https://github.com/ttm/music/tree/master/music/examples/thirty_numpy_notes.py) generate a sequence of sounds by using a synth class (in this case the class [`Being`](https://github.com/ttm/music/tree/master/music/legacy/classes.py)).
* [campanology](https://github.com/ttm/music/tree/master/music/examples/campanology.py) and [geometric_music](https://github.com/ttm/music/tree/master/music/examples/geometric_music.py) both use `Being` as their synth, but this time with permutations.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there URLs you gave don't work

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will double check and fix. Thanks.


import numpy as np
from scipy.io import wavfile
import soundfile as sf
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not convinced we should move from scipy.io.wavfile to soundfile. I understand it is more flexible, but do we need it? I am open to discuss it here or even in a call.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me get back to you in a day or two. I'm in the middle of a project, but thank you for the response and reviewing this!

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you would move it to the music folder? So that it will be inside the package itself whenever we import music?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I can do that. Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants