MyGo 云音乐
Go와 MyGo로 만든 서드파티 네이버 클라우드 뮤직 데스크톱 클라이언트
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
Extreme-fidelity synthesis of musical elements. Music generates and manipulates sound in LPCM audio, sample by sample. It implements MASS (Music and Audio in Sample Sequences), a collection of psychophysical descriptions of musical elements expressed as equations and corresponding Python routines.README 전문 보기 ↗
Extreme-fidelity synthesis of musical elements.
Music generates and manipulates sound in LPCM audio, sample by sample. It implements MASS (Music and Audio in Sample Sequences), a collection of psychophysical descriptions of musical elements expressed as equations and corresponding Python routines.
import music
# a chromatic scale, written to a WAV file
notes = [music.note(440 * 2 ** (i / 12), duration=0.25) for i in range(13)]
scale = [music.adsr(sonic_vector=n) for n in notes] # so the joins are silent
music.write_wav_mono(music.horizontal_stack(*scale), "scale.wav")
The adsr is not decoration. A note ends wherever its phase lands and the
next one opens at the bottom of its wavetable, so concatenating them raw
steps -- by up to the full scale, and on nine of this scale's twelve joins.
An envelope runs each note to silence at both ends, which is what makes the
joins joins rather than clicks.
📖 Tutorial — from a single note to a short stereo piece. 📖 Where to start — the few routines a common task needs. 📖 API reference — every routine documented with the equation it implements and the article it comes from.
note keeps that, as MASS specifies.
bandlimited_note renders a fixed pitch without them,
render_oversampled renders any generator at several times the rate and
filters it, for FM, glissandi and hard gates, and music.core.synths
has oscillators for a pitch that moves.iir applies coefficients; low_pass, high_pass,
band_pass and band_reject compute them, from the four designs the
article specifies.setup_hrtf() fetches the KEMAR
measurements into your cache and hrir() reads a direction out of them,
so a source can be placed above you or behind you -- cues the geometric
localize cannot carry. The package ships no measurements and models no
head; it reads someone else's.StimulationSession that renders a protocol of them: phases in order,
crossfaded rather than cut, lasting exactly the sum of the durations you
wrote down. The sample-accurate synthesis is the point here, because the
frequency difference the stimulus. Each stimulus, and a session,
can be written as an SSTIM description and rendered back from one, and a
description another tool wrote is rendered from SSTIM's own terms, given
only what they cannot say.Music can be used alone or with other packages, and it is well suited to the audiovisualization of data. It works with Percolation and Participation for harnessing open linked social data, and with the audiovisual analytics vocabulary and ontology (AAVO).
To understand the routines further, read Musical elements in the discrete-time representation of sound. If you use this package, please cite that article.
Every release is archived on Zenodo, so a specific version can be cited too: 10.5281/zenodo.22151793 always resolves to the newest one. GitHub's Cite this repository button reads CITATION.cff and gives you both, formatted.
pip install music
Requires Python 3.10 or newer. Everything needed to synthesise, filter and write audio comes with it; the dependencies are declared in pyproject.toml.
The rest is optional, an extra at a time. PrimaryTables.draw_tables(),
which plots the waveform tables so you can look at them, needs matplotlib:
pip install 'music[plot]'
Nothing else in the package uses it, and it is imported only when
draw_tables runs, so leaving it out costs nothing else.
sing needs espeak-ng, a system program, and Praat, through
praat-parselmouth:
sudo apt install espeak-ng # or: brew install espeak-ng
pip install 'music[singing]'
Its second backend, eCantorix, is a Perl program the package clones for you; see Singing for what it needs.
render_oversampled needs SciPy, and writing stimuli as SSTIM
descriptions needs RDFLib and SSTIM's own validator:
pip install 'music[antialias]'
pip install 'music[sstim]'
play_audio plays a sound through sounddevice, which on Linux needs the
PortAudio library as well (sudo apt install libportaudio2). The
permutation groups and change-ringing peals are built from sympy's, about
74 MB that most callers never need, so they are an extra too; their names
import without it, and building one says what to install:
pip install 'music[play]'
pip install 'music[structures]'
To hack on it, install from a checkout so your edits take effect immediately:
git clone https://github.com/ttm/music.git
pip install -e music
Every routine returns a numpy array of PCM samples, so results compose with each other and with anything else you can express in numpy.
Every routine works at 44,100 Hz unless told otherwise. Rather than pass
sample_rate to each call, set it once -- for the rest of a script, or in
a with statement for one block; a rate given to a routine still wins:
music.set_sample_rate(48000)
with music.set_sample_rate(96000):
high = music.note(440, duration=1) # 96,000 samples
samples, rate = music.read_audio("in.wav", return_sample_rate=True)
read_audio gives a file's rate when asked, and warns when the file is at
a rate other than the default and you did not ask, since played back at
the default it would be at the wrong pitch. The writers normalize to full
scale unless told normalize=False, which keeps the levels a piece was
rendered at across the files it is written to.
note = music.note_with_vibrato(freq=220, duration=2,
vibrato_freq=6, max_pitch_dev=0.5)
shaped = music.adsr(sonic_vector=note, attack_ms=80,
sustain_level=-6, release_ms=200)
Durations are in seconds, envelope stages in milliseconds, levels in decibels
and pitch deviations in semitones — each parameter in the unit it is usually
thought about in. The few in milliseconds end in _ms, so a duration
without that ending is in seconds.
note reads its table as MASS does, so a rich waveform's partials above
the Nyquist frequency fold back into the band. bandlimited_note drops
them for the note's pitch, and render_oversampled renders a whole
generator at a multiple of the rate, for what one pitch's table cannot
help:
folded = music.note(10000, duration=1,
waveform_table=music.waveform_table("sawtooth"))
clean = music.bandlimited_note(10000, duration=1, waveform="sawtooth")
swept = music.render_oversampled(music.note_with_fm, duration=1,
freq=6000, fm=300, max_fm_deviation=4000)
The sawtooth that kept 24 % of its energy off its harmonics keeps about 1e-16. Band-limited synthesis has the oscillators for a pitch that moves, by partials, with PolyBLEP or from mipmapped tables, what each is measured to do, and where the folding of FM, gates and waveshaping comes from, line by line.
Everything is counted in semitones from a tonic of zero, which is what
pitch_to_freq takes:
freqs = music.pitch_to_freq(220.0, music.scale("dorian"))
chord = music.pitch_to_freq(220.0, music.chord("minor seventh"))
music.write_wav_mono(music.mix_many(
[music.note(f, duration=1.5) for f in chord]), "chord.wav")
The seven modes are one step pattern read from seven places, and
mode_by_rotation reaches them that way rather than by name. Intervals
can be written the way a musician writes them:
music.interval("M3"), music.interval("P11") # (4, 17)
music.interval_names(6) # ('aug4', 'dim5', 'TT')
music.consonance("P4") # 'context dependent'
Cutoffs are fractions of the sample rate — fraction_of converts Hertz —
so the same coefficients describe the same filter at any rate:
a, b = music.low_pass(music.fraction_of(1000))
muffled = music.iir(music.noise("white", duration=2), a, b)
A vibrato whose rate follows the pitch, and a depth that falls as it rises:
voice = music.Bonds(vibrato_freq=music.proportional(1 / 40),
max_pitch_dev=music.inversely_proportional(400))
music.write_wav_mono(voice.render([220, 277, 330, 440], duration=0.5),
"bound.wav")
localize puts a source on the ear axis from its geometry alone, so it
cannot tell front from back. A measured response can:
music.setup_hrtf() # once; about 1.3 MB into your cache
_, _, left, right = music.hrir(elevation=30, azimuth=270) # above, behind
music.write_wav_stereo(music.localize_hrtf(music.note(duration=2),
left, right), "behind.wav")
Permutation groups and the peals of campanology, acted on any domain you like
— here on frequencies, so the peal is the melody. They need
pip install 'music[structures]':
peal = music.PlainChanges(4) # every permutation, once
rows = peal.act([220, 275, 330, 440])
notes = [music.note(freq, duration=0.2) for row in rows for freq in row]
music.write_wav_mono(music.horizontal_stack(*notes), "campanology.wav")
A source moving from one side to the other, its interaural time and intensity differences computed at every sample from its position:
passing = music.localize_linear(music.note(330, duration=3),
theta1=150, theta2=30, dist=0.6)
music.write_wav_stereo(passing, "passing.wav")
seq = music.Sequencer()
for i, freq in enumerate([440, 550, 660]):
seq.add_note(freq, start=i * 0.25, duration=1.0,
adsr_params={"attack_ms": 20, "release_ms": 400})
seq.write("chord.wav")
Six colours, each defined by its gain per octave — brown at −6 dB, pink at −3, white at 0, blue at +3, violet at +6, black at −12 — or any number you pass instead:
colours = [music.noise(kind, duration=0.5)
for kind in ("brown", "pink", "white", "blue", "violet")]
music.write_wav_mono(music.horizontal_stack(*colours), "colours.wav")
A seed draws the same noise again, and leaves NumPy's global random
state alone.
Each stimulus is named for the SSTIM technique it implements, and a session plays them in order, crossfaded:
session = music.StimulationSession(end_ramp=0.5)
session.add(music.binaural_beats, duration=30, carrier_freq=200,
beat_freq=10)
session.add(music.modulated_noise, duration=30, ramp=2,
noise_type="pink", seed=1)
music.write_wav_stereo(session.render(), "session.wav")
With pip install 'music[sstim]', the session can be written as SSTIM
descriptions of its stimuli, in the order the session plays them, and
rendered back from them sample for sample:
from music.stimulation.sstim import render_sstim, to_sstim_graph
graph = to_sstim_graph(session, base="https://your-lab.example/plans/1/")
graph.serialize("session.ttl", format="turtle")
again = render_sstim("session.ttl")
A description says what a stimulus is, not that anyone heard it or that it does anything. Stimuli in SSTIM has the rest: a session written as SSTIM's own plan, a description another tool wrote rendered from SSTIM's terms with only what they cannot say given, and what can be rendered from a graph before anything is.
A lyric is sung one syllable a note: syllables joined by hyphens, words
parted by spaces, each note a pitch in semitones above middle C and a
duration in quarter notes, at 120 a minute unless L and Q say otherwise,
as ABC notation writes them:
twinkle = music.sing(text="Twin-kle, twin-kle, lit-tle star",
notes=(0, 0, 7, 7, 9, 9, 7), durs=(1, 1, 1, 1, 1, 1, 2))
music.write_wav_mono(twinkle, "twinkle.wav")
# in French, trembling in a room: stereo, ringing a second past the line
frere = music.sing(text="Frè-re Jac-ques, dor-mez vous?",
notes=(0, 2, 4, 0, 4, 5, 7), durs=(1, 1, 1, 1, 1, 1, 2),
lang="fr", effect="tremolo")
music.write_wav_stereo(frere, "frere.wav")
# the same score with eCantorix, the reference: same pitches, same lengths
reference = music.sing(text="Twin-kle, twin-kle, lit-tle star",
notes=(0, 0, 7, 7, 9, 9, 7),
durs=(1, 1, 1, 1, 1, 1, 2), backend="ecantorix")
The default backend, psola, is the package's own. It needs espeak-ng and
pip install 'music[singing]'. espeak-ng says each syllable, and Praat's
pitch-synchronous overlap-add holds it at its note's pitch and length. A
syllable said in less time than its note is said again, down to 80 words a
minute, as eCantorix has espeak say it; then only the middle of its vowel is
lengthened, so the consonants keep their length. A long note gets a vibrato,
0.35 semitones each way at 5.5 Hz, setting in a quarter of a second into it.
The notes on the meter's strong beats are sung up to 2.3 dB louder, the
accents abc2midi gives eCantorix. With this timing, Whisper, a speech
recogniser, heard 69 of 192 sung test words, against 44 with the plain
stretch it replaced and 59 with eCantorix; the vibrato and the accents left
that where it was. effect takes eCantorix's "tremolo", "melt"
and "flite", made with the package's own tremolo, reverberation and
resampling.
backend="ecantorix" sings with the eCantorix
engine. It was the default until the package had its own singer, and is
kept as the reference. Run music.singing.setup_engine() once to clone it,
at the revision this package is pinned to, into your user cache directory
(set MUSIC_ECANTORIX_DIR to put it elsewhere). It drives espeak through a
Makefile, so it also needs git, make, perl, espeak, abc2midi (the
abcmidi package) and sox on the system. It needs the Perl modules
MIDI, Math::FFT, URI::Escape and Digest::SHA too
(cpan MIDI Math::FFT URI::Escape). setup_engine() says which of these
are missing.
To compare the two:
singing_backends
and singing_effects
sing the same songs and effects with each, side by side.
python tools/compare_singing.py measures both against the score: the pitch
of every note, the length and the render time.
tools/compare_singing_timing.py renders the singer's timings and syllable
shapes for a level-matched listening review, and
tools/score_singing_asr.py has Whisper count the words it hears in each.
Inside the examples folder you can find some scripts that use the main features of Music.
Being).Being as their synth, but this time with permutations.IteratorSynth, that iterates through arbitrary lists of variables.music.sing, and writes it to a WAV file.The modules are:
structures extra installs.CanonicalSynth, IteratorSynth and Being, and for musical pieces rendered with them, which might be used as material to make more music.StimulationSession for sequencing them into a protocol, and music.stimulation.sstim, which writes them as SSTIM descriptions, reads them back, and reads what other tools write.sing and its two backends: psola, the package's own singer, and perform, which writes the score and runs the eCantorix engine that bootstrap clones and paths finds.See the findings and prioritized roadmap for the September 2026 review, the correctness patches since 1.8.1, the mutation audits of all eighteen areas of the package, every module it has, and the band-limited synthesis and SSTIM work since 1.10.0.
Concrete things the code itself is waiting for, rather than a wish list:
music.hrtf
reads Gardner and Martin's KEMAR measurements, which closes the gap for
anyone willing to fetch them, but the package models no head of its own:
a direction nobody measured is answered with the nearest one somebody
did. (localize and localize2 still carry neither elevation nor
front-against-back, and say so in their own notes.)Being.walk's perm-walk method, which was never restored from its
predecessor and is currently a reconstruction. (Peals.twenty_all_over and
Peals.an_eight_and_forty no longer raise; this entry used to say they did.)music.theory does not implement.RECONCILIATION.md compares the package with the MASS
reference implementation routine by routine — 22 of 35 are reproduced sample
for sample, 9 diverge for reasons it states, and in 4 the reference itself
does not run.
tests/test_article.py checks the article's
equations instead of its code, and covers 46 of 47 labelled equations:
all 46 that a test could settle. Routines without a MASS counterpart
also have signal-property and composition tests, and the mutation audits
measure, one area at a time, whether the assertions detect deliberate
implementation errors; every module is in an area, measured again when
it changes.
DISCREPANCIES.md is where the article, the reference
implementation and this package are recorded as disagreeing.CONTRIBUTING.md is the longer version: what a change has
to survive, why a test that checks the shape of the output is worth little
here, and how to report something that sounds wrong. Participation is under
the Code of Conduct.
The test, type-check, lint and documentation tooling comes with the dev and
docs extras:
pip install -e '.[dev,docs]'
pytest # the suite, at 100% line and branch coverage
mypy music # type check
ruff check music tests examples tools conftest.py # lint, at PEP 8's 79 columns
sphinx-build -b html -W docs docs/_build/html
python tools/run_examples.py # run every example
python tools/assessment_figures.py --fast # the figures the docs quote
python -m build --wheel
python tools/check_wheel.py --wheel dist/*.whl # installed artifact, outside the checkout
These checks run on every push and every pull request. Lint, types and
tests run on Python 3.10 through 3.14 on Linux; a further Linux job
installs the exact lower bounds pyproject.toml declares and runs the
tests against those, so the floors cannot drift into fiction. The docs
build, examples and figures run once each. Installed wheels are checked
on Linux, macOS and Windows with Python 3.12. CI runs
pytest with --cov=music --cov-branch --cov-fail-under=100; Sphinx treats
documentation warnings as errors.
run_examples.py exercises the code readers copy. A change that broke
three examples once passed every package check. assessment_figures.py
is there because the numbers in
ASSESSMENT.md and this file went stale four times in two days when
keeping them current was a habit rather than a check. Its measured
figures are one table in ASSESSMENT.md, written and checked at release;
on every push --fast checks what prose quotes, the version stamps and
the reconciliation register's counts.
Four more run at release time rather than on every push, being slower or needing something the runner does not have:
python tools/check_sdist.py # build the sdist, unpack it, run its tests
python tools/article_coverage.py --strict # fail missing sources or test citations
python tools/mass_reconcile.py # this package against the MASS reference
python tools/verify_subjects.py --strict # fail unexpected unresolved subjects
The last three need a MASS checkout or the
network. MASS comparisons are documented in RECONCILIATION.md and
DISCREPANCIES.md; two known EuroSciVoc exceptions remain explicitly
unverified. Run the complete workflow during development with
python tools/release.py verify --mass ../mass. See
RELEASING.md for prerequisites and release behavior.
Docstrings are numpydoc style throughout, and the code follows PEP 8. For the maths behind a routine, examples of its use, and the article it comes from, read its docstring — or the rendered API reference.
music has been developed and maintained in the open since 2016. If it is
useful to you, your research or your institution, please consider supporting
its continued development through
GitHub Sponsors.
Sponsorship pays for the unglamorous work that makes a scientific package trustworthy — the fidelity tests, the full coverage, the documented equations, the archived and citable releases — and keeps every bit of it free for everyone.
For institutions and companies: commissioned features, integration support and sponsored development are available, with the results released under the same open license. Open an issue or get in touch to discuss scope.
Music is primarily intended for artistic use, psychophysics experiments and data sonification.
You can find an example in Versinus, an animated visualization method for evolving networks that uses Music to render the musical track that represents networks structures.
sing sings a lyric to a melody, one syllable a note, in
any language espeak speaks. The package's own singer has espeak-ng say
each syllable and Praat's PSOLA hold it at its note's pitch: a syllable
shorter than its note is said again more slowly, only the middle of its
vowel is lengthened so the consonants keep the length they were said
with, a long note gets a vibrato, and the strong beats of the meter are
sung louder. The
eCantorix engine sings the same score
as a second backend, kept as the reference, and both sing its tremolo,
melted and flite voices.play_audio to listen to a result without saving a file, with the
play extra.music.stimulation, and one three-phase session, which is the form these stimuli are actually delivered in.