From 0d152ea6bd456af98c69a0b2af6ad036d257268b Mon Sep 17 00:00:00 2001 From: Jacopo Donati <33698919+jacopodonati@users.noreply.github.com> Date: Thu, 13 Jun 2024 14:28:01 +0200 Subject: [PATCH 1/3] Remove singing --- README.md | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/README.md b/README.md index 6d23218..7bba251 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,6 @@ The precision of Music makes it the perfect choice for many scientific uses. At * **Sample-based synth**, meaning that the state is updated at each sample. For example, when we have a note with a vibrato, each sample is associated to a different frequency. By doing this the synthesized sound is the closest it can be to the mathematical model that describes it. * **Musical structures** with emphasis in symmetry and discourse. -* **Speech and singing interfaces** available to synthesize voices. Music can be used alone or with other packages, and it's ideal for audiovisualization of data. For example, it can be used with [Percolation](https://github.com/ttm/percolation) and [Participation](https://github.com/ttm/participation) for harnessing open linked social data, or with [audiovisual analytics vocabulary and ontology (AAVO)](https://github.com/ttm/aavo). @@ -38,32 +37,6 @@ This install method is especially useful when reloading the modified module in s 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). -To use the singing interface you'll also nead eSpeak, SoX, and abcMIDI, while MIDI.pm and FFT.pm are needed by eCantorix to synthesize singing sequences. - -#### Linux - -On Ubuntu everything can be installed with: - -```console -sudo apt install espeak sox abcmidi -sudo cpan install MIDI -sudo cpan install Math::FFT -``` - - - -#### macOS - -On macOS you can first install [Homebrew](https://brew.sh/), and then: - -```console -brew install espeak sox abcmidi perl -cpan install MIDI -cpan install Math::FFT -``` - - - ## 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. @@ -78,10 +51,6 @@ The modules are: * **io** for reading and writing WAV files, both mono and stereo. * **functions** for normalization. * **structures** for higher level musical structures such as permutations (and related to algebraic groups and change ringing peals), scales, chords, counterpoint, tunings, etc. -* **singing** for singing with eCantorix. While it's not properly documented, and it might need some tweaks, it's working. Speech is currently achieved through espeak in the most obvious way, using os.system as in: - * [Penalva](https://github.com/ttm/penalva/blob/master/penalva.py) - * [Lunhani](https://github.com/ttm/lunhani/blob/master/lunhani.py) - * [Soares](https://github.com/ttm/soares/blob/master/soares.py) * **legacy** for musical pieces that are rendered with the Music package and might be used as material to make more music. * **tables** for the generation of lookup tables for some basic waveform. * **utils** for various functions regarding conversions, mix, etc. From e2e5ff0b46b9472d0abdd9721b9df29a5ff87fc9 Mon Sep 17 00:00:00 2001 From: Jacopo Donati <33698919+jacopodonati@users.noreply.github.com> Date: Thu, 13 Jun 2024 15:12:39 +0200 Subject: [PATCH 2/3] Fix self.peal_direct not initialized --- music/structures/peals/plain_changes.py | 1 + 1 file changed, 1 insertion(+) diff --git a/music/structures/peals/plain_changes.py b/music/structures/peals/plain_changes.py index 7315cd1..d483fbb 100644 --- a/music/structures/peals/plain_changes.py +++ b/music/structures/peals/plain_changes.py @@ -37,6 +37,7 @@ def __init__(self, nelements=4, nhunts=None, hunts=None): sympy.combinatorics.Permutation(i, i + 1, size=nelements) for i in range(nelements - 1)] self.domains = [] + self.perform_peal(nelements, dict(hunts)) self.hunts = hunts self.nelements = nelements From 1beb944ad629379d14014488de81fa30faf7940f Mon Sep 17 00:00:00 2001 From: Jacopo Donati <33698919+jacopodonati@users.noreply.github.com> Date: Thu, 13 Jun 2024 15:29:20 +0200 Subject: [PATCH 3/3] Update examples --- README.md | 7 +++++ examples/campanology.py | 30 ++++++++++---------- examples/chromatic_scale.py | 30 ++++++++++++++++++++ examples/geometric_music.py | 25 ++++++++-------- examples/isynth.py | 11 +++---- examples/noisy.py | 22 ++++++++++++++ examples/penta_effects.py | 52 ++++++++++++++++++++++++++++++++++ examples/thirty_notes.py | 12 ++++---- examples/thirty_numpy_notes.py | 26 +++++++---------- 9 files changed, 161 insertions(+), 54 deletions(-) create mode 100644 examples/chromatic_scale.py create mode 100644 examples/noisy.py create mode 100644 examples/penta_effects.py diff --git a/README.md b/README.md index 7bba251..50ab7e9 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,13 @@ Every dependency is installed by default by `pip`, but you can take a look at [r Inside [the examples folder](https://github.com/ttm/music/tree/master/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. + ## Package structure The modules are: diff --git a/examples/campanology.py b/examples/campanology.py index fdc0dbd..4190368 100644 --- a/examples/campanology.py +++ b/examples/campanology.py @@ -1,21 +1,21 @@ import music -############## -# Notice that you might relate a peal or any set of permutations -# to a sonic characteristic (frequency, duration, vibrato depth, vibrato frequency, -# attack duration, etc) through at least 3 methods: -# 1) initiate a Being(), set its permutations to the permutation sequence, -# its domain to the values to be permuted, and its curseq to -# the name of the Being sequence to be yielded by the permutation of the domain. -# -# 2) Achieve the sequence of values through peal.act() or just using permutation(domain) -# for all the permutations at hand. -# Then render the notes directly (e.g. using M.core.V_) or passing the sequence of values -# to a synth, such as Being() +""" +Notice that you might relate a peal or any set of permutations to a sonic +characteristic (frequency, duration, vibrato depth, vibrato frequency, attack +duration, etc.) through at least 3 methods: +1) initiate a Being(), set its permutations to the permutation sequence, its + domain to the values to be permuted, and its curseq to the name of theBeing + sequence to be yielded by the permutation of the domain. +2) Achieve the sequence of values through peal.act() or just using permutation + (domain) for all the permutations at hand. Then render the note directly + (e.g. using M.core.V_) or passing the sequence of values to a synth, such + as Being(). +""" -pe3 = music.structures.peals.PlainChanges.PlainChanges(3) +pe3 = music.structures.peals.PlainChanges(3) music.structures.symmetry.print_peal(pe3.act(), [0]) -freqs = sum(pe3.act([220,440,330]), []) +freqs = sum(pe3.act([220, 440, 330]), []) nnotes = len(freqs) @@ -23,7 +23,7 @@ being.f_ = freqs being.render(nnotes, 'campanology_1.wav') -### OR +# OR being = music.legacy.Being() being.domain = [220, 440, 330] being.perms = pe3.peal_direct diff --git a/examples/chromatic_scale.py b/examples/chromatic_scale.py new file mode 100644 index 0000000..8909bc3 --- /dev/null +++ b/examples/chromatic_scale.py @@ -0,0 +1,30 @@ +""" Simple script that writes a chromatic scale on a WAV file. """ + +import music + +scale = [ + 261.63, # C4 + 277.18, # C#4 + 293.66, # D4 + 311.13, # D#4 + 329.63, # E4 + 349.23, # F4 + 369.99, # F#4 + 392.00, # G4 + 415.30, # G#4 + 440.00, # A4 + 466.16, # A#4 + 493.88 # B4 +] + +sonic_vector = [] + +for note in scale: + sound = music.core.synths.note(freq=note, + duration=0.4) + sonic_vector.append(sound) + +stack = music.utils.horizontal_stack(*sonic_vector) + +music.core.io.write_wav_mono(sonic_vector=stack, + filename='chromatic_scale.wav') diff --git a/examples/geometric_music.py b/examples/geometric_music.py index 96f304d..265bc88 100644 --- a/examples/geometric_music.py +++ b/examples/geometric_music.py @@ -6,29 +6,28 @@ # 2) set its parameters using sequences to be iterated through being.d_ = [1/2, 1/4, 1/4] # durations in seconds -being.fv_ = [0, 1,5,15,150,1500,15000] # vibrato frequency +being.fv_ = [0, 1, 5, 15, 150, 1500, 15000] # vibrato frequency being.nu_ = [5] # vibrato depth in semitones (maximum deviation of pitch) being.f_ = [220, 330] # frequencies for the notes s1 = being.render(30) being.f_ += [440] -being.fv_ = [1,2,3,4,5] +being.fv_ = [1, 2, 3, 4, 5] s2 = being.render(30) -s3 = music.utils.horizontal_stack(s1, s2, s1 + s2, (s1, s2), - s1*s2[::-1], - s1[::7] + s2[::7]) +s3 = music.utils.horizontal_stack(s1, s2, s1 + s2, (s1, s2), s1*s2[::-1], + s1[::7] + s2[::7]) -# X) Tweak with special sets of permutations derived from change ringing (campanology) -# or from finite group theory (algebra): +# X) Tweak with special sets of permutations derived from change ringing +# (campanology) or from finite group theory (algebra): nel = 4 -pe4 = music.structures.peals.PlainChanges.PlainChanges(nel) +pe4 = music.structures.PlainChanges(nel) being.perms = pe4.peal_direct -being.domain = [220*2**(i/12) for i in (0,3,6,9)] +being.domain = [220 * 2 ** (i / 12) for i in (0, 3, 6, 9)] being.curseq = 'f_' being.f_ = [] nnotes = len(being.perms)*nel # len(being.perms) == factorial(nel) being.stay(nnotes) -being.nu_= [0] +being.nu_ = [0] being.d_ += [1/2] s4 = being.render(nnotes) @@ -39,9 +38,9 @@ b2.f_ = [] nnotes = len(being.perms)*nel # len(being.perms) == factorial(nel) b2.stay(nnotes) -b2.nu_= [2,5,10,30,37] -b2.fv_ = [1,3,6,15,100,1000,10000] -b2.d_ = [1,1/6,1/6,1/6] +b2.nu_ = [2, 5, 10, 30, 37] +b2.fv_ = [1, 3, 6, 15, 100, 1000, 10000] +b2.d_ = [1, 1/6, 1/6, 1/6] s42 = b2.render(nnotes) i4 = music.structures.permutations.InterestingPermutations(4) diff --git a/examples/isynth.py b/examples/isynth.py index d96b3b9..9af124c 100644 --- a/examples/isynth.py +++ b/examples/isynth.py @@ -1,14 +1,15 @@ import music -tables = music.legacy.tables.Basic() -pe3 = music.structures.peals.PlainChanges.PlainChanges(3) +tables = music.tables.PrimaryTables() +pe3 = music.structures.PlainChanges(3) music.structures.symmetry.print_peal(pe3.act(), [0]) -freqs = sum(pe3.act([220,440,330]), []) +freqs = sum(pe3.act([220, 440, 330]), []) -isynth = music.legacy.IteratorSynth.IteratorSynth() +isynth = music.legacy.IteratorSynth() isynth.fundamental_frequency_sequence = freqs isynth.tab_sequence = [tables.sine, tables.triangle, tables.square, tables.saw] -pcm_samples = music.utils.horizontal_stack(*[isynth.renderIterate() for i in range(len(freqs))]) +pcm_samples = music.utils.horizontal_stack(*[isynth.renderIterate() + for i in range(len(freqs))]) music.core.io.write_wav_mono(pcm_samples, 'isynth.wav') diff --git a/examples/noisy.py b/examples/noisy.py new file mode 100644 index 0000000..a4b6300 --- /dev/null +++ b/examples/noisy.py @@ -0,0 +1,22 @@ +""" Simple script that writes a pentatonic scale on a WAV file + with different effects. +""" + +import music + +noises = ['brown', 'pink', 'white', 'blue', 'violet', 'black'] +sonic_vector = [] +silence = music.core.synths.silence(duration=0.4) +beep = music.core.synths.note(duration=0.1) + +for noise in noises: + sonic_vector.append(music.core.synths.noises.noise(noise_type=noise)) + sonic_vector.append(silence) + sonic_vector.append(beep) + sonic_vector.append(silence) + +sonic_vector.append(music.core.synths.noises.gaussian_noise()) + +stack = music.utils.horizontal_stack(*sonic_vector) +music.core.io.write_wav_stereo(sonic_vector=stack, + filename='noisy.wav') diff --git a/examples/penta_effects.py b/examples/penta_effects.py new file mode 100644 index 0000000..71f98d7 --- /dev/null +++ b/examples/penta_effects.py @@ -0,0 +1,52 @@ +""" Simple script that writes a pentatonic scale on a WAV file + with different effects. +""" + +import music + +scale = [ + 261.63, # C4 + 293.66, # D4 + 329.63, # E4 + 392.00, # G4 + 440.00 # A4 +] + +sonic_vector = [] +for note in scale: + sound = music.core.synths.note(freq=note, + duration=0.4) + sonic_vector.append(sound) + +sonic_vector.append(music.core.synths.silence()) + +for note in scale: + sound = music.core.synths.note_with_glissando(start_freq=note, + end_freq=note+30, + duration=0.4) + sonic_vector.append(sound) + +sonic_vector.append(music.core.synths.silence()) + +for note in scale: + sound = music.core.synths.note_with_vibrato(freq=note, + duration=0.4) + sonic_vector.append(sound) + +sonic_vector.append(music.core.synths.silence()) + +for note in scale: + sound = music.core.synths.note_with_doppler(freq=note, + duration=0.4) + sonic_vector.append(sound) + +sonic_vector.append(music.core.synths.silence()) + +for note in scale: + sound = music.core.synths.note_with_fm(freq=note, + duration=0.4) + sonic_vector.append(sound) + +stack = music.utils.horizontal_stack(*sonic_vector) +music.core.io.write_wav_stereo(sonic_vector=stack, + filename='penta_effects.wav') diff --git a/examples/thirty_notes.py b/examples/thirty_notes.py index 2c1ddf5..b909e33 100644 --- a/examples/thirty_notes.py +++ b/examples/thirty_notes.py @@ -1,13 +1,13 @@ -import music +from music.legacy import Being -# 1) start a ѕynth -being = music.legacy.Being() +# 1) start a synth +being = Being() # 2) set its parameters using sequences to be iterated through being.d_ = [1/2, 1/4, 1/4] # durations in seconds -being.fv_ = [0, 1,5,15,150,1500,15000] # vibrato frequency +being.fv_ = [0, 1, 5, 15, 150, 1500, 15000] # vibrato frequency being.nu_ = [5] # vibrato depth in semitones (maximum deviation of pitch) being.f_ = [220, 330] # frequencies for the notes -# 3) render the wavfile -being.render(30, 'thirty_notes.wav') # render 30 notes iterating though the lists above +# 3) render the wavfile with 30 notes iterating though the lists above +being.render(30, 'thirty_notes.wav') diff --git a/examples/thirty_numpy_notes.py b/examples/thirty_numpy_notes.py index 772d155..766a9b6 100644 --- a/examples/thirty_numpy_notes.py +++ b/examples/thirty_numpy_notes.py @@ -1,23 +1,19 @@ -import music +from music.legacy import Being +from music.utils import horizontal_stack +from music.core.io import write_wav_stereo # 1) start a ѕynth -being = music.legacy.Being() - -# 2) set its parameters using sequences to be iterated through -being.d_ = [1/2, 1/4, 1/4] # durations in seconds -being.fv_ = [0, 1,5,15,150,1500,15000] # vibrato frequency -being.nu_ = [5] # vibrato depth in semitones (maximum deviation of pitch) -being.f_ = [220, 330] # frequencies for the notes +being = Being() # 3) Use numpy arrays directly and use them to concatenate and/or mix sounds: s1 = being.render(30) being.f_ += [440] -being.fv_ = [1,2,3,4,5] +being.fv_ = [1, 2, 3, 4, 5] s2 = being.render(30) -# s1 then s2 then s1 and s2 at the same time, then at the same time but one in each LR channel, -# then s1 times s2 reversed, then s1+s2 but jumping 6 samples before using one: -s3 = music.utils.horizontal_stack(s1, s2, s1 + s2, (s1, s2), - s1*s2[::-1], - s1[::7] + s2[::7]) -music.core.io.write_wav_stereo(s3, 'thirty_numpy_notes.wav') +# s1 then s2 then s1 and s2 at the same time, then at the same time but one in +# each LR channel, then s1 times s2 reversed, then s1+s2 but jumping 6 samples +# before using one: +s3 = horizontal_stack(s1, s2, s1 + s2, (s1, s2), s1*s2[::-1], s1[::7] + + s2[::7]) +write_wav_stereo(s3, 'thirty_numpy_notes.wav')