Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ ipython_log.py
dist/
tests/
music/singing/ecantorix/
.vscode/
23 changes: 11 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,29 +24,28 @@ To install music you can either install it directly with `pip`:
pip3 install music
```

or you can clone this repository and install it from there:
To install the Music package in editable mode from source:

```console
git clone https://github.com/ttm/music.git
pip3 install -e <path_to_repo>
cd music
uv sync
```

This install method is especially useful when reloading the modified module in subsequent runs of music, and for greater control of customization, hacking and debugging.

### Dependencies

Every dependency is installed by default by `pip`, but you can take a look at [requirements.txt](https://github.com/ttm/music/blob/master/requirements.txt).
Dependencies are managed via UV and locked in [uv.lock](uv.lock).

## Examples

Inside [the examples folder](https://github.com/ttm/music/tree/master/examples) you can find some scripts that use the main features of Music.
Inside [the examples folder](https://github.com/ttm/music/tree/master/music/examples) you can find some scripts that use the main features of Music.

* [chromatic_scale](https://github.com/ttm/music/tree/master/examples/chromatic_scale.py): writes twelve notes into a WAV file from a sequence of frequencies.
* [penta_effects](https://github.com/ttm/music/tree/master/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/examples/noisy.py): writes into a WAV file a sequence of different noises.
* [thirty_notes](https://github.com/ttm/music/tree/master/examples/thirty_notes.py) and [thirty_numpy_notes](https://github.com/ttm/music/tree/master/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/examples/campanology.py) and [geometric_music](https://github.com/ttm/music/tree/master/examples/geometric_music.py) both use `Being` as their synth, but this time with permutations.
* [isynth](https://github.com/ttm/music/tree/master/examples/isynth.py) also uses a synth class, but of a different kind, [`IteratorSynth`](https://github.com/ttm/music/tree/master/music/legacy/classes.py), that iterates through arbitrary lists of variables.
* [chromatic_scale](https://github.com/ttm/music/tree/master/music/examples/chromatic_scale.py): writes twelve notes into a WAV file from a sequence of frequencies.
* [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.

* [isynth](https://github.com/ttm/music/tree/master/music/examples/isynth.py) also uses a synth class, but of a different kind, [`IteratorSynth`](https://github.com/ttm/music/tree/master/music/legacy/classes.py), that iterates through arbitrary lists of variables.

## Package structure

Expand Down
23 changes: 14 additions & 9 deletions music/core/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"""

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!

from .functions import normalize_mono, normalize_stereo
from .filters import adsr, adsr_stereo

Expand All @@ -25,14 +25,17 @@ def read_wav(filename: str):
NDArray
Values of the WAV file
"""
s = wavfile.read(filename)
print(type(s[1] / 2 ** 15))
if s[1].dtype != 'int16':
# Read integer PCM data (dtype=int16)
data, sr = sf.read(filename, dtype='int16')
print(type(data / 2 ** 15))
if data.dtype != 'int16':
print('implement non 16bit samples!')
return np.array(None)
if len(s[1].shape) == 2:
return np.array(s[1].transpose() / 2 ** 15)
return s[1] / 2 ** 15
# data: shape (nsamples,) or (nsamples, channels)
if data.ndim == 2:
# convert to (channels, nsamples)
return np.array(data.T / 2 ** 15)
return data / 2 ** 15


def write_wav_mono(sonic_vector=SONIC_VECTOR_MONO, filename="asound.wav",
Expand Down Expand Up @@ -79,7 +82,8 @@ def write_wav_mono(sonic_vector=SONIC_VECTOR_MONO, filename="asound.wav",
print(f"File {filename} not written")
nn = eval("np.int" + str(bit_depth))
result = nn(result)
wavfile.write(filename, sample_rate, result)
# Write to WAV using SoundFile
sf.write(filename, result, sample_rate)


def write_wav_stereo(sonic_vector=SONIC_VECTOR_STEREO, filename="asound.wav",
Expand Down Expand Up @@ -126,4 +130,5 @@ def write_wav_stereo(sonic_vector=SONIC_VECTOR_STEREO, filename="asound.wav",
print(f"File {filename} not written")
nn = eval("np.int" + str(bit_depth))
result = nn(result)
wavfile.write(filename, sample_rate, result.T)
# Write to WAV using SoundFile
sf.write(filename, result.T, sample_rate)
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
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

File renamed without changes.
File renamed without changes.
File renamed without changes.
9 changes: 5 additions & 4 deletions music/singing/perform.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
import os
import re
from scipy.io import wavfile
import soundfile as sf
from music.core import normalize_mono

here = os.path.abspath(os.path.dirname(__file__))
Expand Down Expand Up @@ -44,9 +44,10 @@ def sing(text="Mar-ry had a litt-le lamb",
os.system('cp {}/Makefile {}/Makefile'.format(ECANTORIXDIR,
ECANTORIXCACHE))
os.system('make -C {}'.format(ECANTORIXCACHE))
wread = wavfile.read(ECANTORIXCACHE + '/achant.wav')
assert wread[0] == 44100
return normalize_mono(wread[1])
# Read generated chant as int16 PCM
data, sr = sf.read(ECANTORIXCACHE + '/achant.wav', dtype='int16')
assert sr == 44100
return normalize_mono(data)
# return wread[1]


Expand Down
22 changes: 16 additions & 6 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
[project]
name = 'music'
version = '1.0.0-b5'
version = '1.0.0-b6'
authors = [
{ name = 'Renato Fabbri', email = 'renato.fabbri@gmail.com' },
{ name = 'Jacopo Donati', email = 'jacopo.donati@gmail.com' }
{ name = 'Jacopo Donati', email = 'jacopo.donati@gmail.com' },
]
description = 'Extreme-fidelity synthesis of musical elements, based on the MASS framework'
license = { file = "LICENSE" }
license = "MIT"
readme = 'README.md'
requires-python = '>=3.0'
requires-python = '>=3.10'
dependencies = [
'colorama >= 0.4.6',
'matplotlib >= 3.7.1',
'numpy >= 1.26.4',
'scipy >= 1.12.0',
'soundfile >= 0.12.1',
'sympy >= 1.12',
'termcolor >= 2.4.0',
]
classifiers = [
'Programming Language :: Python :: 3',
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Science/Research',
Expand Down Expand Up @@ -62,7 +62,7 @@ keywords = [
'sound',
'spatialization',
'speech',
'synth'
'synth',
]

[project.urls]
Expand All @@ -72,3 +72,13 @@ Issues = 'https://github.com/ttm/music/issues'
[build-system]
requires = ['setuptools>=61.0']
build-backend = 'setuptools.build_meta'

[tool.setuptools]
include-package-data = true

[tool.setuptools.packages.find]
include = ["music*"]

# explicitly ship everything under my_pkg/examples
[tool.setuptools.package-data]
"music" = ["examples/*"]
Loading