From 6dc9922d88d62bdc0f524194c1dcae0518d77991 Mon Sep 17 00:00:00 2001 From: James Trayford Date: Thu, 8 Aug 2024 11:38:14 +0100 Subject: [PATCH 01/86] remove pycache and ignore in future --- .gitignore | 2 ++ .../__pycache__/tts_caption.cpython-310.pyc | Bin 1132 -> 0 bytes .../__pycache__/utilities.cpython-310.pyc | Bin 3104 -> 0 bytes 3 files changed, 2 insertions(+) delete mode 100644 src/strauss/__pycache__/tts_caption.cpython-310.pyc delete mode 100644 src/strauss/__pycache__/utilities.cpython-310.pyc diff --git a/.gitignore b/.gitignore index cdbcc06..5221220 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,8 @@ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm __pypackages__/ +__pycache__/ + # Celery stuff celerybeat-schedule celerybeat.pid diff --git a/src/strauss/__pycache__/tts_caption.cpython-310.pyc b/src/strauss/__pycache__/tts_caption.cpython-310.pyc deleted file mode 100644 index e567f6a4358dcc3a64761ffda13a07c82bcbf487..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1132 zcmY*Y&2H2%5KiJ`lihBYpC|CGtkm5IaYG1!Dk1gKODiNSq{vOYT^Dcc+RiR3r8lm9 z2HGP>9)g$1feR;IfdgXfv{i9rXYBEO^UY^l?(U8PXg^@^H2n=I!8Gj9$;mPN z(hXfNxL{yrytYiM=haaOJnmZtRs#J1x_J+S4ce#*;ft=&W!%Q+VH>olLRV23*ytLy z=qCIaT_%@2Ueb0LVtuofKQeTYgSiG7bR7e z%Nj45y7a~wtC^;j8BA+Tn~F<(zemdzw(PUTyjG1JVQ93j@J4gDpIb~J3PZKnl{?3W z3v3~kp(`aIBcNj0FRf(71m*_U(qeYeP=P(1l@LKKR@4|);f2d+WX%`6q>kQPZTDRw z&3aISeFVd$)S8vxa7loaN7HO}%gxc~Ih{EE9=9<}_mwyzrL5OXTS&U$cInHUSvfO` zv2y9rj9l0a)d1vJ9mtD@&)y#58D3he%*#AiTgB&6pXEx+b0Cw$@#lDsKgcB$LKY1u zS>rWlo4#1poC;owEv9SA1w93g){>VDdv#1tWDh{*f*V`(tIwu;p3S>y)Qut08AxG@ zQ>qC%Qw=P}?u5FbtF}|NPHiV`mv|OreCzQUIt0ihb^e0aWB?aHo}QCY%{E1Ee+%>k68_oGmvY7Ot!xCa z&LJ!}y3CE$wCU$}7ZMJ-oBV%AQ$R2dW-^+D6O^Gz_%w|F>c_A&-65$HiYne0o0*cU z?VL+rDS2``^{-C~V5;5w>yo-zL8hoyWawnzmNTP=;PSAZ^@HJ&i^*Pr22j9jcuIj8 P!!QoU;W*6T8%O^DI}%Jh diff --git a/src/strauss/__pycache__/utilities.cpython-310.pyc b/src/strauss/__pycache__/utilities.cpython-310.pyc deleted file mode 100644 index 9b0e084696582b15565a228b8dd5bb84f382fdce..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3104 zcmai0U2hvj7v7oOwKuNgCM7K?@WMh7>7t}*Ktia5r~>X@DblNQbMeQ~d5oeoD&Yv0I|J5bjAWkkF1M}Q_0YPxKWKCt_~r@!h0|?{hG?PuVjk@c(GfS%w#9;2L_05TiMP;p#FDs;_NL%ZoaoMLoa{u*G-N79NxBU& z$O@gx;f6>^zv<6|;G=wtqP|2`IcLsG=R0TT_7D7&pSqe=uI6JdZ)>-5D^~Hl&X_aj zN3ZT}LK5tW^NOEr$TS&Am4>=6%h7&66j`FPvWR7tsxU4@s7AYM7YDZ^&-j^62ghxq9E0ygRuRIg%$#P(4G<0cO_RO%uSB~sJ|2Lnm_sc=L zeq4_7RH=2X)w+_&y3#Tpp+C}Do@prbV0bJ8ETE#upbNakP+aeg9lr(>L1kfj3}FV- z-5REfSL~3<#dA<*!2JinS=cJK29Ds8v|Hp!!d^D0YN9d=7w^26<++@rZ*;M`x=GRk zs#_?E#us?NT2Q^i`A<-tz9lZI>5opBS$GSbnd-|>-97c7sAnyF$13XHbt{-#m?YnFWIRpxNu)Mr%)T0a4MJF>Ss$?$o^et6OIN4X`KAt3h^WQScV=yvhu2} z!*cf^O?0TsaLT*zFwRF{ak*#LJxh;=vF<+#L95kpC0^fMPgcUDlrmMrvJi^a(&kTB z;+37JE4x%BD?6cGZTETvk2qef_q}w|xlWUh&okY(>)x>SOROYECLTROv0TuOW(Qzm z<&0h2l5Iz>q)8?yhpCKpDNS=f)#PyzH%u7*EWcs}6Kv-3U{}OjYqTq^?l>o(PjTo? z>`Ytq$Ps%JC?0I)WfJFV3qrkq`}JS=A@cZY2$zd|(>lLew>ep7e2a#mex5090w~y{ z?XN2K;lJwe3298kXf*=%K9e6nyaX75HS>j!(3$x{YZmR`s&G`xesa`^pZpAM0N#0A zi^DuC(pX-g&m{}i7)>Z+EjL1N+YcvKR^ffI0b*Fv?1XDuq4?|yPryT(v9hzaHTf28 zN4$;}bgTat7R>v#Ry|!#tweawJdqs5qO;(3nD>_q(N7qv>5pWJ98}aYD((F0jG1iw z3WojS>@mRve-Qu^_vD*wFFXn}6~g9uyql-1a1}k0MR>Ftra6+pP?m?8NW(bX8x_{e zkQZV-nWn;{YiASJ-lDT9LR4_*C7k~@&W`xDB;_n4Kqz+n3e{0f5E~Im+plOT?a_8l z?j^swxa0@wM8J%XwBlE_f+XC>OyEMD8>3p#=b7I2TXxg^3}A{ZS{#YUzj z2pSgwgaKj^8-q?2U5A~l{xf{{bu>Y63aI=T8^QYb&={t#`bGPqnqOniM8kl2T;50j zQtYI3H*pEk5}hWJsr%ajICzY9YM{?a{dR*lNXXmiP_d}|i>9G~;4Fg^vs}t&sDRer@@OYa7IvK|CC$f*huv@$})&+CNbn?xfR?pFS~Bp&x~8-C8~agM^vrURiru-WqY-%-{BBt zp8!aq@2~|XA7JDfT9lZrJr5vM`~Y|$B?1?E9G!82m7?SC9E{II?#|+CTd*?v~Ud02m{ zBNJt+A4eIUdl!c-n9&>}a6d)@_!Ji~0iuK&#&~AAco9mM#@P49$CIrPrV%TBJWP!* zsEGp8r2TtYVFEgiI;QbFK17Bzff%8W^w|w`Hb{H0%|wE6OuK$>mbpzkEe!SQbyuwt zOfUIgsG{io(&0<6$P$87+g)ZJlbaZf7R;QDBxRW^o7E&anDL9z08cr4w?&m?!{Z0% zC#Uv)_C04`M)oaX^MVb3)`6{$Skv2cE~sbUCpHuWn}br|LAgc5RPUf_I{^z=n+0Cb Nn*19 Date: Thu, 8 Aug 2024 11:53:27 +0100 Subject: [PATCH 02/86] update version number to 0.2 --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 557cfd4..c15dcfb 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = strauss -version = 0.1.1 +version = 0.2 author = James Trayford author_email = james.trayford@port.ac.uk description = Sonification Tools and Resources for Astronomers Using Sound Synthesis From 2d3297c0c256c7fd867d7d3e2c9ddf4027f7e102 Mon Sep 17 00:00:00 2001 From: James Trayford Date: Thu, 8 Aug 2024 11:53:59 +0100 Subject: [PATCH 03/86] update version number properly... --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index c15dcfb..4061c62 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = strauss -version = 0.2 +version = 0.2.0 author = James Trayford author_email = james.trayford@port.ac.uk description = Sonification Tools and Resources for Astronomers Using Sound Synthesis From 64f404c4f05dc6dacbb5b5db0d2aa4e32cb09663 Mon Sep 17 00:00:00 2001 From: James Trayford Date: Wed, 7 Aug 2024 11:43:29 +0100 Subject: [PATCH 04/86] some spectraliser speed up options: fftw acceleration, spectra multiple skipping --- src/strauss/generator.py | 15 +++++++++++---- src/strauss/presets/spec/default.yml | 3 +++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/strauss/generator.py b/src/strauss/generator.py index c2d2a3b..d402c18 100644 --- a/src/strauss/generator.py +++ b/src/strauss/generator.py @@ -22,8 +22,12 @@ from . import filters import numpy as np import scipy -# use FFTW backend in scipy -#scipy.fft.set_backend(pyfftw.interfaces.scipy_fft) +# can we use FFTW backend in scipy? +try: + import pyfftw + scipy.fft.set_backend(pyfftw.interfaces.scipy_fft) +except (OSError, ModuleNotFoundError): + pass from scipy.fft import fft, ifft, fftfreq import glob import copy @@ -1028,8 +1032,11 @@ def play(self, mapping): spectra_multiples = (discrete_freqs - 1)/(spectrum.size - 1) # the minimum factor by which to increase the stream length to accomodate spectra in whole number multiples - buffer_factor = np.ceil(spectra_multiples)/spectra_multiples - + if params['fit_spec_multiples']: + buffer_factor = np.ceil(spectra_multiples)/spectra_multiples + else: + buffer_factor = 1 + # number of samples to generate including buffer new_nlen = int(buffer_factor * nlength) diff --git a/src/strauss/presets/spec/default.yml b/src/strauss/presets/spec/default.yml index 49622ad..7bbebb5 100644 --- a/src/strauss/presets/spec/default.yml +++ b/src/strauss/presets/spec/default.yml @@ -78,6 +78,9 @@ interpolation_type: 'sample' # These have differing effects regen_phases: true +# Whether or not to generate IFFT such that the spectrum sample points are hit exactly +fit_spec_multiples: true + # frequency limits in Hz min_freq: 50. max_freq: 2000. From 0b069050d8637302c33cf5275c3ed9b386155362 Mon Sep 17 00:00:00 2001 From: James Trayford Date: Wed, 14 Aug 2024 15:47:35 +0100 Subject: [PATCH 05/86] first implementation of equal loudness contour implementation for spectraliser sonification --- MANIFEST.in | 3 +- src/strauss/data/params.csv | 30 +++++++++++++++++ src/strauss/generator.py | 15 +++++++-- src/strauss/presets/spec/default.yml | 3 ++ src/strauss/utilities.py | 49 +++++++++++++++++++++++++++- 5 files changed, 95 insertions(+), 5 deletions(-) create mode 100644 src/strauss/data/params.csv diff --git a/MANIFEST.in b/MANIFEST.in index 454970a..e744ea1 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1,3 @@ include src/strauss/presets/*/*.yml -include src/strauss/presets/*/*/*.yml \ No newline at end of file +include src/strauss/presets/*/*/*.yml +imclude src/strauss/data/*.* \ No newline at end of file diff --git a/src/strauss/data/params.csv b/src/strauss/data/params.csv new file mode 100644 index 0000000..f3879dc --- /dev/null +++ b/src/strauss/data/params.csv @@ -0,0 +1,30 @@ +freq,alpha_f,L_U,T_f +20,0.635,-31.5,78.1 +25,0.602,-27.2,68.7 +31.5,0.569,-23.1,59.5 +40,0.537,-19.3,51.1 +50,0.509,-16.1,44.0 +63,0.482,-13.1,37.5 +80,0.456,-10.4,31.5 +100,0.433,-8.2,26.5 +125,0.412,-6.3,22.1 +160,0.391,-4.6,17.9 +200,0.373,-3.2,14.4 +250,0.357,-2.1,11.4 +315,0.343,-1.2,8.6 +400,0.330,-0.5,6.2 +500,0.320,0.0,4.4 +630,0.311,0.4,3.0 +800,0.303,0.5,2.2 +1000,0.300,0.0,2.4 +1250,0.295,-2.7,3.5 +1600,0.292,-4.2,1.7 +2000,0.290,-1.2,-1.3 +2500,0.290,1.4,-4.2 +3150,0.289,2.3,-6.0 +4000,0.289,1.0,-5.4 +5000,0.289,-2.3,-1.5 +6300,0.293,-7.2,6.0 +8000,0.303,-11.2,12.6 +10000,0.323,-10.9,13.9 +12500,0.354,-3.5,12.3 diff --git a/src/strauss/generator.py b/src/strauss/generator.py index d402c18..1f935a0 100644 --- a/src/strauss/generator.py +++ b/src/strauss/generator.py @@ -950,14 +950,14 @@ class Spectralizer(Generator): """Spectralizer generator class """ def __init__(self, params=None, samprate=48000): - # default synth preset self.gtype = 'spec' self.preset = getattr(presets, self.gtype).load_preset() self.preset['ranges'] = getattr(presets, self.gtype).load_ranges() + self.eq = utils.Equaliser() self.freqwarn = True - + # universal initialisation for generator objects: super().__init__(params, samprate) @@ -1043,7 +1043,16 @@ def play(self, mapping): # the frequency bound indices which the spectrum will be mapped into mindx = int(params['min_freq'] * duration * buffer_factor) maxdx = int(params['max_freq'] * duration * buffer_factor) - + + if params['equal_loudness_normalisation']: + freqs = np.linspace(params['min_freq'], params['max_freq'], len(spectrum)) + norm = self.eq.get_relative_loudness_norm(freqs) + if not self.eq.factor_rms: + self.eq.factor_rms = [] + rms1 = np.sqrt(np.mean(spectrum**2)) + spectrum *= norm + self.eq.factor_rms.append(np.sqrt(np.mean(spectrum**2))/rms1) + # hardcode phase randomisation for now phases = 2*np.pi*np.random.random(new_nlen) diff --git a/src/strauss/presets/spec/default.yml b/src/strauss/presets/spec/default.yml index 7bbebb5..6783b6c 100644 --- a/src/strauss/presets/spec/default.yml +++ b/src/strauss/presets/spec/default.yml @@ -81,6 +81,9 @@ regen_phases: true # Whether or not to generate IFFT such that the spectrum sample points are hit exactly fit_spec_multiples: true +# Do we equalise the spectra for equal loudness? +equal_loudness_normalisation: false + # frequency limits in Hz min_freq: 50. max_freq: 2000. diff --git a/src/strauss/utilities.py b/src/strauss/utilities.py index fdfb38a..ada038b 100644 --- a/src/strauss/utilities.py +++ b/src/strauss/utilities.py @@ -8,6 +8,8 @@ import sys from pathlib import Path +# Some utility classes (these may graduate to somewhere else eventually) + class NoSoundDevice: """ drop-in replacement for sounddevice module if not working, @@ -17,7 +19,52 @@ def __init__(self, err): self.err = err def play(self, audio, rate, blocking=1): raise self.err - + +class Equaliser: + def __init__(self): + + self.factor_rms = None + parpath = Path(f"{Path(__file__).parent}","data","params.csv") + # Read in parameters for ISO 226:2024 standard. + pars = np.genfromtxt(parpath, delimiter=',', names=True) + self.parfuncs = {} + for c in pars.dtype.names: + if c == 'freq': + continue + self.parfuncs[c] = interp1d(np.log10(pars['freq']), + pars[c], + fill_value='extrapolate') + + def get_relative_loudness_norm(self, freq, phon=70.): + """ + Relative normalisation of sound frequencies + To compensate for pereceptual loudness, following + the ISO 226:2024 standard. + + Args: + freq (:obj:`array-like`) audio frequencies in Hz + phon (:obj:`float`) listening level for a 1 kHz + note + + Returns: + rnorm (:obj:`array-like`) volume normalisation + for spectra + """ + lfreq = np.log10(freq) + L_U = self.parfuncs['L_U'](lfreq) + alpha_f = self.parfuncs['alpha_f'](lfreq) + T_f = self.parfuncs['T_f'](lfreq) + + A = pow(4e-10, 0.3 - alpha_f) + B = pow(10., (phon)*3e-2) - pow(10, 7.2e-2) + C = pow(10., alpha_f * 0.1*(T_f + L_U)) + + L_f = 10*np.log10(A*B + C)/alpha_f - L_U + norm = pow(10., (L_f - phon)/20) + rnorm = norm/norm.max() + return rnorm + + # a load of utility functions used by STRAUSS def nested_dict_reassign(fromdict, todict): From 6f6fff9943cd208714a39d57ff34d0572cbcb3c6 Mon Sep 17 00:00:00 2001 From: James Trayford Date: Thu, 15 Aug 2024 16:16:20 +0100 Subject: [PATCH 06/86] fix typo in manifest --- MANIFEST.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MANIFEST.in b/MANIFEST.in index e744ea1..e35ab62 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,3 @@ include src/strauss/presets/*/*.yml include src/strauss/presets/*/*/*.yml -imclude src/strauss/data/*.* \ No newline at end of file +include src/strauss/data/*.* \ No newline at end of file From 8b9bc04465fcaaa0f8b9cc4e4ef04c615f857c2b Mon Sep 17 00:00:00 2001 From: James Trayford Date: Thu, 15 Aug 2024 16:35:18 +0100 Subject: [PATCH 07/86] try change in manifest --- MANIFEST.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MANIFEST.in b/MANIFEST.in index e35ab62..9f30167 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,3 @@ include src/strauss/presets/*/*.yml include src/strauss/presets/*/*/*.yml -include src/strauss/data/*.* \ No newline at end of file +recursive-include src/strauss/data * \ No newline at end of file From 4dfd7da8f3589bfea658ce66d7270688133bf5b4 Mon Sep 17 00:00:00 2001 From: James Trayford Date: Thu, 15 Aug 2024 16:59:06 +0100 Subject: [PATCH 08/86] another update --- MANIFEST.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MANIFEST.in b/MANIFEST.in index 9f30167..721dafd 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,3 @@ include src/strauss/presets/*/*.yml include src/strauss/presets/*/*/*.yml -recursive-include src/strauss/data * \ No newline at end of file +include src/strauss/data/*.csv \ No newline at end of file From a36ca60e2da8549a47589eb49002810667b50f1c Mon Sep 17 00:00:00 2001 From: James Trayford Date: Thu, 15 Aug 2024 17:05:35 +0100 Subject: [PATCH 09/86] see if this works --- src/strauss/data/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/strauss/data/__init__.py diff --git a/src/strauss/data/__init__.py b/src/strauss/data/__init__.py new file mode 100644 index 0000000..e69de29 From b1a4f4700e9b077c86de522fd9d7e41119156141 Mon Sep 17 00:00:00 2001 From: James Trayford Date: Tue, 20 Aug 2024 17:43:22 +0100 Subject: [PATCH 10/86] fix conditional indentation --- src/strauss/generator.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/strauss/generator.py b/src/strauss/generator.py index 1f935a0..014601e 100644 --- a/src/strauss/generator.py +++ b/src/strauss/generator.py @@ -1158,9 +1158,9 @@ def play(self, mapping): if hasattr(params['cutoff'], "__iter__"): # if static cutoff, use minimum buffer count sstream.bufferize(sstream.length/4) - else: - # 30 ms buffer (hardcoded for now) - sstream.bufferize(0.03) + else: + # 30 ms buffer (hardcoded for now) + sstream.bufferize(0.03) sstream.filt_sweep(getattr(filters, params['filter_type']), utils.const_or_evo_func(params['cutoff'])) return sstream From ca815d88b193fa8f9db4ebf6aac4bb1649fb6aa6 Mon Sep 17 00:00:00 2001 From: James Trayford Date: Thu, 12 Dec 2024 00:19:59 +0000 Subject: [PATCH 11/86] make tqdm optional --- README.md | 6 +++--- setup.cfg | 5 ++++- src/strauss/sonification.py | 5 ++++- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 94f738f..1ee4155 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,9 @@ Access the [full documentation here](https://strauss.readthedocs.io/) *(under construction!)* and read more about the associated [Audio Universe project here](https://www.audiouniverse.org/). -*STRAUSS* is [PyPI hosted package](https://pypi.org/project/strauss/) and can be installed directly via `pip`: +*STRAUSS* is [PyPI hosted package](https://pypi.org/project/strauss/) and `pip` can be used for the default installation: -`pip install strauss` +`pip install 'strauss[default]'` For a standard install (without text-to speech support). @@ -51,7 +51,7 @@ and activate the environment with *STRAUSS* can also be installed with text-to-speech (TTS) support, allowing audio captioning of sonifications and future accessibility features, via the [TTS module](https://github.com/coqui-ai/TTS). Due to the specific module requirements of this module, install can sometimes lead to incompatibilities with other modules and be slower, so is packaged with *STRAUSS* as an optional extra. If you'd like to use these features, its easy to directly from PyPI: -`pip install strauss[TTS]` +`pip install 'strauss[TTS]'` or if you're working from a local copy of the repository, as above, use diff --git a/setup.cfg b/setup.cfg index 4061c62..f0cf811 100644 --- a/setup.cfg +++ b/setup.cfg @@ -29,7 +29,6 @@ install_requires = scipy setuptools >= 4.2 sphinx - tqdm wavio wheel sounddevice @@ -37,5 +36,9 @@ install_requires = [options.packages.find] where = src [options.extras_require] +default = + tqdm TTS = + tqdm TTS + diff --git a/src/strauss/sonification.py b/src/strauss/sonification.py index ce17480..e0dea16 100644 --- a/src/strauss/sonification.py +++ b/src/strauss/sonification.py @@ -19,7 +19,6 @@ from .tts_caption import render_caption import numpy as np import matplotlib.pyplot as plt -from tqdm import tqdm import sys import os import ffmpeg as ff @@ -34,6 +33,10 @@ import sounddevice as sd except (OSError, ModuleNotFoundError) as sderr: sd = NoSoundDevice(sderr) +try: + from tqdm import tqdm +except ModuleNotFoundError: + tqdm = list class Sonification: """Representing the overall sonification From 35b765797575198c592bae59c4c5b6d359b23375 Mon Sep 17 00:00:00 2001 From: James Trayford Date: Thu, 12 Dec 2024 00:23:21 +0000 Subject: [PATCH 12/86] increment version --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index f0cf811..9b8c860 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = strauss -version = 0.2.0 +version = 0.3.0 author = James Trayford author_email = james.trayford@port.ac.uk description = Sonification Tools and Resources for Astronomers Using Sound Synthesis From 42419e17d7efbff2dba8ac0a28b0771a808c04ea Mon Sep 17 00:00:00 2001 From: James Trayford Date: Fri, 3 Jan 2025 10:59:43 +0000 Subject: [PATCH 13/86] add conditional dependency on externally managed version of former core package dropped in py3.13, used by sf2utils (pending update to that package), and streamline conda install script to install via setup.cfg so we only need to maintain 1 set of dependencies --- environment.yml | 20 ++------------------ setup.cfg | 1 + 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/environment.yml b/environment.yml index 4c973e5..c46c3ca 100644 --- a/environment.yml +++ b/environment.yml @@ -2,23 +2,7 @@ name: strauss channels: - anaconda dependencies: - - python=3.8 + - python >= 3.6 - pip - pip: - - jupyterlab - - ffmpeg_python - - ipython - - matplotlib - - numpy - - pandas - - pychord - - pyyaml - - scipy - - setuptools >= 4.2 - - sf2utils - - sphinx - - tqdm - - TTS - - wavio - - wheel - - sounddevice \ No newline at end of file + - -e ".[default]" \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index 9b8c860..47c5c07 100644 --- a/setup.cfg +++ b/setup.cfg @@ -33,6 +33,7 @@ install_requires = wheel sounddevice sf2utils + audioop-lts; python_version >= "3.13" [options.packages.find] where = src [options.extras_require] From 3e8b95386d67d63fb4aaf3aa85abf287ae2313c1 Mon Sep 17 00:00:00 2001 From: James Trayford Date: Mon, 13 Jan 2025 13:49:10 +0000 Subject: [PATCH 14/86] increment versions --- setup.cfg | 2 +- src/strauss/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index 47c5c07..e7cc5b0 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = strauss -version = 0.3.0 +version = 0.4.0 author = James Trayford author_email = james.trayford@port.ac.uk description = Sonification Tools and Resources for Astronomers Using Sound Synthesis diff --git a/src/strauss/__init__.py b/src/strauss/__init__.py index 2fc8e8d..19fcdd0 100644 --- a/src/strauss/__init__.py +++ b/src/strauss/__init__.py @@ -13,4 +13,4 @@ from . import stream from . import presets -__version__ = "0.1.2" +__version__ = "0.4.0" From c0d5a610124fb12c602ffb4f3a7e69a4087d968f Mon Sep 17 00:00:00 2001 From: James Trayford Date: Thu, 15 Aug 2024 15:43:59 +0100 Subject: [PATCH 15/86] Add more documentations --- docs/detailed.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/detailed.rst b/docs/detailed.rst index c3d20c4..63f7802 100644 --- a/docs/detailed.rst +++ b/docs/detailed.rst @@ -30,3 +30,13 @@ Sonification ************ .. automodule:: strauss.sonification :members: + +Utilities +********* + .. automodule:: strauss.utilities + :members: + +Text-to-Speech +************** + .. automodule:: strauss.tts_caption + :members: From b74fb570d8f65a8f68b6c111dbc350a6b9c7ab3d Mon Sep 17 00:00:00 2001 From: James Trayford Date: Tue, 20 Aug 2024 14:57:12 +0100 Subject: [PATCH 16/86] add documentation to src/strauss/utilities.py --- src/strauss/utilities.py | 151 ++++++++++++++++++++++++++++++++++----- 1 file changed, 133 insertions(+), 18 deletions(-) diff --git a/src/strauss/utilities.py b/src/strauss/utilities.py index ada038b..4f5b5a8 100644 --- a/src/strauss/utilities.py +++ b/src/strauss/utilities.py @@ -12,12 +12,33 @@ class NoSoundDevice: """ - drop-in replacement for sounddevice module if not working, - so can still use other functionality. + Drop-in replacement for sounddevice module if not working, + so we can still use other functionality. + + Attributes + ---------- + err : Exception + Error message from trying to import sounddevice + + Methods + ------- + play(*args, **kwargs) + Dummy function replacing `sounddevice.play` when ` + `sounddevice` is unavailable to raise self.err """ def __init__(self, err): self.err = err - def play(self, audio, rate, blocking=1): + def play(self, *args, **kwargs): + """ + Dummy function replacing `sounddevice.play` when ` `sounddevice` is unavailable to raise self.err + + Parameters + ---------- + *args + arguments (ignored) + **kwargs + keyword-only arguments (ignored) + """ raise self.err class Equaliser: @@ -68,7 +89,18 @@ def get_relative_loudness_norm(self, freq, phon=70.): # a load of utility functions used by STRAUSS def nested_dict_reassign(fromdict, todict): - """recurse through dictionaries and sub-dictionaries""" + """ + Recurse through dictionaries and sub-dictionaries in + `fromdict` and reassign equivalent values in `todict` + + Parameters + ---------- + fromdict : dict + Dictionary containing values to assign + todict : dict + Dictionary containing values to be reassigned + + """ for k, v in fromdict.items(): if isinstance(v, dict): # recurse through nested dictionaries @@ -78,7 +110,18 @@ def nested_dict_reassign(fromdict, todict): todict[k] = v def nested_dict_fill(fromdict, todict): - """recurse through dictionaries and sub-dictionaries""" + """ + Recurse through dictionaries and sub-dictionaries in + `fromdict` and assign to any entries missing from + `todict` + + Parameters + ---------- + fromdict : dict + Dictionary containing values to assign + todict : dict + Dictionary containing values to be reassigned + """ for k, v in fromdict.items(): if k not in todict: # assign todict value @@ -88,7 +131,20 @@ def nested_dict_fill(fromdict, todict): nested_dict_fill(todict[k], v) def nested_dict_idx_reassign(fromdict, todict, idx): - """recurse through dictionaries and sub-dictionaries""" + """ + Recurse through dictionaries and sub-dictionaries of + iterables in `fromdict` and index value idx to assign + or replact value in todict + + Parameters + ---------- + fromdict : dict + Dictionary containing values to assign + todict : dict + Dictionary containing values to be reassigned + idx : int + index value for retrieving value from iterables + """ for k, v in fromdict.items(): if isinstance(v, dict): # recurse through nested dictionaries @@ -99,10 +155,19 @@ def nested_dict_idx_reassign(fromdict, todict, idx): def reassign_nested_item_from_keypath(dictionary, keypath, value): """ - dictionary: dict, dict object to reassign values of - keypath: str, 'a/b/c' corresponds to dict['a']['b']['c'] - or (for Windows systems): str, 'a\b\c' corresponds to dict['a']['b']['c'] - value: any, value to reassign dictionary value with + Reassign item in a nested dictionary to value using keypath syntax, + to traverse multiple dictionaries + + Parameters + ---------- + dictionary : dict + dict object to reassign values within + keypath : str + Using filepath syntax on given OS to traverse dictionary, i.e + 'a/b/c' ('a\\b\\c') corresponds to dict['a']['b']['c'] on Unix + (Windows) + value : + value to reassign dictionary value with """ p = Path(keypath) keylist = list(p.parts) @@ -110,34 +175,80 @@ def reassign_nested_item_from_keypath(dictionary, keypath, value): get_item(dictionary, keylist[:-1])[keylist[-1]] = value def linear_to_nested_dict_reassign(fromdict, todict): - """iterate through a linear dictionary to reassign nested values - using keypaths (d1['a/b/c'] -> d2['a']['b']['c'], d1['a']->d2['a'])""" + """ + Iterate through a linear dictionary to reassign nested values + using keypaths (d1['a/b/c'] -> d2['a']['b']['c'], d1['a']->d2['a']) + + Parameters + ---------- + fromdict : dict + Dictionary containing values to assign + todict : dict + Dictionary containing values to be reassigned + """ for k, v in fromdict.items(): reassign_nested_item_from_keypath(todict, k, v) def const_or_evo_func(x): - """if x is callable, return x, else provide a function that returns x""" + """ + If x is callable, return x, else provide a function that just + returns x + + Args: + x: input value, either a numerical value or a + function + """ if callable(x): return x else: return lambda y: y*0 + x def const_or_evo(x,t): - """if x is callable, return x(t), else return x""" + """ + If x is callable, return x(t), else return x + + Args: + x: input value, either a numerical value or a + function + t (numerical): values to evaluate x function + """ if callable(x): return x(t) else: return x def rescale_values(x, oldlims, newlims): - """ rescale x values to range limits such that 0-1 is mapped to limits[0]-limits[1] """ + """ + Rescale x values defined by limits oldlims to new limits newlims + + Args: + x (array-like): Array of input values + oldlims (:obj:`tuple`): tuple representing the original limits + of `x` (low, high) + newlims (:obj:`tuple`): tuple representing the new limits + + Returns: + x_rs (array-like): Rescaled array + """ olo, ohi = oldlims nlo, nhi = newlims descale = np.clip((x - olo) / (ohi-olo), 0 , 1) return (nhi-nlo)*descale + nlo def resample(rate_in, samprate, wavobj): - """ resample audio from original samplerate to required samplerate """ + """ + Resample audio from original samplerate to required samplerate + + Args: + rate_in (:obj:`int`) sample rate of input wave object + samprate (:obj:`int`) desired sample rate for output + wavobj (:obj:`tuple`) sample rate, sample array tuple, output + by `scipy.io.wavfile` function + + Returns: + new_wavobj (:obj:`tuple`) as `wavobj`, with new sample rate + and resampled sample values + """ duration = wavobj.shape[0] / rate_in time_old = np.linspace(0, duration, wavobj.shape[0]) @@ -150,13 +261,17 @@ def resample(rate_in, samprate, wavobj): @contextmanager def suppress_stdout_stderr(): - """A context manager that redirects stdout and stderr to devnull""" + """ + A context manager that redirects stdout and stderr to devnull + """ with open(devnull, 'w') as fnull: with redirect_stderr(fnull) as err, redirect_stdout(fnull) as out: yield (err, out) class Capturing(list): - """ Context manager for handling stdout (see https://stackoverflow.com/a/16571630) """ + """ + Context manager for handling stdout (see https://stackoverflow.com/a/16571630) + """ def __enter__(self): self._stdout = sys.stdout sys.stdout = self._stringio = StringIO() From 0ddd2ffcfcb1ff6bc7d42ad3d4188b2b2dd89197 Mon Sep 17 00:00:00 2001 From: James Trayford Date: Tue, 20 Aug 2024 15:10:43 +0100 Subject: [PATCH 17/86] update src/strauss/filters.py docs --- src/strauss/filters.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/strauss/filters.py b/src/strauss/filters.py index 9720836..1b10958 100644 --- a/src/strauss/filters.py +++ b/src/strauss/filters.py @@ -2,11 +2,35 @@ import scipy.signal as sig def LPF1(data, cutoff, q, order=5): + """ + Low-pass filter data array given cutoff, q and LPF order + + Args: + data (array-like): Array containing signal for filtering + cutoff (:obj:`float`): Cutoff frequency + q (:obj:`float`): Filter quality-factor or 'Q' value + order (:obj:`int`): polynomial order of filter function + + Return + y (array-like): Filtered array for output + """ b, a = sig.butter(order, cutoff, btype='low', analog=False) y = sig.lfilter(b, a, data) return y def HPF1(data, cutoff, q, order=5): + """ + High-pass filter data array given cutoff, q and LPF order + + Args: + data (array-like): Array containing signal for filtering + cutoff (:obj:`float`): Cutoff frequency + q (:obj:`float`): Filter quality-factor or 'Q' value + order (:obj:`int`): polynomial order of filter function + + Return + y (array-like): Filtered array for output + """ b, a = sig.butter(order, cutoff, btype='high', analog=False) y = sig.lfilter(b, a, data) return y From d721f6444355ed82f280ae689748232354cd5487 Mon Sep 17 00:00:00 2001 From: James Trayford Date: Tue, 20 Aug 2024 15:38:20 +0100 Subject: [PATCH 18/86] update src/strauss/notes.py docs --- src/strauss/notes.py | 48 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/src/strauss/notes.py b/src/strauss/notes.py index d6f89e1..3c2ddee 100644 --- a/src/strauss/notes.py +++ b/src/strauss/notes.py @@ -16,7 +16,15 @@ def parse_note(notename): """ Takes scientific pitch name and returns frequency in Hz. - flat and sharp numbers supported + Flat and sharp values supported. Assumes equal temperament + and A4 = 440 Hz tuning (ISO 16) + + Args: + notename (:obj:`str`): scientific pitch name, in format + , e.g. 'Ab4', 'E3' or 'F#2' + + Returns: + out (numerical): Frequency of note in Hertz """ nsplit = re.findall("(\D+|\d+)", notename) semi = semitone_dict[nsplit[0]]/12. @@ -24,6 +32,20 @@ def parse_note(notename): return tuneC0*pow(2.,semi+octv) def parse_chord(chordname, rootoct=3): + """ + Takes name of a chord and root octave to generate a valid + chord voicing as an array of frequencies in Hz, using the + `pychord` library + + Args: + chordname (:obj:`str`): Standard chord name, e.g. 'A7' + or 'Dm7add9' etc. + rootoct (:obj:`int`): Octave number + + Returns: + out (:obj:`ndarray`) array of frequencies constituting + chord + """ chord = chrd.Chord(chordname) notes = chord.components_with_pitch(rootoct) frqs = [] @@ -32,11 +54,35 @@ def parse_chord(chordname, rootoct=3): return np.array(frqs) def chord_notes(chordname, rootoct=3): + """ + Takes name of a chord and root octave to generate a valid + chord voicing as a list of note names, using the `pychord` + library + + Args: + chordname (:obj:`str`): Standard chord name, e.g. 'A7' + or 'Dm7add9' etc. + rootoct (:obj:`int`): Octave number + + Returns: + out (:obj:`list`) list of note names constituting chord + """ chord = chrd.Chord(chordname) notes = chord.components_with_pitch(int(rootoct)) return notes def mkey_to_note(val): + """ + Take MIDI key value and return the note name in scientific + notation + + Args: + val (:obj:`int`): MIDI key value + + Returns: + out (:obj:`str`) scientific pitch name, in format + , e.g. 'E3' or 'F#2' + """ from strauss.notes import notesharps octv = val // 12 - 1 semi = val % 12 From a268e322c31821186b42621e3d64c885063c983d Mon Sep 17 00:00:00 2001 From: James Trayford Date: Wed, 21 Aug 2024 10:39:32 +0100 Subject: [PATCH 19/86] update documentation in src/strauss/stream.py --- src/strauss/stream.py | 143 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 124 insertions(+), 19 deletions(-) diff --git a/src/strauss/stream.py b/src/strauss/stream.py index 9c9bda6..bbc64f7 100644 --- a/src/strauss/stream.py +++ b/src/strauss/stream.py @@ -1,4 +1,4 @@ -import numpy as np +>import numpy as np import wavio import matplotlib.pyplot as plt from scipy.signal.windows import hann @@ -6,9 +6,48 @@ # - implement filter Q-parameter mapping class Stream: - """ Stream object representing audio samples""" + """ + Stream object representing audio samples. + + Houses audio samples and associates metadata representing the + actual audio signal produced by the `Generator` class and output + via the `audio_channels` class. + + Attributes: + samprate (:obj:`int`): Samples per second of audio stream (Hz) + length (:obj:`float`): Duration of the stream in seconds + values (:obj:`ndarray`): Values of individual samples + samples (:obj:`ndarray`): Indices of each sample + samptype (:obj:`ndarray`): Time in seconds each sample occurs + buffers (:obj:`Buffers`): Buffered stream if generated + + Methods: + bufferize(bufflength=0.1) + wrapper to initialise Buffers subclass + consolidate_buffers() + wrapper to reassign stream values to consolidated stream + filt_sweep(self, ffunc, fmap, qmap=lambda x:x*0 + 0.1, + flo=20, fhi=2.205e4, qlo=0.5, qhi=10) + Apply time varying filter to buffered stream + get_sampfracs() + Get fractional position of the sample in total stream duration + save_wav(filename) + Save audio stream to wav file, specified by filename + reset() + Zero audio stream and buffers if present + """ def __init__(self, length, samprate=44100, ltype='seconds'): - + """ + Args: + length (numerical): Number representing the length of the + stream either as an integer number of samples, or a value + of seconds + samprate (optional :obj:`int`): Samples per second of audio + stream (Hz) + ltype (optional :obj:`str`): quantity represented by + `length`, either duration in 'seconds' or precise number + of 'samples' + """ # variables we want to keep constant self.samprate = samprate self._nyqfrq = 0.5*self.samprate @@ -34,21 +73,42 @@ def __init__(self, length, samprate=44100, ltype='seconds'): self.samptime = self.samples / self.samprate def bufferize(self, bufflength=0.1): - """ wrapper to initialise Buffers subclass """ + """Wrapper to initialise Buffers subclass + + Args: + bufflength (optional, :obj:`float`): duration in seconds of + each buffer to be generated + """ self.buffers = Buffers(self, bufflength) def consolidate_buffers(self): - """ wrapper to reassign stream values to consolidated stream """ + """ + Wrapper to reassign stream values to consolidated buffers + + See :func:`~stream.Buffers.buffers.to_stream` + """ self.values = self.buffers.to_stream() def filt_sweep(self, ffunc, fmap, qmap=lambda x:x*0 + 0.1, flo=20, fhi=2.205e4, qlo=0.5, qhi=10): """ - ffunc: function that applies filter - fmap: mapping function representing filter cutoff sweep - qmap: mapping function for a filters Q parameter, default: lambda:None - flo: lowest frequency of sweep in Hz, default 20 - fhi: highest frequency of sweep in Hz, default 22.05 kHz + Apply time varying filter to buffered stream + + Args: + ffunc (function): function that applies filter + fmap (function): mapping function representing filter cutoff + sweep + qmap (optional, function): mapping function for a filters + Q parameter + flo (optional, :obj:`float`): lowest frequency of sweep in Hz, + default 20 + fhi (optional, :obj:`float`): lowest frequency of sweep in Hz, + default 22.05 kHz + qlo (optional, :obj:`float`): lowest 'Q' value of sweep, + default 0.5 + qhi (optional, :obj:`float`): lowest frequency of sweep, + default 10 + """ if not hasattr(self, "buffers"): Exception("needs bufferized stream, please run 'bufferize' method first.") @@ -77,21 +137,57 @@ def filt_sweep(self, ffunc, fmap, qmap=lambda x:x*0 + 0.1, self.consolidate_buffers() def get_sampfracs(self): + """ Get fractional position of the sample in total stream duration + """ self.sampfracs = np.linspace(0, 1, self.values.size) def save_wav(self, filename): - """ save audio stream to wav file, specified by filename""" + """Save audio stream to wav file, specified by filename + + Args: + filename (:obj:`str`): name of output WAV file + """ wavio.write(filename, self.values, self.samprate, sampwidth=3) def reset(self): - """ zero audio stream and buffers if present """ + """Zero audio stream and buffers if present.""" self.values *= 0. if hasattr(self, "buffers"): self.buffers.buffs_tile *= 0. self.buffers.buffs_olap *= 0. -class Buffers: +class Buffers: + """Audio buffers split into uniform discrete chunks or 'buffers'. + + Audio ~:class:`stream.Stream` as a discrete sequence of individual + 'buffers' of fixed duration (number of samples). This allows time + varying operations in frequency space, such as signal filtering. + Buffers are tiled in a 'brickwork' fashion so they always overlap + with another buffer. + + Attributes: + fade (:obj:`ndarray`): Window function for recombining + overlapping buffers + nsamp_padstream (:obj:`int`): Number of samples needed to split + the stream into discrete buffers of chosen length + nsamp_pad (:obj:`int`): Number of additional samples needed to + add to the original `Stream` size in this case + buffs_tile (:obj:`ndarray`): 2d array of buffers completely + enclosing the stream (number of buffers x samples per buffer) + buffs_olap (:obj:`ndarray`) 2d array of overlap buffers, + allowing for cross fading + Methods: + to_stream(): + Reconstruct stream by cross-fading buffers + """ def __init__(self, stream, bufflength=0.1): + """ + Args: + stream (~:class:`stream.Stream`): Stream object to be + represented using the buffers + bufflength (optional, :obj:`float`): duration in seconds of + each buffer to be generated + """ nbuff = stream.samprate*bufflength if nbuff < 20: Exception(f"Error: buffer length {nbuff} samples below " @@ -115,18 +211,27 @@ def __init__(self, stream, bufflength=0.1): # pad the stream up to an exact multiple of buffer sample length self.nsamp_padstream = self._nbuffs * self._nsamp_buff self.nsamp_pad = self.nsamp_padstream-stream._nsamp_stream - self.olap_pad = self.nsamp_pad-self._nsamp_halfbuff - self.olap_lim = min(stream._nsamp_stream, stream._nsamp_stream+self.olap_pad) + self._olap_pad = self.nsamp_pad-self._nsamp_halfbuff + self._olap_lim = min(stream._nsamp_stream, stream._nsamp_stream+self.olap_pad) # construct tile and overlap buffer arrays self.buffs_tile = np.pad(stream.values, (0,self.nsamp_pad) ).reshape((self._nbuffs, self._nsamp_buff)) - self.buffs_olap = np.pad(stream.values[self._nsamp_halfbuff:self.olap_lim], - (0,max(0, self.olap_pad)) + self.buffs_olap = np.pad(stream.values[self._nsamp_halfbuff:self._olap_lim], + (0,max(0, self._olap_pad)) ).reshape((self._nbuffs-1), self._nsamp_buff) def to_stream(self): - """ reconstruct stream by x-fading buffers """ + """Reconstruct stream by cross-fading buffers + + Takes the `self.buffs_tile` and `self.buffs_olap` arrays and using + the `self.fade` window function, add overlapping sample values + together to yield a 1d array of samples. + + Returns: + out (:obj:`ndarray`): 1d array of sample values representing the + new audio signal for the parent `Stream`. + """ # apply fades to buffers, first special edge cases... self.buffs_tile[0,self._nsamp_halfbuff:] *= self.fade[self._nsamp_halfbuff:] self.buffs_tile[-1,:self._nsamp_halfbuff] *= self.fade[:self._nsamp_halfbuff] @@ -140,6 +245,6 @@ def to_stream(self): padded_stream += self.buffs_tile.flatten() flat_olaps = self.buffs_olap.flatten() padded_stream[self._nsamp_halfbuff:-self._nsamp_halfbuff] += flat_olaps - +` # remove padding on returning reconstructed stream return padded_stream[:-self.nsamp_pad] From b6dba2889402bbf5f229ffecf8ecfb7e1a9b675c Mon Sep 17 00:00:00 2001 From: James Trayford Date: Wed, 21 Aug 2024 10:42:16 +0100 Subject: [PATCH 20/86] fix typo --- src/strauss/stream.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/strauss/stream.py b/src/strauss/stream.py index bbc64f7..6131e17 100644 --- a/src/strauss/stream.py +++ b/src/strauss/stream.py @@ -1,4 +1,4 @@ ->import numpy as np +import numpy as np import wavio import matplotlib.pyplot as plt from scipy.signal.windows import hann From 076970ff5fb2ed521a8ec6d6758569f182c3e3ce Mon Sep 17 00:00:00 2001 From: James Trayford Date: Wed, 21 Aug 2024 10:42:40 +0100 Subject: [PATCH 21/86] fix typo --- src/strauss/stream.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/strauss/stream.py b/src/strauss/stream.py index 6131e17..48c84ec 100644 --- a/src/strauss/stream.py +++ b/src/strauss/stream.py @@ -245,6 +245,6 @@ def to_stream(self): padded_stream += self.buffs_tile.flatten() flat_olaps = self.buffs_olap.flatten() padded_stream[self._nsamp_halfbuff:-self._nsamp_halfbuff] += flat_olaps -` + # remove padding on returning reconstructed stream return padded_stream[:-self.nsamp_pad] From 35bea6e57df51bc399de38d9f8cc8784f11233c7 Mon Sep 17 00:00:00 2001 From: James Trayford Date: Wed, 21 Aug 2024 10:50:37 +0100 Subject: [PATCH 22/86] fix formatting --- src/strauss/stream.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/strauss/stream.py b/src/strauss/stream.py index 48c84ec..a10d8d3 100644 --- a/src/strauss/stream.py +++ b/src/strauss/stream.py @@ -26,8 +26,7 @@ class Stream: wrapper to initialise Buffers subclass consolidate_buffers() wrapper to reassign stream values to consolidated stream - filt_sweep(self, ffunc, fmap, qmap=lambda x:x*0 + 0.1, - flo=20, fhi=2.205e4, qlo=0.5, qhi=10) + filt_sweep(self, ffunc, fmap, **kwargs) Apply time varying filter to buffered stream get_sampfracs() Get fractional position of the sample in total stream duration From 67af013ce0d59f0ee6e4abd3465b1e23282933e8 Mon Sep 17 00:00:00 2001 From: James Trayford Date: Wed, 21 Aug 2024 13:47:03 +0100 Subject: [PATCH 23/86] update generator docs --- docs/detailed.rst | 16 ++++++ src/strauss/generator.py | 107 +++++++++++++++++++++++++++++++-------- 2 files changed, 103 insertions(+), 20 deletions(-) diff --git a/docs/detailed.rst b/docs/detailed.rst index 63f7802..593d728 100644 --- a/docs/detailed.rst +++ b/docs/detailed.rst @@ -20,6 +20,7 @@ Generator ********* .. automodule:: strauss.generator :members: + :show-inheritance: Channels ******** @@ -36,6 +37,21 @@ Utilities .. automodule:: strauss.utilities :members: +Stream +****** + .. automodule:: strauss.stream + :members: + +Notes +***** + .. automodule:: strauss.notes + :members: + +Filters +******* + .. automodule:: strauss.filters + :members: + Text-to-Speech ************** .. automodule:: strauss.tts_caption diff --git a/src/strauss/generator.py b/src/strauss/generator.py index 014601e..024a41f 100644 --- a/src/strauss/generator.py +++ b/src/strauss/generator.py @@ -45,7 +45,6 @@ # ignore wavfile read warning that complains due to WAV file metadata warnings.filterwarnings("ignore", message="Chunk \(non-data\) not understood, skipping it\.") - # TO DO: # - Ultimately have Synth and Sampler classes that own their own stream (stream.py) object # allowing ADSR volume and filter enveloping, LFO implementation etc. @@ -53,10 +52,39 @@ # musical choices and uses these to generate sound, but can be interfaced with directly. def forward_loopsamp(s, start, end): + """Produce array of sample indices for looping a sample forward. + + Sample indices between values `start` and `end` that will loop the sample + such that it loops "forward", i.e. start, start+1, ..., end-1, end, start, + ... etc. + + Args: + s (:obj:`ndarray`): array of input sample indices + start (:obj:`int`): Index of sample after which looping should commence + end (:obj:`int`): Index of sample after which audio loops + + Returns: + out (:obj:`ndarray`): array of output sample indices + """ delsamp = end-start return np.piecewise(s, [s < start, s >= start], [lambda x: x, lambda x: (x-start)%(delsamp) + start]) def forward_back_loopsamp(s, start, end): + """Produce array of sample indices for looping a sample forward-back. + + Sample indices between values `start` and `end` that will loop the sample + such that it loops "forward-back", i.e. `start, start+1, ..., end-1, end, + end-1, ..., start+1, start, start+1, ...` etc. + ... etc. + + Args: + s (:obj:`ndarray`): array of input sample indices + start (:obj:`int`): Index of sample after which looping should commence + end (:obj:`int`): Index of sample after which audio loops + + Returns: + out (:obj:`ndarray`): array of output sample indices + """ delsamp = end-start return np.piecewise(s, [s < start, s >= start], [lambda x: x, lambda x: end - abs((x-start)%(2*(delsamp)) - (delsamp))]) @@ -66,16 +94,44 @@ class Generator: Generators have common initialisation and methods that are defined by this parent class. - - Args: + + Attributes: + samprate (:obj:`int`): Samples per second of audio stream (Hz) + audbuff (:obj:`int`): Samples per audio buffer + preset (:obj:`dict`): Dictionary of parameters defining the + generator. + + Methods: + load_preset(self, preset='default'): + Load parameters from a preset YAML file. + modify_preset(self, parameters, cleargroup=[]) + Modify parameters within current preset + preset_details(self, term="*"): + Print the names and descriptions of presets + envelope(self, samp, params, etype='volume') + Envelope function for modulating a single note + env_segment_curve(self, t, t1, y0, k) + Formula for segments of the envelope function + sine(self, s,f,p) + Sine-wave oscillator + saw(self,s,f,p) + Square-wave oscillator + tri(self,s,f,p) + Triangle-wave oscillator + noise(self,s,f,p) + White noise oscillator + lfo(self, samp, sampfrac, params, ltype='pitch') + Low-Frequency oscillator (LFO) + """ + def __init__(self, params={}, samprate=48000): + """ + Args: params (`optional`, :obj:`dict`): any generator parameters that differ from the generator :obj:`preset`, where keys and values are parameters names and values respectively. samprate (`optional`, :obj:`int`): the sample rate of the generated audio in samples per second (Hz) - """ - def __init__(self, params={}, samprate=48000): - """universal generator initialisation""" + """ self.samprate = samprate # samples per buffer (use 30Hz as minimum) @@ -86,7 +142,7 @@ def __init__(self, params={}, samprate=48000): self.preset = self.modify_preset(params) def load_preset(self, preset='default'): - """ load parameters from a preset YAML file. + """Load parameters from a preset YAML file. Wrapper method for the :obj:`presets.synth.load_preset` or :obj:`presets.sampler.load_preset` functions. Always load the @@ -113,7 +169,7 @@ def load_preset(self, preset='default'): self.modify_preset(preset) def modify_preset(self, parameters, cleargroup=[]): - """modify parameters within current preset + """Modify parameters within current preset method allows user to tweak generator parameters directly, using a dictionary of parameters and their values. subgroups @@ -136,7 +192,7 @@ def modify_preset(self, parameters, cleargroup=[]): del self.preset[grp][k] def preset_details(self, term="*"): - """ Print the names and descriptions of presets + """Print the names and descriptions of presets Wrapper for preset_details function. lists the name and description of built-in presets with names matching the search term. @@ -148,7 +204,7 @@ def preset_details(self, term="*"): getattr(presets, self.gtype).preset_details(name=term) def envelope(self, samp, params, etype='volume'): - """ Envelope function for modulating a single note + """Envelope function for modulating a single note The envelope function takes the pre-defined envelope parameters for the specified envelope type and returns the @@ -216,7 +272,7 @@ def envelope(self, samp, params, etype='volume'): return lvl*env def env_segment_curve(self, t, t1, y0, k): - """formula for segments of the envelope function + """Formula for segments of the envelope function Function to evaluate the segments of the envelope, allowing for curvature, i.e. concave & convex envelope segments. @@ -281,7 +337,7 @@ def tri(self,s,f,p): s (:obj:`array`-like): sample index f (:obj:`float`): samples per cycle p (:obj:`float` or :obj:`str`): if numerical, phase in units - of cycles, :obj:`'random'` indicates randomised. + of cycles :obj:`'random'` indicates randomised. Returns: v (:obj:`array`-like): values for each sample """ @@ -334,7 +390,6 @@ def lfo(self, samp, sampfrac, params, ltype='pitch'): env_dict = {} lfo_key = f'{ltype}_lfo' lfo_params = params[lfo_key] - env_dict['note_length'] = params['note_length'] env_dict['lfo_envelope'] = lfo_params @@ -372,19 +427,22 @@ class Synthesizer(Generator): the preset, and linearly combined to produce the sound. defines attribute :obj:`self.gtype = 'synth'`. - Args: - params (`optional`, :obj:`dict`): any generator parameters - that differ from the generator :obj:`preset`, where keys and - values are parameters names and values respectively. - samprate (`optional`, :obj:`int`): the sample rate of - the generated audio in samples per second (Hz) + Attributes: + gtype (:obj:`str`): Generator type Todo: * Add other synthesiser types, aside from additive (e.g. FM, vector, wavetable)? """ def __init__(self, params=None, samprate=48000): - + """ + Args: + params (`optional`, :obj:`dict`): any generator parameters + that differ from the generator :obj:`preset`, where keys + and values are parameters names and values respectively. + samprate (`optional`, :obj:`int`): the sample rate of + the generated audio in samples per second (Hz) + """ # default synth preset self.gtype = 'synth' self.preset = getattr(presets, self.gtype).load_preset() @@ -406,6 +464,10 @@ def setup_oscillators(self): specifying randomisation (:obj:`'random'`). Sets the :obj:`self.generate` method, using the :obj:`self.combine_oscs`. + + Note: + This is deprecated and will likely be removed from future + versions """ # oscdict = self.preset['oscillators'] # self.osclist = [] @@ -427,6 +489,9 @@ def setup_oscillators(self): def modify_preset(self, parameters, clear_oscs=True): """Synthesizer-specific wrapper for the modify_preset method. + This gives control over whether or not to clear the arbitrary + number of oscillators for synthesizer. + Args: parameters (:obj:`dict`): keys and items are the preset parameter names and new values. Nested dictionaries are @@ -948,6 +1013,8 @@ def play(self, mapping): class Spectralizer(Generator): """Spectralizer generator class + + """ def __init__(self, params=None, samprate=48000): # default synth preset From 54995b955be15086c7610fe211950c9565376f81 Mon Sep 17 00:00:00 2001 From: James Trayford Date: Wed, 21 Aug 2024 16:42:21 +0100 Subject: [PATCH 24/86] add generator documentation --- src/strauss/generator.py | 83 ++++++++++++++++++++++++++++++---------- 1 file changed, 63 insertions(+), 20 deletions(-) diff --git a/src/strauss/generator.py b/src/strauss/generator.py index 024a41f..bf6f6a4 100644 --- a/src/strauss/generator.py +++ b/src/strauss/generator.py @@ -428,7 +428,7 @@ class Synthesizer(Generator): attribute :obj:`self.gtype = 'synth'`. Attributes: - gtype (:obj:`str`): Generator type + gtype (:obj:`str`): Generator type Todo: * Add other synthesiser types, aside from additive (e.g. FM, @@ -621,22 +621,9 @@ class Sampler(Generator): controlling how these defines attribute :obj:`self.gtype = 'sampler'`. - Args: - sampfiles (`required`, :obj:`str`): string pointing to samples - to load. This can either point to a directory containing - samples, where `"path/to/samples"` contains files named - as `samples_A#4.wav` (ie. `_.wav`), - or a *Soundfont* file, with extension `.sf2`. - params (`optional`, :obj:`dict`): any generator parameters - that differ from the generator :obj:`preset`, where keys and - values are parameters names and values respectively. - samprate (`optional`, :obj:`int`): the sample rate of - the generated audio in samples per second (Hz) - sf_preset (`optional`, :obj:`int`) if using a *Soundfont* - (`.sf2`) file, this is the number of the preset to use. - All `.sf2` files should contain at least one preset. When - given default `None` value, will print available presets - and select the first preset. Note presets are 1-indexed. + Attributes: + gtype (:obj:`str`): Generator type + Todo: * Add zone mapping for samples (e.g. allow a sample to define a range of notes played at different speeds). @@ -647,6 +634,24 @@ class Sampler(Generator): """ def __init__(self, sampfiles, params=None, samprate=48000, sf_preset=None): + """ + Args: + sampfiles (`required`, :obj:`str`): string pointing to samples + to load. This can either point to a directory containing + samples, where `"path/to/samples"` contains files named + as `samples_A#4.wav` (ie. `_.wav`), + or a *Soundfont* file, with extension `.sf2`. + params (`optional`, :obj:`dict`): any generator parameters + that differ from the generator :obj:`preset`, where keys and + values are parameters names and values respectively. + samprate (`optional`, :obj:`int`): the sample rate of + the generated audio in samples per second (Hz) + sf_preset (`optional`, :obj:`int`) if using a *Soundfont* + (`.sf2`) file, this is the number of the preset to use. + All `.sf2` files should contain at least one preset. When + given default `None` value, will print available presets + and select the first preset. Note presets are 1-indexed. + """ # default sampler preset self.gtype = 'sampler' self.preset = getattr(presets, self.gtype).load_preset() @@ -1014,9 +1019,29 @@ def play(self, mapping): class Spectralizer(Generator): """Spectralizer generator class - - """ + This generator class synthesises sound from a spectrum input + using an *inverse Fast Fourier Transform* (iFFT) algorithm. + Defining a minimum and maximum frequency in Hz, input spectrum + is interpolated between these points such that the output + audio signal has the requested length. Phases are randomised + to avoid phase correlations. + + Attributes: + gtype (:obj:`str`): Generator type + + Todo: + * Add other synthesiser types, aside from additive (e.g. FM, + vector, wavetable)? + """ def __init__(self, params=None, samprate=48000): + """ + Args: + params (`optional`, :obj:`dict`): any generator parameters + that differ from the generator :obj:`preset`, where keys + and values are parameters names and values respectively. + samprate (`optional`, :obj:`int`): the sample rate of + the generated audio in samples per second (Hz) + """ # default synth preset self.gtype = 'spec' self.preset = getattr(presets, self.gtype).load_preset() @@ -1030,6 +1055,25 @@ def __init__(self, params=None, samprate=48000): def spectrum_to_signal(self, spectrum, phases, new_nlen, mindx, maxdx, interp_type): """ Convert the input spectrum into sound signal + + Performs the inverse fast fourier transform to produce spectral + sonification. + + Args: + spectrum (:obj:`ndarray`): Values of the spectrum, ordered + from high to low frequency + phases (:obj:`ndarray`): Array of values of `[0,2*numpy.pi]` + representing the complex number argument + new_nlen (:obj:`int`): Number of samples needed to enclose + the output signal. + mindx (:obj:`int`): Index in total Fourier transform + represnting the minimum audio frequency + maxdx (:obj:`int`): Index in total Fourier transform + represnting the maximum audio frequency + interp_type (:obj:`str`): Interpolation approach, either + `"sample"` interpolating between samples, or + `"preserve_power"` where cumulative power is interpolated + and then differentiated to avoid missing power. """ # NOTE: interpolation around a delta function can lead to splitting power between adjacent @@ -1193,7 +1237,6 @@ def play(self, mapping): sstream.consolidate_buffers() - sstream.values /= abs(sstream.values).max() # get volume envelope From f2bdf700318dd0af008f32b60db7debb7c31ea92 Mon Sep 17 00:00:00 2001 From: James Trayford Date: Fri, 23 Aug 2024 11:41:55 +0100 Subject: [PATCH 25/86] update more documentation --- docs/conf.py | 3 +- docs/detailed.rst | 49 ++++++++++++-------- setup.cfg | 2 +- src/strauss/channels.py | 5 -- src/strauss/filters.py | 12 ++++- src/strauss/generator.py | 49 ++++++-------------- src/strauss/notes.py | 34 ++++++++++---- src/strauss/score.py | 45 +++++++++--------- src/strauss/sonification.py | 63 +++++++++++++++---------- src/strauss/sources.py | 27 +++++++---- src/strauss/stream.py | 29 ++++-------- src/strauss/tts_caption.py | 7 +++ src/strauss/utilities.py | 92 ++++++++++++++++--------------------- 13 files changed, 218 insertions(+), 199 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 0b88976..6ec2bfb 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -35,7 +35,8 @@ 'sphinx.ext.ifconfig', 'sphinx.ext.viewcode', 'sphinx.ext.inheritance_diagram', 'sphinx.ext.autosummary','sphinx.ext.coverage', - 'sphinx.ext.napoleon'] + 'sphinx.ext.napoleon', 'recommonmark', + 'sphinx_markdown_tables'] print(extensions) diff --git a/docs/detailed.rst b/docs/detailed.rst index 593d728..42e08f0 100644 --- a/docs/detailed.rst +++ b/docs/detailed.rst @@ -1,58 +1,71 @@ .. _detailed: Detailed Documentation -^^^^^^^^^^^^^^^^^^^^^^ +###################### .. automodule:: strauss :imported-members: +User-Facing Modules +******************* + +These are the user-facing modules of the ``strauss`` code, that are used to define a ``Sonification``. + + Sources -******* +======= .. automodule:: strauss.sources :members: - + :show-inheritance: Score -***** +===== .. automodule:: strauss.score :members: - + :show-inheritance: Generator -********* +========= .. automodule:: strauss.generator :members: :show-inheritance: Channels -******** +======== .. automodule:: strauss.channels :members: - + :show-inheritance: Sonification -************ +============ .. automodule:: strauss.sonification :members: + :show-inheritance: + +Ancillary Modules +***************** +These are modules intended for internal use by the ``strauss`` module + Utilities -********* +========= .. automodule:: strauss.utilities :members: - + :show-inheritance: Stream -****** +====== .. automodule:: strauss.stream :members: - + :show-inheritance: Notes -***** +===== .. automodule:: strauss.notes :members: - + :show-inheritance: Filters -******* +======= .. automodule:: strauss.filters :members: - + :show-inheritance: Text-to-Speech -************** +============== .. automodule:: strauss.tts_caption :members: + :show-inheritance: diff --git a/setup.cfg b/setup.cfg index e7cc5b0..e5928e0 100644 --- a/setup.cfg +++ b/setup.cfg @@ -35,7 +35,7 @@ install_requires = sf2utils audioop-lts; python_version >= "3.13" [options.packages.find] -where = src +where = src' [options.extras_require] default = tqdm diff --git a/src/strauss/channels.py b/src/strauss/channels.py index 800450d..a68161c 100644 --- a/src/strauss/channels.py +++ b/src/strauss/channels.py @@ -5,11 +5,6 @@ objects that are channeled to different speakers in the sonification output. -Todo: - * Allow microphones to have a :obj:`polar` as well as :obj:`azimuth` - value, to place them anywhere on a sphere around a listener. - * parameterise the :obj:`"soundsphere"` standard setup, for VR - applications (in development). """ import numpy as np diff --git a/src/strauss/filters.py b/src/strauss/filters.py index 1b10958..7d8bba3 100644 --- a/src/strauss/filters.py +++ b/src/strauss/filters.py @@ -1,3 +1,13 @@ +"""The :obj:`filters` submodule: containing audio filter functions + +These are audio filters that can be applied to the audio signal in +frequency space to attenuate (filter out) frequencies. These can be +applied to individual ``Buffers`` as an evolvable parameter. + +To do: + * Support More Filter Types + * Implement resonance or `'Q'` variation +""" import numpy as np import scipy.signal as sig @@ -20,7 +30,7 @@ def LPF1(data, cutoff, q, order=5): def HPF1(data, cutoff, q, order=5): """ - High-pass filter data array given cutoff, q and LPF order + High-pass filter data array given cutoff, q and HPF order Args: data (array-like): Array containing signal for filtering diff --git a/src/strauss/generator.py b/src/strauss/generator.py index bf6f6a4..85aecd7 100644 --- a/src/strauss/generator.py +++ b/src/strauss/generator.py @@ -101,27 +101,6 @@ class Generator: preset (:obj:`dict`): Dictionary of parameters defining the generator. - Methods: - load_preset(self, preset='default'): - Load parameters from a preset YAML file. - modify_preset(self, parameters, cleargroup=[]) - Modify parameters within current preset - preset_details(self, term="*"): - Print the names and descriptions of presets - envelope(self, samp, params, etype='volume') - Envelope function for modulating a single note - env_segment_curve(self, t, t1, y0, k) - Formula for segments of the envelope function - sine(self, s,f,p) - Sine-wave oscillator - saw(self,s,f,p) - Square-wave oscillator - tri(self,s,f,p) - Triangle-wave oscillator - noise(self,s,f,p) - White noise oscillator - lfo(self, samp, sampfrac, params, ltype='pitch') - Low-Frequency oscillator (LFO) """ def __init__(self, params={}, samprate=48000): """ @@ -711,8 +690,11 @@ def __init__(self, sampfiles, params=None, samprate=48000, sf_preset=None): self.load_samples() def get_sfpreset_samples(self, sfpreset): - """Reading samples from a soundfont file along with metadata to - scale and tune notes. + """Reading samples from a soundfont file along with metadata. + + Read in the audio samples from a ``.sf2`` file to populate + available notes, mapping the MIDI key values to musical notes, + scaling and tuning samples as appropriate. Args: sf_preset (`optional`, :obj:`int`) The number of the *Soundfont* @@ -723,10 +705,10 @@ def get_sfpreset_samples(self, sfpreset): Returns: sfpre_dict (:obj:`dict`): dictionary of data required to load - soundfont samples in to the `Sampler`, including raw `samples`, - `sample_rate`, `original_pitch` of the samples, the `min_note` - and `max_note` in midi values to use the sample, and the - `sample_map`, assigning each sample to a note. + soundfont samples in to the `Sampler`, including raw `samples`, + `sample_rate`, `original_pitch` of the samples, the `min_note` + and `max_note` in midi values to use the sample, and the + `sample_map`, assigning each sample to a note. """ minmidi = np.inf maxmidi = -np.inf @@ -798,8 +780,8 @@ def reconstruct_samples(self, sfpre_dict): Return: sampdict (:obj:`dict`): output dictionary of mapped notes, with - values of arrays of sample values at the samplerate of the - `Generator`. + values of arrays of sample values at the samplerate of the + `Generator`. """ minkey = sfpre_dict['min_note'] maxkey = sfpre_dict['max_note'] @@ -879,7 +861,7 @@ def forward_loopsamp(self, s, start, end): Returns: s_new (:obj:`array`-like): new sample indices to create a - forward-looping effect + forward-looping effect """ delsamp = end-start return np.piecewise(s, [s < start, s >= start], @@ -899,7 +881,7 @@ def forward_back_loopsamp(self, s, start, end): Returns: s_new (:obj:`array`-like): new sample indices to create a - back and forth looping effect + back and forth looping effect """ delsamp = end-start @@ -1075,10 +1057,6 @@ def spectrum_to_signal(self, spectrum, phases, new_nlen, mindx, maxdx, interp_ty `"preserve_power"` where cumulative power is interpolated and then differentiated to avoid missing power. """ - - # NOTE: interpolation around a delta function can lead to splitting power between adjacent - # frequencies and result in an artificial beating. This can be avoided by choosing values - # a length that places the spectrum on the grid exactly if interp_type == "sample": ps = np.interp(np.linspace(0,1,maxdx-mindx), np.linspace(0, 1, spectrum.size), spectrum) @@ -1117,7 +1095,6 @@ def play(self, mapping): (not nested, see :meth:`strauss.generator.modify_preset`) where group members are indicated using :obj:`'/'` notation (e.g. :obj:`{'volume_envelope/A': 0.5, ...`). - """ samprate = self.samprate audbuff = self.audbuff diff --git a/src/strauss/notes.py b/src/strauss/notes.py index 3c2ddee..45c12af 100644 --- a/src/strauss/notes.py +++ b/src/strauss/notes.py @@ -1,3 +1,19 @@ +"""The :obj:`notes` submodule: translating musical note representations + +This submodule contains functions for translating between different +representations of musical notes or musical chords, and representative +sound frequencies and MIDI notes. + +Attributes: + tuneC0 (:obj:`float`): The frequency in Hz of the ``C0`` musical + note + notecount (:obj:`int`): Semitone offset above C in an octave + notesharps (:obj:`list`): Names of musical notes using sharp notation + noteflats (:obj:`list`): Names of musical notes using flat notation. + semitone_dict (:obj:`dict`): Dictionary of note names to semitone + offsets above C. +""" + import numpy as np import pychord as chrd import re @@ -21,7 +37,7 @@ def parse_note(notename): Args: notename (:obj:`str`): scientific pitch name, in format - , e.g. 'Ab4', 'E3' or 'F#2' + , e.g. 'Ab4', 'E3' or 'F#2' Returns: out (numerical): Frequency of note in Hertz @@ -38,13 +54,13 @@ def parse_chord(chordname, rootoct=3): `pychord` library Args: - chordname (:obj:`str`): Standard chord name, e.g. 'A7' - or 'Dm7add9' etc. + chordname (:obj:`str`): Standard chord name, e.g. `'A7'` + or `'Dm7add9'` etc. rootoct (:obj:`int`): Octave number Returns: out (:obj:`ndarray`) array of frequencies constituting - chord + chord """ chord = chrd.Chord(chordname) notes = chord.components_with_pitch(rootoct) @@ -60,12 +76,12 @@ def chord_notes(chordname, rootoct=3): library Args: - chordname (:obj:`str`): Standard chord name, e.g. 'A7' - or 'Dm7add9' etc. + chordname (:obj:`str`): Standard chord name, e.g. `'A7'` + or `'Dm7add9'` etc. rootoct (:obj:`int`): Octave number Returns: - out (:obj:`list`) list of note names constituting chord + out (:obj:`list`): list of note names constituting chord """ chord = chrd.Chord(chordname) notes = chord.components_with_pitch(int(rootoct)) @@ -80,8 +96,8 @@ def mkey_to_note(val): val (:obj:`int`): MIDI key value Returns: - out (:obj:`str`) scientific pitch name, in format - , e.g. 'E3' or 'F#2' + out (:obj:`str`): scientific pitch name, in format + ``, e.g. `'E3'` or `'F#2'` """ from strauss.notes import notesharps octv = val // 12 - 1 diff --git a/src/strauss/score.py b/src/strauss/score.py index 8c5d3ed..d74044a 100644 --- a/src/strauss/score.py +++ b/src/strauss/score.py @@ -37,28 +37,29 @@ class Score: D9_3 | Gmaj7_2"` plays each chord for 20s each. Chaining the same chord can be used to change intervals, (e.g. :obj:`chord_sequence = "F_3 | F_3 | C_4"` plays F for - 40s and C for 20s.) - - Args: - chord_sequence: (:obj:`str` or :obj:`list`): The chord or chord - sequence used for the sonification. If a string, parse using - :obj:`parse_chord_sequence`. If a :obj:`list`, each entry is - a :obj:`list` of strings or floats, representing the notes of a - chord. notes are represented as strings using scientific - notation, e.g. :obj:`[['C3','E3', 'G3'], ['C3', 'F3', 'A4']]`. If - floats, take values as note frequency in Hz. NOTE: currently - only supported in compination with the :obj:`Synthesiser` - generator class. - length: (:obj:`str` or :obj:`float`): the length of the - sonification. If a string, parse minutes and seconds from - format 'Xm Y.Zs'. If a float read as seconds. - pitch_binning (optional, :obj:`str`): pitch binning mode - choose - from 'adaptive', where sources are binned by the pitch mapping - such that each interval is represented the same fraction of the - time, and 'uniform' where the pitch binning is based on uniform - size bins in the mapped pitch parameter. + 40s and C for 20s.) """ def __init__(self, chord_sequence, length, pitch_binning='adaptive'): + """ + Args: + chord_sequence: (:obj:`str` or :obj:`list`): The chord or chord + sequence used for the sonification. If a string, parse using + :obj:`parse_chord_sequence`. If a :obj:`list`, each entry is + a :obj:`list` of strings or floats, representing the notes of a + chord. notes are represented as strings using scientific + notation, e.g. :obj:`[['C3','E3', 'G3'], ['C3', 'F3', 'A4']]`. + If floats, take values as note frequency in Hz. NOTE: currently + only supported in compination with the :obj:`Synthesiser` + generator class. + length: (:obj:`str` or :obj:`float`): the length of the + sonification. If a string, parse minutes and seconds from + format 'Xm Y.Zs'. If a float read as seconds. + pitch_binning (optional, :obj:`str`): pitch binning mode - choose + from 'adaptive', where sources are binned by the pitch mapping + such that each interval is represented the same fraction of the + time, and 'uniform' where the pitch binning is based on uniform + size bins in the mapped pitch parameter. + """ # check types to handle score length correctly if isinstance(length, str): regex = "([0-9]*)m\s*([0-9]*.[0-9]*)s" @@ -97,8 +98,8 @@ def parse_chord_sequence(chord_sequence): Returns: note_list (:obj:`list(list)`): the chord sequence represented as - a list of lists, where each sub-list is a chord comprised of - strings representing each note in scentific notation (e.g. 'A4') + a list of lists, where each sub-list is a chord comprised of + strings representing each note in scentific notation (e.g. 'A4') """ chord_list = chord_sequence.split("|") note_list = [] diff --git a/src/strauss/sonification.py b/src/strauss/sonification.py index e0dea16..02801ba 100644 --- a/src/strauss/sonification.py +++ b/src/strauss/sonification.py @@ -46,22 +46,6 @@ class Sonification: sonification for saving or playing in the :obj:`jupyter-notebook` environment - Args: - score (:class:`~strauss.score.Score`): Sonification :obj:`Score` - object - sources (:class:`~strauss.sources.Source`): Sonification - :obj:`Sources` child object (:class:`~strauss.sources.Events` - or :class:`~strauss.sources.Objects`) - generator (:class:`~strauss.generator.Generator`): Sonification - :obj:`Generator` child object - (:class:`~strauss.generator.Synthesizer` or - :class:`~strauss.generator.Sampler`) - audio_setup (:obj:`str`) The requested audio setup preset to - pass to :class:`~strauss.channels.audio_channels` - samprate (:obj:`int`) Integer sample rate in samples per second - (Hz), typically :obj:`44100` or :obj:`48000` for most audio - applications - ttsmodel (:obj:`str`) The text-to-speech model used for captions. Todo: * Support custom audio setups here too. @@ -69,6 +53,25 @@ class Sonification: def __init__(self, score, sources, generator, audio_setup='stereo', caption=None, samprate=48000, ttsmodel=Path('tts_models','en','jenny', 'jenny')): + """ + Args: + score (:class:`~strauss.score.Score`): Sonification :obj:`Score` + object + sources (:class:`~strauss.sources.Source`): Sonification + :obj:`Sources` child object (:class:`~strauss.sources.Events` + or :class:`~strauss.sources.Objects`) + generator (:class:`~strauss.generator.Generator`): Sonification + :obj:`Generator` child object + (:class:`~strauss.generator.Synthesizer` or + :class:`~strauss.generator.Sampler`) + audio_setup (:obj:`str`) The requested audio setup preset to + pass to :class:`~strauss.channels.audio_channels` + samprate (:obj:`int`) Integer sample rate in samples per second + (Hz), typically :obj:`44100` or :obj:`48000` for most audio + applications + ttsmodel (:obj:`str` or :obj:`PosixPath`) file path to the + text-to-speech model used for captions. + """ # sampling rate in Hz self.samprate = samprate @@ -115,8 +118,8 @@ def render(self, downsamp=1): Args: downsamp (optional, :obj:`int`): Optionally downsample - sources for multi-source sonifications for a quicker test - render by some integer factor. + sources for multi-source sonifications for a quicker test + render by some integer factor. """ # first determine if time is provided, if not assume all start at zero @@ -268,10 +271,6 @@ def save_combined(self, fname, ffmpeg_output=False, master_volume=1.): output to screen master_volume (:obj:`float`) Amplitude of the largest volume peak, from 0-1 - - Todo: - * Either find a way to avoid the need to unscramble channle - order, or find alternative to save wav files """ # setup list to house wav stream data inputs = [None]*len(self.out_channels) @@ -313,7 +312,7 @@ def save_combined(self, fname, ffmpeg_output=False, master_volume=1.): print("Saved.") def save(self, fname, master_volume=1.): - """ Save render as a combined multi-channel wav file + """ Save render as a combined multi-channel wav file. Can use this function to save sonification of any audio_setup to a 32-bit depth WAV using `scipy.io.wavfile` @@ -399,8 +398,14 @@ def notebook_display(self, show_waveform=True): display(ipd.Audio(outfmt,rate=self.out_channels['0'].samprate, autoplay=False)) def hear(self): - """ Play audio directly to the sound device, for command-line - playback. + """ Play audio directly to the sound device, for command-line playback. + + If available, use the ``sounddevice`` module to stream the sonification to + the sound device directly (speakers, headphones, etc.) via the underlying + ``PortAudio`` C-library. if unavaialable, raise error. + + Todo: + * Add more options to control the streamed audio """ channels = [] @@ -437,6 +442,14 @@ def hear(self): "\t 'sudo apt-get install libportaudio2.'\n") def _make_seamless(self, overlap_dur=0.05): + """ Make a seamlessly looping audio signal. + + Audio signal is made seamless by cross-fading end of signal back into start + over a duration (in seconds) defined by ``overlap_dur`` + + Args: + overlap_dur (:obj:`float`): cross-fade duration in seconds. + """ self.loop_channels = {} buffsize = int(overlap_dur*self.samprate) ramp = np.linspace(0,1, buffsize+1) diff --git a/src/strauss/sources.py b/src/strauss/sources.py index 813c182..307868a 100644 --- a/src/strauss/sources.py +++ b/src/strauss/sources.py @@ -15,7 +15,7 @@ param_lim_dict (:obj:`dict`): Dictionary combining `mappable` (keys) and `param_limits` (items). -Todo: +To do: * Store mappable, evolvable and parameter ranges in YAML files (cleaner). * Specialised Event and Object child classes (eg. spectralisation). """ @@ -96,15 +96,24 @@ class Source: `Source` isn't used directly, instead use child classes `Events` or `Objects`. - Args: - mapped_quantities (:obj:`list(str)`): The subset of parameters to - which data will be mapped. - + Attributes: + mapped_quantities (:obj:`list(str)`): The subset of parameters to + which data will be mapped. + raw_mapping (:obj:`dict`): Housing the input mapped parameters + and data, with keys corresponding to :obj:`mapped_quantities`. + mapping (:obj:`dict`): processed mapping :obj:`dict` rescaled + to parameter ranges, or interpolation funtions for evolving + parameters. + Raises: - UnrecognisedProperty: if `mapped_quantities` entry not in `mappable`. - + UnrecognisedProperty: if `mapped_quantities` entry not in `mappable`. """ def __init__(self, mapped_quantities): + """ + Args: + mapped_quantities (:obj:`list(str)`): The subset of parameters to + which data will be mapped. + """ # check these are all mappable parameters @@ -131,7 +140,6 @@ def __init__(self, mapped_quantities): self.mapped_quantities = mapped_quantities self.raw_mapping = {} self.mapping = {} - self.mapping_evo = {} def apply_mapping_functions(self, map_funcs={}, map_lims={}, param_lims={}): """ Taking input data and mapping to parameters. @@ -254,7 +262,8 @@ class Events(Source): Child class of `Source`, for `Event`-type sources. Each `Event` is discrete in `time` with single data values mapped to each - sonification parameter. + sonification parameter. + """ def fromfile(self, datafile, coldict): """Take input data from ASCII file diff --git a/src/strauss/stream.py b/src/strauss/stream.py index a10d8d3..0a5cc8d 100644 --- a/src/strauss/stream.py +++ b/src/strauss/stream.py @@ -1,9 +1,17 @@ +""" The :obj:`stream` submodule: representing the sound signal + +Containing the ``Stream`` class to house the ``Sonificiation`` audio +signal for each channel in the ``Channels`` object. This can be +split into uniform segments or `buffers` via the ``Buffers`` object, +for processing. + +To Do: + * implement filter Q-parameter mapping +""" import numpy as np import wavio import matplotlib.pyplot as plt from scipy.signal.windows import hann -# To Do -# - implement filter Q-parameter mapping class Stream: """ @@ -20,20 +28,6 @@ class Stream: samples (:obj:`ndarray`): Indices of each sample samptype (:obj:`ndarray`): Time in seconds each sample occurs buffers (:obj:`Buffers`): Buffered stream if generated - - Methods: - bufferize(bufflength=0.1) - wrapper to initialise Buffers subclass - consolidate_buffers() - wrapper to reassign stream values to consolidated stream - filt_sweep(self, ffunc, fmap, **kwargs) - Apply time varying filter to buffered stream - get_sampfracs() - Get fractional position of the sample in total stream duration - save_wav(filename) - Save audio stream to wav file, specified by filename - reset() - Zero audio stream and buffers if present """ def __init__(self, length, samprate=44100, ltype='seconds'): """ @@ -175,9 +169,6 @@ class Buffers: enclosing the stream (number of buffers x samples per buffer) buffs_olap (:obj:`ndarray`) 2d array of overlap buffers, allowing for cross fading - Methods: - to_stream(): - Reconstruct stream by cross-fading buffers """ def __init__(self, stream, bufflength=0.1): """ diff --git a/src/strauss/tts_caption.py b/src/strauss/tts_caption.py index 8288228..83b7806 100644 --- a/src/strauss/tts_caption.py +++ b/src/strauss/tts_caption.py @@ -1,3 +1,10 @@ +"""The :obj:`tts_caption` submodule: tool for generating spoken captions + +This uses text-to-speech via the the ``TTS`` module to allow captions +represented as strings to be converted to spoken audio to precede the +sonification. +""" + from scipy.io import wavfile from scipy.interpolate import interp1d import numpy as np diff --git a/src/strauss/utilities.py b/src/strauss/utilities.py index 4f5b5a8..6da9573 100644 --- a/src/strauss/utilities.py +++ b/src/strauss/utilities.py @@ -1,3 +1,9 @@ +""" The :obj:`utilities` submodule: useful functions for ``strauss`` + +This submodule is for useful utility functions used by other +``strauss`` modules. Generally these are not intended for direct +use by the user. +""" from functools import reduce import operator import numpy as np @@ -15,29 +21,18 @@ class NoSoundDevice: Drop-in replacement for sounddevice module if not working, so we can still use other functionality. - Attributes - ---------- - err : Exception - Error message from trying to import sounddevice - - Methods - ------- - play(*args, **kwargs) - Dummy function replacing `sounddevice.play` when ` - `sounddevice` is unavailable to raise self.err + Attributes: + err (:obj:`Exception`): Error message from trying to import + sounddevice """ def __init__(self, err): self.err = err def play(self, *args, **kwargs): - """ - Dummy function replacing `sounddevice.play` when ` `sounddevice` is unavailable to raise self.err + """Dummy function replacing `sounddevice.play` when unavailable. - Parameters - ---------- - *args - arguments (ignored) - **kwargs - keyword-only arguments (ignored) + Args: + *args: arguments (ignored) + **kwargs: keyword-only arguments (ignored) """ raise self.err @@ -93,13 +88,11 @@ def nested_dict_reassign(fromdict, todict): Recurse through dictionaries and sub-dictionaries in `fromdict` and reassign equivalent values in `todict` - Parameters - ---------- - fromdict : dict - Dictionary containing values to assign - todict : dict - Dictionary containing values to be reassigned - + Args + fromdict (:obj:`dict`): Dictionary containing values + to assign + todict (:obj:`dict`): Dictionary containing values + to be reassigned """ for k, v in fromdict.items(): if isinstance(v, dict): @@ -115,12 +108,11 @@ def nested_dict_fill(fromdict, todict): `fromdict` and assign to any entries missing from `todict` - Parameters - ---------- - fromdict : dict - Dictionary containing values to assign - todict : dict - Dictionary containing values to be reassigned + Args: + fromdict (:obj:`dict`): Dictionary containing values + to assign + todict (:obj:`dict`): Dictionary containing values to + be reassigned """ for k, v in fromdict.items(): if k not in todict: @@ -136,14 +128,13 @@ def nested_dict_idx_reassign(fromdict, todict, idx): iterables in `fromdict` and index value idx to assign or replact value in todict - Parameters - ---------- - fromdict : dict - Dictionary containing values to assign - todict : dict - Dictionary containing values to be reassigned - idx : int - index value for retrieving value from iterables + Args: + fromdict (:obj:`dict`): Dictionary containing values + to assign + todict (:obj:`dict`): Dictionary containing values + to be reassigned + idx (:obj:`dict`): Index value for retrieving value + from iterables """ for k, v in fromdict.items(): if isinstance(v, dict): @@ -158,16 +149,12 @@ def reassign_nested_item_from_keypath(dictionary, keypath, value): Reassign item in a nested dictionary to value using keypath syntax, to traverse multiple dictionaries - Parameters - ---------- - dictionary : dict - dict object to reassign values within - keypath : str - Using filepath syntax on given OS to traverse dictionary, i.e - 'a/b/c' ('a\\b\\c') corresponds to dict['a']['b']['c'] on Unix - (Windows) - value : - value to reassign dictionary value with + Args: + dictionary (:obj:`dict`): dict object to reassign values within + keypath (:obj:`str`): Using filepath syntax on given OS to + traverse dictionary, i.e 'a/b/c' ('a\\b\\c') corresponds to + dict['a']['b']['c'] on Unix (Windows). + value: value to reassign dictionary value with """ p = Path(keypath) keylist = list(p.parts) @@ -179,11 +166,10 @@ def linear_to_nested_dict_reassign(fromdict, todict): Iterate through a linear dictionary to reassign nested values using keypaths (d1['a/b/c'] -> d2['a']['b']['c'], d1['a']->d2['a']) - Parameters - ---------- - fromdict : dict + Args: + fromdict (:obj:`dict`): Dictionary containing values to assign - todict : dict + todict (:obj:`dict`): Dictionary containing values to be reassigned """ for k, v in fromdict.items(): From 2ff4e8032edde34d421a1bf84987438d83bf1ac8 Mon Sep 17 00:00:00 2001 From: James Trayford Date: Fri, 23 Aug 2024 11:43:46 +0100 Subject: [PATCH 26/86] update more documentation --- src/strauss/filters.py | 2 +- src/strauss/sources.py | 2 +- src/strauss/stream.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/strauss/filters.py b/src/strauss/filters.py index 7d8bba3..25cbbdb 100644 --- a/src/strauss/filters.py +++ b/src/strauss/filters.py @@ -4,7 +4,7 @@ frequency space to attenuate (filter out) frequencies. These can be applied to individual ``Buffers`` as an evolvable parameter. -To do: +Todo: * Support More Filter Types * Implement resonance or `'Q'` variation """ diff --git a/src/strauss/sources.py b/src/strauss/sources.py index 307868a..4a2469b 100644 --- a/src/strauss/sources.py +++ b/src/strauss/sources.py @@ -15,7 +15,7 @@ param_lim_dict (:obj:`dict`): Dictionary combining `mappable` (keys) and `param_limits` (items). -To do: +Todo: * Store mappable, evolvable and parameter ranges in YAML files (cleaner). * Specialised Event and Object child classes (eg. spectralisation). """ diff --git a/src/strauss/stream.py b/src/strauss/stream.py index 0a5cc8d..1b4fd9f 100644 --- a/src/strauss/stream.py +++ b/src/strauss/stream.py @@ -5,7 +5,7 @@ split into uniform segments or `buffers` via the ``Buffers`` object, for processing. -To Do: +Todo: * implement filter Q-parameter mapping """ import numpy as np From 10f5a1c80cf4506c20be40b990e553dac7dc54b3 Mon Sep 17 00:00:00 2001 From: James Trayford Date: Fri, 30 Aug 2024 15:54:23 +0100 Subject: [PATCH 27/86] get rid of extra packages --- docs/conf.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 6ec2bfb..a7f44f2 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -35,8 +35,9 @@ 'sphinx.ext.ifconfig', 'sphinx.ext.viewcode', 'sphinx.ext.inheritance_diagram', 'sphinx.ext.autosummary','sphinx.ext.coverage', - 'sphinx.ext.napoleon', 'recommonmark', - 'sphinx_markdown_tables'] + 'sphinx.ext.napoleon'] +#'recommonmark', +# 'sphinx_markdown_tables' print(extensions) From c7b2b68eae2c7e48315e25c4e618e14a6d68dd46 Mon Sep 17 00:00:00 2001 From: Samantha Youles Date: Mon, 9 Sep 2024 11:16:34 +0100 Subject: [PATCH 28/86] Added _meta data to yml files, added .md files for Sphinx documentation --- src/strauss/presets/sampler/default.md | 110 ++++++++++++++++ src/strauss/presets/sampler/default.yml | 79 ++++++++++++ src/strauss/presets/sampler/staccato.md | 16 +++ src/strauss/presets/sampler/staccato.yml | 17 ++- src/strauss/presets/sampler/sustain.md | 10 ++ src/strauss/presets/sampler/sustain.yml | 10 +- src/strauss/presets/spec/default.md | 116 +++++++++++++++++ src/strauss/presets/spec/default.yml | 82 ++++++++++++ src/strauss/presets/synth/default.md | 138 +++++++++++++++++++++ src/strauss/presets/synth/default.yml | 91 ++++++++++++++ src/strauss/presets/synth/pitch_mapper.md | 22 ++++ src/strauss/presets/synth/pitch_mapper.yml | 19 ++- src/strauss/presets/synth/spectraliser.md | 16 +++ src/strauss/presets/synth/spectraliser.yml | 13 +- src/strauss/presets/synth/windy.md | 22 ++++ src/strauss/presets/synth/windy.yml | 19 ++- 16 files changed, 775 insertions(+), 5 deletions(-) create mode 100644 src/strauss/presets/sampler/default.md create mode 100644 src/strauss/presets/sampler/staccato.md create mode 100644 src/strauss/presets/sampler/sustain.md create mode 100644 src/strauss/presets/spec/default.md create mode 100644 src/strauss/presets/synth/default.md create mode 100644 src/strauss/presets/synth/pitch_mapper.md create mode 100644 src/strauss/presets/synth/spectraliser.md create mode 100644 src/strauss/presets/synth/windy.md diff --git a/src/strauss/presets/sampler/default.md b/src/strauss/presets/sampler/default.md new file mode 100644 index 0000000..f8568b2 --- /dev/null +++ b/src/strauss/presets/sampler/default.md @@ -0,0 +1,110 @@ +## Name +preset name +## Description +full description +## Note_Length +Numerical note length in s or "sample" for the sample length or "none" to last to the end of the +## Looping +"off" for no looping "forward" to loop forward "forwardback" to loop back and forth +## Loop_Start +If looping, start and end point of loop in seconds. +## Loop_End +If loop_end is longer than the sample, clip to end of the sample. +## Volume_Envelope +### A +Attack +### D +Decay +### S +Sustain +### R +Release +### Ac + +### Dc + +### Rc + +### Level + + +## Filter +Do we apply a filter? +## Filter_Type +filter type +## Cutoff +filter cutoff +## Pitch_Lfo +### Use +switch feature on or off +### Wave +type of waveform +### Amount +amount +### Freq +frequency +### Freq_Shift +frequency shift +### Phase +random +### A +Attack +### D +Decay +### S +Sustain +### R +Release +### Ac + +### Dc + +### Rc + +### Level + + +## Volume_Lfo +### Use +switch feature on or off +### Wave +type of waveform +### Amount +amount +### Freq +frequency +### Freq_Shift +frequency shift +### Phase +random +### A +Attack +### D +Decay +### S +Sustain +### R +Release +### Ac + +### Dc + +### Rc + +### Level + + +## Volume +Master Volume +## Pitch +Default pitch selection +## Azimuth +azimuth coordinate for center panning +## Polar +polar coordinate for center panning +## Pitch_Hi +pitch range maximum in semitones +## Pitch_Lo +pitch range minimum in semitones +## Pitch_Shift +default shift in semitones diff --git a/src/strauss/presets/sampler/default.yml b/src/strauss/presets/sampler/default.yml index 3bb3bf8..2e90e6a 100644 --- a/src/strauss/presets/sampler/default.yml +++ b/src/strauss/presets/sampler/default.yml @@ -85,3 +85,82 @@ pitch_hi: 36 pitch_lo: 0 pitch_shift: 0. +_meta: + name: preset name + description: full description + note_length: >- + Numerical note length in s or "sample" for the sample length or "none" + to last to the end of the + looping: >- + "off" for no looping "forward" to loop forward "forwardback" to loop back and forth + loop_start: >- + If looping, start and end point of loop in seconds. + loop_end: >- + If loop_end is longer than the sample, clip to end of the sample. + volume_envelope: >- + define the note volume envelope applied to the samples + A,D,S & R correspond to 'attack', 'decay', 'sustain' and 'release' + volume_envelope: + A: Attack + D: Decay + S: Sustain + R: Release + Ac: "" + Dc: "" + Rc: "" + level: "" + filter: >- + Do we apply a filter? + filter_type: >- + filter type + cutoff: >- + filter cutoff + pitch_lfo: >- + or apply 'vibrato'? + pitch_lfo: + use: switch feature on or off + wave: type of waveform + amount: amount + freq: frequency + freq_shift: frequency shift + phase: random + A: Attack + D: Decay + S: Sustain + R: Release + Ac: "" + Dc: "" + Rc: "" + level: "" + volume_lfo: >- + or apply 'tremolo'? + volume_lfo: + use: switch feature on or off + wave: type of waveform + amount: amount + freq: frequency + freq_shift: frequency shift + phase: random + A: Attack + D: Decay + S: Sustain + R: Release + Ac: "" + Dc: "" + Rc: "" + level: "" + volume: >- + Master Volume + pitch: >- + Default pitch selection + azimuth: >- + azimuth coordinate for center panning + polar: >- + polar coordinate for center panning + pitch_hi: >- + pitch range maximum in semitones + pitch_lo: >- + pitch range minimum in semitones + pitch_shift: >- + default shift in semitones + diff --git a/src/strauss/presets/sampler/staccato.md b/src/strauss/presets/sampler/staccato.md new file mode 100644 index 0000000..e5ef052 --- /dev/null +++ b/src/strauss/presets/sampler/staccato.md @@ -0,0 +1,16 @@ +## Name +preset name +## Description +full description +## Note_Length +Numerical note length in s or "sample" for the sample length or "none" to last to the end of the +## Volume_Envelope +### A +Attack +### D +Decay +### S +Sustain +### R +Release + diff --git a/src/strauss/presets/sampler/staccato.yml b/src/strauss/presets/sampler/staccato.yml index c14210f..5c0ad14 100644 --- a/src/strauss/presets/sampler/staccato.yml +++ b/src/strauss/presets/sampler/staccato.yml @@ -16,4 +16,19 @@ volume_envelope: A: 0.01 D: 0. S: 1. - R: 0.07 \ No newline at end of file + R: 0.07 + +_meta: + name: preset name + description: full description + note_length: >- + Numerical note length in s or "sample" for the sample length or "none" to last to the end of the + volume_envelope: >- + Define the note volume envelope applied to the samples. + A,D,S & R correspond to 'attack', 'decay', 'sustain' and 'release'. + volume_envelope: + A: Attack + D: Decay + S: Sustain + R: Release + diff --git a/src/strauss/presets/sampler/sustain.md b/src/strauss/presets/sampler/sustain.md new file mode 100644 index 0000000..f947ed3 --- /dev/null +++ b/src/strauss/presets/sampler/sustain.md @@ -0,0 +1,10 @@ +## Name +preset name +## Description +full description +## Looping +use the 'forwardback' looping mode by default, between 0.2 and 0.5 seconds for each sample +## Loop_Start +start of the loop in seconds +## Loop_End +end of the loop in seconds diff --git a/src/strauss/presets/sampler/sustain.yml b/src/strauss/presets/sampler/sustain.yml index ac8d9e5..577bb34 100644 --- a/src/strauss/presets/sampler/sustain.yml +++ b/src/strauss/presets/sampler/sustain.yml @@ -13,4 +13,12 @@ description: >- # use the 'forwardback' looping mode by default, between 0.2 and 0.5 seconds for each sample looping: "forwardback" loop_start: 0.2 -loop_end: 0.5 \ No newline at end of file +loop_end: 0.5 + +_meta: + name: preset name + description: full description + looping: use the 'forwardback' looping mode by default, between 0.2 and 0.5 seconds for each sample + loop_start: start of the loop in seconds + loop_end: end of the loop in seconds + diff --git a/src/strauss/presets/spec/default.md b/src/strauss/presets/spec/default.md new file mode 100644 index 0000000..f8485c8 --- /dev/null +++ b/src/strauss/presets/spec/default.md @@ -0,0 +1,116 @@ +## _Meta +### Name +preset name +### Description +full description +### Note_Length +Numerical note length in s +### Volume_Envelope +#### A +Attack +#### D +Decay +#### S +Sustain +#### R +Release +#### Ac + +#### Dc + +#### Rc + +#### Level + + +### Filter +Do we apply a filter? +### Filter_Type +filter type +### Cutoff +filter cutoff +### Pitch_Lfo +#### Use +switch feature on or off +#### Wave +type of waveform +#### Amount +amount +#### Freq +frequency +#### Freq_Shift +frequency shift +#### Phase +random +#### A +Attack +#### D +Decay +#### S +Sustain +#### R +Release +#### Ac + +#### Dc + +#### Rc + +#### Level + + +### Volume_Lfo +#### Use +switch feature on or off +#### Wave +type of waveform +#### Amount +amount +#### Freq +frequency +#### Freq_Shift +frequency shift +#### Phase +random +#### A +Attack +#### D +Decay +#### S +Sustain +#### R +Release +#### Ac + +#### Dc + +#### Rc + +#### Level + + +### Volume +Master Volume +### Interpolation_Type +How to interpolate and resample in the spectrum. "sample": interpolate spectrum values directly; "preserve_power": integrate, interpolate then differentiate to avoid missing power in narrow features. +### Regen_Phases +For an evolving spectrum, do we regenerate phases for each buffer, or keep the same? These have differing effects. +### Fit_Spec_Multiples +Whether or not to generate IFFT such that the spectrum sample points are hit exactly. +### Min_Freq +Minimum frequency in Hz +### Max_Freq +Maximum frequency in Hz +### Pitch +Default pitch selection +### Azimuth +azimuth coordinate for center panning +### Polar +polar coordinate for center panning +### Pitch_Hi +pitch range maximum in semitones +### Pitch_Lo +pitch range minimum in semitones +### Pitch_Shift +default shift in semitones + diff --git a/src/strauss/presets/spec/default.yml b/src/strauss/presets/spec/default.yml index 6783b6c..708e656 100644 --- a/src/strauss/presets/spec/default.yml +++ b/src/strauss/presets/spec/default.yml @@ -100,3 +100,85 @@ pitch_hi: 0.1 pitch_lo: 0 pitch_shift: 0. +_meta: + name: preset name + description: full description + note_length: >- + Numerical note length in s + volume_envelope: >- + Define the note volume envelope applied to the samples. + A,D,S & R correspond to 'attack', 'decay', 'sustain' and 'release'. + volume_envelope: + A: Attack + D: Decay + S: Sustain + R: Release + Ac: "" + Dc: "" + Rc: "" + level: "" + filter: >- + Do we apply a filter? + filter_type: >- + filter type + cutoff: >- + filter cutoff + pitch_lfo: >- + or apply 'vibrato'? + pitch_lfo: + use: switch feature on or off + wave: type of waveform + amount: amount + freq: frequency + freq_shift: frequency shift + phase: random + A: Attack + D: Decay + S: Sustain + R: Release + Ac: "" + Dc: "" + Rc: "" + level: "" + volume_lfo: >- + or apply 'tremolo'? + volume_lfo: + use: switch feature on or off + wave: type of waveform + amount: amount + freq: frequency + freq_shift: frequency shift + phase: random + A: Attack + D: Decay + S: Sustain + R: Release + Ac: "" + Dc: "" + Rc: "" + level: "" + volume: >- + Master Volume + interpolation_type: >- + How to interpolate and resample in the spectrum. "sample": interpolate spectrum values directly; "preserve_power": integrate, interpolate then differentiate to avoid missing power in narrow features. + regen_phases: >- + For an evolving spectrum, do we regenerate phases for each buffer, or keep the same? These have differing effects. + fit_spec_multiples: >- + Whether or not to generate IFFT such that the spectrum sample points are hit exactly. + min_freq: >- + Minimum frequency in Hz + max_freq: >- + Maximum frequency in Hz + pitch: >- + Default pitch selection + azimuth: >- + azimuth coordinate for center panning + polar: >- + polar coordinate for center panning + pitch_hi: >- + pitch range maximum in semitones + pitch_lo: >- + pitch range minimum in semitones + pitch_shift: >- + default shift in semitones + diff --git a/src/strauss/presets/synth/default.md b/src/strauss/presets/synth/default.md new file mode 100644 index 0000000..cf3eafd --- /dev/null +++ b/src/strauss/presets/synth/default.md @@ -0,0 +1,138 @@ +## _Meta +### Name +preset name +### Description +full description +### Oscillators +#### Osc1 +##### Form +waveform, choose from ['saw', 'square', 'sine', 'tri', 'noise'] +##### Level +intrinsic volume +##### Detune +change in tuning as a percentage of the input frequency +##### Phase +phase + +#### Osc2 +##### Form +waveform, choose from ['saw', 'square', 'sine', 'tri', 'noise'] +##### Level +intrinsic volume +##### Detune +change in tuning as a percentage of the input frequency +##### Phase +phase + +#### Osc3 +##### Form +waveform, choose from ['saw', 'square', 'sine', 'tri', 'noise'] +##### Level +intrinsic volume +##### Detune +change in tuning as a percentage of the input frequency +##### Phase +phase + + +### Note_Length +Numerical note length in s +### Volume_Envelope +#### A +Attack +#### D +Decay +#### S +Sustain +#### R +Release +#### Ac + +#### Dc + +#### Rc + +#### Level + + +### Filter +Do we apply a filter? +### Filter_Type +filter type +### Cutoff +filter cutoff +### Pitch_Lfo +#### Use +switch feature on or off +#### Wave +type of waveform +#### Amount +amount +#### Freq +frequency +#### Freq_Shift +frequency shift +#### Phase +random +#### A +Attack +#### D +Decay +#### S +Sustain +#### R +Release +#### Ac + +#### Dc + +#### Rc + +#### Level + + +### Volume_Lfo +#### Use +switch feature on or off +#### Wave +type of waveform +#### Amount +amount +#### Freq +frequency +#### Freq_Shift +frequency shift +#### Phase +random +#### A +Attack +#### D +Decay +#### S +Sustain +#### R +Release +#### Ac + +#### Dc + +#### Rc + +#### Level + + +### Volume +Master Volume +### Pitch +Default pitch selection +### Azimuth +azimuth coordinate for center panning +### Polar +polar coordinate for center panning +### Pitch_Hi +pitch range maximum in semitones +### Pitch_Lo +pitch range minimum in semitones +### Pitch_Shift +default shift in semitones + diff --git a/src/strauss/presets/synth/default.yml b/src/strauss/presets/synth/default.yml index a1ca87e..6c71548 100644 --- a/src/strauss/presets/synth/default.yml +++ b/src/strauss/presets/synth/default.yml @@ -109,3 +109,94 @@ pitch_hi: 0.1 pitch_lo: 0 pitch_shift: 0. +_meta: + name: preset name + description: full description + oscillators: >- + Oscillator information. Oscillator are denoted osc with n=3 by default. + oscillators: + osc1: + form: waveform, choose from ['saw', 'square', 'sine', 'tri', 'noise'] + level: intrinsic volume + detune: change in tuning as a percentage of the input frequency + phase: phase + osc2: + form: waveform, choose from ['saw', 'square', 'sine', 'tri', 'noise'] + level: intrinsic volume + detune: change in tuning as a percentage of the input frequency + phase: phase + osc3: + form: waveform, choose from ['saw', 'square', 'sine', 'tri', 'noise'] + level: intrinsic volume + detune: change in tuning as a percentage of the input frequency + phase: phase + + note_length: >- + Numerical note length in s + volume_envelope: >- + Define the note volume envelope applied to the samples. + A,D,S & R correspond to 'attack', 'decay', 'sustain' and 'release'. + volume_envelope: + A: Attack + D: Decay + S: Sustain + R: Release + Ac: "" + Dc: "" + Rc: "" + level: "" + filter: >- + Do we apply a filter? + filter_type: >- + filter type + cutoff: >- + filter cutoff + pitch_lfo: >- + or apply 'vibrato'? + pitch_lfo: + use: switch feature on or off + wave: type of waveform + amount: amount + freq: frequency + freq_shift: frequency shift + phase: random + A: Attack + D: Decay + S: Sustain + R: Release + Ac: "" + Dc: "" + Rc: "" + level: "" + volume_lfo: >- + or apply 'tremolo'? + volume_lfo: + use: switch feature on or off + wave: type of waveform + amount: amount + freq: frequency + freq_shift: frequency shift + phase: random + A: Attack + D: Decay + S: Sustain + R: Release + Ac: "" + Dc: "" + Rc: "" + level: "" + volume: >- + Master Volume + pitch: >- + Default pitch selection + azimuth: >- + azimuth coordinate for center panning + polar: >- + polar coordinate for center panning + pitch_hi: >- + pitch range maximum in semitones + pitch_lo: >- + pitch range minimum in semitones + pitch_shift: >- + default shift in semitones + diff --git a/src/strauss/presets/synth/pitch_mapper.md b/src/strauss/presets/synth/pitch_mapper.md new file mode 100644 index 0000000..eb75c6b --- /dev/null +++ b/src/strauss/presets/synth/pitch_mapper.md @@ -0,0 +1,22 @@ +## Name +preset name +## Description +full description +## Oscillators +### Osc1 +#### Form +waveform, choose from ['saw', 'square', 'sine', 'tri', 'noise'] +#### Level +intrinsic volume +#### Detune +change in tuning as a percentage of the input frequency +#### Phase +phase + + +## Pitch_Hi +pitch range maximum in semitones +## Pitch_Lo +pitch range minimum in semitones +## Pitch_Shift +default shift in semitones diff --git a/src/strauss/presets/synth/pitch_mapper.yml b/src/strauss/presets/synth/pitch_mapper.yml index 590080b..5031924 100644 --- a/src/strauss/presets/synth/pitch_mapper.yml +++ b/src/strauss/presets/synth/pitch_mapper.yml @@ -20,4 +20,21 @@ pitch_hi: 36 pitch_lo: 0 pitch_shift: 0. - +_meta: + name: preset name + description: full description + oscillators: >- + Oscillator information. + oscillators: + osc1: + form: waveform, choose from ['saw', 'square', 'sine', 'tri', 'noise'] + level: intrinsic volume + detune: change in tuning as a percentage of the input frequency + phase: phase + pitch_hi: >- + pitch range maximum in semitones + pitch_lo: >- + pitch range minimum in semitones + pitch_shift: >- + default shift in semitones + diff --git a/src/strauss/presets/synth/spectraliser.md b/src/strauss/presets/synth/spectraliser.md new file mode 100644 index 0000000..cb53ecb --- /dev/null +++ b/src/strauss/presets/synth/spectraliser.md @@ -0,0 +1,16 @@ +## Name +preset name +## Description +full description +## Oscillators +### Osc1 +#### Form +waveform, choose from ['saw', 'square', 'sine', 'tri', 'noise'] +#### Level +intrinsic volume +#### Detune +change in tuning as a percentage of the input frequency +#### Phase +phase + + diff --git a/src/strauss/presets/synth/spectraliser.yml b/src/strauss/presets/synth/spectraliser.yml index 9e8d77f..3a1d13a 100644 --- a/src/strauss/presets/synth/spectraliser.yml +++ b/src/strauss/presets/synth/spectraliser.yml @@ -14,4 +14,15 @@ oscillators: detune: 0. phase: 'random' - +_meta: + name: preset name + description: full description + oscillators: >- + Oscillator information + oscillators: + osc1: + form: waveform, choose from ['saw', 'square', 'sine', 'tri', 'noise'] + level: intrinsic volume + detune: change in tuning as a percentage of the input frequency + phase: phase + diff --git a/src/strauss/presets/synth/windy.md b/src/strauss/presets/synth/windy.md new file mode 100644 index 0000000..60f909d --- /dev/null +++ b/src/strauss/presets/synth/windy.md @@ -0,0 +1,22 @@ +## Name +preset name +## Description +full description +## Oscillators +### Osc1 +#### Form +waveform, choose from ['saw', 'square', 'sine', 'tri', 'noise'] +#### Level +intrinsic volume +#### Detune +change in tuning as a percentage of the input frequency +#### Phase +phase + + +## Filter +Do we apply a filter? +## Filter_Type +filter type +## Cutoff +filter cutoff diff --git a/src/strauss/presets/synth/windy.yml b/src/strauss/presets/synth/windy.yml index 3397cc6..4a6f918 100644 --- a/src/strauss/presets/synth/windy.yml +++ b/src/strauss/presets/synth/windy.yml @@ -18,4 +18,21 @@ filter: "on" filter_type: "LPF1" cutoff: 0.3 - +_meta: + name: preset name + description: full description + oscillators: >- + Oscillator information + oscillators: + osc1: + form: waveform, choose from ['saw', 'square', 'sine', 'tri', 'noise'] + level: intrinsic volume + detune: change in tuning as a percentage of the input frequency + phase: phase + filter: >- + Do we apply a filter? + filter_type: >- + filter type + cutoff: >- + filter cutoff + From 07178bf9df06af449fb5c2d92c894165a8edb240 Mon Sep 17 00:00:00 2001 From: Samantha Youles Date: Mon, 9 Sep 2024 11:40:35 +0100 Subject: [PATCH 29/86] Added _meta data to yml files, added .md files for Sphinx documentation --- src/strauss/presets/sampler/ranges/default.md | 62 ++++++++++++++++ .../presets/sampler/ranges/default.yml | 39 +++++++++++ src/strauss/presets/spec/ranges/default.md | 70 +++++++++++++++++++ src/strauss/presets/spec/ranges/default.yml | 43 ++++++++++++ src/strauss/presets/synth/ranges/default.md | 62 ++++++++++++++++ src/strauss/presets/synth/ranges/default.yml | 39 +++++++++++ 6 files changed, 315 insertions(+) create mode 100644 src/strauss/presets/sampler/ranges/default.md create mode 100644 src/strauss/presets/spec/ranges/default.md create mode 100644 src/strauss/presets/synth/ranges/default.md diff --git a/src/strauss/presets/sampler/ranges/default.md b/src/strauss/presets/sampler/ranges/default.md new file mode 100644 index 0000000..f6caa40 --- /dev/null +++ b/src/strauss/presets/sampler/ranges/default.md @@ -0,0 +1,62 @@ +## Note_Length +Numerical note length +## Note_Length_Units +Units for numerical note length, e.g. seconds +## Volume_Envelope +### A +Attack +### D +Decay +### S +Sustain +### R +Release +### Ac + +### Dc + +### Rc + +### Level + +### A_Unit +units for Attack +### D_Unit +units for Decay +### S_Unit +units for Sustain +### R_Unit +units for Release +### Ac_Unit +units for Ac +### Dc_Unit +units for Dc +### Rc_Unit +units for Rc +### Level_Unit +units for level + +## Cutoff +filter cutoff +## Cutoff_Unit +units for filter cutoff +## Volume +Master volume +## Volume_Unit +Units for master volume +## Pitch +Default pitch selection +## Pitch_Unit +Units for Default pitch selection +## Azimuth +azimuth coordinate for center panning +## Azimuth_Unit +units for azimuth coordinate for center panning +## Polar +polar coordinate for center panning +## Polar_Unit +units for polar coordinate for center panning +## Pitch_Shift +default shift in semitones +## Pitch_Shift_Unit +units for default shift in semitones diff --git a/src/strauss/presets/sampler/ranges/default.yml b/src/strauss/presets/sampler/ranges/default.yml index 7d71059..723333a 100644 --- a/src/strauss/presets/sampler/ranges/default.yml +++ b/src/strauss/presets/sampler/ranges/default.yml @@ -45,3 +45,42 @@ polar_unit: 'unitless' # pitch range and default shift in semitones pitch_shift: [0., 36.] pitch_shift_unit: 'semitones' + +_meta: + note_length: >- + Numerical note length + note_length_units: >- + Units for numerical note length, e.g. seconds + volume_envelope: >- + define the note volume envelope applied to the samples + A,D,S & R correspond to 'attack', 'decay', 'sustain' and 'release' + volume_envelope: + A: Attack + D: Decay + S: Sustain + R: Release + Ac: "" + Dc: "" + Rc: "" + level: "" + A_unit: units for Attack + D_unit: units for Decay + S_unit: units for Sustain + R_unit: units for Release + Ac_unit: units for Ac + Dc_unit: units for Dc + Rc_unit: units for Rc + level_unit: units for level + cutoff: filter cutoff + cutoff_unit: units for filter cutoff + volume: Master volume + volume_unit: Units for master volume + pitch: Default pitch selection + pitch_unit: Units for Default pitch selection + azimuth: azimuth coordinate for center panning + azimuth_unit: units for azimuth coordinate for center panning + polar: polar coordinate for center panning + polar_unit: units for polar coordinate for center panning + pitch_shift: default shift in semitones + pitch_shift_unit: units for default shift in semitones + diff --git a/src/strauss/presets/spec/ranges/default.md b/src/strauss/presets/spec/ranges/default.md new file mode 100644 index 0000000..4c5bea3 --- /dev/null +++ b/src/strauss/presets/spec/ranges/default.md @@ -0,0 +1,70 @@ +## Note_Length +Numerical note length +## Note_Length_Units +Units for numerical note length, e.g. seconds +## Volume_Envelope +### A +Attack +### D +Decay +### S +Sustain +### R +Release +### Ac + +### Dc + +### Rc + +### Level + +### A_Unit +units for Attack +### D_Unit +units for Decay +### S_Unit +units for Sustain +### R_Unit +units for Release +### Ac_Unit +units for Ac +### Dc_Unit +units for Dc +### Rc_Unit +units for Rc +### Level_Unit +units for level + +## Cutoff +filter cutoff +## Cutoff_Unit +units for filter cutoff +## Volume +Master volume +## Volume_Unit +Units for master volume +## Min_Freq +Minimum frequency +## Max_Freq +Maximum frequency +## Min_Freq_Unit +Units for minimum frequency +## Max_Freq_Unit +Units for maximum frequency +## Pitch +Default pitch selection +## Pitch_Unit +Units for Default pitch selection +## Phi +phi coordinate for center panning +## Phi_Unit +units for phi coordinate for center panning +## Theta +theta coordinate for center panning +## Theta_Unit +units for theta coordinate for center panning +## Pitch_Shift +default shift in semitones +## Pitch_Shift_Unit +units for default shift in semitones diff --git a/src/strauss/presets/spec/ranges/default.yml b/src/strauss/presets/spec/ranges/default.yml index e593f07..a62ace7 100644 --- a/src/strauss/presets/spec/ranges/default.yml +++ b/src/strauss/presets/spec/ranges/default.yml @@ -51,3 +51,46 @@ theta_unit: 'unitless' # pitch range and default shift in semitones pitch_shift: [0., 36.] pitch_shift_unit: 'semitones' + +_meta: + note_length: >- + Numerical note length + note_length_units: >- + Units for numerical note length, e.g. seconds + volume_envelope: >- + define the note volume envelope applied to the samples + A,D,S & R correspond to 'attack', 'decay', 'sustain' and 'release' + volume_envelope: + A: Attack + D: Decay + S: Sustain + R: Release + Ac: "" + Dc: "" + Rc: "" + level: "" + A_unit: units for Attack + D_unit: units for Decay + S_unit: units for Sustain + R_unit: units for Release + Ac_unit: units for Ac + Dc_unit: units for Dc + Rc_unit: units for Rc + level_unit: units for level + cutoff: filter cutoff + cutoff_unit: units for filter cutoff + volume: Master volume + volume_unit: Units for master volume + min_freq: Minimum frequency + max_freq: Maximum frequency + min_freq_unit: Units for minimum frequency + max_freq_unit: Units for maximum frequency + pitch: Default pitch selection + pitch_unit: Units for Default pitch selection + phi: phi coordinate for center panning + phi_unit: units for phi coordinate for center panning + theta: theta coordinate for center panning + theta_unit: units for theta coordinate for center panning + pitch_shift: default shift in semitones + pitch_shift_unit: units for default shift in semitones + diff --git a/src/strauss/presets/synth/ranges/default.md b/src/strauss/presets/synth/ranges/default.md new file mode 100644 index 0000000..f6caa40 --- /dev/null +++ b/src/strauss/presets/synth/ranges/default.md @@ -0,0 +1,62 @@ +## Note_Length +Numerical note length +## Note_Length_Units +Units for numerical note length, e.g. seconds +## Volume_Envelope +### A +Attack +### D +Decay +### S +Sustain +### R +Release +### Ac + +### Dc + +### Rc + +### Level + +### A_Unit +units for Attack +### D_Unit +units for Decay +### S_Unit +units for Sustain +### R_Unit +units for Release +### Ac_Unit +units for Ac +### Dc_Unit +units for Dc +### Rc_Unit +units for Rc +### Level_Unit +units for level + +## Cutoff +filter cutoff +## Cutoff_Unit +units for filter cutoff +## Volume +Master volume +## Volume_Unit +Units for master volume +## Pitch +Default pitch selection +## Pitch_Unit +Units for Default pitch selection +## Azimuth +azimuth coordinate for center panning +## Azimuth_Unit +units for azimuth coordinate for center panning +## Polar +polar coordinate for center panning +## Polar_Unit +units for polar coordinate for center panning +## Pitch_Shift +default shift in semitones +## Pitch_Shift_Unit +units for default shift in semitones diff --git a/src/strauss/presets/synth/ranges/default.yml b/src/strauss/presets/synth/ranges/default.yml index 7d71059..723333a 100644 --- a/src/strauss/presets/synth/ranges/default.yml +++ b/src/strauss/presets/synth/ranges/default.yml @@ -45,3 +45,42 @@ polar_unit: 'unitless' # pitch range and default shift in semitones pitch_shift: [0., 36.] pitch_shift_unit: 'semitones' + +_meta: + note_length: >- + Numerical note length + note_length_units: >- + Units for numerical note length, e.g. seconds + volume_envelope: >- + define the note volume envelope applied to the samples + A,D,S & R correspond to 'attack', 'decay', 'sustain' and 'release' + volume_envelope: + A: Attack + D: Decay + S: Sustain + R: Release + Ac: "" + Dc: "" + Rc: "" + level: "" + A_unit: units for Attack + D_unit: units for Decay + S_unit: units for Sustain + R_unit: units for Release + Ac_unit: units for Ac + Dc_unit: units for Dc + Rc_unit: units for Rc + level_unit: units for level + cutoff: filter cutoff + cutoff_unit: units for filter cutoff + volume: Master volume + volume_unit: Units for master volume + pitch: Default pitch selection + pitch_unit: Units for Default pitch selection + azimuth: azimuth coordinate for center panning + azimuth_unit: units for azimuth coordinate for center panning + polar: polar coordinate for center panning + polar_unit: units for polar coordinate for center panning + pitch_shift: default shift in semitones + pitch_shift_unit: units for default shift in semitones + From 72298a38ff5e5f7a2a863859c256c2eec62fe54d Mon Sep 17 00:00:00 2001 From: Samantha Youles Date: Mon, 21 Oct 2024 12:18:22 +0100 Subject: [PATCH 30/86] Documented notebooks --- docs/examples.rst | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/docs/examples.rst b/docs/examples.rst index b34a7a5..2ffa08e 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -6,9 +6,46 @@ Examples Here, we explain some of the example sonfications included in the :code:`examples/` directory of the Strauss repo. These are all in *Python Notebook* (:code:`.ipynb`) format. +Audio Caption (:code:`AudioCaption.ipynb`) +****************************************** +The *Audio Caption* example demonstrates how to add audio captions to a sonification, using a text-to-speech (TTS) module. The TTS module is not included in the standard Strauss installation, but it can be installed by using pip install strauss[TTS]. This example uses the Strauss :code:`Sampler` to play a short sequence of glockenspiel notes, then generates an audio caption using a standard TTS voice. The notebook allows the user to try different voices and languages from TTS. + + +Day Sequence (:code:`DaySequence.ipynb`) +**************************************** +The *Day Sequence* sonification generates the sunrise to sunset sonification used in the "Audible Universe" planetarium show. Samples are downloaded from a Google drive to a local directory, and are played using the Strauss :code:`Sampler`. This spatialisation can be mapped to any audio setup, and a :code:`5.1` system was used for the planetarium, but for the example we use :code:`stereo`. + + +Earth System (:code:`EarthSystem.ipynb`) +**************************************** +The *Earth System* sonification represents the ratio of ocean to land along lines of longitude as the Earth spins through three rotation cycles. We use the Strauss :code:`Synthesiser` to generate chords. A low pass filter is employed to generate a brighter sound to represent a high water fraction and a duller sound for high land fraction. + +A video of this sequence is available starting at 2:17 in `this video `_. + +Light Curve Soundfonts (:code:`LightCurveSoundfonts .ipynb`) +************************************************************ +The *Light Curve Soundfonts* example demonstrates how to use imported soundfont files to use virtual musical instruments in a sonification. Soundfont files are widely available online. We download a flute and guitar sounds from `Soundfonts 4 U `_. + +Sonifying Data 1D (:code:`SonifyingData1D.ipynb`) +************************************************* +The *Sonifying Data 1D* example demonstrates some generic techniques for sonifying one-dimensional data series. We construct some mock data with features and noise. For all examples we use the Strauss :code:`Synthesiser` to create a 30 second, mono sonification. We demonstrate a variety of ways to map y as a function of x, using the change in some expressive property of sound (e.g. pitch_shift, volume and filter-cutoff) as a function of time. + + +Spectral Data (:code:`SpectralData.ipynb`) +****************************************** +The *Spectral Data* sonification demonstrates use of the Strauss :code:`Spectraliser` to represent Data. A direct spectralisation approach where the sound is generated by treating the 1D data as a sound spectrum. This uses a direct inverse Fourier transform, which is relatively intuitive for spectral data, especially where the spectral features are similar to those that can be identified in sound. We use Planetary Nebulae data, objects dominated by strong emission lines, to demonstrate this. We plot the spectra vs wavelength and spectra vs frequency, and use the Strauss :code:`Synthesiser` to create a 30 second, mono sonification. We set the ranges for the mapped parameters and render the sonification. A second example uses an "Object" type sonification with an evolving Spectrum to sonify an image. We represent the imageby evolving from left to right, with higher features in the y-axis having a higher pitch. + + Stars Appearing (:code:`StarsAppearing.ipynb`) ********************************************** -The *Stars Appearing* sonification demonstrates the generation of a sonification that was used directly in `a planetarium show for visually impaired children `_. This is intended to represent the appearance of stars in the night sky to an observer. Over time, the sky darkens and our eyes adjust, allowing us to see more and more stars. To represent this, the brightest stars appear first, with dimmer stars appearing later. Data on the colours of the stars is used to set the note used for each stars sound, with bluer stars higher notes and redder stars lower notes. We use the Strauss :code:`Sampler` to play a glockenspiel sound for each star as it appears. The actual positions of stars in the sky is used to spatialise the audio, with westerly stars positioned in the right speaker and easterly stars in the left. This spatialisation can be mapped to any audio setup, and a :code:`5.1` system was used fo the planetarium, but for the example we use :code:`stereo` +The *Stars Appearing* sonification demonstrates the generation of a sonification that was used directly in `a planetarium show for visually impaired children `_. This is intended to represent the appearance of stars in the night sky to an observer. Over time, the sky darkens and our eyes adjust, allowing us to see more and more stars. To represent this, the brightest stars appear first, with dimmer stars appearing later. Data on the colours of the stars is used to set the note used for each stars sound, with bluer stars higher notes and redder stars lower notes. We use the Strauss :code:`Sampler` to play a glockenspiel sound for each star as it appears. The actual positions of stars in the sky is used to spatialise the audio, with westerly stars positioned in the right speaker and easterly stars in the left. This spatialisation can be mapped to any audio setup, and a :code:`5.1` system was used for the planetarium, but for the example we use :code:`stereo`. A video of this sequence with the audio is available `here `_. From 35ae2105c8038cf5fe21f3334791b341b4e598fb Mon Sep 17 00:00:00 2001 From: Rose Shepherd Date: Thu, 31 Oct 2024 16:52:46 +0000 Subject: [PATCH 31/86] Updated examples.rst to reference .py files and Tour of the Solar System --- docs/examples.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/examples.rst b/docs/examples.rst index 2ffa08e..2cf7dc8 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -4,7 +4,7 @@ Examples ^^^^^^^^ -Here, we explain some of the example sonfications included in the :code:`examples/` directory of the Strauss repo. These are all in *Python Notebook* (:code:`.ipynb`) format. +Here, we explain some of the example sonfications included in the :code:`examples/` directory of the Strauss repo. These are all in both *Python Notebook* (:code:`.ipynb`) and *Python script* (:code:`.py`) format so you can choose which format you prefer to use. Audio Caption (:code:`AudioCaption.ipynb`) ****************************************** @@ -13,7 +13,7 @@ The *Audio Caption* example demonstrates how to add audio captions to a sonifica Day Sequence (:code:`DaySequence.ipynb`) **************************************** -The *Day Sequence* sonification generates the sunrise to sunset sonification used in the "Audible Universe" planetarium show. Samples are downloaded from a Google drive to a local directory, and are played using the Strauss :code:`Sampler`. This spatialisation can be mapped to any audio setup, and a :code:`5.1` system was used for the planetarium, but for the example we use :code:`stereo`. +The *Day Sequence* sonification generates the sunrise to sunset sonification used in the `"Audio Universe: Tour of the Solar System" `_, an immersive planetarium show designed with sonifications so it can be enjoyed and understood irrespective of level of vision. Samples are downloaded from a Google drive to a local directory, and are played using the Strauss :code:`Sampler`. This spatialisation can be mapped to any audio setup, and a :code:`5.1` system was used for the planetarium, but for the example we use :code:`stereo`. Earth System (:code:`EarthSystem.ipynb`) @@ -22,14 +22,14 @@ The *Earth System* sonification represents the ratio of ocean to land along line A video of this sequence is available starting at 2:17 in `this video `_. -Light Curve Soundfonts (:code:`LightCurveSoundfonts .ipynb`) +Light Curve Soundfonts (:code:`LightCurveSoundfonts.ipynb`) ************************************************************ -The *Light Curve Soundfonts* example demonstrates how to use imported soundfont files to use virtual musical instruments in a sonification. Soundfont files are widely available online. We download a flute and guitar sounds from `Soundfonts 4 U `_. @@ -40,12 +40,12 @@ The *Sonifying Data 1D* example demonstrates some generic techniques for sonifyi Spectral Data (:code:`SpectralData.ipynb`) ****************************************** -The *Spectral Data* sonification demonstrates use of the Strauss :code:`Spectraliser` to represent Data. A direct spectralisation approach where the sound is generated by treating the 1D data as a sound spectrum. This uses a direct inverse Fourier transform, which is relatively intuitive for spectral data, especially where the spectral features are similar to those that can be identified in sound. We use Planetary Nebulae data, objects dominated by strong emission lines, to demonstrate this. We plot the spectra vs wavelength and spectra vs frequency, and use the Strauss :code:`Synthesiser` to create a 30 second, mono sonification. We set the ranges for the mapped parameters and render the sonification. A second example uses an "Object" type sonification with an evolving Spectrum to sonify an image. We represent the imageby evolving from left to right, with higher features in the y-axis having a higher pitch. +The *Spectral Data* sonification demonstrates use of the Strauss :code:`Spectraliser` to represent data. We use a direct spectralisation approach where the sound is generated by treating the 1D data as a sound spectrum. This uses a direct inverse Fourier transform, which is relatively intuitive for spectral data, especially where the spectral features are similar to those that can be identified in sound. We use Planetary Nebulae data, objects dominated by strong emission lines, to demonstrate this. We plot the spectra vs wavelength and spectra vs frequency, and use the Strauss :code:`Synthesiser` to create a 30 second, mono sonification. We set the ranges for the mapped parameters and render the sonification. A second example uses an "Object" type sonification with an evolving Spectrum to sonify an image. We represent the image by evolving from left to right, with higher features in the y-axis having a higher pitch. Stars Appearing (:code:`StarsAppearing.ipynb`) ********************************************** -The *Stars Appearing* sonification demonstrates the generation of a sonification that was used directly in `a planetarium show for visually impaired children `_. This is intended to represent the appearance of stars in the night sky to an observer. Over time, the sky darkens and our eyes adjust, allowing us to see more and more stars. To represent this, the brightest stars appear first, with dimmer stars appearing later. Data on the colours of the stars is used to set the note used for each stars sound, with bluer stars higher notes and redder stars lower notes. We use the Strauss :code:`Sampler` to play a glockenspiel sound for each star as it appears. The actual positions of stars in the sky is used to spatialise the audio, with westerly stars positioned in the right speaker and easterly stars in the left. This spatialisation can be mapped to any audio setup, and a :code:`5.1` system was used for the planetarium, but for the example we use :code:`stereo`. +The *Stars Appearing* sonification demonstrates the generation of a sonification that was used directly in the "Audio Universe: Tour of the Solar System" planetarium show. This is intended to represent the appearance of stars in the night sky to an observer. Over time, the sky darkens and our eyes adjust, allowing us to see more and more stars. To represent this, the brightest stars appear first, with dimmer stars appearing later. Data on the colours of the stars is used to set the note used for each stars sound, with bluer stars having higher notes and redder stars having lower notes. We use the Strauss :code:`Sampler` to play a glockenspiel sound for each star as it appears. The actual positions of stars in the sky is used to spatialise the audio, with westerly stars positioned in the right speaker and easterly stars in the left. This spatialisation can be mapped to any audio setup, and a :code:`5.1` system was used for the planetarium, but for the example we use :code:`stereo`. A video of this sequence with the audio is available `here `_. From e9163fad1c11d0a2a8cfda6ee256a2d788105b14 Mon Sep 17 00:00:00 2001 From: Rose Shepherd Date: Thu, 31 Oct 2024 17:09:45 +0000 Subject: [PATCH 32/86] Added a missing link and fixed typo, need to fix ref to score. --- docs/motivation.rst | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/motivation.rst b/docs/motivation.rst index 56c9895..2219047 100644 --- a/docs/motivation.rst +++ b/docs/motivation.rst @@ -12,17 +12,16 @@ So, given the dominance of visualisation techniques, *why use sonification?* Her **2) Unique Applications**: There are applications for which sonification is uniquely suited. Sound is a time varying signal so can represent *time-series data* intuitively, or data with rapidly or subtly varying rates of change. The human ear can cover about 10 octaves in frequency, while the visible range of light only covers around 1 octave. Used well, sonification can provide a new perspective on your data, and potentially new insights into your data that are missed with standard, visual approaches. -**3) Enhancing Visuals**: for scientific images or video data without narration, typically nothing is being conveyed through sound. Combining sonification with your visualisation provides a new channel for conveying data, either enhancing what is already shown, or expressing new variables that are absent from the visual. For communicating to a broader audience, sonification can reveal the beauty and complexity in data, and is active area of interest `in academic music `_. +**3) Enhancing Visuals**: for scientific images or video data without narration, typically nothing is being conveyed through sound. Combining sonification with your visualisation provides a new channel for conveying data, either enhancing what is already shown, or expressing new variables that are absent from the visual. For communicating to a broader audience, sonification can reveal the beauty and complexity in data, and is an active area of interest `in academic music `_. Strauss approach **************** -Strauss is intended to be a flexible toolkit and engine for sonification, allowing detailed control over the sonification process if required. Casual users can run Strauss with preset parameters and setups, exemplified by the python notebook examples (see :ref:`examples`), while more technical users can experiment with every aspect of the sonification (see :ref:`elements`). +Strauss is intended to be a flexible toolkit and engine for sonification, allowing detailed control over the sonification process if required. Casual users can run Strauss with preset parameters and setups, demonstrate by the Python Notebook and script examples (see :ref:`examples`), while more technical users can experiment with every aspect of the sonification (see :ref:`elements`). -Strauss incorporates *musical* elements, with the idea that *music theory* provides a system for organising abstract sounds and conveying meaning through them. These systems have -e been developed over centuries, and we are often encultured to them. Incorporating these systems allows us to exploit our understanding of music to convey information and create better sounding sonifications. No knowledge of music theory is required to use Strauss, and presets can again be used fror all musical aspects (see :ref:`score`). +Strauss incorporates *musical* elements, with the idea that *music theory* provides a system for organising abstract sounds and conveying meaning through them. These systems have been developed over centuries, and we are often encultured to them. Incorporating these systems allows us to exploit our understanding of music to convey information and create better sounding sonifications. No knowledge of music theory is required to use Strauss, and presets can again be used fror all musical aspects (see :ref:`score`). -Strauss is currently limited to a *western* view of music theory, though with ongoing development and expertise, the aim is to incorporate more culturally diverse musical and tonal systems. +Strauss is currently limited to a *Western* view of music theory, though with ongoing development and expertise, the aim is to incorporate more culturally diverse musical and tonal systems. References ********** From 5a4367bf4fef87518f3c71ba7f310b847ee09a27 Mon Sep 17 00:00:00 2001 From: Rose Shepherd Date: Fri, 1 Nov 2024 10:18:04 +0000 Subject: [PATCH 33/86] Editing start.rst: Fixed a link and some typos and finished a sentence about the examples. --- docs/start.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/start.rst b/docs/start.rst index a448209..3caeb83 100644 --- a/docs/start.rst +++ b/docs/start.rst @@ -1,12 +1,12 @@ Getting Started ^^^^^^^^^^^^^^^ -This walkthrough will take you through a clean install of the code, including optional dependencies and trying your first sonification +This walkthrough will take you through a clean install of the code, including optional dependencies and trying your first sonification. Installation ************ -the Strauss code can be downloaded from **GitHub** at `the repository url `_ +The Strauss code can be downloaded from **GitHub** at `the repository url `_. Using :code:`git` make a copy of the STRAUSS repository via SSH, @@ -40,9 +40,9 @@ where the :code:`-e` option allows a local install, such that you can modify and Example jupyter notebooks ************************* -There are a number of example applications of Strauss in the :code:`example` subdirectory of the :code:`strauss` repo. These are in Python Notebook (:code:`.ipynb`) format for an interactive, step-by-step . +There are a number of example applications of Strauss in the :code:`example` subdirectory of the :code:`strauss` repo. These are in Python Notebook (:code:`.ipynb`) format for an interactive, step-by-step. They are also provided in Python script format (:code:`.py`). -In order to run the exampes, first ensure that :code:`jupyter` is installed on your system. These were developed in :code:`jupyter-lab`, which can also be installed using pip, as. +In order to run the Notebook examples, first ensure that :code:`jupyter` is installed on your system. These were developed in :code:`jupyter-lab`, which can also be installed using pip, as .. code-block:: bash @@ -53,10 +53,10 @@ Then, running :code:`jupyter-lab` in the :code:`strauss` should initiate the :co Running some examples ********************* -From the :code:`jupyter-lab` interface, a good starting point is the :code:`SonifyingData1D.ipynb` notebook. demonstrating various method of representing a single 1D dataset sonically, using a single :code:`Object`-type source representation. The code and instruction cells provide a step-by-step gude to setting up, rendering and saving a sonification with Strauss. +From the :code:`jupyter-lab` interface, a good starting point is the :code:`SonifyingData1D.ipynb` Notebook. This demonstrates various methods of representing a single 1D dataset sonically, using a single :code:`Object`-type source representation. The code and instruction cells provide a step-by-step gude to setting up, rendering and saving a sonification with Strauss. -For a multivariate :code:`Event`-type sonification, the :code:`StarsAppearing.ipynb` notebook provides a step-by-step example, and demonstrates realistic stereo imaging for panoramic data. The output from this example was used in the `*Audible Universe* 2021 planetarium show `_. +For a multivariate :code:`Event`-type sonification, the :code:`StarsAppearing.ipynb` notebook provides a step-by-step example, and demonstrates realistic stereo imaging for panoramic data. The output from this example was used in the `"Audio Universe: Tour of the Solar System" 2021 planetarium show `_. -For a multivariate, multi-source example using an :code:`Object`-type source representation, see ... +For a multivariate, multi-source example using an :code:`Object`-type source representation, see the :code:`PlanetaryOrbits.ipynb` Notebook, the output of which was also used in the "Audio Universe: Tour of the Solar System" planetarium show. -In addition to the above-mentioned examples, there are a number of other notebooks, each representing the diverse applications and uses of the Strauss code to sonify data in different ways. A more detailed overview of the example notebooks can be found in :ref:`examples`. +In addition to the above-mentioned examples, there are a number of other Notebooks, each representing the diverse applications and uses of the Strauss code to sonify data in different ways. A more detailed overview of the example Notebooks and scripts can be found in :ref:`examples`. From 3a44d1e305229686b2aab462b5d722a6938ffeed Mon Sep 17 00:00:00 2001 From: Rose Shepherd Date: Fri, 1 Nov 2024 13:48:14 +0000 Subject: [PATCH 34/86] Fixed soundfonts link --- docs/examples.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/examples.rst b/docs/examples.rst index 2cf7dc8..2ab7f6c 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -24,7 +24,7 @@ A video of this sequence is available starting at 2:17 in `this video `_. We load the sounds into the Strauss :code:`Sampler`, and select a preset. The soundfonts are used to sonify a light curve for the variable star 55 Cancri, creating the audio equivalent of a scatter plot. The note length and volume envelope can be adjusted to improve the articulation of individual data points. To demonstrate an alternative approach, we use an :code: `Object` source type, where we evolve a sound over time to represent the data. This is analogous to a line graph, representing a continuous data series. We use a held chord, changing the cutoff frequency of the low-pass filter to create a "brighter" timbre when the star is brighter and a "duller" sound when the star is darker. Planetary Orbits (:code:`PlanetaryOrbits.ipynb`) From adf7bd4a7c62a366621e30e9982a6f374cab6a7c Mon Sep 17 00:00:00 2001 From: Rose Shepherd Date: Fri, 1 Nov 2024 14:49:57 +0000 Subject: [PATCH 35/86] Added details to elements.rst about other classes. Text taken from Trayford and Harrison 2023. --- docs/elements.rst | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/docs/elements.rst b/docs/elements.rst index c9a407c..f3deadc 100644 --- a/docs/elements.rst +++ b/docs/elements.rst @@ -23,7 +23,7 @@ Source Class The :code:`Source` classes in Strauss are so named because they act as `sources of sound` in the sonification. Sources are used to represent the input data, by mapping this data to properties of sound (volume, position, frequency, etc). The choice of how to set up the sources depends on the data being sonified, and what the user wants to convey. For this, Strauss defines two generic classes that inherit the parent :code:`Source` class; :code:`Events` and :code:`Objects`, described below. -**Full documentation of the mappable parameters are coming soon!** See :obj:`./examples` for example mappings in jupyter notebooks. +**Full documentation of the mappable parameters is coming soon!** See :obj:`./examples` for example mappings in Jupyter Notebooks and Python scripts. `Events` '''''''' @@ -46,3 +46,46 @@ In this case, sound is produced continuously by the source, with the evolving pr Some examples of :code:`Objects` in scientific data could be; `A galaxy evolving, planets orbiting, a plant growing, a glacier flowing, a climate changing etc...` .. _score: + +Score Class +*********** + +With the audio :code:`Sources` defined, the :code:`Score` class allows us to place ‘musical’ constraints on the sound they produce to represent the underlying data. The duration of the output sonification is also specified via the Score with the timeline of the sonification scaled to fit this duration. + +.. _generator: + +Generator Class +*************** + +The :code:`Generator` class takes instruction from the two prior classes and generates audio for each individual source. This can be achieved using either the :code:`Sampler` or :code:`Synthesiser` child classes (along with the :code:`Spectraliser` special case), detailed below. + +`Sampler` +''''''''' + +This class generates audio by triggering **pre-recorded audio samples**. + +A directory of audio files is used to specify which sample to use for each note of the sampler. These samples are loaded into the sampler and are interpolated to allow arbitrary pitch shifting. Samples can also be looped in a number of ways to allow notes to sustain perpetually. + +`Synthesiser` +''''''''''''' + +This class instead **generates audio additively using mathematical functions** via an arbitrary number of oscillators. The strauss synthesiser supports a number of oscillator forms. + +`Spectraliser` +'''''''''''''' + +A special case of the :code:`Synthesiser`, this generator synthesises sound from an input spectrum, via an inverse Fast Fourier Transform (IFFT), with randomised phases. The user can specify the audible frequency range that the ‘spectralised’ audio is mapped over. + +.. _channels + +Channels Class +************** + +Once sound has been produced for each :code: `source`, the final step is to mix the audio down into some multi-channel audio format. The :code:`Channels` class essentially represents a bank of virtual microphones, with 3D antennae patterns, that each correspond to a channel in the output file. + +.. _sonification + +Sonification Class +****************** + +The top-level :code:`Sonification` class loads in all the above classes and produces the final sonification. Once :code:`Sources`, :code:`Score`, :code:`Generator` and :code:`Channels` classes are defined, the :code:`Sonification` class is invoked. The :code:`render()` method can then be run to produce the sonification. From aed286002c03f7d7405394649a91ce7cf4eafacf Mon Sep 17 00:00:00 2001 From: Rose Shepherd Date: Fri, 1 Nov 2024 16:29:52 +0000 Subject: [PATCH 36/86] Reworded one thing in sources.py for clarity. --- src/strauss/sources.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/strauss/sources.py b/src/strauss/sources.py index 4a2469b..d87db4d 100644 --- a/src/strauss/sources.py +++ b/src/strauss/sources.py @@ -149,7 +149,7 @@ def apply_mapping_functions(self, map_funcs={}, map_lims={}, param_lims={}): function (x' = x by default), descaling by the x' upper and lower limits and rescaling to the sonification parameter limits. These values are stored for non-evolving parameters, - while for evolving properties are converted to interpolation + while for evolving properties they are converted to interpolation functions. Args: From 418704f79bfad9fb4dd18f211d5bfbfccce093e6 Mon Sep 17 00:00:00 2001 From: Rose Shepherd Date: Fri, 1 Nov 2024 16:45:53 +0000 Subject: [PATCH 37/86] Fixed some typos in score.py documentation. --- src/strauss/score.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/strauss/score.py b/src/strauss/score.py index d74044a..99e0fc5 100644 --- a/src/strauss/score.py +++ b/src/strauss/score.py @@ -46,14 +46,14 @@ def __init__(self, chord_sequence, length, pitch_binning='adaptive'): sequence used for the sonification. If a string, parse using :obj:`parse_chord_sequence`. If a :obj:`list`, each entry is a :obj:`list` of strings or floats, representing the notes of a - chord. notes are represented as strings using scientific + chord. notes are represented as strings using scientific pitch notation, e.g. :obj:`[['C3','E3', 'G3'], ['C3', 'F3', 'A4']]`. If floats, take values as note frequency in Hz. NOTE: currently - only supported in compination with the :obj:`Synthesiser` + only supported in combination with the :obj:`Synthesiser` generator class. length: (:obj:`str` or :obj:`float`): the length of the sonification. If a string, parse minutes and seconds from - format 'Xm Y.Zs'. If a float read as seconds. + format 'Xm Y.Zs'. If a float, read as seconds. pitch_binning (optional, :obj:`str`): pitch binning mode - choose from 'adaptive', where sources are binned by the pitch mapping such that each interval is represented the same fraction of the @@ -99,7 +99,7 @@ def parse_chord_sequence(chord_sequence): Returns: note_list (:obj:`list(list)`): the chord sequence represented as a list of lists, where each sub-list is a chord comprised of - strings representing each note in scentific notation (e.g. 'A4') + strings representing each note in scientific pitch notation (e.g. 'A4') """ chord_list = chord_sequence.split("|") note_list = [] From f4ae6640ef89f123e4b819bc4345da6468439644 Mon Sep 17 00:00:00 2001 From: Rose Shepherd Date: Fri, 1 Nov 2024 16:49:01 +0000 Subject: [PATCH 38/86] Updated STRAUSS acronym in __init__.py. --- src/strauss/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/strauss/__init__.py b/src/strauss/__init__.py index 19fcdd0..cfa3b3e 100644 --- a/src/strauss/__init__.py +++ b/src/strauss/__init__.py @@ -1,4 +1,4 @@ -"""STRAUSS (Sonification Tools and Resources for Astronomer Using Sound Synthesis) +"""STRAUSS (Sonification Tools and Resources for Analysis Using Sound Synthesis) This module provides a toolkit for *sonification*, i.e. the representation of data using sound.""" From af9e6e42db209083d32e52409ad527a3a98e05b1 Mon Sep 17 00:00:00 2001 From: Rose Shepherd Date: Fri, 1 Nov 2024 17:04:57 +0000 Subject: [PATCH 39/86] Edited channels.py, Fixed some typos and changed 5p1 to 5.1 for supported setups. --- src/strauss/channels.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/strauss/channels.py b/src/strauss/channels.py index a68161c..5ef50cf 100644 --- a/src/strauss/channels.py +++ b/src/strauss/channels.py @@ -26,7 +26,7 @@ class mic: mic_type (:obj:`str`): Type of microphone, choose from :obj:`"directional"` (collects using a cardioid antenna pattern), :obj:`"omni"` (collects sound from all directions equally) and - :obj:`"mute"` (collects no sound useful for e.g. muting + :obj:`"mute"` (collects no sound, useful for e.g. muting auxillary channels) label (:obj:`str`): A label for the mic channel (:obj:`int`) The index of the channel, corresponding to @@ -60,11 +60,11 @@ class audio_channels: Args: setup (:obj:`str`): Type of audio setup. Supported options are - :obj:`"mono"`, :obj:`"stereo"`, :obj:`"5p1"` and - :obj:`"7p1"`, or :obj:`"custom"`. + :obj:`"mono"`, :obj:`"stereo"`, :obj:`"5.1"` and + :obj:`"7.1"`, or :obj:`"custom"`. custom_setup (:obj:`dict`): Dictionary defining a customised audio setup, containing keys for :obj:`"azimuths"`, :obj:`"types"` - and :obj:`"labels"`, containing lists parametrising the first + and :obj:`"labels"`, containing lists parameterising the first three arguments of the :class:`mic` object, respectively in the order of their channel index. Also optionally an forder list to unscramble any channel order scrambling done by ffmpeg From a9bbfcf556a1f374174fa1d458a890dafb88ac18 Mon Sep 17 00:00:00 2001 From: Rose Shepherd Date: Sun, 3 Nov 2024 10:52:13 +0000 Subject: [PATCH 40/86] Fixed typo in tts_caption.py documentation. --- src/strauss/tts_caption.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/strauss/tts_caption.py b/src/strauss/tts_caption.py index 83b7806..cc47010 100644 --- a/src/strauss/tts_caption.py +++ b/src/strauss/tts_caption.py @@ -32,9 +32,9 @@ def render_caption(caption, samprate, model, caption_path): Args: caption (:obj:`str`): script to be spoken by the TTS voice samprate (:obj:`int`): samples per second - model (:obj:`str`): valid name of TTS voice from the underying TTS + model (:obj:`str`): valid name of TTS voice from the underlying TTS module - model (:obj:`str`): valid name of TTS voice from the underying TTS + model (:obj:`str`): valid name of TTS voice from the underlying TTS module caption_path (:obj:`str`): filepath for spoken caption output ''' From fa1c1b8b6bd701b25bfdb999dc811eea0e193b4e Mon Sep 17 00:00:00 2001 From: Rose Shepherd Date: Sun, 3 Nov 2024 13:15:02 +0000 Subject: [PATCH 41/86] Fixed some typos in generator.py documentation. --- src/strauss/generator.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/strauss/generator.py b/src/strauss/generator.py index 85aecd7..47be85f 100644 --- a/src/strauss/generator.py +++ b/src/strauss/generator.py @@ -1,7 +1,7 @@ """ The :obj:`generator` submodule: creating sounds for the sonification. This submodule handles the actual generation of sound for the -sonfication, after parametrisation by the :obj:`Sources` and musical +sonification, after parameterisation by the :obj:`Sources` and musical choices dictated by the :obj:`Score`. Todo: @@ -107,7 +107,7 @@ def __init__(self, params={}, samprate=48000): Args: params (`optional`, :obj:`dict`): any generator parameters that differ from the generator :obj:`preset`, where keys and - values are parameters names and values respectively. + values are parameter's names and values respectively. samprate (`optional`, :obj:`int`): the sample rate of the generated audio in samples per second (Hz) """ @@ -130,7 +130,7 @@ def load_preset(self, preset='default'): :obj:`preset` Args: - preset (:obj:`str`): name of the preset. built-in presets + preset (:obj:`str`): name of the preset. Built-in presets can be named directly and looks to import the preset from the :obj:`/presets//` directory as :obj:`.yml`, where :obj:`` is either @@ -350,7 +350,7 @@ def lfo(self, samp, sampfrac, params, ltype='pitch'): cycles or :obj:`'random'` to indicate randomised. Note: - To modulate the frequency of an ocillator, use the + To modulate the frequency of an oscillator, use the :obj:`freq_shift` parameter, rather than :obj:`freq` Args: @@ -436,7 +436,7 @@ def __init__(self, params=None, samprate=48000): def setup_oscillators(self): """Setup and consolidate oscs into a two-variable function. - Reads the parametrisation of each oscillator from the preset, + Reads the parameterisation of each oscillator from the preset, specifying their waveform (:obj:`wave`), relative amplitude (:obj:`level`), detuning in cents (:obj:`det`) and :obj:`phase`, either a number in units of cycles, or a string @@ -492,7 +492,7 @@ def combine_oscs(self, s, f): Args: s (:obj:`array`-like): Sample index f (:obj:`float` or :obj:`str`): If numerical, frequency in - cycles per second, if string, note name in scientific + cycles per second, if string, note name in scientific pitch notation (e.g. :obj:`'A4'`) Returns: tot (:obj:`array`-like): values for each sample @@ -596,8 +596,8 @@ class Sampler(Generator): """Sampler generator class This generator class generates sound using pre-loaded audio - samples, representing d`ifferent notes. Presets define parameters - controlling how these defines + samples, representing different notes. Presets define parameters + controlling these defines attribute :obj:`self.gtype = 'sampler'`. Attributes: @@ -817,7 +817,7 @@ def load_samples(self): Read audio samples in from a specified directory or via a dictionary of filepaths, generate interpolation functions for - each, and assign them to a named note in scientific notation + each, and assign them to a named note in scientific pitch notation (e.g. :obj:`'A4'`). """ self.samples = {} From 7b09ebc1faa0adcadf56dbbc1a2d85bcd8d02440 Mon Sep 17 00:00:00 2001 From: James Trayford Date: Tue, 10 Sep 2024 13:51:56 +0100 Subject: [PATCH 42/86] update doc conf --- docs/conf.py | 3 ++- docs/index.rst | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index a7f44f2..21c3d75 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -13,6 +13,7 @@ import os import sys sys.path.insert(0, os.path.abspath('..')) +sys.path.insert(0, os.path.abspath('../../src/strauss/presets')) # -- Project information ----------------------------------------------------- @@ -35,7 +36,7 @@ 'sphinx.ext.ifconfig', 'sphinx.ext.viewcode', 'sphinx.ext.inheritance_diagram', 'sphinx.ext.autosummary','sphinx.ext.coverage', - 'sphinx.ext.napoleon'] + 'sphinx.ext.napoleon', "myst_parser"] #'recommonmark', # 'sphinx_markdown_tables' diff --git a/docs/index.rst b/docs/index.rst index d569c87..19f23c9 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -20,6 +20,7 @@ The code aims to make rich and evocative sonification straightforward, with a nu start elements detailed + presets examples todo From 5a4d7e098339429a33f61dcb12a50def7ea0b1d2 Mon Sep 17 00:00:00 2001 From: James Trayford Date: Tue, 20 Aug 2024 14:57:12 +0100 Subject: [PATCH 43/86] add documentation to src/strauss/utilities.py --- src/strauss/utilities.py | 86 +++++++++++++++++++++++++--------------- 1 file changed, 53 insertions(+), 33 deletions(-) diff --git a/src/strauss/utilities.py b/src/strauss/utilities.py index 6da9573..9fdd733 100644 --- a/src/strauss/utilities.py +++ b/src/strauss/utilities.py @@ -21,18 +21,29 @@ class NoSoundDevice: Drop-in replacement for sounddevice module if not working, so we can still use other functionality. - Attributes: - err (:obj:`Exception`): Error message from trying to import - sounddevice + Attributes + ---------- + err : Exception + Error message from trying to import sounddevice + + Methods + ------- + play(*args, **kwargs) + Dummy function replacing `sounddevice.play` when ` + `sounddevice` is unavailable to raise self.err """ def __init__(self, err): self.err = err def play(self, *args, **kwargs): - """Dummy function replacing `sounddevice.play` when unavailable. + """ + Dummy function replacing `sounddevice.play` when ` `sounddevice` is unavailable to raise self.err - Args: - *args: arguments (ignored) - **kwargs: keyword-only arguments (ignored) + Parameters + ---------- + *args + arguments (ignored) + **kwargs + keyword-only arguments (ignored) """ raise self.err @@ -88,11 +99,13 @@ def nested_dict_reassign(fromdict, todict): Recurse through dictionaries and sub-dictionaries in `fromdict` and reassign equivalent values in `todict` - Args - fromdict (:obj:`dict`): Dictionary containing values - to assign - todict (:obj:`dict`): Dictionary containing values - to be reassigned + Parameters + ---------- + fromdict : dict + Dictionary containing values to assign + todict : dict + Dictionary containing values to be reassigned + """ for k, v in fromdict.items(): if isinstance(v, dict): @@ -108,11 +121,12 @@ def nested_dict_fill(fromdict, todict): `fromdict` and assign to any entries missing from `todict` - Args: - fromdict (:obj:`dict`): Dictionary containing values - to assign - todict (:obj:`dict`): Dictionary containing values to - be reassigned + Parameters + ---------- + fromdict : dict + Dictionary containing values to assign + todict : dict + Dictionary containing values to be reassigned """ for k, v in fromdict.items(): if k not in todict: @@ -128,13 +142,14 @@ def nested_dict_idx_reassign(fromdict, todict, idx): iterables in `fromdict` and index value idx to assign or replact value in todict - Args: - fromdict (:obj:`dict`): Dictionary containing values - to assign - todict (:obj:`dict`): Dictionary containing values - to be reassigned - idx (:obj:`dict`): Index value for retrieving value - from iterables + Parameters + ---------- + fromdict : dict + Dictionary containing values to assign + todict : dict + Dictionary containing values to be reassigned + idx : int + index value for retrieving value from iterables """ for k, v in fromdict.items(): if isinstance(v, dict): @@ -149,12 +164,16 @@ def reassign_nested_item_from_keypath(dictionary, keypath, value): Reassign item in a nested dictionary to value using keypath syntax, to traverse multiple dictionaries - Args: - dictionary (:obj:`dict`): dict object to reassign values within - keypath (:obj:`str`): Using filepath syntax on given OS to - traverse dictionary, i.e 'a/b/c' ('a\\b\\c') corresponds to - dict['a']['b']['c'] on Unix (Windows). - value: value to reassign dictionary value with + Parameters + ---------- + dictionary : dict + dict object to reassign values within + keypath : str + Using filepath syntax on given OS to traverse dictionary, i.e + 'a/b/c' ('a\\b\\c') corresponds to dict['a']['b']['c'] on Unix + (Windows) + value : + value to reassign dictionary value with """ p = Path(keypath) keylist = list(p.parts) @@ -166,10 +185,11 @@ def linear_to_nested_dict_reassign(fromdict, todict): Iterate through a linear dictionary to reassign nested values using keypaths (d1['a/b/c'] -> d2['a']['b']['c'], d1['a']->d2['a']) - Args: - fromdict (:obj:`dict`): + Parameters + ---------- + fromdict : dict Dictionary containing values to assign - todict (:obj:`dict`): + todict : dict Dictionary containing values to be reassigned """ for k, v in fromdict.items(): From 386cb8c0a9306c0d3ecdd26febd6c094c35713d2 Mon Sep 17 00:00:00 2001 From: James Trayford Date: Wed, 21 Aug 2024 13:47:03 +0100 Subject: [PATCH 44/86] update generator docs --- docs/detailed.rst | 1 + src/strauss/generator.py | 31 ++++++++++++++++++++++++++++--- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/docs/detailed.rst b/docs/detailed.rst index 42e08f0..0158d7c 100644 --- a/docs/detailed.rst +++ b/docs/detailed.rst @@ -64,6 +64,7 @@ Filters .. automodule:: strauss.filters :members: :show-inheritance: + Text-to-Speech ============== .. automodule:: strauss.tts_caption diff --git a/src/strauss/generator.py b/src/strauss/generator.py index 47be85f..bdd307f 100644 --- a/src/strauss/generator.py +++ b/src/strauss/generator.py @@ -101,13 +101,34 @@ class Generator: preset (:obj:`dict`): Dictionary of parameters defining the generator. + Methods: + load_preset(self, preset='default'): + Load parameters from a preset YAML file. + modify_preset(self, parameters, cleargroup=[]) + Modify parameters within current preset + preset_details(self, term="*"): + Print the names and descriptions of presets + envelope(self, samp, params, etype='volume') + Envelope function for modulating a single note + env_segment_curve(self, t, t1, y0, k) + Formula for segments of the envelope function + sine(self, s,f,p) + Sine-wave oscillator + saw(self,s,f,p) + Square-wave oscillator + tri(self,s,f,p) + Triangle-wave oscillator + noise(self,s,f,p) + White noise oscillator + lfo(self, samp, sampfrac, params, ltype='pitch') + Low-Frequency oscillator (LFO) """ def __init__(self, params={}, samprate=48000): """ Args: params (`optional`, :obj:`dict`): any generator parameters that differ from the generator :obj:`preset`, where keys and - values are parameter's names and values respectively. + values are parameter names and values respectively. samprate (`optional`, :obj:`int`): the sample rate of the generated audio in samples per second (Hz) """ @@ -407,7 +428,7 @@ class Synthesizer(Generator): attribute :obj:`self.gtype = 'synth'`. Attributes: - gtype (:obj:`str`): Generator type + gtype (:obj:`str`): Generator type Todo: * Add other synthesiser types, aside from additive (e.g. FM, @@ -1000,7 +1021,11 @@ def play(self, mapping): class Spectralizer(Generator): """Spectralizer generator class + + """ + def __init__(self, params=None, samprate=48000): + """ This generator class synthesises sound from a spectrum input using an *inverse Fast Fourier Transform* (iFFT) algorithm. Defining a minimum and maximum frequency in Hz, input spectrum @@ -1014,7 +1039,7 @@ class Spectralizer(Generator): Todo: * Add other synthesiser types, aside from additive (e.g. FM, vector, wavetable)? - """ + """ def __init__(self, params=None, samprate=48000): """ Args: From 2493bc0bda0159165ca6fc0b08d28a8fa0c9a33f Mon Sep 17 00:00:00 2001 From: James Trayford Date: Mon, 16 Dec 2024 16:38:12 +0000 Subject: [PATCH 45/86] clean up merge --- src/strauss/generator.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/strauss/generator.py b/src/strauss/generator.py index bdd307f..6ac8bcc 100644 --- a/src/strauss/generator.py +++ b/src/strauss/generator.py @@ -428,7 +428,7 @@ class Synthesizer(Generator): attribute :obj:`self.gtype = 'synth'`. Attributes: - gtype (:obj:`str`): Generator type + gtype (:obj:`str`): Generator type Todo: * Add other synthesiser types, aside from additive (e.g. FM, @@ -1021,11 +1021,7 @@ def play(self, mapping): class Spectralizer(Generator): """Spectralizer generator class - - """ - def __init__(self, params=None, samprate=48000): - """ This generator class synthesises sound from a spectrum input using an *inverse Fast Fourier Transform* (iFFT) algorithm. Defining a minimum and maximum frequency in Hz, input spectrum @@ -1040,6 +1036,7 @@ def __init__(self, params=None, samprate=48000): * Add other synthesiser types, aside from additive (e.g. FM, vector, wavetable)? """ + def __init__(self, params=None, samprate=48000): """ Args: From ebc934a10c933064e273ed71997d1a9393fb692b Mon Sep 17 00:00:00 2001 From: James Trayford Date: Wed, 21 Aug 2024 16:42:21 +0100 Subject: [PATCH 46/86] add generator documentation --- docs/index.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/index.rst b/docs/index.rst index 19f23c9..d569c87 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -20,7 +20,6 @@ The code aims to make rich and evocative sonification straightforward, with a nu start elements detailed - presets examples todo From f66ae6087f47c2e924d2023c37ba0ae1b7318903 Mon Sep 17 00:00:00 2001 From: James Trayford Date: Fri, 23 Aug 2024 11:41:55 +0100 Subject: [PATCH 47/86] update more documentation --- docs/conf.py | 1 - docs/detailed.rst | 1 - src/strauss/generator.py | 21 ---------- src/strauss/score.py | 3 +- src/strauss/sources.py | 2 +- src/strauss/utilities.py | 86 +++++++++++++++------------------------- 6 files changed, 36 insertions(+), 78 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 21c3d75..ecabe57 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -40,7 +40,6 @@ #'recommonmark', # 'sphinx_markdown_tables' - print(extensions) # Add any paths that contain templates here, relative to this directory. diff --git a/docs/detailed.rst b/docs/detailed.rst index 0158d7c..42e08f0 100644 --- a/docs/detailed.rst +++ b/docs/detailed.rst @@ -64,7 +64,6 @@ Filters .. automodule:: strauss.filters :members: :show-inheritance: - Text-to-Speech ============== .. automodule:: strauss.tts_caption diff --git a/src/strauss/generator.py b/src/strauss/generator.py index 6ac8bcc..47e20db 100644 --- a/src/strauss/generator.py +++ b/src/strauss/generator.py @@ -101,27 +101,6 @@ class Generator: preset (:obj:`dict`): Dictionary of parameters defining the generator. - Methods: - load_preset(self, preset='default'): - Load parameters from a preset YAML file. - modify_preset(self, parameters, cleargroup=[]) - Modify parameters within current preset - preset_details(self, term="*"): - Print the names and descriptions of presets - envelope(self, samp, params, etype='volume') - Envelope function for modulating a single note - env_segment_curve(self, t, t1, y0, k) - Formula for segments of the envelope function - sine(self, s,f,p) - Sine-wave oscillator - saw(self,s,f,p) - Square-wave oscillator - tri(self,s,f,p) - Triangle-wave oscillator - noise(self,s,f,p) - White noise oscillator - lfo(self, samp, sampfrac, params, ltype='pitch') - Low-Frequency oscillator (LFO) """ def __init__(self, params={}, samprate=48000): """ diff --git a/src/strauss/score.py b/src/strauss/score.py index 99e0fc5..27783f9 100644 --- a/src/strauss/score.py +++ b/src/strauss/score.py @@ -99,7 +99,8 @@ def parse_chord_sequence(chord_sequence): Returns: note_list (:obj:`list(list)`): the chord sequence represented as a list of lists, where each sub-list is a chord comprised of - strings representing each note in scientific pitch notation (e.g. 'A4') + strings representing each note in scientific pitch notation + (e.g. 'A4') """ chord_list = chord_sequence.split("|") note_list = [] diff --git a/src/strauss/sources.py b/src/strauss/sources.py index d87db4d..01e31d1 100644 --- a/src/strauss/sources.py +++ b/src/strauss/sources.py @@ -15,7 +15,7 @@ param_lim_dict (:obj:`dict`): Dictionary combining `mappable` (keys) and `param_limits` (items). -Todo: +To do: * Store mappable, evolvable and parameter ranges in YAML files (cleaner). * Specialised Event and Object child classes (eg. spectralisation). """ diff --git a/src/strauss/utilities.py b/src/strauss/utilities.py index 9fdd733..6da9573 100644 --- a/src/strauss/utilities.py +++ b/src/strauss/utilities.py @@ -21,29 +21,18 @@ class NoSoundDevice: Drop-in replacement for sounddevice module if not working, so we can still use other functionality. - Attributes - ---------- - err : Exception - Error message from trying to import sounddevice - - Methods - ------- - play(*args, **kwargs) - Dummy function replacing `sounddevice.play` when ` - `sounddevice` is unavailable to raise self.err + Attributes: + err (:obj:`Exception`): Error message from trying to import + sounddevice """ def __init__(self, err): self.err = err def play(self, *args, **kwargs): - """ - Dummy function replacing `sounddevice.play` when ` `sounddevice` is unavailable to raise self.err + """Dummy function replacing `sounddevice.play` when unavailable. - Parameters - ---------- - *args - arguments (ignored) - **kwargs - keyword-only arguments (ignored) + Args: + *args: arguments (ignored) + **kwargs: keyword-only arguments (ignored) """ raise self.err @@ -99,13 +88,11 @@ def nested_dict_reassign(fromdict, todict): Recurse through dictionaries and sub-dictionaries in `fromdict` and reassign equivalent values in `todict` - Parameters - ---------- - fromdict : dict - Dictionary containing values to assign - todict : dict - Dictionary containing values to be reassigned - + Args + fromdict (:obj:`dict`): Dictionary containing values + to assign + todict (:obj:`dict`): Dictionary containing values + to be reassigned """ for k, v in fromdict.items(): if isinstance(v, dict): @@ -121,12 +108,11 @@ def nested_dict_fill(fromdict, todict): `fromdict` and assign to any entries missing from `todict` - Parameters - ---------- - fromdict : dict - Dictionary containing values to assign - todict : dict - Dictionary containing values to be reassigned + Args: + fromdict (:obj:`dict`): Dictionary containing values + to assign + todict (:obj:`dict`): Dictionary containing values to + be reassigned """ for k, v in fromdict.items(): if k not in todict: @@ -142,14 +128,13 @@ def nested_dict_idx_reassign(fromdict, todict, idx): iterables in `fromdict` and index value idx to assign or replact value in todict - Parameters - ---------- - fromdict : dict - Dictionary containing values to assign - todict : dict - Dictionary containing values to be reassigned - idx : int - index value for retrieving value from iterables + Args: + fromdict (:obj:`dict`): Dictionary containing values + to assign + todict (:obj:`dict`): Dictionary containing values + to be reassigned + idx (:obj:`dict`): Index value for retrieving value + from iterables """ for k, v in fromdict.items(): if isinstance(v, dict): @@ -164,16 +149,12 @@ def reassign_nested_item_from_keypath(dictionary, keypath, value): Reassign item in a nested dictionary to value using keypath syntax, to traverse multiple dictionaries - Parameters - ---------- - dictionary : dict - dict object to reassign values within - keypath : str - Using filepath syntax on given OS to traverse dictionary, i.e - 'a/b/c' ('a\\b\\c') corresponds to dict['a']['b']['c'] on Unix - (Windows) - value : - value to reassign dictionary value with + Args: + dictionary (:obj:`dict`): dict object to reassign values within + keypath (:obj:`str`): Using filepath syntax on given OS to + traverse dictionary, i.e 'a/b/c' ('a\\b\\c') corresponds to + dict['a']['b']['c'] on Unix (Windows). + value: value to reassign dictionary value with """ p = Path(keypath) keylist = list(p.parts) @@ -185,11 +166,10 @@ def linear_to_nested_dict_reassign(fromdict, todict): Iterate through a linear dictionary to reassign nested values using keypaths (d1['a/b/c'] -> d2['a']['b']['c'], d1['a']->d2['a']) - Parameters - ---------- - fromdict : dict + Args: + fromdict (:obj:`dict`): Dictionary containing values to assign - todict : dict + todict (:obj:`dict`): Dictionary containing values to be reassigned """ for k, v in fromdict.items(): From 9af822dcca9a30ee86f372a6d971c78a2d7b4aca Mon Sep 17 00:00:00 2001 From: James Trayford Date: Mon, 11 Nov 2024 12:14:10 +0000 Subject: [PATCH 48/86] initial tool to autodoc the yaml files (building MD tables from them) --- src/strauss/presets/sampler/default.yml | 7 +-- yamls_to_tables.py | 64 +++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 5 deletions(-) create mode 100644 yamls_to_tables.py diff --git a/src/strauss/presets/sampler/default.yml b/src/strauss/presets/sampler/default.yml index 2e90e6a..4c6bed9 100644 --- a/src/strauss/presets/sampler/default.yml +++ b/src/strauss/presets/sampler/default.yml @@ -86,6 +86,7 @@ pitch_lo: 0 pitch_shift: 0. _meta: + _doc: A top-level documentation string for the generator's defaults name: preset name description: full description note_length: >- @@ -97,9 +98,6 @@ _meta: If looping, start and end point of loop in seconds. loop_end: >- If loop_end is longer than the sample, clip to end of the sample. - volume_envelope: >- - define the note volume envelope applied to the samples - A,D,S & R correspond to 'attack', 'decay', 'sustain' and 'release' volume_envelope: A: Attack D: Decay @@ -132,9 +130,8 @@ _meta: Dc: "" Rc: "" level: "" - volume_lfo: >- - or apply 'tremolo'? volume_lfo: + _doc: define the note volume envelope applied to the samples A,D,S & R correspond to 'attack', 'decay', 'sustain' and 'release' use: switch feature on or off wave: type of waveform amount: amount diff --git a/yamls_to_tables.py b/yamls_to_tables.py new file mode 100644 index 0000000..7261357 --- /dev/null +++ b/yamls_to_tables.py @@ -0,0 +1,64 @@ +import yaml +from glob import glob +from pathlib import Path + +generators = {'spec' : "`Spectraliser` Generator", + 'synth' : "`Synthesiser` Generator", + 'sampler' : "`Sampler` Generator"} + +# p = Path(__file__) +p = Path("src", "strauss", "presets", "*", "default.yml") + +def read_yaml(filename): + with filename.open(mode='r') as fdata: + # try: + yamldict = yaml.safe_load(fdata) + # except yaml.YAMLError as err: + # print(err) + return yamldict + +def yaml_traverse(metadict, valdict, rdict, headlev=1): + if hasattr(metadict, 'keys'): + topstr = '' + tabstr = '' + secstr = '' + tabstr += "\n| Parameter | Description | Default Value | Default Range | Unit |\n" + tabstr += "| ----------- | ----------- | ----------- | ----------- | ----------- |\n" + + for k in metadict.keys(): + # print (f">>>>>> {k}") + if hasattr(metadict[k], 'keys'): + secstr += '\n'+''.join(['#']*headlev) + f" `{k}` parameter group\n" + if not k in rdict: + rdict[k] = {} + secstr += yaml_traverse(metadict[k], valdict[k], rdict[k], headlev+1) + continue + if k == '_doc': + topstr += f"\n{metadict[k]}\n" + # print('\n',metadict[k]) + continue + if k not in rdict: + # unspecified => '-' + rdict[k] = '-' + else: + # lets avoid these line-breaking with special characters + rdict[k] = str(rdict[k]).replace(" ","").replace(",","\u2011").replace("-","\u2011") + if k+"_unit" not in rdict: + # unspecified => '-' + rdict[k+"_unit"] = '-' + # print(f"|`{k}` | _{metadict[k]}_ | `{str(valdict[k]).strip()}` | `{rdict[k]}` | {rdict[k+'_unit']}") + tabstr += f"|`{k}` | _{str(metadict[k]).strip()}_ | `valdict[k]` | `{rdict[k]}` | {rdict[k+'_unit']}\n" + if k not in rdict: + rdict[k] = {} + return f'{topstr}{tabstr}{secstr}' + + else: + return + +for f in glob(str(p)): + p = Path(f) + ydat = read_yaml(p) + rdat = read_yaml(p.parents[0] / "ranges" / "default.yml") + print(f"\n# {generators[p.parents[0].name]}\n") + ystr = yaml_traverse(ydat['_meta'], ydat, rdat, 2) + print(ystr) From 0c653990ba23398be85d51cfdc7c4a9adfacbc53 Mon Sep 17 00:00:00 2001 From: James Trayford Date: Mon, 11 Nov 2024 12:20:48 +0000 Subject: [PATCH 49/86] multi-line string example --- src/strauss/presets/sampler/default.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/strauss/presets/sampler/default.yml b/src/strauss/presets/sampler/default.yml index 4c6bed9..77994c4 100644 --- a/src/strauss/presets/sampler/default.yml +++ b/src/strauss/presets/sampler/default.yml @@ -131,7 +131,9 @@ _meta: Rc: "" level: "" volume_lfo: - _doc: define the note volume envelope applied to the samples A,D,S & R correspond to 'attack', 'decay', 'sustain' and 'release' + _doc: >- + define the note volume envelope applied to the samples A,D,S & R + correspond to 'attack', 'decay', 'sustain' and 'release' use: switch feature on or off wave: type of waveform amount: amount From 475024d8f962c7e834ab6e3ec9c12e3c10bd4331 Mon Sep 17 00:00:00 2001 From: James Trayford Date: Wed, 13 Nov 2024 11:02:56 +0000 Subject: [PATCH 50/86] Fix and example change --- src/strauss/presets/sampler/default.yml | 4 ++-- yamls_to_tables.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/strauss/presets/sampler/default.yml b/src/strauss/presets/sampler/default.yml index 77994c4..9ba1ed6 100644 --- a/src/strauss/presets/sampler/default.yml +++ b/src/strauss/presets/sampler/default.yml @@ -113,9 +113,9 @@ _meta: filter type cutoff: >- filter cutoff - pitch_lfo: >- - or apply 'vibrato'? pitch_lfo: + _doc: >- + or apply 'vibrato'? use: switch feature on or off wave: type of waveform amount: amount diff --git a/yamls_to_tables.py b/yamls_to_tables.py index 7261357..af48ccb 100644 --- a/yamls_to_tables.py +++ b/yamls_to_tables.py @@ -1,3 +1,4 @@ + import yaml from glob import glob from pathlib import Path @@ -47,7 +48,7 @@ def yaml_traverse(metadict, valdict, rdict, headlev=1): # unspecified => '-' rdict[k+"_unit"] = '-' # print(f"|`{k}` | _{metadict[k]}_ | `{str(valdict[k]).strip()}` | `{rdict[k]}` | {rdict[k+'_unit']}") - tabstr += f"|`{k}` | _{str(metadict[k]).strip()}_ | `valdict[k]` | `{rdict[k]}` | {rdict[k+'_unit']}\n" + tabstr += f"|`{k}` | _{str(metadict[k]).strip()}_ | {valdict[k]} | `{rdict[k]}` | {rdict[k+'_unit']}\n" if k not in rdict: rdict[k] = {} return f'{topstr}{tabstr}{secstr}' From a4cab6e9c43482c17914e25c72fe4ef8fa2179f3 Mon Sep 17 00:00:00 2001 From: James Trayford Date: Mon, 2 Dec 2024 15:11:32 +0000 Subject: [PATCH 51/86] some formatting script edits and adding metadata to the Spectraliser default.yml --- src/strauss/presets/spec/default.yml | 120 +++++++++++++++++---------- yamls_to_tables.py | 5 +- 2 files changed, 80 insertions(+), 45 deletions(-) diff --git a/src/strauss/presets/spec/default.yml b/src/strauss/presets/spec/default.yml index 708e656..9da1f43 100644 --- a/src/strauss/presets/spec/default.yml +++ b/src/strauss/presets/spec/default.yml @@ -101,10 +101,16 @@ pitch_lo: 0 pitch_shift: 0. _meta: - name: preset name - description: full description + _doc: >- + The `Spectraliser` generator type can be used to represent a frequency spectrum, by mapping + any frequency range to an audible range, and generating a representative sound signal (using + an [IFFT approach](https://docs.scipy.org/doc/scipy/reference/generated/scipy.fft.ifft.html)). + In this approach, narrow spikes become tones at their frequency position, a sloped continuum + becomes coloured noise, etc. + name: name for a particular preset. + description: full description of what a preset does. note_length: >- - Numerical note length in s + Numerical note length volume_envelope: >- Define the note volume envelope applied to the samples. A,D,S & R correspond to 'attack', 'decay', 'sustain' and 'release'. @@ -113,72 +119,100 @@ _meta: D: Decay S: Sustain R: Release - Ac: "" - Dc: "" - Rc: "" - level: "" + Ac: >- + "Curvature" of the attack portion of a note. Values from -1 to 1, positive indicates increases quickly then slow, + negative slowly then quick. a value of 0 is a linear attack, increasing in volume at a constant rate. + Dc: >- + "Curvature" of the Decay portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, + negative slowly then quick. a value of 0 is a linear decay, decreasing in volume at a constant rate. + Rc: >- + "Curvature" of the release portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, + negative slowly then quick. a value of 0 is a linear release, decreasing in volume at a constant rate. + + level: Total amplitude level of the envelope from 0 to 1, contolling maximum volume of the note filter: >- - Do we apply a filter? + Do we apply a frequency filter to the audio signal? This can be used to change the balance of frequencies and manipulate + the 'timbre' of a note filter_type: >- - filter type + Choose from available filter types cutoff: >- - filter cutoff - pitch_lfo: >- - or apply 'vibrato'? + The cut-off frequency (or 'knee') of the filter, at which frequencies are attenuated beyond. + specified between 0 and 1 as a fraction of the audible range of notes we can hear (E0 to D#10). pitch_lfo: - use: switch feature on or off - wave: type of waveform - amount: amount - freq: frequency - freq_shift: frequency shift - phase: random + _doc: >- + Controls for the 'Low Frequency Oscillator' (LFO) used to modulate pitch of notes at rhythmic + frequencies. In music this is often referred to a 'vibrato' + use: Switch pitch LFO effects on or off + wave: >- + Type of waveform used for the oscillator. As with `Synthesizer` oscillators can be sawtooth + (`'saw'`), square (`'square'`), sinusoid (`'sine'`), triangle (`'tri'`) or noise (`'noise'`). + amount: the amplitude of the maximal pitch oscillation from the underlying pitch + freq: Base frequency of the LFO oscillations. + freq_shift: shift relative to the bae LFO frequency + phase: The phase of the LFO oscillations, defined in terms of fraction of a whole cycle A: Attack D: Decay S: Sustain R: Release - Ac: "" - Dc: "" - Rc: "" - level: "" - volume_lfo: >- - or apply 'tremolo'? + Ac: >- + "Curvature" of the attack portion of a note. Values from -1 to 1, positive indicates increases quickly then slow, + negative slowly then quick. a value of 0 is a linear attack, increasing in volume at a constant rate. + Dc: >- + "Curvature" of the Decay portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, + negative slowly then quick. a value of 0 is a linear decay, decreasing in volume at a constant rate. + Rc: >- + "Curvature" of the release portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, + negative slowly then quick. a value of 0 is a linear release, decreasing in volume at a constant rate. + + level: Total amplitude level of the envelope from 0 to 1, contolling maximum volume of the note volume_lfo: - use: switch feature on or off - wave: type of waveform - amount: amount - freq: frequency - freq_shift: frequency shift - phase: random + _doc: >- + Controls for the 'Low Frequency Oscillator' (LFO) used to modulate volume of notes at rhythmic + frequencies. In music this is often referred to as 'tremolo' + use: Switch pitch LFO effects on or off + wave: >- + Type of waveform used for the oscillator. As with `Synthesizer` oscillators can be sawtooth + (`'saw'`), square (`'square'`), sinusoid (`'sine'`), triangle (`'tri'`) or noise (`'noise'`). + amount: the amplitude of the maximal pitch oscillation from the underlying pitch + freq: Base frequency of the LFO oscillations. + freq_shift: shift relative to the bae LFO frequency + phase: The phase of the LFO oscillations, defined in terms of fraction of a whole cycle A: Attack D: Decay S: Sustain R: Release - Ac: "" - Dc: "" - Rc: "" - level: "" + Ac: >- + "Curvature" of the attack portion of a note. Values from -1 to 1, positive indicates increases quickly then slow, + negative slowly then quick. a value of 0 is a linear attack, increasing in volume at a constant rate. + Dc: >- + "Curvature" of the Decay portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, + negative slowly then quick. a value of 0 is a linear decay, decreasing in volume at a constant rate. + Rc: >- + "Curvature" of the release portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, + negative slowly then quick. a value of 0 is a linear release, decreasing in volume at a constant rate. + level: Total amplitude level of the envelope from 0 to 1, contolling maximum volume of the note volume: >- - Master Volume + Master Volume of generator interpolation_type: >- - How to interpolate and resample in the spectrum. "sample": interpolate spectrum values directly; "preserve_power": integrate, interpolate then differentiate to avoid missing power in narrow features. + How to interpolate and resample points in the spectrum. "sample": interpolate spectrum values directly; + "preserve_power": integrate, interpolate then differentiate to avoid missing power in narrow features. regen_phases: >- - For an evolving spectrum, do we regenerate phases for each buffer, or keep the same? These have differing effects. + For an evolving spectrum, do we regenerate phases for each buffer, or keep the same? fit_spec_multiples: >- Whether or not to generate IFFT such that the spectrum sample points are hit exactly. min_freq: >- - Minimum frequency in Hz + Minimum sound frequency used to represent the data max_freq: >- - Maximum frequency in Hz + Maximum sound frequency used to represent the data pitch: >- - Default pitch selection + Default pitch selection (used by all generators) azimuth: >- - azimuth coordinate for center panning + azimuth coordinate for spatialising audio into differing channels polar: >- - polar coordinate for center panning + polar coordinate for spatialising audio into differing channels pitch_hi: >- pitch range maximum in semitones pitch_lo: >- pitch range minimum in semitones pitch_shift: >- default shift in semitones - diff --git a/yamls_to_tables.py b/yamls_to_tables.py index af48ccb..a2f9221 100644 --- a/yamls_to_tables.py +++ b/yamls_to_tables.py @@ -48,11 +48,11 @@ def yaml_traverse(metadict, valdict, rdict, headlev=1): # unspecified => '-' rdict[k+"_unit"] = '-' # print(f"|`{k}` | _{metadict[k]}_ | `{str(valdict[k]).strip()}` | `{rdict[k]}` | {rdict[k+'_unit']}") - tabstr += f"|`{k}` | _{str(metadict[k]).strip()}_ | {valdict[k]} | `{rdict[k]}` | {rdict[k+'_unit']}\n" + tabstr += f"| `{k}` | {str(metadict[k]).strip()} | {valdict[k]} | `{rdict[k]}` | {rdict[k+'_unit']}\n" if k not in rdict: rdict[k] = {} return f'{topstr}{tabstr}{secstr}' - + else: return @@ -63,3 +63,4 @@ def yaml_traverse(metadict, valdict, rdict, headlev=1): print(f"\n# {generators[p.parents[0].name]}\n") ystr = yaml_traverse(ydat['_meta'], ydat, rdat, 2) print(ystr) + print('---') From 480b088f88f0c8f3e08bb8ca5c6d97119439fa6f Mon Sep 17 00:00:00 2001 From: James Trayford Date: Thu, 5 Dec 2024 12:20:41 +0000 Subject: [PATCH 52/86] some more edits --- src/strauss/presets/spec/ranges/default.yml | 51 +++++++++++++++++++-- src/strauss/sources.py | 6 +-- 2 files changed, 50 insertions(+), 7 deletions(-) diff --git a/src/strauss/presets/spec/ranges/default.yml b/src/strauss/presets/spec/ranges/default.yml index a62ace7..cd97c5a 100644 --- a/src/strauss/presets/spec/ranges/default.yml +++ b/src/strauss/presets/spec/ranges/default.yml @@ -29,7 +29,7 @@ cutoff: [0., 1.] cutoff_unit: 'unitless' # Master volume -volume: [0,1.] +volume: [0., 1.] volume_unit: 'unitless' # frequency limits in Hz @@ -44,14 +44,57 @@ pitch_unit: 'unitless' # center panning: phi: [0., 1.] -phi_unit: 'unitless' +phi_unit: 'half-cycles (π)' theta: [0.,1.] -theta_unit: 'unitless' +theta_unit: 'cycles (2π)' # pitch range and default shift in semitones -pitch_shift: [0., 36.] +pitch_shift: [0., 24.] pitch_shift_unit: 'semitones' +# Do we apply a filter, and if so specify the cutoff and filter type +filter: "off" +filter_type: "LPF1" +cutoff: 1. + +# or 'vibrato' +pitch_lfo: + amount: [0, 2] + freq: [1,12] + freq_shift: [0,3] + A: [1e-2, 10] + D: [1e-2, 10] + S: [0, 1] + R: [1e-2, 10] + freq_unit: "Hz" + amount_unit: "semitones" + +volume_lfo: + amount: [0, 1] + freq: [1,12] + freq_shift: [0,3] + A: [1e-2, 10] + D: [1e-2, 10] + S: [0, 1] + R: [1e-2, 10] + freq_unit: "Hz" + amount_unit: "fraction" + + use: off + wave: 'sine' + amount: 0.5 + freq: 3 + freq_shift: 0 + phase: 'random' + A: 0. + D: 0.1 + S: 1. + R: 0. + Ac: 0. + Dc: 0. + Rc: 0. + level: 1 + _meta: note_length: >- Numerical note length diff --git a/src/strauss/sources.py b/src/strauss/sources.py index 01e31d1..f949a62 100644 --- a/src/strauss/sources.py +++ b/src/strauss/sources.py @@ -46,7 +46,7 @@ 'volume_lfo/amount', 'pitch_lfo/freq', 'pitch_lfo/freq_shift', - 'pitch_lfo/amount'] + 'pitch_lfo/amount'] evolvable = ['polar', 'azimuth', @@ -79,10 +79,10 @@ (1e-2, 10), (1,12), (0,3), - (0,2), + (0,1), (1,12), (0,3), - (0,1)] + (0,2)] param_lim_dict = dict(zip(mappable, param_limits)) From c5a538a3f767ed0de5156c8cc047099e486a4aed Mon Sep 17 00:00:00 2001 From: SamYoules Date: Thu, 5 Dec 2024 17:01:11 +0000 Subject: [PATCH 53/86] Added metadata to Sampler default.yaml --- pyproject.toml | 3 +- src/strauss/presets/sampler/default.yml | 125 ++++++++++++++---------- 2 files changed, 76 insertions(+), 52 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 5ccbad9..8a85b18 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,6 +5,7 @@ requires = [ "numpy", "pandas", "pychord", + "pyyaml", "scipy", "setuptools>=42", "sf2utils", @@ -12,4 +13,4 @@ requires = [ "wavio", "wheel" ] -build-backend = "setuptools.build_meta" \ No newline at end of file +build-backend = "setuptools.build_meta" diff --git a/src/strauss/presets/sampler/default.yml b/src/strauss/presets/sampler/default.yml index 9ba1ed6..7d4e8b2 100644 --- a/src/strauss/presets/sampler/default.yml +++ b/src/strauss/presets/sampler/default.yml @@ -86,76 +86,99 @@ pitch_lo: 0 pitch_shift: 0. _meta: - _doc: A top-level documentation string for the generator's defaults - name: preset name - description: full description + _doc: >- + The `Sampler` generator type can be used to modify and play audio samples (sound recordings) . + name: Preset name + description: Full description of the parameters selected for this preset, e.g. looping, volume and pitch envelopes, filters, etc. note_length: >- - Numerical note length in s or "sample" for the sample length or "none" - to last to the end of the + Numerical note length in seconds, or "sample" for the sample length, or "none" + to last to the end of the sonification. looping: >- - "off" for no looping "forward" to loop forward "forwardback" to loop back and forth + Option to play the sample on a loop. "off" for no looping "forward" to loop forwards, "forwardback" to play the loop back and forth loop_start: >- - If looping, start and end point of loop in seconds. + If looping, starting point of loop in seconds. loop_end: >- - If loop_end is longer than the sample, clip to end of the sample. + If looping, ending point of loop in seconds. If loop_end is longer than the sample, clip to end of the sample. volume_envelope: - A: Attack - D: Decay - S: Sustain - R: Release - Ac: "" - Dc: "" - Rc: "" - level: "" + _doc: >- + Define the note volume envelope applied to the samples + A: Attack, how long it takes for a tone to sound after it"'"s triggered + D: Decay, how long it takes for the tones attack to die down after it"'"s triggered + S: Sustain, the volume/level of the sound while it"'"s being triggered + R: Release, how long the tone takes to go silent after the trigger is released + Ac: Curvature of the attack portion of a note. Values from -1 to 1, positive indicates increases quickly then slow, + negative slowly then quick. a value of 0 is a linear attack, increasing in volume at a constant rate. + Dc: Curvature of the Decay portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, + negative slowly then quick. a value of 0 is a linear decay, decreasing in volume at a constant rate. + Rc: Curvature of the release portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, + negative slowly then quick. a value of 0 is a linear release, decreasing in volume at a constant rate. + level: Total amplitude level of the envelope from 0 to 1, contolling maximum volume of the note. filter: >- - Do we apply a filter? + Apply a frequency filter to to the audio signal. Values are 'on' or 'off'. A filter affects the timbre by filtering out certain harmonics filter_type: >- - filter type + Low pass filter (only allows frequencies lower than your cutoff to pass through) + High pass filter (only allows frequencies higher than your cutoff to pass through) cutoff: >- +<<<<<<< HEAD filter cutoff pitch_lfo: _doc: >- or apply 'vibrato'? +======= + The cutoff frequency (or "knee") of the filter, beyond which frequencies are attenuated. as a fraction of the maximum frequency + pitch_lfo: + _doc: >- + Controls for the "'Low Frequency Oscillator'"" (LFO) used to modulate pitch of notes at rhythmic + frequencies. In music this is often referred to a "'vibrato'". +>>>>>>> e3d6432 (Added metadata to Sampler default.yaml) use: switch feature on or off - wave: type of waveform - amount: amount - freq: frequency - freq_shift: frequency shift - phase: random - A: Attack - D: Decay - S: Sustain - R: Release - Ac: "" - Dc: "" - Rc: "" - level: "" + wave: Type of waveform used for the oscillator. As with `Synthesizer` oscillators can be sawtooth + (`'saw'`), square (`'square'`), sinusoid (`'sine'`), triangle (`'tri'`) or noise (`'noise'`). + amount: the amplitude of the maximal pitch oscillation from the underlying pitch + freq: Base frequency of the LFO oscillations. + freq_shift: Shift relative to the base LFO frequency. + phase: The phase of the LFO oscillations, defined in terms of fraction of a whole cycle + A: Attack, how long it takes for a tone to sound after it’s triggered + D: Decay, how long it takes for the tone’s attack to die down after it’s triggered + S: Sustain, the volume/level of the sound while it’s being triggered + R: Release, how long the tone takes to go silent after the trigger is released + Ac: Curvature of the attack portion of a note. Values from -1 to 1, positive indicates increases quickly then slow, + negative slowly then quick. a value of 0 is a linear attack, increasing in volume at a constant rate. + Dc: Curvature of the Decay portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, + negative slowly then quick. a value of 0 is a linear decay, decreasing in volume at a constant rate. + Rc: Curvature of the release portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, + negative slowly then quick. a value of 0 is a linear release, decreasing in volume at a constant rate. + level: Total amplitude level of the envelope from 0 to 1, contolling maximum volume of the note. volume_lfo: _doc: >- - define the note volume envelope applied to the samples A,D,S & R - correspond to 'attack', 'decay', 'sustain' and 'release' - use: switch feature on or off - wave: type of waveform - amount: amount - freq: frequency - freq_shift: frequency shift - phase: random - A: Attack - D: Decay - S: Sustain - R: Release - Ac: "" - Dc: "" - Rc: "" - level: "" + Controls for the `'Low Frequency Oscillator'` (LFO) used to modulate volume of notes at rhythmic + frequencies. In music this is often referred to as `'tremolo'`. + use: switch volume LFO effects on or off + wave: Type of waveform used for the oscillator. As with `Synthesizer` oscillators can be sawtooth + (`'saw'`), square (`'square'`), sinusoid (`'sine'`), triangle (`'tri'`) or noise (`'noise'`). + amount: the amplitude of the maximal pitch oscillation from the underlying pitch + freq: Base frequency of the LFO oscillations. + freq_shift: Shift relative to the base LFO frequency + phase: The phase of the LFO oscillations, defined in terms of fraction of a whole cycle. + A: Attack, how long it takes for a tone to sound after it’s triggered + D: Decay, how long it takes for the tone’s attack to die down after it’s triggered + S: Sustain, the volume/level of the sound while it’s being triggered + R: Release, how long the tone takes to go silent after the trigger is released + Ac: Curvature of the attack portion of a note. Values from -1 to 1, positive indicates increases quickly then slow, + negative slowly then quick. a value of 0 is a linear attack, increasing in volume at a constant rate. + Dc: Curvature of the Decay portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, + negative slowly then quick. a value of 0 is a linear decay, decreasing in volume at a constant rate. + Rc: Curvature of the release portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, + negative slowly then quick. a value of 0 is a linear release, decreasing in volume at a constant rate. + level: Total amplitude level of the envelope from 0 to 1, contolling maximum volume of the note volume: >- - Master Volume + Master Volume of generator. pitch: >- - Default pitch selection + Default pitch selection (used by all generators) azimuth: >- - azimuth coordinate for center panning + azimuth coordinate for spatialising audio into differing channels polar: >- - polar coordinate for center panning + polar coordinate for spatialising audio into differing channels pitch_hi: >- pitch range maximum in semitones pitch_lo: >- From 3c465697909f35a0f1147244ac31d686406d15bc Mon Sep 17 00:00:00 2001 From: SamYoules Date: Mon, 9 Dec 2024 09:19:32 +0000 Subject: [PATCH 54/86] clean up merge conflict --- src/strauss/presets/sampler/default.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/strauss/presets/sampler/default.yml b/src/strauss/presets/sampler/default.yml index 7d4e8b2..21f1161 100644 --- a/src/strauss/presets/sampler/default.yml +++ b/src/strauss/presets/sampler/default.yml @@ -119,18 +119,11 @@ _meta: Low pass filter (only allows frequencies lower than your cutoff to pass through) High pass filter (only allows frequencies higher than your cutoff to pass through) cutoff: >- -<<<<<<< HEAD - filter cutoff - pitch_lfo: - _doc: >- - or apply 'vibrato'? -======= The cutoff frequency (or "knee") of the filter, beyond which frequencies are attenuated. as a fraction of the maximum frequency pitch_lfo: _doc: >- Controls for the "'Low Frequency Oscillator'"" (LFO) used to modulate pitch of notes at rhythmic frequencies. In music this is often referred to a "'vibrato'". ->>>>>>> e3d6432 (Added metadata to Sampler default.yaml) use: switch feature on or off wave: Type of waveform used for the oscillator. As with `Synthesizer` oscillators can be sawtooth (`'saw'`), square (`'square'`), sinusoid (`'sine'`), triangle (`'tri'`) or noise (`'noise'`). From 2064c17e1f83101fd9ef73a78be571f18c272122 Mon Sep 17 00:00:00 2001 From: SamYoules Date: Mon, 9 Dec 2024 12:13:51 +0000 Subject: [PATCH 55/86] Added metadata for 'synth' --- src/strauss/presets/synth/default.yml | 123 ++++++++++++++++---------- 1 file changed, 74 insertions(+), 49 deletions(-) diff --git a/src/strauss/presets/synth/default.yml b/src/strauss/presets/synth/default.yml index 6c71548..1fdc27c 100644 --- a/src/strauss/presets/synth/default.yml +++ b/src/strauss/presets/synth/default.yml @@ -110,29 +110,33 @@ pitch_lo: 0 pitch_shift: 0. _meta: - name: preset name - description: full description + _doc: >- + The `Synth` generator type can be used to synthesise sound using mathmatically + generated waveforms or `oscillators`. The preset can be used to modify the relative + frequency, phase and amplitude of these oscillators. + name: Name of the preset + description: Full description of the preset purpose and parameters. oscillators: >- Oscillator information. Oscillator are denoted osc with n=3 by default. oscillators: osc1: - form: waveform, choose from ['saw', 'square', 'sine', 'tri', 'noise'] - level: intrinsic volume - detune: change in tuning as a percentage of the input frequency - phase: phase + form: Type of waveform used for oscillator 1, choose from ['saw', 'square', 'sine', 'tri', 'noise'] + level: Intrinsic volume + detune: Change in tuning as a percentage of the input frequency + phase: The phase of the oscillator, defined in terms of fraction of a whole cycle osc2: - form: waveform, choose from ['saw', 'square', 'sine', 'tri', 'noise'] - level: intrinsic volume - detune: change in tuning as a percentage of the input frequency - phase: phase + form: Type of waveform used for oscillator 2, choose from ['saw', 'square', 'sine', 'tri', 'noise'] + level: Intrinsic volume + detune: Change in tuning as a percentage of the input frequency + phase: The phase of the oscillator, defined in terms of fraction of a whole cycle osc3: - form: waveform, choose from ['saw', 'square', 'sine', 'tri', 'noise'] - level: intrinsic volume - detune: change in tuning as a percentage of the input frequency - phase: phase + form: Type of waveform used for oscillator 3, choose from ['saw', 'square', 'sine', 'tri', 'noise'] + level: Intrinsic volume + detune: Change in tuning as a percentage of the input frequency + phase: The phase of the oscillator, defined in terms of fraction of a whole cycle note_length: >- - Numerical note length in s + Numerical note length in seconds volume_envelope: >- Define the note volume envelope applied to the samples. A,D,S & R correspond to 'attack', 'decay', 'sustain' and 'release'. @@ -141,58 +145,79 @@ _meta: D: Decay S: Sustain R: Release - Ac: "" - Dc: "" - Rc: "" - level: "" + Ac: >- + "Curvature" of the attack portion of a note. Values from -1 to 1, positive indicates increases quickly then slow, + negative slowly then quick. a value of 0 is a linear attack, increasing in volume at a constant rate. + Dc: >- + "Curvature" of the Decay portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, + negative slowly then quick. a value of 0 is a linear decay, decreasing in volume at a constant rate. + Rc: >- + "Curvature" of the release portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, + negative slowly then quick. a value of 0 is a linear release, decreasing in volume at a constant rate. + + level: Total amplitude level of the envelope from 0 to 1, contolling maximum volume of the note filter: >- - Do we apply a filter? + Do we apply a frequency filter to the audio signal? This can be used to change the balance of frequencies and manipulate the 'timbre' of a note filter_type: >- - filter type + Choose from available filter types cutoff: >- - filter cutoff - pitch_lfo: >- - or apply 'vibrato'? + The cut-off frequency (or `knee`) of the filter, beyond which frequencies are attenuated. + Specified between 0 and 1 as a fraction of the audible range of notes we can hear (E0 to D#10). pitch_lfo: - use: switch feature on or off - wave: type of waveform - amount: amount - freq: frequency - freq_shift: frequency shift - phase: random + _doc: >- + Controls for the 'Low Frequency Oscillator' (LFO) used to modulate pitch of notes at rhythmic + frequencies. In music this is often referred to a 'vibrato' + use: Switch pitch LFO effects on or off + wave: >- + Type of waveform used for the oscillator. As with `Synthesizer` oscillators can be sawtooth + (`'saw'`), square (`'square'`), sinusoid (`'sine'`), triangle (`'tri'`) or noise (`'noise'`). + amount: the amplitude of the maximal pitch oscillation from the underlying pitch + freq: Base frequency of the LFO oscillations. + freq_shift: shift relative to the bae LFO frequency + phase: The phase of the LFO oscillations, defined in terms of fraction of a whole cycle A: Attack D: Decay S: Sustain R: Release - Ac: "" - Dc: "" - Rc: "" - level: "" - volume_lfo: >- - or apply 'tremolo'? + Ac: >- + "Curvature" of the attack portion of a note. Values from -1 to 1, positive indicates increases quickly then slow, negative slowly then quick. a value of 0 is a linear attack, increasing in volume at a constant rate. + Dc: >- + "Curvature" of the Decay portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, negative slowly then quick. a value of 0 is a linear decay, decreasing in volume at a constant rate. + Rc: >- + "Curvature" of the release portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, negative slowly then quick. a value of 0 is a linear release, decreasing in volume at a constant rate. + + level: Total amplitude level of the envelope from 0 to 1, contolling maximum volume of the note volume_lfo: - use: switch feature on or off - wave: type of waveform - amount: amount - freq: frequency - freq_shift: frequency shift - phase: random + _doc: >- + Controls for the 'Low Frequency Oscillator' (LFO) used to modulate volume of notes at rhythmic + frequencies. In music this is often referred to as 'tremolo'. + use: Switch pitch LFO effects on or off + wave: >- + Type of waveform used for the oscillator. As with `Synthesizer` oscillators can be sawtooth + (`'saw'`), square (`'square'`), sinusoid (`'sine'`), triangle (`'tri'`) or noise (`'noise'`). + amount: the amplitude of the maximal pitch oscillation from the underlying pitch + freq: Base frequency of the LFO oscillations. + freq_shift: shift relative to the bae LFO frequency + phase: The phase of the LFO oscillations, defined in terms of fraction of a whole cycle A: Attack D: Decay S: Sustain R: Release - Ac: "" - Dc: "" - Rc: "" - level: "" + Ac: >- + "Curvature" of the attack portion of a note. Values from -1 to 1, positive indicates increases quickly then slow, negative slowly then quick. a value of 0 is a linear attack, increasing in volume at a constant rate. + Dc: >- + "Curvature" of the Decay portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, negative slowly then quick. a value of 0 is a linear decay, decreasing in volume at a constant rate. + Rc: >- + "Curvature" of the release portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, negative slowly then quick. a value of 0 is a linear release, decreasing in volume at a constant rate. + level: Total amplitude level of the envelope from 0 to 1, contolling maximum volume of the note volume: >- - Master Volume + Master Volume of synthesizer pitch: >- Default pitch selection azimuth: >- - azimuth coordinate for center panning + azimuth coordinate for spatialising audio into differing channels polar: >- - polar coordinate for center panning + polar coordinate for spatialising audio into differing channels pitch_hi: >- pitch range maximum in semitones pitch_lo: >- From 3d8b1d3497c1e6dae5f0ce0814a70c5192123c49 Mon Sep 17 00:00:00 2001 From: James Trayford Date: Mon, 9 Dec 2024 16:19:36 +0000 Subject: [PATCH 56/86] spectraliserr changers and merge remote --- src/strauss/presets/spec/ranges/default.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/strauss/presets/spec/ranges/default.yml b/src/strauss/presets/spec/ranges/default.yml index cd97c5a..d5effd3 100644 --- a/src/strauss/presets/spec/ranges/default.yml +++ b/src/strauss/presets/spec/ranges/default.yml @@ -67,7 +67,7 @@ pitch_lfo: S: [0, 1] R: [1e-2, 10] freq_unit: "Hz" - amount_unit: "semitones" + amount_unit: "semitones" volume_lfo: amount: [0, 1] @@ -78,8 +78,7 @@ volume_lfo: S: [0, 1] R: [1e-2, 10] freq_unit: "Hz" - amount_unit: "fraction" - + amount_unit: "fraction" use: off wave: 'sine' amount: 0.5 From 0cd57a9679df0237689362f2d68b56c260ffa533 Mon Sep 17 00:00:00 2001 From: James Trayford Date: Thu, 12 Dec 2024 16:49:11 +0000 Subject: [PATCH 57/86] get yaml to md tables working with sphinx --- docs/conf.py | 13 +++++---- docs/index.rst | 6 +++-- yamls_to_tables.py => docs/yamls_to_tables.py | 27 ++++++++++++------- setup.cfg | 8 +++--- 4 files changed, 34 insertions(+), 20 deletions(-) rename yamls_to_tables.py => docs/yamls_to_tables.py (80%) diff --git a/docs/conf.py b/docs/conf.py index ecabe57..c1a131d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -14,7 +14,7 @@ import sys sys.path.insert(0, os.path.abspath('..')) sys.path.insert(0, os.path.abspath('../../src/strauss/presets')) - +import sphinx_pdj_theme # -- Project information ----------------------------------------------------- @@ -36,7 +36,8 @@ 'sphinx.ext.ifconfig', 'sphinx.ext.viewcode', 'sphinx.ext.inheritance_diagram', 'sphinx.ext.autosummary','sphinx.ext.coverage', - 'sphinx.ext.napoleon', "myst_parser"] + 'sphinx.ext.napoleon', "myst_parser", + "sphinx_exec_code"] #'recommonmark', # 'sphinx_markdown_tables' @@ -57,12 +58,14 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = 'alabaster' +html_theme = 'nature' +# html_theme_path = [sphinx_pdj_theme.get_html_theme_path()] # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] -# enable markdown -#extensions.append('myst_parser') +# html_theme_options = { +# 'page_width': '1200px' +# } diff --git a/docs/index.rst b/docs/index.rst index d569c87..b4ebe6d 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,4 +1,5 @@ -.. strauss documentation master file, created by +.. strauss + documentation master file, created by sphinx-quickstart on Tue Oct 26 14:56:02 2021. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. @@ -19,10 +20,11 @@ The code aims to make rich and evocative sonification straightforward, with a nu motivation start elements + params detailed examples todo - + Indices and tables ================== diff --git a/yamls_to_tables.py b/docs/yamls_to_tables.py similarity index 80% rename from yamls_to_tables.py rename to docs/yamls_to_tables.py index a2f9221..5f013b4 100644 --- a/yamls_to_tables.py +++ b/docs/yamls_to_tables.py @@ -1,13 +1,14 @@ - +# --- hide: start --- import yaml +import os from glob import glob from pathlib import Path + generators = {'spec' : "`Spectraliser` Generator", 'synth' : "`Synthesiser` Generator", 'sampler' : "`Sampler` Generator"} -# p = Path(__file__) p = Path("src", "strauss", "presets", "*", "default.yml") def read_yaml(filename): @@ -56,11 +57,17 @@ def yaml_traverse(metadict, valdict, rdict, headlev=1): else: return -for f in glob(str(p)): - p = Path(f) - ydat = read_yaml(p) - rdat = read_yaml(p.parents[0] / "ranges" / "default.yml") - print(f"\n# {generators[p.parents[0].name]}\n") - ystr = yaml_traverse(ydat['_meta'], ydat, rdat, 2) - print(ystr) - print('---') +with open('docs/tables.md', 'w') as outfile: + for f in glob(str(p)): + p = Path(f) + ydat = read_yaml(p) + rdat = read_yaml(p.parents[0] / "ranges" / "default.yml") + # print(f"\n# {generators[p.parents[0].name]}\n") + ystr = yaml_traverse(ydat['_meta'], ydat, rdat, 2) + # print(ystr) + # print('---') + outfile.write(f"\n# {generators[p.parents[0].name]}\n") + outfile.write(ystr) + outfile.write('---') + +# --- hide: stop --- diff --git a/setup.cfg b/setup.cfg index e5928e0..66c23f5 100644 --- a/setup.cfg +++ b/setup.cfg @@ -28,18 +28,20 @@ install_requires = pyyaml scipy setuptools >= 4.2 - sphinx wavio wheel sounddevice sf2utils audioop-lts; python_version >= "3.13" [options.packages.find] -where = src' +where = src [options.extras_require] default = tqdm TTS = tqdm TTS - +docs = + sphinx + sphinx-exec-code + myst-parser From 74c6e8e6b9590f30c728e1255ffc00207ccb2740 Mon Sep 17 00:00:00 2001 From: James Trayford Date: Mon, 11 Nov 2024 12:14:10 +0000 Subject: [PATCH 58/86] initial tool to autodoc the yaml files (building MD tables from them) --- src/strauss/presets/sampler/default.yml | 15 ------ yamls_to_tables.py | 64 +++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 15 deletions(-) create mode 100644 yamls_to_tables.py diff --git a/src/strauss/presets/sampler/default.yml b/src/strauss/presets/sampler/default.yml index 21f1161..e02f989 100644 --- a/src/strauss/presets/sampler/default.yml +++ b/src/strauss/presets/sampler/default.yml @@ -164,18 +164,3 @@ _meta: Rc: Curvature of the release portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, negative slowly then quick. a value of 0 is a linear release, decreasing in volume at a constant rate. level: Total amplitude level of the envelope from 0 to 1, contolling maximum volume of the note - volume: >- - Master Volume of generator. - pitch: >- - Default pitch selection (used by all generators) - azimuth: >- - azimuth coordinate for spatialising audio into differing channels - polar: >- - polar coordinate for spatialising audio into differing channels - pitch_hi: >- - pitch range maximum in semitones - pitch_lo: >- - pitch range minimum in semitones - pitch_shift: >- - default shift in semitones - diff --git a/yamls_to_tables.py b/yamls_to_tables.py new file mode 100644 index 0000000..7261357 --- /dev/null +++ b/yamls_to_tables.py @@ -0,0 +1,64 @@ +import yaml +from glob import glob +from pathlib import Path + +generators = {'spec' : "`Spectraliser` Generator", + 'synth' : "`Synthesiser` Generator", + 'sampler' : "`Sampler` Generator"} + +# p = Path(__file__) +p = Path("src", "strauss", "presets", "*", "default.yml") + +def read_yaml(filename): + with filename.open(mode='r') as fdata: + # try: + yamldict = yaml.safe_load(fdata) + # except yaml.YAMLError as err: + # print(err) + return yamldict + +def yaml_traverse(metadict, valdict, rdict, headlev=1): + if hasattr(metadict, 'keys'): + topstr = '' + tabstr = '' + secstr = '' + tabstr += "\n| Parameter | Description | Default Value | Default Range | Unit |\n" + tabstr += "| ----------- | ----------- | ----------- | ----------- | ----------- |\n" + + for k in metadict.keys(): + # print (f">>>>>> {k}") + if hasattr(metadict[k], 'keys'): + secstr += '\n'+''.join(['#']*headlev) + f" `{k}` parameter group\n" + if not k in rdict: + rdict[k] = {} + secstr += yaml_traverse(metadict[k], valdict[k], rdict[k], headlev+1) + continue + if k == '_doc': + topstr += f"\n{metadict[k]}\n" + # print('\n',metadict[k]) + continue + if k not in rdict: + # unspecified => '-' + rdict[k] = '-' + else: + # lets avoid these line-breaking with special characters + rdict[k] = str(rdict[k]).replace(" ","").replace(",","\u2011").replace("-","\u2011") + if k+"_unit" not in rdict: + # unspecified => '-' + rdict[k+"_unit"] = '-' + # print(f"|`{k}` | _{metadict[k]}_ | `{str(valdict[k]).strip()}` | `{rdict[k]}` | {rdict[k+'_unit']}") + tabstr += f"|`{k}` | _{str(metadict[k]).strip()}_ | `valdict[k]` | `{rdict[k]}` | {rdict[k+'_unit']}\n" + if k not in rdict: + rdict[k] = {} + return f'{topstr}{tabstr}{secstr}' + + else: + return + +for f in glob(str(p)): + p = Path(f) + ydat = read_yaml(p) + rdat = read_yaml(p.parents[0] / "ranges" / "default.yml") + print(f"\n# {generators[p.parents[0].name]}\n") + ystr = yaml_traverse(ydat['_meta'], ydat, rdat, 2) + print(ystr) From cf924c09ad52c6b24559b540924bf6698f8a38ac Mon Sep 17 00:00:00 2001 From: James Trayford Date: Wed, 13 Nov 2024 11:02:56 +0000 Subject: [PATCH 59/86] Fix and example change --- yamls_to_tables.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/yamls_to_tables.py b/yamls_to_tables.py index 7261357..af48ccb 100644 --- a/yamls_to_tables.py +++ b/yamls_to_tables.py @@ -1,3 +1,4 @@ + import yaml from glob import glob from pathlib import Path @@ -47,7 +48,7 @@ def yaml_traverse(metadict, valdict, rdict, headlev=1): # unspecified => '-' rdict[k+"_unit"] = '-' # print(f"|`{k}` | _{metadict[k]}_ | `{str(valdict[k]).strip()}` | `{rdict[k]}` | {rdict[k+'_unit']}") - tabstr += f"|`{k}` | _{str(metadict[k]).strip()}_ | `valdict[k]` | `{rdict[k]}` | {rdict[k+'_unit']}\n" + tabstr += f"|`{k}` | _{str(metadict[k]).strip()}_ | {valdict[k]} | `{rdict[k]}` | {rdict[k+'_unit']}\n" if k not in rdict: rdict[k] = {} return f'{topstr}{tabstr}{secstr}' From 87fb940659f454e97d38fcebcd44b816aec2fdd0 Mon Sep 17 00:00:00 2001 From: James Trayford Date: Mon, 2 Dec 2024 15:11:32 +0000 Subject: [PATCH 60/86] some formatting script edits and adding metadata to the Spectraliser default.yml --- yamls_to_tables.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/yamls_to_tables.py b/yamls_to_tables.py index af48ccb..a2f9221 100644 --- a/yamls_to_tables.py +++ b/yamls_to_tables.py @@ -48,11 +48,11 @@ def yaml_traverse(metadict, valdict, rdict, headlev=1): # unspecified => '-' rdict[k+"_unit"] = '-' # print(f"|`{k}` | _{metadict[k]}_ | `{str(valdict[k]).strip()}` | `{rdict[k]}` | {rdict[k+'_unit']}") - tabstr += f"|`{k}` | _{str(metadict[k]).strip()}_ | {valdict[k]} | `{rdict[k]}` | {rdict[k+'_unit']}\n" + tabstr += f"| `{k}` | {str(metadict[k]).strip()} | {valdict[k]} | `{rdict[k]}` | {rdict[k+'_unit']}\n" if k not in rdict: rdict[k] = {} return f'{topstr}{tabstr}{secstr}' - + else: return @@ -63,3 +63,4 @@ def yaml_traverse(metadict, valdict, rdict, headlev=1): print(f"\n# {generators[p.parents[0].name]}\n") ystr = yaml_traverse(ydat['_meta'], ydat, rdat, 2) print(ystr) + print('---') From eddbea2725e819093051b6951b0bee0cd48f9d02 Mon Sep 17 00:00:00 2001 From: SamYoules Date: Thu, 5 Dec 2024 17:01:11 +0000 Subject: [PATCH 61/86] Added metadata to Sampler default.yaml --- src/strauss/presets/sampler/default.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/strauss/presets/sampler/default.yml b/src/strauss/presets/sampler/default.yml index e02f989..abf2aa2 100644 --- a/src/strauss/presets/sampler/default.yml +++ b/src/strauss/presets/sampler/default.yml @@ -164,3 +164,17 @@ _meta: Rc: Curvature of the release portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, negative slowly then quick. a value of 0 is a linear release, decreasing in volume at a constant rate. level: Total amplitude level of the envelope from 0 to 1, contolling maximum volume of the note + volume: >- + Master Volume of generator. + pitch: >- + Default pitch selection (used by all generators) + azimuth: >- + azimuth coordinate for spatialising audio into differing channels + polar: >- + polar coordinate for spatialising audio into differing channels + pitch_hi: >- + pitch range maximum in semitones + pitch_lo: >- + pitch range minimum in semitones + pitch_shift: >- + default shift in semitones \ No newline at end of file From ba50c70fb211db08a43b170d7d0f1e280ad65f4d Mon Sep 17 00:00:00 2001 From: SamYoules Date: Thu, 12 Dec 2024 15:51:16 +0000 Subject: [PATCH 62/86] Updates to Sphinx docs --- README.md | 14 +++++++------- docs/index.rst | 2 +- docs/js/custom.js | 3 +++ docs/motivation.rst | 7 +++---- docs/start.rst | 42 ++++++++++++++++++++++-------------------- src/strauss/stream.py | 2 +- 6 files changed, 37 insertions(+), 33 deletions(-) create mode 100644 docs/js/custom.js diff --git a/README.md b/README.md index 1ee4155..f062ccb 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ ## Getting Started -Access the [full documentation here](https://strauss.readthedocs.io/) *(under construction!)* and read more about the associated [Audio Universe project here](https://www.audiouniverse.org/). +Access the [full documentation here](https://strauss.readthedocs.io/) and read more about the associated [Audio Universe project here](https://www.audiouniverse.org/). *STRAUSS* is [PyPI hosted package](https://pypi.org/project/strauss/) and `pip` can be used for the default installation: @@ -17,6 +17,12 @@ Access the [full documentation here](https://strauss.readthedocs.io/) *(under co For a standard install (without text-to speech support). +For development purposes, you can instead use: + +`pip install -e .` + +where the `-e` option allows a local install, such that you can modify and run the source code on the fly without needing to reinstall each time. + If you would like access to all the resources and explore the code directly, make a copy of the *STRAUSS* repository via SSH, `git clone git@github.com:james-trayford/strauss.git strauss` @@ -31,12 +37,6 @@ and install *STRAUSS* from your local repository using `pip` `pip install .` -For development purposes, you can instead use: - -`pip install -e .` - -where the `-e` option allows a local install, such that you can modify and run the source code on the fly without needing to reinstall each time. - We recommend using a conda environment to avoid package conflicts. Type `conda env create -f environment.yml` diff --git a/docs/index.rst b/docs/index.rst index b4ebe6d..5fbe803 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -9,7 +9,7 @@ Welcome to the STRAUSS documentation! Strauss is a python toolkit for data *"sonification"* - the representation of data using sound - with both scientific and outreach applications. -The code aims to make rich and evocative sonification straightforward, with a number of presets and examples enabling a quick start. At the same time, it is intended to be flexible enough to allow high level of control over the sonification and various expressive elements of sound and harmony if required. You can read about the associated `Audio Universe project here `_ for outreach and examples using Strauss. +The code aims to make rich and evocative sonification straightforward, with a number of presets and examples enabling a quick start. At the same time, it is intended to be flexible enough to allow high level of control over the sonification and various expressive elements of sound and harmony if required. The project is described in more detail in the paper `Introducing STRAUSS: a flexible sonification Python package `_, presented at the 28th Proceedings of the `International Community of Auditory Displays (2023) `_. You can read about the associated `Audio Universe project here `_ for examples of using Strauss for a variety of applications. .. note:: Strauss and its documentation are currently in development, with more details and features coming soon. Look out for / follow the repo our first numbered release, and accompanying article! diff --git a/docs/js/custom.js b/docs/js/custom.js new file mode 100644 index 0000000..184b5ae --- /dev/null +++ b/docs/js/custom.js @@ -0,0 +1,3 @@ +$(document).ready(function () { + $('a.external').attr('target', '_blank'); +}); diff --git a/docs/motivation.rst b/docs/motivation.rst index 2219047..431518d 100644 --- a/docs/motivation.rst +++ b/docs/motivation.rst @@ -8,12 +8,11 @@ Why sonification? So, given the dominance of visualisation techniques, *why use sonification?* Here are three motivating reasons: -**1) Accessibility**: For many people, visualisation is inherently inaccessible. Around 4% of the world population are visually impaired (VI) according to `IAPB statistics `_ [1]. Combining visualisation and sonification techniques ensures broader accessibility of science and data presentation. - +**1) Enhancing Visuals**: for scientific images or video data without narration, typically nothing is being conveyed through sound. Combining sonification with your visualisation provides a new channel for conveying data, either enhancing what is already shown, or expressing new variables that are absent from the visual. For communicating to a broader audience, sonification can reveal the beauty and complexity in data, and is active area of interest `in academic music `_. + **2) Unique Applications**: There are applications for which sonification is uniquely suited. Sound is a time varying signal so can represent *time-series data* intuitively, or data with rapidly or subtly varying rates of change. The human ear can cover about 10 octaves in frequency, while the visible range of light only covers around 1 octave. Used well, sonification can provide a new perspective on your data, and potentially new insights into your data that are missed with standard, visual approaches. -**3) Enhancing Visuals**: for scientific images or video data without narration, typically nothing is being conveyed through sound. Combining sonification with your visualisation provides a new channel for conveying data, either enhancing what is already shown, or expressing new variables that are absent from the visual. For communicating to a broader audience, sonification can reveal the beauty and complexity in data, and is an active area of interest `in academic music `_. - +**3) Accessibility**: For many people, visualisation is inherently inaccessible. Around 4% of the world population are visually impaired (VI) according to `IAPB statistics `_ [1]. Combining visualisation and sonification techniques ensures broader accessibility of science and data presentation. Strauss approach **************** diff --git a/docs/start.rst b/docs/start.rst index 3caeb83..a10ef7e 100644 --- a/docs/start.rst +++ b/docs/start.rst @@ -1,26 +1,12 @@ Getting Started ^^^^^^^^^^^^^^^ -This walkthrough will take you through a clean install of the code, including optional dependencies and trying your first sonification. +This walkthrough will take you through a clean install of the code, including optional dependencies and trying your first sonification. There are also example notebooks available on `Google Colab `_ which you can run without installing Strauss on your local system. Installation ************ -The Strauss code can be downloaded from **GitHub** at `the repository url `_. - -Using :code:`git` make a copy of the STRAUSS repository via SSH, - -.. code-block:: bash - - git clone git@github.com:james-trayford/strauss.git strauss - -or HTTPS if you don't have SSH keys set up, - -.. code-block:: bash - - git clone https://github.com/james-trayford/strauss.git strauss - -throughout the documentation, I will refer to this as the **strauss repo** or **code directory**. +Strauss can be installed in three different ways, depending on whether you want to develop the code or simply use it as it is. It can be installed using pip install, with or without the option for development, or you can clone it from the GitHub repository. if you just want to use the code, STRAUSS may then be installed using pip, as @@ -37,12 +23,28 @@ If you want to develop the code, you can instead use where the :code:`-e` option allows a local install, such that you can modify and run the source code on the fly without needing to reinstall each time. -Example jupyter notebooks -************************* +Alternatively, the Strauss code can be downloaded from **GitHub** at `the repository url `_ + +Using :code:`git` make a copy of the STRAUSS repository via SSH, + +.. code-block:: bash + + git clone git@github.com:james-trayford/strauss.git strauss + +or HTTPS if you don't have SSH keys set up, + +.. code-block:: bash + + git clone https://github.com/james-trayford/strauss.git strauss + +throughout the documentation, I will refer to this as the **strauss repo** or **code directory**. + +Example jupyter notebooks/scripts +********************************* -There are a number of example applications of Strauss in the :code:`example` subdirectory of the :code:`strauss` repo. These are in Python Notebook (:code:`.ipynb`) format for an interactive, step-by-step. They are also provided in Python script format (:code:`.py`). +There are a number of example applications of Strauss in the :code:`example` subdirectory of the :code:`strauss` repo. These are in Python Notebook (:code:`.ipynb`) format for an interactive, step-by-step experience. They are also provided in Python script format (.py) in the :code:`examples` directory. The Python scripts can be run from the command line. -In order to run the Notebook examples, first ensure that :code:`jupyter` is installed on your system. These were developed in :code:`jupyter-lab`, which can also be installed using pip, as +In order to run the notebook examples, first ensure that :code:`jupyter` is installed on your system. These were developed in :code:`jupyter-lab`, which can also be installed using pip, as: .. code-block:: bash diff --git a/src/strauss/stream.py b/src/strauss/stream.py index 1b4fd9f..797cd01 100644 --- a/src/strauss/stream.py +++ b/src/strauss/stream.py @@ -202,7 +202,7 @@ def __init__(self, stream, bufflength=0.1): self.nsamp_padstream = self._nbuffs * self._nsamp_buff self.nsamp_pad = self.nsamp_padstream-stream._nsamp_stream self._olap_pad = self.nsamp_pad-self._nsamp_halfbuff - self._olap_lim = min(stream._nsamp_stream, stream._nsamp_stream+self.olap_pad) + self._olap_lim = min(stream._nsamp_stream, stream._nsamp_stream+self._olap_pad) # construct tile and overlap buffer arrays self.buffs_tile = np.pad(stream.values, (0,self.nsamp_pad) From aa31bb3c4835b9b841c692eac3cb5427d9490a99 Mon Sep 17 00:00:00 2001 From: James Trayford Date: Mon, 16 Dec 2024 14:48:27 +0000 Subject: [PATCH 63/86] do we need jquery? removing for now --- docs/conf.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index c1a131d..dba30bd 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -38,8 +38,11 @@ 'sphinx.ext.autosummary','sphinx.ext.coverage', 'sphinx.ext.napoleon', "myst_parser", "sphinx_exec_code"] -#'recommonmark', -# 'sphinx_markdown_tables' + "sphinx_exec_code"]#, 'sphinxcontrib.jquery'] + +html_js_files = [ + 'js/custom.js' +] print(extensions) From 3bf44a0dc5599501093577179d64339c893349aa Mon Sep 17 00:00:00 2001 From: James Trayford Date: Mon, 16 Dec 2024 15:20:01 +0000 Subject: [PATCH 64/86] fix MD table building logic --- docs/yamls_to_tables.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/yamls_to_tables.py b/docs/yamls_to_tables.py index 5f013b4..5343dd2 100644 --- a/docs/yamls_to_tables.py +++ b/docs/yamls_to_tables.py @@ -19,13 +19,15 @@ def read_yaml(filename): # print(err) return yamldict +tstr1 = "\n| Parameter | Description | Default Value | Default Range | Unit |\n" +tstr2 = "| ----------- | ----------- | ----------- | ----------- | ----------- |\n" + def yaml_traverse(metadict, valdict, rdict, headlev=1): if hasattr(metadict, 'keys'): + starttab = 1 topstr = '' tabstr = '' secstr = '' - tabstr += "\n| Parameter | Description | Default Value | Default Range | Unit |\n" - tabstr += "| ----------- | ----------- | ----------- | ----------- | ----------- |\n" for k in metadict.keys(): # print (f">>>>>> {k}") @@ -49,6 +51,9 @@ def yaml_traverse(metadict, valdict, rdict, headlev=1): # unspecified => '-' rdict[k+"_unit"] = '-' # print(f"|`{k}` | _{metadict[k]}_ | `{str(valdict[k]).strip()}` | `{rdict[k]}` | {rdict[k+'_unit']}") + if starttab: + tabstr = tstr1 + tstr2 + tabstr + starttab = 0 tabstr += f"| `{k}` | {str(metadict[k]).strip()} | {valdict[k]} | `{rdict[k]}` | {rdict[k+'_unit']}\n" if k not in rdict: rdict[k] = {} From c26b8cd5ea56c5d08e1fdf1a8a6f16eb0b569c25 Mon Sep 17 00:00:00 2001 From: James Trayford Date: Mon, 16 Dec 2024 15:25:32 +0000 Subject: [PATCH 65/86] remove vestigial import --- docs/conf.py | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index dba30bd..7ae2b23 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -14,7 +14,6 @@ import sys sys.path.insert(0, os.path.abspath('..')) sys.path.insert(0, os.path.abspath('../../src/strauss/presets')) -import sphinx_pdj_theme # -- Project information ----------------------------------------------------- From c4a72ff52ddb99ed037e276c581b519aa5abf11c Mon Sep 17 00:00:00 2001 From: James Trayford Date: Mon, 16 Dec 2024 15:33:20 +0000 Subject: [PATCH 66/86] commit the params.rst file --- docs/params.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 docs/params.rst diff --git a/docs/params.rst b/docs/params.rst new file mode 100644 index 0000000..b9d8b48 --- /dev/null +++ b/docs/params.rst @@ -0,0 +1,10 @@ +.. _params: + +Parameter Reference +################### + +.. exec_code:: + :filename: yamls_to_tables.py + +.. include:: tables.md + :parser: myst_parser.sphinx_ From e06f53dab02030b6ea688a9436ae51cd9ff736a1 Mon Sep 17 00:00:00 2001 From: SamYoules Date: Mon, 16 Dec 2024 16:04:34 +0000 Subject: [PATCH 67/86] Corrected apostrophes --- src/strauss/presets/sampler/default.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/strauss/presets/sampler/default.yml b/src/strauss/presets/sampler/default.yml index abf2aa2..43bfca2 100644 --- a/src/strauss/presets/sampler/default.yml +++ b/src/strauss/presets/sampler/default.yml @@ -91,10 +91,10 @@ _meta: name: Preset name description: Full description of the parameters selected for this preset, e.g. looping, volume and pitch envelopes, filters, etc. note_length: >- - Numerical note length in seconds, or "sample" for the sample length, or "none" + Numerical note length in seconds, or `'sample'` for the sample length, or `'none'` to last to the end of the sonification. looping: >- - Option to play the sample on a loop. "off" for no looping "forward" to loop forwards, "forwardback" to play the loop back and forth + Option to play the sample on a loop. `'off'` for no looping `'forward'` to loop forwards, `'forwardback'` to play the loop back and forth loop_start: >- If looping, starting point of loop in seconds. loop_end: >- @@ -102,9 +102,9 @@ _meta: volume_envelope: _doc: >- Define the note volume envelope applied to the samples - A: Attack, how long it takes for a tone to sound after it"'"s triggered - D: Decay, how long it takes for the tones attack to die down after it"'"s triggered - S: Sustain, the volume/level of the sound while it"'"s being triggered + A: Attack, how long it takes for a tone to sound after it’s triggered + D: Decay, how long it takes for the tones attack to die down after it’s triggered + S: Sustain, the volume/level of the sound while it’s being triggered R: Release, how long the tone takes to go silent after the trigger is released Ac: Curvature of the attack portion of a note. Values from -1 to 1, positive indicates increases quickly then slow, negative slowly then quick. a value of 0 is a linear attack, increasing in volume at a constant rate. @@ -119,11 +119,11 @@ _meta: Low pass filter (only allows frequencies lower than your cutoff to pass through) High pass filter (only allows frequencies higher than your cutoff to pass through) cutoff: >- - The cutoff frequency (or "knee") of the filter, beyond which frequencies are attenuated. as a fraction of the maximum frequency + The cutoff frequency (or `'knee'`) of the filter, beyond which frequencies are attenuated. as a fraction of the maximum frequency pitch_lfo: _doc: >- - Controls for the "'Low Frequency Oscillator'"" (LFO) used to modulate pitch of notes at rhythmic - frequencies. In music this is often referred to a "'vibrato'". + Controls for the `'Low Frequency Oscillator'` (LFO) used to modulate pitch of notes at rhythmic + frequencies. In music this is often referred to a `'vibrato'`. use: switch feature on or off wave: Type of waveform used for the oscillator. As with `Synthesizer` oscillators can be sawtooth (`'saw'`), square (`'square'`), sinusoid (`'sine'`), triangle (`'tri'`) or noise (`'noise'`). From 8b862ed2b9f6bbbe9ccd9101f000c4dfec2ddbcf Mon Sep 17 00:00:00 2001 From: James Trayford Date: Tue, 17 Dec 2024 15:17:40 +0000 Subject: [PATCH 68/86] clean up p[ost-merge --- docs/conf.py | 1 - docs/motivation.rst | 6 +-- src/strauss/presets/sampler/default.yml | 15 ++++---- src/strauss/presets/synth/default.yml | 50 ++++++++++++------------- src/strauss/sources.py | 2 +- 5 files changed, 37 insertions(+), 37 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 7ae2b23..f13325c 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -36,7 +36,6 @@ 'sphinx.ext.inheritance_diagram', 'sphinx.ext.autosummary','sphinx.ext.coverage', 'sphinx.ext.napoleon', "myst_parser", - "sphinx_exec_code"] "sphinx_exec_code"]#, 'sphinxcontrib.jquery'] html_js_files = [ diff --git a/docs/motivation.rst b/docs/motivation.rst index 431518d..9084128 100644 --- a/docs/motivation.rst +++ b/docs/motivation.rst @@ -8,10 +8,10 @@ Why sonification? So, given the dominance of visualisation techniques, *why use sonification?* Here are three motivating reasons: -**1) Enhancing Visuals**: for scientific images or video data without narration, typically nothing is being conveyed through sound. Combining sonification with your visualisation provides a new channel for conveying data, either enhancing what is already shown, or expressing new variables that are absent from the visual. For communicating to a broader audience, sonification can reveal the beauty and complexity in data, and is active area of interest `in academic music `_. +**1) Enhancing Visuals**: for scientific images or video data without narration, typically nothing is being conveyed through sound. Combining sonification with your visualisation provides a new channel for conveying data, either enhancing what is already shown, or expressing new variables that are absent from the visual. For communicating to a broader audience, sonification can reveal the beauty and complexity in data, and is active area of interest `in academic music `_. + +**2) Unique Applications**: There are applications for which sonification is uniquely suited. Sound is a time varying signal so can represent *time-series data* intuitively, or data with rapidly or subtly varying rates of change. The human ear can cover about 10 octaves in frequency, while the visible range of light only covers around 1 octave. Used well, sonification can provide a new perspective on your data, and potentially new insights into your data that are missed with standard, visual approaches. -**2) Unique Applications**: There are applications for which sonification is uniquely suited. Sound is a time varying signal so can represent *time-series data* intuitively, or data with rapidly or subtly varying rates of change. The human ear can cover about 10 octaves in frequency, while the visible range of light only covers around 1 octave. Used well, sonification can provide a new perspective on your data, and potentially new insights into your data that are missed with standard, visual approaches. - **3) Accessibility**: For many people, visualisation is inherently inaccessible. Around 4% of the world population are visually impaired (VI) according to `IAPB statistics `_ [1]. Combining visualisation and sonification techniques ensures broader accessibility of science and data presentation. Strauss approach diff --git a/src/strauss/presets/sampler/default.yml b/src/strauss/presets/sampler/default.yml index 43bfca2..c96dcd0 100644 --- a/src/strauss/presets/sampler/default.yml +++ b/src/strauss/presets/sampler/default.yml @@ -124,7 +124,7 @@ _meta: _doc: >- Controls for the `'Low Frequency Oscillator'` (LFO) used to modulate pitch of notes at rhythmic frequencies. In music this is often referred to a `'vibrato'`. - use: switch feature on or off + use: Switch feature on or off wave: Type of waveform used for the oscillator. As with `Synthesizer` oscillators can be sawtooth (`'saw'`), square (`'square'`), sinusoid (`'sine'`), triangle (`'tri'`) or noise (`'noise'`). amount: the amplitude of the maximal pitch oscillation from the underlying pitch @@ -149,7 +149,7 @@ _meta: use: switch volume LFO effects on or off wave: Type of waveform used for the oscillator. As with `Synthesizer` oscillators can be sawtooth (`'saw'`), square (`'square'`), sinusoid (`'sine'`), triangle (`'tri'`) or noise (`'noise'`). - amount: the amplitude of the maximal pitch oscillation from the underlying pitch + amount: The amplitude of the maximal pitch oscillation from the underlying pitch freq: Base frequency of the LFO oscillations. freq_shift: Shift relative to the base LFO frequency phase: The phase of the LFO oscillations, defined in terms of fraction of a whole cycle. @@ -169,12 +169,13 @@ _meta: pitch: >- Default pitch selection (used by all generators) azimuth: >- - azimuth coordinate for spatialising audio into differing channels + Azimuth coordinate for spatialising audio into differing channels polar: >- - polar coordinate for spatialising audio into differing channels + Polar coordinate for spatialising audio into differing channels pitch_hi: >- - pitch range maximum in semitones + Pitch range maximum in semitones pitch_lo: >- - pitch range minimum in semitones + Pitch range minimum in semitones pitch_shift: >- - default shift in semitones \ No newline at end of file + Default shift in semitones + diff --git a/src/strauss/presets/synth/default.yml b/src/strauss/presets/synth/default.yml index 1fdc27c..956a6c0 100644 --- a/src/strauss/presets/synth/default.yml +++ b/src/strauss/presets/synth/default.yml @@ -111,27 +111,27 @@ pitch_shift: 0. _meta: _doc: >- - The `Synth` generator type can be used to synthesise sound using mathmatically + The `Synth` generator type can be used to synthesise sound using mathematically generated waveforms or `oscillators`. The preset can be used to modify the relative frequency, phase and amplitude of these oscillators. name: Name of the preset description: Full description of the preset purpose and parameters. - oscillators: >- - Oscillator information. Oscillator are denoted osc with n=3 by default. oscillators: + _doc: >- + Oscillator information. Oscillator are denoted osc with n=3 by default. osc1: form: Type of waveform used for oscillator 1, choose from ['saw', 'square', 'sine', 'tri', 'noise'] - level: Intrinsic volume + level: Amplitude of the oscillator from 0 to 1, contolling maximum volume of the note detune: Change in tuning as a percentage of the input frequency phase: The phase of the oscillator, defined in terms of fraction of a whole cycle osc2: form: Type of waveform used for oscillator 2, choose from ['saw', 'square', 'sine', 'tri', 'noise'] - level: Intrinsic volume + level: Amplitude of the oscillator from 0 to 1, contolling maximum volume of the note detune: Change in tuning as a percentage of the input frequency phase: The phase of the oscillator, defined in terms of fraction of a whole cycle osc3: form: Type of waveform used for oscillator 3, choose from ['saw', 'square', 'sine', 'tri', 'noise'] - level: Intrinsic volume + level: Amplitude of the oscillator from 0 to 1, contolling maximum volume of the note detune: Change in tuning as a percentage of the input frequency phase: The phase of the oscillator, defined in terms of fraction of a whole cycle @@ -141,10 +141,10 @@ _meta: Define the note volume envelope applied to the samples. A,D,S & R correspond to 'attack', 'decay', 'sustain' and 'release'. volume_envelope: - A: Attack - D: Decay - S: Sustain - R: Release + A: Attack, how long it takes for a tone to sound after it’s triggered + D: Decay, how long it takes for the tone’s attack to die down after it’s triggered + S: Sustain, the volume/level of the sound while it’s being triggered + R: Release, how long the tone takes to go silent after the trigger is released Ac: >- "Curvature" of the attack portion of a note. Values from -1 to 1, positive indicates increases quickly then slow, negative slowly then quick. a value of 0 is a linear attack, increasing in volume at a constant rate. @@ -171,14 +171,14 @@ _meta: wave: >- Type of waveform used for the oscillator. As with `Synthesizer` oscillators can be sawtooth (`'saw'`), square (`'square'`), sinusoid (`'sine'`), triangle (`'tri'`) or noise (`'noise'`). - amount: the amplitude of the maximal pitch oscillation from the underlying pitch + amount: The amplitude of the maximal pitch oscillation from the underlying pitch freq: Base frequency of the LFO oscillations. - freq_shift: shift relative to the bae LFO frequency + freq_shift: Shift relative to the bae LFO frequency phase: The phase of the LFO oscillations, defined in terms of fraction of a whole cycle - A: Attack - D: Decay - S: Sustain - R: Release + A: Attack, how long it takes for a tone to sound after it’s triggered + D: Decay, how long it takes for the tone’s attack to die down after it’s triggered + S: Sustain, the volume/level of the sound while it’s being triggered + R: Release, how long the tone takes to go silent after the trigger is released Ac: >- "Curvature" of the attack portion of a note. Values from -1 to 1, positive indicates increases quickly then slow, negative slowly then quick. a value of 0 is a linear attack, increasing in volume at a constant rate. Dc: >- @@ -199,10 +199,10 @@ _meta: freq: Base frequency of the LFO oscillations. freq_shift: shift relative to the bae LFO frequency phase: The phase of the LFO oscillations, defined in terms of fraction of a whole cycle - A: Attack - D: Decay - S: Sustain - R: Release + A: Attack, how long it takes for a tone to sound after it’s triggered + D: Decay, how long it takes for the tone’s attack to die down after it’s triggered + S: Sustain, the volume/level of the sound while it’s being triggered + R: Release, how long the tone takes to go silent after the trigger is released Ac: >- "Curvature" of the attack portion of a note. Values from -1 to 1, positive indicates increases quickly then slow, negative slowly then quick. a value of 0 is a linear attack, increasing in volume at a constant rate. Dc: >- @@ -215,13 +215,13 @@ _meta: pitch: >- Default pitch selection azimuth: >- - azimuth coordinate for spatialising audio into differing channels + Azimuth coordinate for spatialising audio into differing channels polar: >- - polar coordinate for spatialising audio into differing channels + Polar coordinate for spatialising audio into differing channels pitch_hi: >- - pitch range maximum in semitones + Pitch range maximum in semitones pitch_lo: >- - pitch range minimum in semitones + Pitch range minimum in semitones pitch_shift: >- - default shift in semitones + Default shift in semitones diff --git a/src/strauss/sources.py b/src/strauss/sources.py index f949a62..1fb720b 100644 --- a/src/strauss/sources.py +++ b/src/strauss/sources.py @@ -15,7 +15,7 @@ param_lim_dict (:obj:`dict`): Dictionary combining `mappable` (keys) and `param_limits` (items). -To do: +Todo: * Store mappable, evolvable and parameter ranges in YAML files (cleaner). * Specialised Event and Object child classes (eg. spectralisation). """ From 6e9d9e0d5764cabfd9258a0fcd9dd6f42c91ae10 Mon Sep 17 00:00:00 2001 From: James Trayford Date: Wed, 18 Dec 2024 12:59:33 +0000 Subject: [PATCH 69/86] add the paper to the repo... --- docs/elements.rst | 4 +- paper.md | 116 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 paper.md diff --git a/docs/elements.rst b/docs/elements.rst index f3deadc..1d9e1ba 100644 --- a/docs/elements.rst +++ b/docs/elements.rst @@ -76,14 +76,14 @@ This class instead **generates audio additively using mathematical functions** v A special case of the :code:`Synthesiser`, this generator synthesises sound from an input spectrum, via an inverse Fast Fourier Transform (IFFT), with randomised phases. The user can specify the audible frequency range that the ‘spectralised’ audio is mapped over. -.. _channels +.. _channels: Channels Class ************** Once sound has been produced for each :code: `source`, the final step is to mix the audio down into some multi-channel audio format. The :code:`Channels` class essentially represents a bank of virtual microphones, with 3D antennae patterns, that each correspond to a channel in the output file. -.. _sonification +.. _sonification: Sonification Class ****************** diff --git a/paper.md b/paper.md new file mode 100644 index 0000000..3eeb87e --- /dev/null +++ b/paper.md @@ -0,0 +1,116 @@ +--- +title: 'STRAUSS: Sonification Tools & Resources for Analysis Using Sound Synthesis' +tags: + - Python + - sonification + - data inspection + - astronomy +authors: + - name: James W. Trayford + orcid: 0000-0003-1530-1634 + corresponding: true # (This is how to denote the corresponding author) + equal-contrib: false + affiliation: 1 # (Multiple affiliations must be quoted) + - name: Samantha Youles + equal-contrib: true # (This is how you can denote equal contributions between multiple authors) + affiliation: 1 + - name: Chris Harrison + affiliation: 2 + equal-contrib: true # (This is how you can denote equal contributions between multiple authors) +affiliations: + - name: Institute of Cosmology and Gravitation, University of Portsmouth, Dennis Sciama Building, Burnaby Road, Portsmouth PO1 3FX, UK + index: 1 + + - name: School of Mathematics, Statistics and Physics, Newcastle University, NE1 7RU, UK + index: 2 +date: 23 August 2024 +bibliography: paper.bib + +# Optional fields if submitting to a AAS journal too, see this blog post: +# https://blog.joss.theoj.org/2018/12/a-new-collaboration-with-aas-publishing +aas-doi: 10.3847/xxxxx <- update this with the DOI from AAS once you know it. +aas-journal: Astrophysical Journal <- The name of the AAS journal. +--- + +# Summary + +Sonification, + +The forces on stars, galaxies, and dark matter under external gravitational +fields lead to the dynamical evolution of structures in the universe. The orbits +of these bodies are therefore key to understanding the formation, history, and +future state of galaxies. The field of "galactic dynamics," which aims to model +the gravitating components of galaxies to study their structure and evolution, +is now well-established, commonly taught, and frequently used in astronomy. +Aside from toy problems and demonstrations, the majority of problems require +efficient numerical tools, many of which require the same base code (e.g., for +performing numerical orbit integration). + +# Statement of need + +`Gala` is an Astropy-affiliated Python package for galactic dynamics. Python +enables wrapping low-level languages (e.g., C) for speed without losing +flexibility or ease-of-use in the user-interface. The API for `Gala` was +designed to provide a class-based and user-friendly interface to fast (C or +Cython-optimized) implementations of common operations such as gravitational +potential and force evaluation, orbit integration, dynamical transformations, +and chaos indicators for nonlinear dynamics. `Gala` also relies heavily on and +interfaces well with the implementations of physical units and astronomical +coordinate systems in the `Astropy` package [@astropy] (`astropy.units` and +`astropy.coordinates`). + +`Gala` was designed to be used by both astronomical researchers and by +students in courses on gravitational dynamics or astronomy. It has already been +used in a number of scientific publications [@Pearson:2017] and has also been +used in graduate courses on Galactic dynamics to, e.g., provide interactive +visualizations of textbook material [@Binney:2008]. The combination of speed, +design, and support for Astropy functionality in `Gala` will enable exciting +scientific explorations of forthcoming data releases from the *Gaia* mission +[@gaia] by students and experts alike. + +# Mathematics + +Single dollars ($) are required for inline mathematics e.g. $f(x) = e^{\pi/x}$ + +Double dollars make self-standing equations: + +$$\Theta(x) = \left\{\begin{array}{l} +0\textrm{ if } x < 0\cr +1\textrm{ else} +\end{array}\right.$$ + +You can also use plain \LaTeX for equations +\begin{equation}\label{eq:fourier} +\hat f(\omega) = \int_{-\infty}^{\infty} f(x) e^{i\omega x} dx +\end{equation} +and refer to \autoref{eq:fourier} from text. + +# Citations + +Citations to entries in paper.bib should be in +[rMarkdown](http://rmarkdown.rstudio.com/authoring_bibliographies_and_citations.html) +format. + +If you want to cite a software repository URL (e.g. something on GitHub without a preferred +citation) then you can do it with the example BibTeX entry below for @fidgit. + +For a quick reference, the following citation commands can be used: +- `@author:2001` -> "Author et al. (2001)" +- `[@author:2001]` -> "(Author et al., 2001)" +- `[@author1:2001; @author2:2001]` -> "(Author1 et al., 2001; Author2 et al., 2002)" + +# Figures + +Figures can be included like this: +![Caption for example figure.\label{fig:example}](figure.png) +and referenced from text using \autoref{fig:example}. + +Figure sizes can be customized by adding an optional second parameter: +![Caption for example figure.](figure.png){ width=20% } + +# Acknowledgements + +We acknowledge contributions from Brigitta Sipocz, Syrtis Major, and Semyeong +Oh, and support from Kathryn Johnston during the genesis of this project. + +# References~ \ No newline at end of file From 225bd8980f1398460dbb569a173d8aedfdb6c7f5 Mon Sep 17 00:00:00 2001 From: James Trayford Date: Wed, 18 Dec 2024 13:00:39 +0000 Subject: [PATCH 70/86] some post-merge fixes --- docs/conf.py | 2 + docs/yamls_to_tables.py | 6 +- src/strauss/presets/sampler/default.yml | 51 ++++---- .../presets/sampler/ranges/default.yml | 51 +++++++- src/strauss/presets/spec/default.yml | 111 +++++++++--------- src/strauss/presets/spec/ranges/default.yml | 49 +++++++- src/strauss/presets/synth/default.yml | 46 ++++---- src/strauss/presets/synth/ranges/default.yml | 52 +++++++- 8 files changed, 257 insertions(+), 111 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index f13325c..f4846fd 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -67,6 +67,8 @@ # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] +myst_footnote_transition = False + # html_theme_options = { # 'page_width': '1200px' # } diff --git a/docs/yamls_to_tables.py b/docs/yamls_to_tables.py index 5343dd2..af67837 100644 --- a/docs/yamls_to_tables.py +++ b/docs/yamls_to_tables.py @@ -63,6 +63,8 @@ def yaml_traverse(metadict, valdict, rdict, headlev=1): return with open('docs/tables.md', 'w') as outfile: + l = len(glob(str(p))) + i = 0 for f in glob(str(p)): p = Path(f) ydat = read_yaml(p) @@ -73,6 +75,8 @@ def yaml_traverse(metadict, valdict, rdict, headlev=1): # print('---') outfile.write(f"\n# {generators[p.parents[0].name]}\n") outfile.write(ystr) - outfile.write('---') + if i < l-1: + outfile.write('---') + i += 1 # --- hide: stop --- diff --git a/src/strauss/presets/sampler/default.yml b/src/strauss/presets/sampler/default.yml index c96dcd0..a766101 100644 --- a/src/strauss/presets/sampler/default.yml +++ b/src/strauss/presets/sampler/default.yml @@ -101,11 +101,12 @@ _meta: If looping, ending point of loop in seconds. If loop_end is longer than the sample, clip to end of the sample. volume_envelope: _doc: >- - Define the note volume envelope applied to the samples - A: Attack, how long it takes for a tone to sound after it’s triggered - D: Decay, how long it takes for the tones attack to die down after it’s triggered - S: Sustain, the volume/level of the sound while it’s being triggered - R: Release, how long the tone takes to go silent after the trigger is released + Define the note volume envelope applied to the samples. _'ADSR'_ is a common parametrisation in sound synthesis, + find out more e.g. [at this link](https://blg.native-instruments.com/adsr-explained/) + A: Attack, how long it takes for a sound to rise to 100% of the `level` after it’s triggered. + D: Decay, how long it takes for the sounds volume to die down to the `Sustain` value after the `Attack` period. + S: Sustain, the volume level (from 0 to 1.0) maintained after the `Decay` period, while the note is held. + R: Release, how long the tone takes to finally die away once the note is released. Ac: Curvature of the attack portion of a note. Values from -1 to 1, positive indicates increases quickly then slow, negative slowly then quick. a value of 0 is a linear attack, increasing in volume at a constant rate. Dc: Curvature of the Decay portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, @@ -123,7 +124,7 @@ _meta: pitch_lfo: _doc: >- Controls for the `'Low Frequency Oscillator'` (LFO) used to modulate pitch of notes at rhythmic - frequencies. In music this is often referred to a `'vibrato'`. + frequencies. In music this is often referred to as `'vibrato'`. use: Switch feature on or off wave: Type of waveform used for the oscillator. As with `Synthesizer` oscillators can be sawtooth (`'saw'`), square (`'square'`), sinusoid (`'sine'`), triangle (`'tri'`) or noise (`'noise'`). @@ -131,17 +132,17 @@ _meta: freq: Base frequency of the LFO oscillations. freq_shift: Shift relative to the base LFO frequency. phase: The phase of the LFO oscillations, defined in terms of fraction of a whole cycle - A: Attack, how long it takes for a tone to sound after it’s triggered - D: Decay, how long it takes for the tone’s attack to die down after it’s triggered - S: Sustain, the volume/level of the sound while it’s being triggered - R: Release, how long the tone takes to go silent after the trigger is released + A: Attack, how long it takes for the LFO depth to rise to 100% of the `level` after it’s triggered. + D: Decay, how long it takes for the LFO depth to die down to the `Sustain` value after the `Attack` period. + S: Sustain, the LFO depth (from 0 to 1.0) maintained after the `Decay` period, while the note is held. + R: Release, how long LFO depth takes to finally die to 0 once the note is released. Ac: Curvature of the attack portion of a note. Values from -1 to 1, positive indicates increases quickly then slow, negative slowly then quick. a value of 0 is a linear attack, increasing in volume at a constant rate. Dc: Curvature of the Decay portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, negative slowly then quick. a value of 0 is a linear decay, decreasing in volume at a constant rate. Rc: Curvature of the release portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, negative slowly then quick. a value of 0 is a linear release, decreasing in volume at a constant rate. - level: Total amplitude level of the envelope from 0 to 1, contolling maximum volume of the note. + level: Total level of the envelope from 0 to 1, contolling maximum depth of the LFO. volume_lfo: _doc: >- Controls for the `'Low Frequency Oscillator'` (LFO) used to modulate volume of notes at rhythmic @@ -149,21 +150,24 @@ _meta: use: switch volume LFO effects on or off wave: Type of waveform used for the oscillator. As with `Synthesizer` oscillators can be sawtooth (`'saw'`), square (`'square'`), sinusoid (`'sine'`), triangle (`'tri'`) or noise (`'noise'`). - amount: The amplitude of the maximal pitch oscillation from the underlying pitch + amount: The amplitude of the maximal volume oscillation from the underlying pitch freq: Base frequency of the LFO oscillations. freq_shift: Shift relative to the base LFO frequency phase: The phase of the LFO oscillations, defined in terms of fraction of a whole cycle. - A: Attack, how long it takes for a tone to sound after it’s triggered - D: Decay, how long it takes for the tone’s attack to die down after it’s triggered - S: Sustain, the volume/level of the sound while it’s being triggered - R: Release, how long the tone takes to go silent after the trigger is released - Ac: Curvature of the attack portion of a note. Values from -1 to 1, positive indicates increases quickly then slow, - negative slowly then quick. a value of 0 is a linear attack, increasing in volume at a constant rate. - Dc: Curvature of the Decay portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, - negative slowly then quick. a value of 0 is a linear decay, decreasing in volume at a constant rate. - Rc: Curvature of the release portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, - negative slowly then quick. a value of 0 is a linear release, decreasing in volume at a constant rate. - level: Total amplitude level of the envelope from 0 to 1, contolling maximum volume of the note + A: Attack, how long it takes for the LFO depth to rise to 100% of the `level` after it’s triggered. + D: Decay, how long it takes for the LFO depth to die down to the `Sustain` value after the `Attack` period. + S: Sustain, the LFO depth (from 0 to 1.0) maintained after the `Decay` period, while the note is held. + R: Release, how long LFO depth takes to finally die to 0 once the note is released. + Ac: >- + Curvature of the attack portion of a note. Values from -1 to 1, positive indicates increases quickly then slow, + negative slowly then quick. a value of 0 is a linear attack, increasing in volume at a constant rate. + Dc: >- + Curvature of the Decay portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, + negative slowly then quick. a value of 0 is a linear decay, decreasing in volume at a constant rate. + Rc: >- + Curvature of the release portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, + negative slowly then quick. a value of 0 is a linear release, decreasing in volume at a constant rate. + level: Total level of the envelope from 0 to 1, contolling maximum depth of the LFO. volume: >- Master Volume of generator. pitch: >- @@ -178,4 +182,3 @@ _meta: Pitch range minimum in semitones pitch_shift: >- Default shift in semitones - diff --git a/src/strauss/presets/sampler/ranges/default.yml b/src/strauss/presets/sampler/ranges/default.yml index 723333a..c5ee0b6 100644 --- a/src/strauss/presets/sampler/ranges/default.yml +++ b/src/strauss/presets/sampler/ranges/default.yml @@ -23,7 +23,54 @@ volume_envelope: Dc_unit: 'unitless' Rc_unit: 'unitless' level_unit: 'unitless' - + +volume_lfo: + A: [1e-2, 20.] + D: [1e-2, 20.] + S: [0., 1.] + R: [1e-2, 20] + Ac: [-1. ,1.] + Dc: [-1. ,1.] + Rc: [-1. ,1.] + level: [0., 1.] + amount: [0, 1] + freq: [1., 12.] + freq_shift: [0., 3.] + A_unit: 'seconds' + D_unit: 'seconds' + S_unit: 'unitless' + R_unit: 'seconds' + Ac_unit: 'unitless' + Dc_unit: 'unitless' + Rc_unit: 'unitless' + level_unit: 'unitless' + freq_unit: 'Hz' + freq_shift_unit: 'octave' + +pitch_lfo: + A: [1e-2, 20.] + D: [1e-2, 20.] + S: [0., 1.] + R: [1e-2, 20] + Ac: [-1. ,1.] + Dc: [-1. ,1.] + Rc: [-1. ,1.] + level: [0., 1.] + amount: [0, 2] + freq: [1., 12.] + freq_shift: [0., 3.] + A_unit: 'seconds' + D_unit: 'seconds' + S_unit: 'unitless' + R_unit: 'seconds' + Ac_unit: 'unitless' + Dc_unit: 'unitless' + Rc_unit: 'unitless' + level_unit: 'unitless' + amount_unit: 'semitones' + freq_unit: 'Hz' + freq_shift_unit: 'octave' + # filter cutoff cutoff: [0., 1.] cutoff_unit: 'unitless' @@ -43,7 +90,7 @@ polar: [0.,1.] polar_unit: 'unitless' # pitch range and default shift in semitones -pitch_shift: [0., 36.] +pitch_shift: [0., 24.] pitch_shift_unit: 'semitones' _meta: diff --git a/src/strauss/presets/spec/default.yml b/src/strauss/presets/spec/default.yml index 9da1f43..d18367e 100644 --- a/src/strauss/presets/spec/default.yml +++ b/src/strauss/presets/spec/default.yml @@ -106,30 +106,29 @@ _meta: any frequency range to an audible range, and generating a representative sound signal (using an [IFFT approach](https://docs.scipy.org/doc/scipy/reference/generated/scipy.fft.ifft.html)). In this approach, narrow spikes become tones at their frequency position, a sloped continuum - becomes coloured noise, etc. + becomes coloured noise, etc. _Note_: this generator must take a `spectrum` input, as an array, + representing 'flux' or 'power' values of a spectrum, arranged from lowest to highest frequncy. name: name for a particular preset. description: full description of what a preset does. note_length: >- Numerical note length volume_envelope: >- - Define the note volume envelope applied to the samples. - A,D,S & R correspond to 'attack', 'decay', 'sustain' and 'release'. volume_envelope: - A: Attack - D: Decay - S: Sustain - R: Release - Ac: >- - "Curvature" of the attack portion of a note. Values from -1 to 1, positive indicates increases quickly then slow, + _doc : >- + Define the note volume envelope applied to the samples A,D,S & R correspond to 'attack', + 'decay', 'sustain' and 'release'. _'ADSR'_ is a common parametrisation in sound synthesis, + Find out more e.g. [at this link](https://blg.native-instruments.com/adsr-explained/) + A: Attack, how long it takes for a sound to rise to 100% of the `level` after it’s triggered. + D: Decay, how long it takes for the sounds volume to die down to the `Sustain` value after the `Attack` period. + S: Sustain, the volume level (from 0 to 1.0) maintained after the `Decay` period, while the note is held. + R: Release, how long the tone takes to finally die away once the note is released. + Ac: Curvature of the attack portion of a note. Values from -1 to 1, positive indicates increases quickly then slow, negative slowly then quick. a value of 0 is a linear attack, increasing in volume at a constant rate. - Dc: >- - "Curvature" of the Decay portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, + Dc: Curvature of the Decay portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, negative slowly then quick. a value of 0 is a linear decay, decreasing in volume at a constant rate. - Rc: >- - "Curvature" of the release portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, + Rc: Curvature of the release portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, negative slowly then quick. a value of 0 is a linear release, decreasing in volume at a constant rate. - - level: Total amplitude level of the envelope from 0 to 1, contolling maximum volume of the note + level: Total amplitude level of the envelope from 0 to 1, contolling maximum volume of the note. filter: >- Do we apply a frequency filter to the audio signal? This can be used to change the balance of frequencies and manipulate the 'timbre' of a note @@ -140,66 +139,58 @@ _meta: specified between 0 and 1 as a fraction of the audible range of notes we can hear (E0 to D#10). pitch_lfo: _doc: >- - Controls for the 'Low Frequency Oscillator' (LFO) used to modulate pitch of notes at rhythmic - frequencies. In music this is often referred to a 'vibrato' - use: Switch pitch LFO effects on or off - wave: >- - Type of waveform used for the oscillator. As with `Synthesizer` oscillators can be sawtooth + Controls for the `'Low Frequency Oscillator'` (LFO) used to modulate pitch of notes at rhythmic + frequencies. In music this is often referred to as `'vibrato'`. + use: Switch feature on or off + wave: Type of waveform used for the oscillator. As with `Synthesizer` oscillators can be sawtooth (`'saw'`), square (`'square'`), sinusoid (`'sine'`), triangle (`'tri'`) or noise (`'noise'`). amount: the amplitude of the maximal pitch oscillation from the underlying pitch freq: Base frequency of the LFO oscillations. - freq_shift: shift relative to the bae LFO frequency + freq_shift: Shift relative to the base LFO frequency. phase: The phase of the LFO oscillations, defined in terms of fraction of a whole cycle - A: Attack - D: Decay - S: Sustain - R: Release - Ac: >- - "Curvature" of the attack portion of a note. Values from -1 to 1, positive indicates increases quickly then slow, + A: Attack, how long it takes for the LFO depth to rise to 100% of the `level` after it’s triggered. + D: Decay, how long it takes for the LFO depth to die down to the `Sustain` value after the `Attack` period. + S: Sustain, the LFO depth (from 0 to 1.0) maintained after the `Decay` period, while the note is held. + R: Release, how long LFO depth takes to finally die to 0 once the note is released. + Ac: Curvature of the attack portion of a note. Values from -1 to 1, positive indicates increases quickly then slow, negative slowly then quick. a value of 0 is a linear attack, increasing in volume at a constant rate. - Dc: >- - "Curvature" of the Decay portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, + Dc: Curvature of the Decay portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, negative slowly then quick. a value of 0 is a linear decay, decreasing in volume at a constant rate. - Rc: >- - "Curvature" of the release portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, + Rc: Curvature of the release portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, negative slowly then quick. a value of 0 is a linear release, decreasing in volume at a constant rate. + level: Total level of the envelope from 0 to 1, contolling maximum depth of the LFO. - level: Total amplitude level of the envelope from 0 to 1, contolling maximum volume of the note volume_lfo: _doc: >- - Controls for the 'Low Frequency Oscillator' (LFO) used to modulate volume of notes at rhythmic - frequencies. In music this is often referred to as 'tremolo' - use: Switch pitch LFO effects on or off - wave: >- - Type of waveform used for the oscillator. As with `Synthesizer` oscillators can be sawtooth + Controls for the `'Low Frequency Oscillator'` (LFO) used to modulate volume of notes at rhythmic + frequencies. In music this is often referred to as `'tremolo'`. + use: switch volume LFO effects on or off + wave: Type of waveform used for the oscillator. As with `Synthesizer` oscillators can be sawtooth (`'saw'`), square (`'square'`), sinusoid (`'sine'`), triangle (`'tri'`) or noise (`'noise'`). - amount: the amplitude of the maximal pitch oscillation from the underlying pitch + amount: The amplitude of the maximal volume oscillation from the underlying pitch freq: Base frequency of the LFO oscillations. - freq_shift: shift relative to the bae LFO frequency - phase: The phase of the LFO oscillations, defined in terms of fraction of a whole cycle - A: Attack - D: Decay - S: Sustain - R: Release - Ac: >- - "Curvature" of the attack portion of a note. Values from -1 to 1, positive indicates increases quickly then slow, + freq_shift: Shift relative to the base LFO frequency + phase: The phase of the LFO oscillations, defined in terms of fraction of a whole cycle. + A: Attack, how long it takes for the LFO depth to rise to 100% of the `level` after it’s triggered. + D: Decay, how long it takes for the LFO depth to die down to the `Sustain` value after the `Attack` period. + S: Sustain, the LFO depth (from 0 to 1.0) maintained after the `Decay` period, while the note is held. + R: Release, how long LFO depth takes to finally die to 0 once the note is released. + Ac: Curvature of the attack portion of a note. Values from -1 to 1, positive indicates increases quickly then slow, negative slowly then quick. a value of 0 is a linear attack, increasing in volume at a constant rate. - Dc: >- - "Curvature" of the Decay portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, + Dc: Curvature of the Decay portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, negative slowly then quick. a value of 0 is a linear decay, decreasing in volume at a constant rate. - Rc: >- - "Curvature" of the release portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, + Rc: Curvature of the release portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, negative slowly then quick. a value of 0 is a linear release, decreasing in volume at a constant rate. - level: Total amplitude level of the envelope from 0 to 1, contolling maximum volume of the note + level: Total level of the envelope from 0 to 1, contolling maximum depth of the LFO. volume: >- Master Volume of generator interpolation_type: >- How to interpolate and resample points in the spectrum. "sample": interpolate spectrum values directly; "preserve_power": integrate, interpolate then differentiate to avoid missing power in narrow features. regen_phases: >- - For an evolving spectrum, do we regenerate phases for each buffer, or keep the same? + Boolean, for an evolving spectrum, do we regenerate phases for each buffer, or keep the same? fit_spec_multiples: >- - Whether or not to generate IFFT such that the spectrum sample points are hit exactly. + Boolean, whether or not to generate IFFT such that the spectrum sample points are hit exactly. min_freq: >- Minimum sound frequency used to represent the data max_freq: >- @@ -207,12 +198,16 @@ _meta: pitch: >- Default pitch selection (used by all generators) azimuth: >- - azimuth coordinate for spatialising audio into differing channels + Azimuth coordinate for spatialising audio into differing channels polar: >- - polar coordinate for spatialising audio into differing channels + Polar coordinate for spatialising audio into differing channels pitch_hi: >- - pitch range maximum in semitones + Pitch range maximum in semitones pitch_lo: >- - pitch range minimum in semitones + Pitch range minimum in semitones pitch_shift: >- - default shift in semitones + Default shift in semitones + equal_loudness_normalisation: >- + Boolean, whether or not the spectrum is _Equalised_ such that single tones at different frequencies should sound equally + loud (to the average listener, at a default loudness of 70 phon), following [ISO:226](https://www.iso.org/standard/83117.html). + \ No newline at end of file diff --git a/src/strauss/presets/spec/ranges/default.yml b/src/strauss/presets/spec/ranges/default.yml index d5effd3..f022e5b 100644 --- a/src/strauss/presets/spec/ranges/default.yml +++ b/src/strauss/presets/spec/ranges/default.yml @@ -23,7 +23,54 @@ volume_envelope: Dc_unit: 'unitless' Rc_unit: 'unitless' level_unit: 'unitless' - + +volume_lfo: + A: [1e-2, 20.] + D: [1e-2, 20.] + S: [0., 1.] + R: [1e-2, 20] + Ac: [-1. ,1.] + Dc: [-1. ,1.] + Rc: [-1. ,1.] + level: [0., 1.] + amount: [0, 1] + freq: [1., 12.] + freq_shift: [0., 3.] + A_unit: 'seconds' + D_unit: 'seconds' + S_unit: 'unitless' + R_unit: 'seconds' + Ac_unit: 'unitless' + Dc_unit: 'unitless' + Rc_unit: 'unitless' + level_unit: 'unitless' + freq_unit: 'Hz' + freq_shift_unit: 'octave' + +pitch_lfo: + A: [1e-2, 20.] + D: [1e-2, 20.] + S: [0., 1.] + R: [1e-2, 20] + Ac: [-1. ,1.] + Dc: [-1. ,1.] + Rc: [-1. ,1.] + level: [0., 1.] + amount: [0, 2] + freq: [1., 12.] + freq_shift: [0., 3.] + A_unit: 'seconds' + D_unit: 'seconds' + S_unit: 'unitless' + R_unit: 'seconds' + Ac_unit: 'unitless' + Dc_unit: 'unitless' + Rc_unit: 'unitless' + level_unit: 'unitless' + amount_unit: 'semitones' + freq_unit: 'Hz' + freq_shift_unit: 'octave' + # filter cutoff cutoff: [0., 1.] cutoff_unit: 'unitless' diff --git a/src/strauss/presets/synth/default.yml b/src/strauss/presets/synth/default.yml index 956a6c0..aa4d518 100644 --- a/src/strauss/presets/synth/default.yml +++ b/src/strauss/presets/synth/default.yml @@ -118,7 +118,10 @@ _meta: description: Full description of the preset purpose and parameters. oscillators: _doc: >- - Oscillator information. Oscillator are denoted osc with n=3 by default. + Oscillator information. Oscillator are denoted `osc`, allowing an arbitrary number + of oscillators to be combined to make the intrtinsic tone. The `default` preset + demontrates this using 3 sawtooth oscillators, slightly detuned from each other to + create a 'detuned saw' sound, hence the identically structured oscillators below. osc1: form: Type of waveform used for oscillator 1, choose from ['saw', 'square', 'sine', 'tri', 'noise'] level: Amplitude of the oscillator from 0 to 1, contolling maximum volume of the note @@ -141,21 +144,20 @@ _meta: Define the note volume envelope applied to the samples. A,D,S & R correspond to 'attack', 'decay', 'sustain' and 'release'. volume_envelope: - A: Attack, how long it takes for a tone to sound after it’s triggered - D: Decay, how long it takes for the tone’s attack to die down after it’s triggered - S: Sustain, the volume/level of the sound while it’s being triggered - R: Release, how long the tone takes to go silent after the trigger is released - Ac: >- - "Curvature" of the attack portion of a note. Values from -1 to 1, positive indicates increases quickly then slow, + _doc: >- + Define the note volume envelope applied to the samples. _'ADSR'_ is a common parametrisation in sound synthesis, + find out more e.g. [at this link](https://blg.native-instruments.com/adsr-explained/) + A: Attack, how long it takes for a sound to rise to 100% of the `level` after it’s triggered. + D: Decay, how long it takes for the sounds volume to die down to the `Sustain` value after the `Attack` period. + S: Sustain, the volume level (from 0 to 1.0) maintained after the `Decay` period, while the note is held. + R: Release, how long the tone takes to finally die away once the note is released. + Ac: Curvature of the attack portion of a note. Values from -1 to 1, positive indicates increases quickly then slow, negative slowly then quick. a value of 0 is a linear attack, increasing in volume at a constant rate. - Dc: >- - "Curvature" of the Decay portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, + Dc: Curvature of the Decay portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, negative slowly then quick. a value of 0 is a linear decay, decreasing in volume at a constant rate. - Rc: >- - "Curvature" of the release portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, + Rc: Curvature of the release portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, negative slowly then quick. a value of 0 is a linear release, decreasing in volume at a constant rate. - - level: Total amplitude level of the envelope from 0 to 1, contolling maximum volume of the note + level: Total level of the envelope from 0 to 1, contolling maximum depth of the LFO. filter: >- Do we apply a frequency filter to the audio signal? This can be used to change the balance of frequencies and manipulate the 'timbre' of a note filter_type: >- @@ -166,7 +168,7 @@ _meta: pitch_lfo: _doc: >- Controls for the 'Low Frequency Oscillator' (LFO) used to modulate pitch of notes at rhythmic - frequencies. In music this is often referred to a 'vibrato' + frequencies. In music this is often referred to as 'vibrato' use: Switch pitch LFO effects on or off wave: >- Type of waveform used for the oscillator. As with `Synthesizer` oscillators can be sawtooth @@ -175,17 +177,16 @@ _meta: freq: Base frequency of the LFO oscillations. freq_shift: Shift relative to the bae LFO frequency phase: The phase of the LFO oscillations, defined in terms of fraction of a whole cycle - A: Attack, how long it takes for a tone to sound after it’s triggered - D: Decay, how long it takes for the tone’s attack to die down after it’s triggered - S: Sustain, the volume/level of the sound while it’s being triggered - R: Release, how long the tone takes to go silent after the trigger is released + A: Attack, how long it takes for the LFO depth to rise to 100% of the `level` after it’s triggered. + D: Decay, how long it takes for the LFO depth to die down to the `Sustain` value after the `Attack` period. + S: Sustain, the LFO depth (from 0 to 1.0) maintained after the `Decay` period, while the note is held. + R: Release, how long LFO depth takes to finally die to 0 once the note is released. Ac: >- "Curvature" of the attack portion of a note. Values from -1 to 1, positive indicates increases quickly then slow, negative slowly then quick. a value of 0 is a linear attack, increasing in volume at a constant rate. Dc: >- "Curvature" of the Decay portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, negative slowly then quick. a value of 0 is a linear decay, decreasing in volume at a constant rate. Rc: >- "Curvature" of the release portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, negative slowly then quick. a value of 0 is a linear release, decreasing in volume at a constant rate. - level: Total amplitude level of the envelope from 0 to 1, contolling maximum volume of the note volume_lfo: _doc: >- @@ -195,7 +196,7 @@ _meta: wave: >- Type of waveform used for the oscillator. As with `Synthesizer` oscillators can be sawtooth (`'saw'`), square (`'square'`), sinusoid (`'sine'`), triangle (`'tri'`) or noise (`'noise'`). - amount: the amplitude of the maximal pitch oscillation from the underlying pitch + amount: the amplitude of the maximal volume oscillation from the underlying pitch freq: Base frequency of the LFO oscillations. freq_shift: shift relative to the bae LFO frequency phase: The phase of the LFO oscillations, defined in terms of fraction of a whole cycle @@ -209,7 +210,7 @@ _meta: "Curvature" of the Decay portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, negative slowly then quick. a value of 0 is a linear decay, decreasing in volume at a constant rate. Rc: >- "Curvature" of the release portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, negative slowly then quick. a value of 0 is a linear release, decreasing in volume at a constant rate. - level: Total amplitude level of the envelope from 0 to 1, contolling maximum volume of the note + level: Total level of the envelope from 0 to 1, contolling maximum depth of the LFO. volume: >- Master Volume of synthesizer pitch: >- @@ -223,5 +224,4 @@ _meta: pitch_lo: >- Pitch range minimum in semitones pitch_shift: >- - Default shift in semitones - + Default shift in semitones \ No newline at end of file diff --git a/src/strauss/presets/synth/ranges/default.yml b/src/strauss/presets/synth/ranges/default.yml index 723333a..3431fe8 100644 --- a/src/strauss/presets/synth/ranges/default.yml +++ b/src/strauss/presets/synth/ranges/default.yml @@ -23,7 +23,55 @@ volume_envelope: Dc_unit: 'unitless' Rc_unit: 'unitless' level_unit: 'unitless' - + +volume_lfo: + A: [1e-2, 20.] + D: [1e-2, 20.] + S: [0., 1.] + R: [1e-2, 20] + Ac: [-1. ,1.] + Dc: [-1. ,1.] + Rc: [-1. ,1.] + level: [0., 1.] + amount: [0, 1] + freq: [1., 12.] + freq_shift: [0., 3.] + A_unit: 'seconds' + D_unit: 'seconds' + S_unit: 'unitless' + R_unit: 'seconds' + Ac_unit: 'unitless' + Dc_unit: 'unitless' + Rc_unit: 'unitless' + level_unit: 'unitless' + freq_unit: 'Hz' + freq_shift_unit: 'octave' + +pitch_lfo: + A: [1e-2, 20.] + D: [1e-2, 20.] + S: [0., 1.] + R: [1e-2, 20] + Ac: [-1. ,1.] + Dc: [-1. ,1.] + Rc: [-1. ,1.] + level: [0., 1.] + amount: [0, 2] + freq: [1., 12.] + freq_shift: [0., 3.] + A_unit: 'seconds' + D_unit: 'seconds' + S_unit: 'unitless' + R_unit: 'seconds' + Ac_unit: 'unitless' + Dc_unit: 'unitless' + Rc_unit: 'unitless' + level_unit: 'unitless' + amount_unit: 'semitones' + freq_unit: 'Hz' + freq_shift_unit: 'octave' + + # filter cutoff cutoff: [0., 1.] cutoff_unit: 'unitless' @@ -43,7 +91,7 @@ polar: [0.,1.] polar_unit: 'unitless' # pitch range and default shift in semitones -pitch_shift: [0., 36.] +pitch_shift: [0., 24.] pitch_shift_unit: 'semitones' _meta: From 2248ecfd1237afa0311d7f9186d14abddece8cf0 Mon Sep 17 00:00:00 2001 From: James Trayford Date: Sun, 12 Jan 2025 13:54:30 +0000 Subject: [PATCH 71/86] get add back in jquery and module specification as well as the _static/js/custom.js file to ensure external links default to new tab --- docs/_static/js/custom.js | 3 +++ docs/conf.py | 2 +- setup.cfg | 3 ++- 3 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 docs/_static/js/custom.js diff --git a/docs/_static/js/custom.js b/docs/_static/js/custom.js new file mode 100644 index 0000000..184b5ae --- /dev/null +++ b/docs/_static/js/custom.js @@ -0,0 +1,3 @@ +$(document).ready(function () { + $('a.external').attr('target', '_blank'); +}); diff --git a/docs/conf.py b/docs/conf.py index f4846fd..81c5f2b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -36,7 +36,7 @@ 'sphinx.ext.inheritance_diagram', 'sphinx.ext.autosummary','sphinx.ext.coverage', 'sphinx.ext.napoleon', "myst_parser", - "sphinx_exec_code"]#, 'sphinxcontrib.jquery'] + "sphinx_exec_code", 'sphinxcontrib.jquery'] html_js_files = [ 'js/custom.js' diff --git a/setup.cfg b/setup.cfg index 66c23f5..8749004 100644 --- a/setup.cfg +++ b/setup.cfg @@ -44,4 +44,5 @@ TTS = docs = sphinx sphinx-exec-code - myst-parser + sphinxcontrib.jquery + myst-parser \ No newline at end of file From 82c2edd3a5081576c398fa9e1c4504c593fa069d Mon Sep 17 00:00:00 2001 From: James Trayford Date: Mon, 13 Jan 2025 11:57:44 +0000 Subject: [PATCH 72/86] adressing Chris suggestions --- src/strauss/generator.py | 19 +++++++++++++++++++ src/strauss/score.py | 5 +++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/strauss/generator.py b/src/strauss/generator.py index 47e20db..b44b758 100644 --- a/src/strauss/generator.py +++ b/src/strauss/generator.py @@ -630,6 +630,18 @@ def __init__(self, sampfiles, params=None, samprate=48000, sf_preset=None): All `.sf2` files should contain at least one preset. When given default `None` value, will print available presets and select the first preset. Note presets are 1-indexed. + + Note: + It is necessary to assign a note for each sample in order to + choose different sample based on the ``pitch`` parameter. This + is also the case for non-pitched sounds, following a similar + approach to a [keyboard sampler] + (https://support.apple.com/en-lk/guide/logicpro/lgcp4eecaaff/mac) + where each key can triggers a different chosen sample. If + `drumset_C1.wav` is a kick drum and `drumset_D1.wav` is a snare + drum for :obj:`Score` with `chord_sequence=[["C1", "D1"]]`, events + mapped to a higher (lower) `pitch` will sound as snare (kick) drums. + """ # default sampler preset self.gtype = 'sampler' @@ -819,6 +831,13 @@ def load_samples(self): dictionary of filepaths, generate interpolation functions for each, and assign them to a named note in scientific pitch notation (e.g. :obj:`'A4'`). + + Note: + Notes are assigned based on a tag in the filename (see :obj:`Sampler`), + not by analysing the audio itself. If a tuned sample is tagged as the + wrong note, this will carry over to the sonification. However, this + allows non-pitched samples to be assigned notes and triggered. + """ self.samples = {} self.samplens = {} diff --git a/src/strauss/score.py b/src/strauss/score.py index 27783f9..21d7c6d 100644 --- a/src/strauss/score.py +++ b/src/strauss/score.py @@ -35,9 +35,10 @@ class Score: sonification. For example for a one minute sonification (:obj:`length = '1m 0s'`) the :obj:`chord_sequence = "Am7_3 | D9_3 | Gmaj7_2"` plays each chord for 20s each. Chaining the - same chord can be used to change intervals, + same chord can be used to change the length of these intervals, (e.g. :obj:`chord_sequence = "F_3 | F_3 | C_4"` plays F for - 40s and C for 20s.) + 40s and C for 20s, `[["C2","G3","E4"]*37+["G2","D3","B4"]*23]` + would play the C voicing for 37s and G voicing for 23s). """ def __init__(self, chord_sequence, length, pitch_binning='adaptive'): """ From 1dcc398a3c5e16f4b35cb6e3babe803c14e5bf48 Mon Sep 17 00:00:00 2001 From: SamYoules Date: Mon, 13 Jan 2025 12:58:15 +0000 Subject: [PATCH 73/86] Added some links in the docs --- docs/start.rst | 3 ++- src/strauss/generator.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/start.rst b/docs/start.rst index a10ef7e..ec15627 100644 --- a/docs/start.rst +++ b/docs/start.rst @@ -59,6 +59,7 @@ From the :code:`jupyter-lab` interface, a good starting point is the :code:`Soni For a multivariate :code:`Event`-type sonification, the :code:`StarsAppearing.ipynb` notebook provides a step-by-step example, and demonstrates realistic stereo imaging for panoramic data. The output from this example was used in the `"Audio Universe: Tour of the Solar System" 2021 planetarium show `_. -For a multivariate, multi-source example using an :code:`Object`-type source representation, see the :code:`PlanetaryOrbits.ipynb` Notebook, the output of which was also used in the "Audio Universe: Tour of the Solar System" planetarium show. +For a multivariate, multi-source example using an :code:`Object`-type source representation, see the :code:`PlanetaryOrbits.ipynb` Notebook, the output of which was also used in the "Audio Universe: Tour of the Solar System" planetarium show. An example of a bivariate data series sonification, described in `this paper `_, can be found `here +`_. In addition to the above-mentioned examples, there are a number of other Notebooks, each representing the diverse applications and uses of the Strauss code to sonify data in different ways. A more detailed overview of the example Notebooks and scripts can be found in :ref:`examples`. diff --git a/src/strauss/generator.py b/src/strauss/generator.py index b44b758..34d7a0e 100644 --- a/src/strauss/generator.py +++ b/src/strauss/generator.py @@ -190,7 +190,7 @@ def envelope(self, samp, params, etype='volume'): envelope value at each sample. envelopes are defined by attack, decay, sustain and release (:obj:`'A','D','S' & 'R`) values, as well as segment curvatures (:obj:`'Ac','Dc', & - 'Rc`) and a normalisation :obj:`'level'`. + 'Rc`) and a normalisation :obj:`'level'`. See `this blog `_ for a more detailed explanation of ADSR envelopes. Args: samp (:obj:`array-like`): Audio sample index From bfc7cb0aff23337659e1efa4be0378b071600ce0 Mon Sep 17 00:00:00 2001 From: SamYoules Date: Mon, 13 Jan 2025 14:45:30 +0000 Subject: [PATCH 74/86] Included links to YouTube examples --- docs/examples.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/examples.rst b/docs/examples.rst index 2ab7f6c..f17d81e 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -10,11 +10,15 @@ Audio Caption (:code:`AudioCaption.ipynb`) ****************************************** The *Audio Caption* example demonstrates how to add audio captions to a sonification, using a text-to-speech (TTS) module. The TTS module is not included in the standard Strauss installation, but it can be installed by using pip install strauss[TTS]. This example uses the Strauss :code:`Sampler` to play a short sequence of glockenspiel notes, then generates an audio caption using a standard TTS voice. The notebook allows the user to try different voices and languages from TTS. +There are examples of audio captioning with different voices in the following spectrogram videos: `English (Irish, female) `_, `English (US, male) `_ and `German (male) `_. + Day Sequence (:code:`DaySequence.ipynb`) **************************************** The *Day Sequence* sonification generates the sunrise to sunset sonification used in the `"Audio Universe: Tour of the Solar System" `_, an immersive planetarium show designed with sonifications so it can be enjoyed and understood irrespective of level of vision. Samples are downloaded from a Google drive to a local directory, and are played using the Strauss :code:`Sampler`. This spatialisation can be mapped to any audio setup, and a :code:`5.1` system was used for the planetarium, but for the example we use :code:`stereo`. +An example of this sonification can be heard `here `_. + Earth System (:code:`EarthSystem.ipynb`) **************************************** @@ -22,10 +26,13 @@ The *Earth System* sonification represents the ratio of ocean to land along line A video of this sequence is available starting at 2:17 in `this video `_. + Light Curve Soundfonts (:code:`LightCurveSoundfonts.ipynb`) ************************************************************ The *Light Curve Soundfonts* example demonstrates how to use imported soundfont files to use virtual musical instruments in a sonification. Soundfont files are widely available online. We download flute and guitar sounds from `Soundfonts 4 U `_. We load the sounds into the Strauss :code:`Sampler`, and select a preset. The soundfonts are used to sonify a light curve for the variable star 55 Cancri, creating the audio equivalent of a scatter plot. The note length and volume envelope can be adjusted to improve the articulation of individual data points. To demonstrate an alternative approach, we use an :code: `Object` source type, where we evolve a sound over time to represent the data. This is analogous to a line graph, representing a continuous data series. We use a held chord, changing the cutoff frequency of the low-pass filter to create a "brighter" timbre when the star is brighter and a "duller" sound when the star is darker. +Two examples using different approaches demonstrated in the notebook can be heard `here `_ and `here `_. + Planetary Orbits (:code:`PlanetaryOrbits.ipynb`) ************************************************ @@ -33,15 +40,20 @@ The *Planetary Orbits* example generates sonifications used in the "Audio Univer A video of this sequence with the audio is available `here `_. + Sonifying Data 1D (:code:`SonifyingData1D.ipynb`) ************************************************* The *Sonifying Data 1D* example demonstrates some generic techniques for sonifying one-dimensional data series. We construct some mock data with features and noise. For all examples we use the Strauss :code:`Synthesiser` to create a 30 second, mono sonification. We demonstrate a variety of ways to map y as a function of x, using the change in some expressive property of sound (e.g. pitch_shift, volume and filter-cutoff) as a function of time. +This `spectrogram video `_ illustrates an example of a 1D data series sonification. + Spectral Data (:code:`SpectralData.ipynb`) ****************************************** The *Spectral Data* sonification demonstrates use of the Strauss :code:`Spectraliser` to represent data. We use a direct spectralisation approach where the sound is generated by treating the 1D data as a sound spectrum. This uses a direct inverse Fourier transform, which is relatively intuitive for spectral data, especially where the spectral features are similar to those that can be identified in sound. We use Planetary Nebulae data, objects dominated by strong emission lines, to demonstrate this. We plot the spectra vs wavelength and spectra vs frequency, and use the Strauss :code:`Synthesiser` to create a 30 second, mono sonification. We set the ranges for the mapped parameters and render the sonification. A second example uses an "Object" type sonification with an evolving Spectrum to sonify an image. We represent the image by evolving from left to right, with higher features in the y-axis having a higher pitch. +An example can be heard `here `_. + Stars Appearing (:code:`StarsAppearing.ipynb`) ********************************************** From 6fc66e97144f5744c1601efedb81eb7d098d0258 Mon Sep 17 00:00:00 2001 From: SamYoules Date: Mon, 13 Jan 2025 14:57:09 +0000 Subject: [PATCH 75/86] Changed ADSR link --- src/strauss/generator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/strauss/generator.py b/src/strauss/generator.py index 34d7a0e..58607f8 100644 --- a/src/strauss/generator.py +++ b/src/strauss/generator.py @@ -190,7 +190,7 @@ def envelope(self, samp, params, etype='volume'): envelope value at each sample. envelopes are defined by attack, decay, sustain and release (:obj:`'A','D','S' & 'R`) values, as well as segment curvatures (:obj:`'Ac','Dc', & - 'Rc`) and a normalisation :obj:`'level'`. See `this blog `_ for a more detailed explanation of ADSR envelopes. + 'Rc`) and a normalisation :obj:`'level'`. See `this article `_ for a more detailed explanation of ADSR envelopes. Args: samp (:obj:`array-like`): Audio sample index From 1c449e3a6149dae6249d80b670365293a8d4ec90 Mon Sep 17 00:00:00 2001 From: James Trayford Date: Mon, 13 Jan 2025 17:07:24 +0000 Subject: [PATCH 76/86] more tweaks --- docs/examples.rst | 2 -- docs/index.rst | 6 ++++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/examples.rst b/docs/examples.rst index f17d81e..5563e69 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -47,7 +47,6 @@ The *Sonifying Data 1D* example demonstrates some generic techniques for sonifyi This `spectrogram video `_ illustrates an example of a 1D data series sonification. - Spectral Data (:code:`SpectralData.ipynb`) ****************************************** The *Spectral Data* sonification demonstrates use of the Strauss :code:`Spectraliser` to represent data. We use a direct spectralisation approach where the sound is generated by treating the 1D data as a sound spectrum. This uses a direct inverse Fourier transform, which is relatively intuitive for spectral data, especially where the spectral features are similar to those that can be identified in sound. We use Planetary Nebulae data, objects dominated by strong emission lines, to demonstrate this. We plot the spectra vs wavelength and spectra vs frequency, and use the Strauss :code:`Synthesiser` to create a 30 second, mono sonification. We set the ranges for the mapped parameters and render the sonification. A second example uses an "Object" type sonification with an evolving Spectrum to sonify an image. We represent the image by evolving from left to right, with higher features in the y-axis having a higher pitch. @@ -57,7 +56,6 @@ An example can be heard `here `_. diff --git a/docs/index.rst b/docs/index.rst index 5fbe803..e0872e1 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -9,10 +9,12 @@ Welcome to the STRAUSS documentation! Strauss is a python toolkit for data *"sonification"* - the representation of data using sound - with both scientific and outreach applications. -The code aims to make rich and evocative sonification straightforward, with a number of presets and examples enabling a quick start. At the same time, it is intended to be flexible enough to allow high level of control over the sonification and various expressive elements of sound and harmony if required. The project is described in more detail in the paper `Introducing STRAUSS: a flexible sonification Python package `_, presented at the 28th Proceedings of the `International Community of Auditory Displays (2023) `_. You can read about the associated `Audio Universe project here `_ for examples of using Strauss for a variety of applications. +The code aims to make rich and evocative sonification straightforward, with a number of presets and examples enabling a quick start. At the same time, it is intended to be flexible enough to allow high level of control over the sonification and various expressive elements of sound and harmony if required. The project is described in more detail in the paper `Introducing STRAUSS: a flexible sonification Python package `_, presented at th-e 28th Proceedings of the `International Community of Auditory Displays (2023) `_. You can read about the associated `Audio Universe project here `_ for examples of using Strauss for a variety of applications. + +Strauss is packaged with a variety of :doc:`example scripts <./examples>`. These mostly follow a `parameter mapping `_ approach, but also other approaches, for example a `spectral audification `_ approach in the `Spectral Data <./examples.html#sonifying-data-1d-sonifyingdata1d-ipynb>`_ example. .. note:: - Strauss and its documentation are currently in development, with more details and features coming soon. Look out for / follow the repo our first numbered release, and accompanying article! + Strauss and its documentation is in continuous development, with more features in the pipeline. Follow the `GitHub repository `_ for the cutting-edge release. .. toctree:: :maxdepth: 2 From 9a0463ef70e7516b548ccc65d28dd40e1ef2bf8b Mon Sep 17 00:00:00 2001 From: James Trayford Date: Wed, 15 Jan 2025 00:37:45 +0000 Subject: [PATCH 77/86] finish up notes and fix evolvables --- docs/elements.rst | 24 ++++++++++++++---------- docs/index.rst | 2 +- docs/params.rst | 40 ++++++++++++++++++++++++++++++++++++++++ src/strauss/sources.py | 2 -- 4 files changed, 55 insertions(+), 13 deletions(-) diff --git a/docs/elements.rst b/docs/elements.rst index 1d9e1ba..44b4529 100644 --- a/docs/elements.rst +++ b/docs/elements.rst @@ -16,14 +16,18 @@ This processing is all handled inside the :code:`Sonification` class, which can **Figure:** Basic flow of the Strauss code, from input data to output audio. +We include further descriptions below, with linked examples, which are hosted in your browser via Google's `Colab` platform. + .. _sources: Source Class ************ -The :code:`Source` classes in Strauss are so named because they act as `sources of sound` in the sonification. Sources are used to represent the input data, by mapping this data to properties of sound (volume, position, frequency, etc). The choice of how to set up the sources depends on the data being sonified, and what the user wants to convey. For this, Strauss defines two generic classes that inherit the parent :code:`Source` class; :code:`Events` and :code:`Objects`, described below. +The :code:`Source` classes in Strauss are so named because they act as `sources of sound` in the sonification. Sources are used to represent the input data, by mapping this data to properties of sound (volume, position, frequency, etc). The choice of how to set up the sources depends on the data being sonified, and what the user wants to convey. For this, Strauss defines two generic classes that inherit the parent :code:`Source` class; :code:`Events` and :code:`Objects`, described below. + +.. note:: -**Full documentation of the mappable parameters is coming soon!** See :obj:`./examples` for example mappings in Jupyter Notebooks and Python scripts. + :doc:`Full documentation of the mappable parameters can be found Here! <./params>` `Events` '''''''' @@ -33,7 +37,7 @@ In this case, sounds are triggered over the duration of the sonification, with p .. note:: - Some examples of :code:`Events` in scientific data could be; `stars forming, supernovae explosions, particle detections, lightning strikes, website interactions, etc...` + Some examples of :code:`Events` in scientific data could be; `stars forming, supernovae explosions, particle detections, lightning strikes, website interactions, etc...`, an example of the :code:`Events` source type is the `'Stars Appearing' example `_. `Objects` ''''''''' @@ -43,45 +47,45 @@ In this case, sound is produced continuously by the source, with the evolving pr .. note:: - Some examples of :code:`Objects` in scientific data could be; `A galaxy evolving, planets orbiting, a plant growing, a glacier flowing, a climate changing etc...` + Some examples of :code:`Objects` in scientific data could be; `A galaxy evolving, planets orbiting, a plant growing, a glacier flowing, a climate changing etc...` an instance of the :code:`Objects` source type is seen in `the 1D sonification example `_. .. _score: Score Class *********** -With the audio :code:`Sources` defined, the :code:`Score` class allows us to place ‘musical’ constraints on the sound they produce to represent the underlying data. The duration of the output sonification is also specified via the Score with the timeline of the sonification scaled to fit this duration. +With the audio :code:`Sources` defined, the :code:`Score` class allows us to place ‘musical’ constraints on the sound they produce to represent the underlying data. The duration of the output sonification is also specified via the Score with the timeline of the sonification scaled to fit this duration. The musical use of the score is demonstrated in examples such as `this `_ or `this `_ .. _generator: Generator Class *************** -The :code:`Generator` class takes instruction from the two prior classes and generates audio for each individual source. This can be achieved using either the :code:`Sampler` or :code:`Synthesiser` child classes (along with the :code:`Spectraliser` special case), detailed below. +The :code:`Generator` class takes instruction from the two prior classes and generates audio for each individual source. This can be achieved using either the :code:`Sampler` or :code:`Synthesiser` child classes (along with the :code:`Spectraliser` special case), detailed below, with some applied examples. `Sampler` ''''''''' This class generates audio by triggering **pre-recorded audio samples**. -A directory of audio files is used to specify which sample to use for each note of the sampler. These samples are loaded into the sampler and are interpolated to allow arbitrary pitch shifting. Samples can also be looped in a number of ways to allow notes to sustain perpetually. +A directory of audio files is used to specify which sample to use for each note of the sampler. These samples are loaded into the sampler and are interpolated to allow arbitrary pitch shifting. Samples can also be looped in a number of ways to allow notes to sustain perpetually. Alternatively, a sound-font (`.sf2`) file can be read in, mapping samples to notes automatically `The 'Stars Appearing' example `_ exemplifies using curated samples paackaged with Strauss, while `the 'Light Curve Soundfonts' example `_ demonstrates using a sound-font. `Synthesiser` ''''''''''''' -This class instead **generates audio additively using mathematical functions** via an arbitrary number of oscillators. The strauss synthesiser supports a number of oscillator forms. +This class instead **generates audio additively using mathematical functions** via an arbitrary number of oscillators. The strauss synthesiser supports a number of oscillator forms. This is exemplified in the `the 1D data example `_. `Spectraliser` '''''''''''''' -A special case of the :code:`Synthesiser`, this generator synthesises sound from an input spectrum, via an inverse Fast Fourier Transform (IFFT), with randomised phases. The user can specify the audible frequency range that the ‘spectralised’ audio is mapped over. +A special case of the :code:`Synthesiser`, this generator synthesises sound from an input spectrum, via an inverse Fast Fourier Transform (IFFT), with randomised phases. The user can specify the audible frequency range that the ‘spectralised’ audio is mapped over. The spectraliser is used `in this example `_. .. _channels: Channels Class ************** -Once sound has been produced for each :code: `source`, the final step is to mix the audio down into some multi-channel audio format. The :code:`Channels` class essentially represents a bank of virtual microphones, with 3D antennae patterns, that each correspond to a channel in the output file. +Once sound has been produced for each :code: `source`, the final step is to mix the audio down into some multi-channel audio format. The :code:`Channels` class essentially represents a bank of virtual microphones, with 3D antennae patterns, that each correspond to a channel in the output file. `The 'Stars Appearing' example `_ discuss different choices of channel set-up. .. _sonification: diff --git a/docs/index.rst b/docs/index.rst index e0872e1..6ffd058 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -9,7 +9,7 @@ Welcome to the STRAUSS documentation! Strauss is a python toolkit for data *"sonification"* - the representation of data using sound - with both scientific and outreach applications. -The code aims to make rich and evocative sonification straightforward, with a number of presets and examples enabling a quick start. At the same time, it is intended to be flexible enough to allow high level of control over the sonification and various expressive elements of sound and harmony if required. The project is described in more detail in the paper `Introducing STRAUSS: a flexible sonification Python package `_, presented at th-e 28th Proceedings of the `International Community of Auditory Displays (2023) `_. You can read about the associated `Audio Universe project here `_ for examples of using Strauss for a variety of applications. +The code aims to make rich and evocative sonification straightforward, with a number of presets and examples enabling a quick start. At the same time, it is intended to be flexible enough to allow high level of control over the sonification and various expressive elements of sound and harmony if required. The project is described in more detail in the paper `Introducing STRAUSS: a flexible sonification Python package `_, presented at the 28th Proceedings of the `International Community of Auditory Displays (2023) `_. You can read about the associated `Audio Universe project here `_ for examples of using Strauss for a variety of applications. Strauss is packaged with a variety of :doc:`example scripts <./examples>`. These mostly follow a `parameter mapping `_ approach, but also other approaches, for example a `spectral audification `_ approach in the `Spectral Data <./examples.html#sonifying-data-1d-sonifyingdata1d-ipynb>`_ example. diff --git a/docs/params.rst b/docs/params.rst index b9d8b48..326cfcd 100644 --- a/docs/params.rst +++ b/docs/params.rst @@ -3,6 +3,46 @@ Parameter Reference ################### +Here, we provide details of parameters that can be set for each of the generators via the preset ``.yml`` files or using the |mod-preset|_. + +A subset of these parameters are also ``'mappable'``, i.e. can be controlled by input data via the ``Sources``: + +.. |mod-preset| replace:: ``modify_preset()`` *Generator* method +.. _mod-preset: ./detailed.html#strauss.generator.Generator.modify_preset + +.. exec_code:: + + # --- hide: start --- + from strauss.sources import mappable + + for m in mappable: + # filter deprecated variable names + if m is not ('phi' or 'theta'): + if 'time' not in m: + print(m) + # --- hide: stop --- + +While a further subset of these are also ``'evolvable'``, i.e. can be evolved over time using an ``Object``-type source: + +.. exec_code:: + + # --- hide: start --- + from strauss.sources import evolvable + + for e in evolvable: + # filter deprecated variable names + if e is not ('phi' or 'theta'): + if 'time' not in e: + print(e) + # --- hide: stop --- + +When mapping data input into ``Sources`` to expressive sound parameters, two numerical ranges are relevant - the mapping limits of the input parameters, and the range of values the mapped parameter can assume - the ``map_lims`` and ``param_lims`` arguments of the Sources |mapping-func|_, respectively. + +The mapping limits, ``map_lims``, are the minimum and maximum allowed values of the input data. Data values above or below the range are clipped to the corresponding limits. These can either be absolute values, specified numerically (e.g. ``1.0``) or percentiles, specified as a string (e.g. ``'100'``). The ``param_lims`` then define the minimum and maximum values of the chosen sound parameters that the data are rescaled between. For example if ``map_lims={'pitch_shift', [0,3]}`` and ``param_lims={'pitch_shift', [0,24]}``, data values of ``1.25``, ``2`` and ``3`` mapped to ``pitch_shift`` will result in pitch shifts of ``10``, ``16`` and ``24`` semitones, respectively. + +.. |mapping-func| replace:: ``modify_preset()`` *Sources* method +.. _mapping-func: ./detailed.html#strauss.sources.Source.apply_mapping_functions + .. exec_code:: :filename: yamls_to_tables.py diff --git a/src/strauss/sources.py b/src/strauss/sources.py index 1fb720b..5e9f742 100644 --- a/src/strauss/sources.py +++ b/src/strauss/sources.py @@ -56,10 +56,8 @@ 'cutoff', 'time_evo', 'pitch_shift', - 'volume_lfo/freq', 'volume_lfo/freq_shift', 'volume_lfo/amount', - 'pitch_lfo/freq', 'pitch_lfo/freq_shift', 'pitch_lfo/amount'] param_limits = [(0,1),#np.pi), From 1cb01ee8c8375c7ad984cf913e10463442272105 Mon Sep 17 00:00:00 2001 From: SamYoules Date: Wed, 15 Jan 2025 12:05:20 +0000 Subject: [PATCH 78/86] Added links to colab notebooks --- docs/examples.rst | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/examples.rst b/docs/examples.rst index 5563e69..02a2b4b 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -11,13 +11,14 @@ Audio Caption (:code:`AudioCaption.ipynb`) The *Audio Caption* example demonstrates how to add audio captions to a sonification, using a text-to-speech (TTS) module. The TTS module is not included in the standard Strauss installation, but it can be installed by using pip install strauss[TTS]. This example uses the Strauss :code:`Sampler` to play a short sequence of glockenspiel notes, then generates an audio caption using a standard TTS voice. The notebook allows the user to try different voices and languages from TTS. There are examples of audio captioning with different voices in the following spectrogram videos: `English (Irish, female) `_, `English (US, male) `_ and `German (male) `_. - +A `Google Colab notebook `_ here. Day Sequence (:code:`DaySequence.ipynb`) **************************************** The *Day Sequence* sonification generates the sunrise to sunset sonification used in the `"Audio Universe: Tour of the Solar System" `_, an immersive planetarium show designed with sonifications so it can be enjoyed and understood irrespective of level of vision. Samples are downloaded from a Google drive to a local directory, and are played using the Strauss :code:`Sampler`. This spatialisation can be mapped to any audio setup, and a :code:`5.1` system was used for the planetarium, but for the example we use :code:`stereo`. An example of this sonification can be heard `here `_. +A `Google Colab notebook `_ here. Earth System (:code:`EarthSystem.ipynb`) @@ -25,6 +26,7 @@ Earth System (:code:`EarthSystem.ipynb`) The *Earth System* sonification represents the ratio of ocean to land along lines of longitude as the Earth spins through three rotation cycles. We use the Strauss :code:`Synthesiser` to generate chords. A low pass filter is employed to generate a brighter sound to represent a high water fraction and a duller sound for high land fraction. A video of this sequence is available starting at 2:17 in `this video `_. +A `Google Colab notebook `_ here. Light Curve Soundfonts (:code:`LightCurveSoundfonts.ipynb`) @@ -32,30 +34,32 @@ Light Curve Soundfonts (:code:`LightCurveSoundfonts.ipynb`) The *Light Curve Soundfonts* example demonstrates how to use imported soundfont files to use virtual musical instruments in a sonification. Soundfont files are widely available online. We download flute and guitar sounds from `Soundfonts 4 U `_. We load the sounds into the Strauss :code:`Sampler`, and select a preset. The soundfonts are used to sonify a light curve for the variable star 55 Cancri, creating the audio equivalent of a scatter plot. The note length and volume envelope can be adjusted to improve the articulation of individual data points. To demonstrate an alternative approach, we use an :code: `Object` source type, where we evolve a sound over time to represent the data. This is analogous to a line graph, representing a continuous data series. We use a held chord, changing the cutoff frequency of the low-pass filter to create a "brighter" timbre when the star is brighter and a "duller" sound when the star is darker. Two examples using different approaches demonstrated in the notebook can be heard `here `_ and `here `_. - +A `Google Colab notebook `_ here. Planetary Orbits (:code:`PlanetaryOrbits.ipynb`) ************************************************ The *Planetary Orbits* example generates sonifications used in the "Audio Universe: Tour of the Solar System" planetarium show. Each planet in the Solar System is assigned a unique note, with the smallest planets having the highest notes and the largest planets having the lowest notes. The sonification length for each is set according to the planet's orbital period, and the volume is varied by orbital azimuth. The audio system is set as 'stereo' by default but for the planetarium '5.1' is used. This creates the effect of the planets moving in orbits at relative speeds to represent the real relative motions. A video of this sequence with the audio is available `here `_. - +A `Google Colab notebook `_ here. Sonifying Data 1D (:code:`SonifyingData1D.ipynb`) ************************************************* The *Sonifying Data 1D* example demonstrates some generic techniques for sonifying one-dimensional data series. We construct some mock data with features and noise. For all examples we use the Strauss :code:`Synthesiser` to create a 30 second, mono sonification. We demonstrate a variety of ways to map y as a function of x, using the change in some expressive property of sound (e.g. pitch_shift, volume and filter-cutoff) as a function of time. This `spectrogram video `_ illustrates an example of a 1D data series sonification. +A `Google Colab notebook `_ here. Spectral Data (:code:`SpectralData.ipynb`) ****************************************** The *Spectral Data* sonification demonstrates use of the Strauss :code:`Spectraliser` to represent data. We use a direct spectralisation approach where the sound is generated by treating the 1D data as a sound spectrum. This uses a direct inverse Fourier transform, which is relatively intuitive for spectral data, especially where the spectral features are similar to those that can be identified in sound. We use Planetary Nebulae data, objects dominated by strong emission lines, to demonstrate this. We plot the spectra vs wavelength and spectra vs frequency, and use the Strauss :code:`Synthesiser` to create a 30 second, mono sonification. We set the ranges for the mapped parameters and render the sonification. A second example uses an "Object" type sonification with an evolving Spectrum to sonify an image. We represent the image by evolving from left to right, with higher features in the y-axis having a higher pitch. An example can be heard `here `_. - +A `Google Colab notebook `_ here. Stars Appearing (:code:`StarsAppearing.ipynb`) ********************************************** The *Stars Appearing* sonification demonstrates the generation of a sonification that was used directly in the "Audio Universe: Tour of the Solar System" planetarium show. This is intended to represent the appearance of stars in the night sky to an observer. Over time, the sky darkens and our eyes adjust, allowing us to see more and more stars. To represent this, the brightest stars appear first, with dimmer stars appearing later. Data on the colours of the stars is used to set the note used for each stars sound, with bluer stars having higher notes and redder stars having lower notes. We use the Strauss :code:`Sampler` to play a glockenspiel sound for each star as it appears. The actual positions of stars in the sky is used to spatialise the audio, with westerly stars positioned in the right speaker and easterly stars in the left. This spatialisation can be mapped to any audio setup, and a :code:`5.1` system was used for the planetarium, but for the example we use :code:`stereo`. A video of this sequence with the audio is available `here `_. +A `Google Colab notebook `_ here. From 4e2bc4083505f41150efe35e7fa640b76ef32c95 Mon Sep 17 00:00:00 2001 From: SamYoules Date: Wed, 15 Jan 2025 12:09:28 +0000 Subject: [PATCH 79/86] Corrected links to colab notebooks --- docs/examples.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/examples.rst b/docs/examples.rst index 02a2b4b..21c2c8c 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -11,14 +11,14 @@ Audio Caption (:code:`AudioCaption.ipynb`) The *Audio Caption* example demonstrates how to add audio captions to a sonification, using a text-to-speech (TTS) module. The TTS module is not included in the standard Strauss installation, but it can be installed by using pip install strauss[TTS]. This example uses the Strauss :code:`Sampler` to play a short sequence of glockenspiel notes, then generates an audio caption using a standard TTS voice. The notebook allows the user to try different voices and languages from TTS. There are examples of audio captioning with different voices in the following spectrogram videos: `English (Irish, female) `_, `English (US, male) `_ and `German (male) `_. -A `Google Colab notebook `_ here. +The code is available in a `Google Colab notebook `_. Day Sequence (:code:`DaySequence.ipynb`) **************************************** The *Day Sequence* sonification generates the sunrise to sunset sonification used in the `"Audio Universe: Tour of the Solar System" `_, an immersive planetarium show designed with sonifications so it can be enjoyed and understood irrespective of level of vision. Samples are downloaded from a Google drive to a local directory, and are played using the Strauss :code:`Sampler`. This spatialisation can be mapped to any audio setup, and a :code:`5.1` system was used for the planetarium, but for the example we use :code:`stereo`. An example of this sonification can be heard `here `_. -A `Google Colab notebook `_ here. +The code is available in a `Google Colab notebook `_. Earth System (:code:`EarthSystem.ipynb`) @@ -26,7 +26,7 @@ Earth System (:code:`EarthSystem.ipynb`) The *Earth System* sonification represents the ratio of ocean to land along lines of longitude as the Earth spins through three rotation cycles. We use the Strauss :code:`Synthesiser` to generate chords. A low pass filter is employed to generate a brighter sound to represent a high water fraction and a duller sound for high land fraction. A video of this sequence is available starting at 2:17 in `this video `_. -A `Google Colab notebook `_ here. +The code is available in a `Google Colab notebook `_. Light Curve Soundfonts (:code:`LightCurveSoundfonts.ipynb`) @@ -34,32 +34,32 @@ Light Curve Soundfonts (:code:`LightCurveSoundfonts.ipynb`) The *Light Curve Soundfonts* example demonstrates how to use imported soundfont files to use virtual musical instruments in a sonification. Soundfont files are widely available online. We download flute and guitar sounds from `Soundfonts 4 U `_. We load the sounds into the Strauss :code:`Sampler`, and select a preset. The soundfonts are used to sonify a light curve for the variable star 55 Cancri, creating the audio equivalent of a scatter plot. The note length and volume envelope can be adjusted to improve the articulation of individual data points. To demonstrate an alternative approach, we use an :code: `Object` source type, where we evolve a sound over time to represent the data. This is analogous to a line graph, representing a continuous data series. We use a held chord, changing the cutoff frequency of the low-pass filter to create a "brighter" timbre when the star is brighter and a "duller" sound when the star is darker. Two examples using different approaches demonstrated in the notebook can be heard `here `_ and `here `_. -A `Google Colab notebook `_ here. +The code is available in a `Google Colab notebook `_. Planetary Orbits (:code:`PlanetaryOrbits.ipynb`) ************************************************ The *Planetary Orbits* example generates sonifications used in the "Audio Universe: Tour of the Solar System" planetarium show. Each planet in the Solar System is assigned a unique note, with the smallest planets having the highest notes and the largest planets having the lowest notes. The sonification length for each is set according to the planet's orbital period, and the volume is varied by orbital azimuth. The audio system is set as 'stereo' by default but for the planetarium '5.1' is used. This creates the effect of the planets moving in orbits at relative speeds to represent the real relative motions. A video of this sequence with the audio is available `here `_. -A `Google Colab notebook `_ here. +The code is available in a `Google Colab notebook `_. Sonifying Data 1D (:code:`SonifyingData1D.ipynb`) ************************************************* The *Sonifying Data 1D* example demonstrates some generic techniques for sonifying one-dimensional data series. We construct some mock data with features and noise. For all examples we use the Strauss :code:`Synthesiser` to create a 30 second, mono sonification. We demonstrate a variety of ways to map y as a function of x, using the change in some expressive property of sound (e.g. pitch_shift, volume and filter-cutoff) as a function of time. This `spectrogram video `_ illustrates an example of a 1D data series sonification. -A `Google Colab notebook `_ here. +The code is available in a `Google Colab notebook `_. Spectral Data (:code:`SpectralData.ipynb`) ****************************************** The *Spectral Data* sonification demonstrates use of the Strauss :code:`Spectraliser` to represent data. We use a direct spectralisation approach where the sound is generated by treating the 1D data as a sound spectrum. This uses a direct inverse Fourier transform, which is relatively intuitive for spectral data, especially where the spectral features are similar to those that can be identified in sound. We use Planetary Nebulae data, objects dominated by strong emission lines, to demonstrate this. We plot the spectra vs wavelength and spectra vs frequency, and use the Strauss :code:`Synthesiser` to create a 30 second, mono sonification. We set the ranges for the mapped parameters and render the sonification. A second example uses an "Object" type sonification with an evolving Spectrum to sonify an image. We represent the image by evolving from left to right, with higher features in the y-axis having a higher pitch. An example can be heard `here `_. -A `Google Colab notebook `_ here. +The code is available in a `Google Colab notebook `_. Stars Appearing (:code:`StarsAppearing.ipynb`) ********************************************** The *Stars Appearing* sonification demonstrates the generation of a sonification that was used directly in the "Audio Universe: Tour of the Solar System" planetarium show. This is intended to represent the appearance of stars in the night sky to an observer. Over time, the sky darkens and our eyes adjust, allowing us to see more and more stars. To represent this, the brightest stars appear first, with dimmer stars appearing later. Data on the colours of the stars is used to set the note used for each stars sound, with bluer stars having higher notes and redder stars having lower notes. We use the Strauss :code:`Sampler` to play a glockenspiel sound for each star as it appears. The actual positions of stars in the sky is used to spatialise the audio, with westerly stars positioned in the right speaker and easterly stars in the left. This spatialisation can be mapped to any audio setup, and a :code:`5.1` system was used for the planetarium, but for the example we use :code:`stereo`. A video of this sequence with the audio is available `here `_. -A `Google Colab notebook `_ here. +The code is available in a `Google Colab notebook `_. From 5db19412adfd82414743154fb0694737345d89d5 Mon Sep 17 00:00:00 2001 From: James Trayford Date: Wed, 15 Jan 2025 12:11:55 +0000 Subject: [PATCH 80/86] be clearer about numerical units --- docs/yamls_to_tables.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/yamls_to_tables.py b/docs/yamls_to_tables.py index af67837..f3449c2 100644 --- a/docs/yamls_to_tables.py +++ b/docs/yamls_to_tables.py @@ -49,7 +49,11 @@ def yaml_traverse(metadict, valdict, rdict, headlev=1): rdict[k] = str(rdict[k]).replace(" ","").replace(",","\u2011").replace("-","\u2011") if k+"_unit" not in rdict: # unspecified => '-' - rdict[k+"_unit"] = '-' + try: + float(valdict[k]) + rdict[k+"_unit"] = 'unitless' + except ValueError: + rdict[k+"_unit"] = '-' # print(f"|`{k}` | _{metadict[k]}_ | `{str(valdict[k]).strip()}` | `{rdict[k]}` | {rdict[k+'_unit']}") if starttab: tabstr = tstr1 + tstr2 + tabstr From 784e4966260c2bf040976458f04c978219cf0b78 Mon Sep 17 00:00:00 2001 From: James Trayford Date: Wed, 15 Jan 2025 21:22:32 +0000 Subject: [PATCH 81/86] docs tweaks --- docs/yamls_to_tables.py | 5 +- src/strauss/presets/sampler/default.yml | 38 +++--- .../presets/sampler/ranges/default.yml | 20 +-- src/strauss/presets/spec/default.yml | 38 +++--- src/strauss/presets/spec/ranges/default.yml | 124 +++++++----------- src/strauss/presets/synth/default.yml | 36 ++--- src/strauss/presets/synth/ranges/default.yml | 21 +-- 7 files changed, 129 insertions(+), 153 deletions(-) diff --git a/docs/yamls_to_tables.py b/docs/yamls_to_tables.py index f3449c2..1e18dd5 100644 --- a/docs/yamls_to_tables.py +++ b/docs/yamls_to_tables.py @@ -51,7 +51,10 @@ def yaml_traverse(metadict, valdict, rdict, headlev=1): # unspecified => '-' try: float(valdict[k]) - rdict[k+"_unit"] = 'unitless' + if not isinstance(valdict[k], bool): + rdict[k+"_unit"] = 'unitless' + else: + rdict[k+"_unit"] = '-' except ValueError: rdict[k+"_unit"] = '-' # print(f"|`{k}` | _{metadict[k]}_ | `{str(valdict[k]).strip()}` | `{rdict[k]}` | {rdict[k+'_unit']}") diff --git a/src/strauss/presets/sampler/default.yml b/src/strauss/presets/sampler/default.yml index a766101..6cfb670 100644 --- a/src/strauss/presets/sampler/default.yml +++ b/src/strauss/presets/sampler/default.yml @@ -102,17 +102,17 @@ _meta: volume_envelope: _doc: >- Define the note volume envelope applied to the samples. _'ADSR'_ is a common parametrisation in sound synthesis, - find out more e.g. [at this link](https://blg.native-instruments.com/adsr-explained/) + find out more e.g. [at this link]( https://learnmusicproduction.in/blogs/music-production-and-audio-engineering/adsr-fundamentals-in-music-everything-you-need-to-know). A: Attack, how long it takes for a sound to rise to 100% of the `level` after it’s triggered. D: Decay, how long it takes for the sounds volume to die down to the `Sustain` value after the `Attack` period. S: Sustain, the volume level (from 0 to 1.0) maintained after the `Decay` period, while the note is held. R: Release, how long the tone takes to finally die away once the note is released. - Ac: Curvature of the attack portion of a note. Values from -1 to 1, positive indicates increases quickly then slow, - negative slowly then quick. a value of 0 is a linear attack, increasing in volume at a constant rate. - Dc: Curvature of the Decay portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, - negative slowly then quick. a value of 0 is a linear decay, decreasing in volume at a constant rate. - Rc: Curvature of the release portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, - negative slowly then quick. a value of 0 is a linear release, decreasing in volume at a constant rate. + Ac: Curvature of the attack portion of the envelope. Values from -1 to 1, positive indicates increases quickly then slow, + negative slowly then quick. a value of 0 is a linear attack, increasing at a constant rate. + Dc: Curvature of the Decay portion of the envelope. Values from -1 to 1, positive indicates decreases quickly then slow, + negative slowly then quick. a value of 0 is a linear decay, decreasing at a constant rate. + Rc: Curvature of the release portion of the envelope. Values from -1 to 1, positive indicates decreases quickly then slow, + negative slowly then quick. a value of 0 is a linear release, decreasing at a constant rate. level: Total amplitude level of the envelope from 0 to 1, contolling maximum volume of the note. filter: >- Apply a frequency filter to to the audio signal. Values are 'on' or 'off'. A filter affects the timbre by filtering out certain harmonics @@ -136,12 +136,12 @@ _meta: D: Decay, how long it takes for the LFO depth to die down to the `Sustain` value after the `Attack` period. S: Sustain, the LFO depth (from 0 to 1.0) maintained after the `Decay` period, while the note is held. R: Release, how long LFO depth takes to finally die to 0 once the note is released. - Ac: Curvature of the attack portion of a note. Values from -1 to 1, positive indicates increases quickly then slow, - negative slowly then quick. a value of 0 is a linear attack, increasing in volume at a constant rate. - Dc: Curvature of the Decay portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, - negative slowly then quick. a value of 0 is a linear decay, decreasing in volume at a constant rate. - Rc: Curvature of the release portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, - negative slowly then quick. a value of 0 is a linear release, decreasing in volume at a constant rate. + Ac: Curvature of the attack portion of the envelope. Values from -1 to 1, positive indicates increases quickly then slow, + negative slowly then quick. a value of 0 is a linear attack, increasing at a constant rate. + Dc: Curvature of the Decay portion of the envelope. Values from -1 to 1, positive indicates decreases quickly then slow, + negative slowly then quick. a value of 0 is a linear decay, decreasing at a constant rate. + Rc: Curvature of the release portion of the envelope. Values from -1 to 1, positive indicates decreases quickly then slow, + negative slowly then quick. a value of 0 is a linear release, decreasing at a constant rate. level: Total level of the envelope from 0 to 1, contolling maximum depth of the LFO. volume_lfo: _doc: >- @@ -159,14 +159,14 @@ _meta: S: Sustain, the LFO depth (from 0 to 1.0) maintained after the `Decay` period, while the note is held. R: Release, how long LFO depth takes to finally die to 0 once the note is released. Ac: >- - Curvature of the attack portion of a note. Values from -1 to 1, positive indicates increases quickly then slow, - negative slowly then quick. a value of 0 is a linear attack, increasing in volume at a constant rate. + Curvature of the attack portion of the envelope. Values from -1 to 1, positive indicates increases quickly then slow, + negative slowly then quick. a value of 0 is a linear attack, increasing at a constant rate. Dc: >- - Curvature of the Decay portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, - negative slowly then quick. a value of 0 is a linear decay, decreasing in volume at a constant rate. + Curvature of the Decay portion of the envelope. Values from -1 to 1, positive indicates decreases quickly then slow, + negative slowly then quick. a value of 0 is a linear decay, decreasing at a constant rate. Rc: >- - Curvature of the release portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, - negative slowly then quick. a value of 0 is a linear release, decreasing in volume at a constant rate. + Curvature of the release portion of the envelope. Values from -1 to 1, positive indicates decreases quickly then slow, + negative slowly then quick. a value of 0 is a linear release, decreasing at a constant rate. level: Total level of the envelope from 0 to 1, contolling maximum depth of the LFO. volume: >- Master Volume of generator. diff --git a/src/strauss/presets/sampler/ranges/default.yml b/src/strauss/presets/sampler/ranges/default.yml index c5ee0b6..333ad9f 100644 --- a/src/strauss/presets/sampler/ranges/default.yml +++ b/src/strauss/presets/sampler/ranges/default.yml @@ -17,12 +17,12 @@ volume_envelope: level: [0., 1.] A_unit: 'seconds' D_unit: 'seconds' - S_unit: 'unitless' + S_unit: 'fraction' R_unit: 'seconds' Ac_unit: 'unitless' Dc_unit: 'unitless' Rc_unit: 'unitless' - level_unit: 'unitless' + level_unit: 'fraction' volume_lfo: A: [1e-2, 20.] @@ -38,12 +38,12 @@ volume_lfo: freq_shift: [0., 3.] A_unit: 'seconds' D_unit: 'seconds' - S_unit: 'unitless' + S_unit: 'fraction' R_unit: 'seconds' Ac_unit: 'unitless' Dc_unit: 'unitless' Rc_unit: 'unitless' - level_unit: 'unitless' + level_unit: 'fraction' freq_unit: 'Hz' freq_shift_unit: 'octave' @@ -61,23 +61,23 @@ pitch_lfo: freq_shift: [0., 3.] A_unit: 'seconds' D_unit: 'seconds' - S_unit: 'unitless' + S_unit: 'fraction' R_unit: 'seconds' Ac_unit: 'unitless' Dc_unit: 'unitless' Rc_unit: 'unitless' - level_unit: 'unitless' + level_unit: 'fraction' amount_unit: 'semitones' freq_unit: 'Hz' freq_shift_unit: 'octave' # filter cutoff cutoff: [0., 1.] -cutoff_unit: 'unitless' +cutoff_unit: 'fraction' # Master volume volume: [0,1.] -volume_unit: 'unitless' +volume_unit: 'fraction' # Default pitch selection pitch: [0,1.] @@ -85,9 +85,9 @@ pitch_unit: 'unitless' # center panning: azimuth: [0., 1.] -azimuth_unit: 'unitless' +azimuth_unit: 'cycles' polar: [0.,1.] -polar_unit: 'unitless' +polar_unit: 'half-cycles' # pitch range and default shift in semitones pitch_shift: [0., 24.] diff --git a/src/strauss/presets/spec/default.yml b/src/strauss/presets/spec/default.yml index d18367e..f3cf1ee 100644 --- a/src/strauss/presets/spec/default.yml +++ b/src/strauss/presets/spec/default.yml @@ -117,17 +117,17 @@ _meta: _doc : >- Define the note volume envelope applied to the samples A,D,S & R correspond to 'attack', 'decay', 'sustain' and 'release'. _'ADSR'_ is a common parametrisation in sound synthesis, - Find out more e.g. [at this link](https://blg.native-instruments.com/adsr-explained/) + Find out more e.g. [at this link]( https://learnmusicproduction.in/blogs/music-production-and-audio-engineering/adsr-fundamentals-in-music-everything-you-need-to-know). A: Attack, how long it takes for a sound to rise to 100% of the `level` after it’s triggered. D: Decay, how long it takes for the sounds volume to die down to the `Sustain` value after the `Attack` period. S: Sustain, the volume level (from 0 to 1.0) maintained after the `Decay` period, while the note is held. R: Release, how long the tone takes to finally die away once the note is released. - Ac: Curvature of the attack portion of a note. Values from -1 to 1, positive indicates increases quickly then slow, - negative slowly then quick. a value of 0 is a linear attack, increasing in volume at a constant rate. - Dc: Curvature of the Decay portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, - negative slowly then quick. a value of 0 is a linear decay, decreasing in volume at a constant rate. - Rc: Curvature of the release portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, - negative slowly then quick. a value of 0 is a linear release, decreasing in volume at a constant rate. + Ac: Curvature of the attack portion of the envelope. Values from -1 to 1, positive indicates increases quickly then slow, + negative slowly then quick. a value of 0 is a linear attack, increasing at a constant rate. + Dc: Curvature of the Decay portion of the envelope. Values from -1 to 1, positive indicates decreases quickly then slow, + negative slowly then quick. a value of 0 is a linear decay, decreasing at a constant rate. + Rc: Curvature of the release portion of the envelope. Values from -1 to 1, positive indicates decreases quickly then slow, + negative slowly then quick. a value of 0 is a linear release, decreasing at a constant rate. level: Total amplitude level of the envelope from 0 to 1, contolling maximum volume of the note. filter: >- Do we apply a frequency filter to the audio signal? This can be used to change the balance of frequencies and manipulate @@ -152,12 +152,12 @@ _meta: D: Decay, how long it takes for the LFO depth to die down to the `Sustain` value after the `Attack` period. S: Sustain, the LFO depth (from 0 to 1.0) maintained after the `Decay` period, while the note is held. R: Release, how long LFO depth takes to finally die to 0 once the note is released. - Ac: Curvature of the attack portion of a note. Values from -1 to 1, positive indicates increases quickly then slow, - negative slowly then quick. a value of 0 is a linear attack, increasing in volume at a constant rate. - Dc: Curvature of the Decay portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, - negative slowly then quick. a value of 0 is a linear decay, decreasing in volume at a constant rate. - Rc: Curvature of the release portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, - negative slowly then quick. a value of 0 is a linear release, decreasing in volume at a constant rate. + Ac: Curvature of the attack portion of the envelope. Values from -1 to 1, positive indicates increases quickly then slow, + negative slowly then quick. a value of 0 is a linear attack, increasing at a constant rate. + Dc: Curvature of the Decay portion of the envelope. Values from -1 to 1, positive indicates decreases quickly then slow, + negative slowly then quick. a value of 0 is a linear decay, decreasing at a constant rate. + Rc: Curvature of the release portion of the envelope. Values from -1 to 1, positive indicates decreases quickly then slow, + negative slowly then quick. a value of 0 is a linear release, decreasing at a constant rate. level: Total level of the envelope from 0 to 1, contolling maximum depth of the LFO. volume_lfo: @@ -175,12 +175,12 @@ _meta: D: Decay, how long it takes for the LFO depth to die down to the `Sustain` value after the `Attack` period. S: Sustain, the LFO depth (from 0 to 1.0) maintained after the `Decay` period, while the note is held. R: Release, how long LFO depth takes to finally die to 0 once the note is released. - Ac: Curvature of the attack portion of a note. Values from -1 to 1, positive indicates increases quickly then slow, - negative slowly then quick. a value of 0 is a linear attack, increasing in volume at a constant rate. - Dc: Curvature of the Decay portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, - negative slowly then quick. a value of 0 is a linear decay, decreasing in volume at a constant rate. - Rc: Curvature of the release portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, - negative slowly then quick. a value of 0 is a linear release, decreasing in volume at a constant rate. + Ac: Curvature of the attack portion of the envelope. Values from -1 to 1, positive indicates increases quickly then slow, + negative slowly then quick. a value of 0 is a linear attack, increasing at a constant rate. + Dc: Curvature of the Decay portion of the envelope. Values from -1 to 1, positive indicates decreases quickly then slow, + negative slowly then quick. a value of 0 is a linear decay, decreasing at a constant rate. + Rc: Curvature of the release portion of the envelope. Values from -1 to 1, positive indicates decreases quickly then slow, + negative slowly then quick. a value of 0 is a linear release, decreasing at a constant rate. level: Total level of the envelope from 0 to 1, contolling maximum depth of the LFO. volume: >- Master Volume of generator diff --git a/src/strauss/presets/spec/ranges/default.yml b/src/strauss/presets/spec/ranges/default.yml index f022e5b..7a21277 100644 --- a/src/strauss/presets/spec/ranges/default.yml +++ b/src/strauss/presets/spec/ranges/default.yml @@ -4,6 +4,48 @@ note_length: [1e-2, 30.] note_length_unit: 'seconds' +azimuth: [0., 1.] +azimuth_unit: 'cycles' +polar: [0.,1.] +polar_unit: 'half-cycles' + +# pitch range and default shift in semitones +pitch_shift: [0., 24.] +pitch_shift_unit: 'semitones' + +# filter cutoff +cutoff: [0., 1.] +cutoff_unit: 'fraction' + +# Master volume +volume: [0., 1.] +volume_unit: 'fraction' + +# frequency limits in Hz +min_freq: [20,2.2e4] +max_freq: [20,2.2e4] +min_freq_unit: 'Hz' +max_freq_unit: 'Hz' + +# Default pitch selection +pitch: [0,1.] +pitch_unit: 'unitless' + +# center panning: +phi: [0., 1.] +phi_unit: 'half-cycles (π)' +theta: [0.,1.] +theta_unit: 'cycles (2π)' + +# pitch range and default shift in semitones +pitch_shift: [0., 24.] +pitch_shift_unit: 'semitones' + +# Do we apply a filter, and if so specify the cutoff and filter type +filter: "off" +filter_type: "LPF1" +cutoff: 1. + # define the note volume envelope applied to the samples # A,D,S & R correspond to 'attack', 'decay', 'sustain' and 'release' volume_envelope: @@ -17,12 +59,12 @@ volume_envelope: level: [0., 1.] A_unit: 'seconds' D_unit: 'seconds' - S_unit: 'unitless' + S_unit: 'fraction' R_unit: 'seconds' Ac_unit: 'unitless' Dc_unit: 'unitless' Rc_unit: 'unitless' - level_unit: 'unitless' + level_unit: 'fraction' volume_lfo: A: [1e-2, 20.] @@ -38,12 +80,12 @@ volume_lfo: freq_shift: [0., 3.] A_unit: 'seconds' D_unit: 'seconds' - S_unit: 'unitless' + S_unit: 'fraction' R_unit: 'seconds' Ac_unit: 'unitless' Dc_unit: 'unitless' Rc_unit: 'unitless' - level_unit: 'unitless' + level_unit: 'fraction' freq_unit: 'Hz' freq_shift_unit: 'octave' @@ -61,86 +103,16 @@ pitch_lfo: freq_shift: [0., 3.] A_unit: 'seconds' D_unit: 'seconds' - S_unit: 'unitless' + S_unit: 'fraction' R_unit: 'seconds' Ac_unit: 'unitless' Dc_unit: 'unitless' Rc_unit: 'unitless' - level_unit: 'unitless' + level_unit: 'fraction' amount_unit: 'semitones' freq_unit: 'Hz' freq_shift_unit: 'octave' -# filter cutoff -cutoff: [0., 1.] -cutoff_unit: 'unitless' - -# Master volume -volume: [0., 1.] -volume_unit: 'unitless' - -# frequency limits in Hz -min_freq: [20,2.2e4] -max_freq: [20,2.2e4] -min_freq_unit: 'Hz' -max_freq_unit: 'Hz' - -# Default pitch selection -pitch: [0,1.] -pitch_unit: 'unitless' - -# center panning: -phi: [0., 1.] -phi_unit: 'half-cycles (π)' -theta: [0.,1.] -theta_unit: 'cycles (2π)' - -# pitch range and default shift in semitones -pitch_shift: [0., 24.] -pitch_shift_unit: 'semitones' - -# Do we apply a filter, and if so specify the cutoff and filter type -filter: "off" -filter_type: "LPF1" -cutoff: 1. - -# or 'vibrato' -pitch_lfo: - amount: [0, 2] - freq: [1,12] - freq_shift: [0,3] - A: [1e-2, 10] - D: [1e-2, 10] - S: [0, 1] - R: [1e-2, 10] - freq_unit: "Hz" - amount_unit: "semitones" - -volume_lfo: - amount: [0, 1] - freq: [1,12] - freq_shift: [0,3] - A: [1e-2, 10] - D: [1e-2, 10] - S: [0, 1] - R: [1e-2, 10] - freq_unit: "Hz" - amount_unit: "fraction" - use: off - wave: 'sine' - amount: 0.5 - freq: 3 - freq_shift: 0 - phase: 'random' - A: 0. - D: 0.1 - S: 1. - R: 0. - Ac: 0. - Dc: 0. - Rc: 0. - level: 1 - _meta: note_length: >- Numerical note length diff --git a/src/strauss/presets/synth/default.yml b/src/strauss/presets/synth/default.yml index aa4d518..f0859fc 100644 --- a/src/strauss/presets/synth/default.yml +++ b/src/strauss/presets/synth/default.yml @@ -146,17 +146,17 @@ _meta: volume_envelope: _doc: >- Define the note volume envelope applied to the samples. _'ADSR'_ is a common parametrisation in sound synthesis, - find out more e.g. [at this link](https://blg.native-instruments.com/adsr-explained/) + find out more e.g. [at this link]( https://learnmusicproduction.in/blogs/music-production-and-audio-engineering/adsr-fundamentals-in-music-everything-you-need-to-know) A: Attack, how long it takes for a sound to rise to 100% of the `level` after it’s triggered. D: Decay, how long it takes for the sounds volume to die down to the `Sustain` value after the `Attack` period. S: Sustain, the volume level (from 0 to 1.0) maintained after the `Decay` period, while the note is held. R: Release, how long the tone takes to finally die away once the note is released. - Ac: Curvature of the attack portion of a note. Values from -1 to 1, positive indicates increases quickly then slow, - negative slowly then quick. a value of 0 is a linear attack, increasing in volume at a constant rate. - Dc: Curvature of the Decay portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, - negative slowly then quick. a value of 0 is a linear decay, decreasing in volume at a constant rate. - Rc: Curvature of the release portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, - negative slowly then quick. a value of 0 is a linear release, decreasing in volume at a constant rate. + Ac: Curvature of the attack portion of the envelope. Values from -1 to 1, positive indicates increases quickly then slow, + negative slowly then quick. a value of 0 is a linear attack, increasing at a constant rate. + Dc: Curvature of the Decay portion of the envelope. Values from -1 to 1, positive indicates decreases quickly then slow, + negative slowly then quick. a value of 0 is a linear decay, decreasing at a constant rate. + Rc: Curvature of the release portion of the envelope. Values from -1 to 1, positive indicates decreases quickly then slow, + negative slowly then quick. a value of 0 is a linear release, decreasing at a constant rate. level: Total level of the envelope from 0 to 1, contolling maximum depth of the LFO. filter: >- Do we apply a frequency filter to the audio signal? This can be used to change the balance of frequencies and manipulate the 'timbre' of a note @@ -182,12 +182,12 @@ _meta: S: Sustain, the LFO depth (from 0 to 1.0) maintained after the `Decay` period, while the note is held. R: Release, how long LFO depth takes to finally die to 0 once the note is released. Ac: >- - "Curvature" of the attack portion of a note. Values from -1 to 1, positive indicates increases quickly then slow, negative slowly then quick. a value of 0 is a linear attack, increasing in volume at a constant rate. + "Curvature" of the attack portion of the envelope. Values from -1 to 1, positive indicates increases quickly then slow, negative slowly then quick. a value of 0 is a linear attack, increasing at a constant rate. Dc: >- - "Curvature" of the Decay portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, negative slowly then quick. a value of 0 is a linear decay, decreasing in volume at a constant rate. + "Curvature" of the Decay portion of the envelope. Values from -1 to 1, positive indicates decreases quickly then slow, negative slowly then quick. a value of 0 is a linear decay, decreasing at a constant rate. Rc: >- - "Curvature" of the release portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, negative slowly then quick. a value of 0 is a linear release, decreasing in volume at a constant rate. - level: Total amplitude level of the envelope from 0 to 1, contolling maximum volume of the note + "Curvature" of the release portion of the envelope. Values from -1 to 1, positive indicates decreases quickly then slow, negative slowly then quick. a value of 0 is a linear release, decreasing at a constant rate. + level: Total amplitude level of the envelope from 0 to 1, contolling maximum depth of the LFO. volume_lfo: _doc: >- Controls for the 'Low Frequency Oscillator' (LFO) used to modulate volume of notes at rhythmic @@ -200,16 +200,16 @@ _meta: freq: Base frequency of the LFO oscillations. freq_shift: shift relative to the bae LFO frequency phase: The phase of the LFO oscillations, defined in terms of fraction of a whole cycle - A: Attack, how long it takes for a tone to sound after it’s triggered - D: Decay, how long it takes for the tone’s attack to die down after it’s triggered - S: Sustain, the volume/level of the sound while it’s being triggered - R: Release, how long the tone takes to go silent after the trigger is released + A: Attack, how long it takes for the LFO depth to rise to 100% of the `level` after it’s triggered. + D: Decay, how long it takes for the LFO depth to die down to the `Sustain` value after the `Attack` period. + S: Sustain, the LFO depth (from 0 to 1.0) maintained after the `Decay` period, while the note is held. + R: Release, how long LFO depth takes to finally die to 0 once the note is released. Ac: >- - "Curvature" of the attack portion of a note. Values from -1 to 1, positive indicates increases quickly then slow, negative slowly then quick. a value of 0 is a linear attack, increasing in volume at a constant rate. + "Curvature" of the attack portion of the envelope. Values from -1 to 1, positive indicates increases quickly then slow, negative slowly then quick. a value of 0 is a linear attack, increasing at a constant rate. Dc: >- - "Curvature" of the Decay portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, negative slowly then quick. a value of 0 is a linear decay, decreasing in volume at a constant rate. + "Curvature" of the Decay portion of the envelope. Values from -1 to 1, positive indicates decreases quickly then slow, negative slowly then quick. a value of 0 is a linear decay, decreasing at a constant rate. Rc: >- - "Curvature" of the release portion of a note. Values from -1 to 1, positive indicates decreases quickly then slow, negative slowly then quick. a value of 0 is a linear release, decreasing in volume at a constant rate. + "Curvature" of the release portion of the envelope. Values from -1 to 1, positive indicates decreases quickly then slow, negative slowly then quick. a value of 0 is a linear release, decreasing at a constant rate. level: Total level of the envelope from 0 to 1, contolling maximum depth of the LFO. volume: >- Master Volume of synthesizer diff --git a/src/strauss/presets/synth/ranges/default.yml b/src/strauss/presets/synth/ranges/default.yml index 3431fe8..a649a93 100644 --- a/src/strauss/presets/synth/ranges/default.yml +++ b/src/strauss/presets/synth/ranges/default.yml @@ -17,12 +17,12 @@ volume_envelope: level: [0., 1.] A_unit: 'seconds' D_unit: 'seconds' - S_unit: 'unitless' + S_unit: 'fraction' R_unit: 'seconds' Ac_unit: 'unitless' Dc_unit: 'unitless' Rc_unit: 'unitless' - level_unit: 'unitless' + level_unit: 'fraction' volume_lfo: A: [1e-2, 20.] @@ -38,12 +38,12 @@ volume_lfo: freq_shift: [0., 3.] A_unit: 'seconds' D_unit: 'seconds' - S_unit: 'unitless' + S_unit: 'fraction' R_unit: 'seconds' Ac_unit: 'unitless' Dc_unit: 'unitless' Rc_unit: 'unitless' - level_unit: 'unitless' + level_unit: 'fraction' freq_unit: 'Hz' freq_shift_unit: 'octave' @@ -61,12 +61,12 @@ pitch_lfo: freq_shift: [0., 3.] A_unit: 'seconds' D_unit: 'seconds' - S_unit: 'unitless' + S_unit: 'fraction' R_unit: 'seconds' Ac_unit: 'unitless' Dc_unit: 'unitless' Rc_unit: 'unitless' - level_unit: 'unitless' + level_unit: 'fraction' amount_unit: 'semitones' freq_unit: 'Hz' freq_shift_unit: 'octave' @@ -74,21 +74,22 @@ pitch_lfo: # filter cutoff cutoff: [0., 1.] -cutoff_unit: 'unitless' +cutoff_unit: 'fraction' # Master volume volume: [0,1.] -volume_unit: 'unitless' +volume_unit: 'fraction' # Default pitch selection pitch: [0,1.] pitch_unit: 'unitless' # center panning: + azimuth: [0., 1.] -azimuth_unit: 'unitless' +azimuth_unit: 'cycles' polar: [0.,1.] -polar_unit: 'unitless' +polar_unit: 'half-cycles' # pitch range and default shift in semitones pitch_shift: [0., 24.] From b589382ad9008df665d32ba531782f2e789f8f1e Mon Sep 17 00:00:00 2001 From: James Trayford Date: Wed, 15 Jan 2025 21:28:21 +0000 Subject: [PATCH 82/86] remove decoy joss paper --- paper.md | 116 ------------------------------------------------------- 1 file changed, 116 deletions(-) delete mode 100644 paper.md diff --git a/paper.md b/paper.md deleted file mode 100644 index 3eeb87e..0000000 --- a/paper.md +++ /dev/null @@ -1,116 +0,0 @@ ---- -title: 'STRAUSS: Sonification Tools & Resources for Analysis Using Sound Synthesis' -tags: - - Python - - sonification - - data inspection - - astronomy -authors: - - name: James W. Trayford - orcid: 0000-0003-1530-1634 - corresponding: true # (This is how to denote the corresponding author) - equal-contrib: false - affiliation: 1 # (Multiple affiliations must be quoted) - - name: Samantha Youles - equal-contrib: true # (This is how you can denote equal contributions between multiple authors) - affiliation: 1 - - name: Chris Harrison - affiliation: 2 - equal-contrib: true # (This is how you can denote equal contributions between multiple authors) -affiliations: - - name: Institute of Cosmology and Gravitation, University of Portsmouth, Dennis Sciama Building, Burnaby Road, Portsmouth PO1 3FX, UK - index: 1 - - - name: School of Mathematics, Statistics and Physics, Newcastle University, NE1 7RU, UK - index: 2 -date: 23 August 2024 -bibliography: paper.bib - -# Optional fields if submitting to a AAS journal too, see this blog post: -# https://blog.joss.theoj.org/2018/12/a-new-collaboration-with-aas-publishing -aas-doi: 10.3847/xxxxx <- update this with the DOI from AAS once you know it. -aas-journal: Astrophysical Journal <- The name of the AAS journal. ---- - -# Summary - -Sonification, - -The forces on stars, galaxies, and dark matter under external gravitational -fields lead to the dynamical evolution of structures in the universe. The orbits -of these bodies are therefore key to understanding the formation, history, and -future state of galaxies. The field of "galactic dynamics," which aims to model -the gravitating components of galaxies to study their structure and evolution, -is now well-established, commonly taught, and frequently used in astronomy. -Aside from toy problems and demonstrations, the majority of problems require -efficient numerical tools, many of which require the same base code (e.g., for -performing numerical orbit integration). - -# Statement of need - -`Gala` is an Astropy-affiliated Python package for galactic dynamics. Python -enables wrapping low-level languages (e.g., C) for speed without losing -flexibility or ease-of-use in the user-interface. The API for `Gala` was -designed to provide a class-based and user-friendly interface to fast (C or -Cython-optimized) implementations of common operations such as gravitational -potential and force evaluation, orbit integration, dynamical transformations, -and chaos indicators for nonlinear dynamics. `Gala` also relies heavily on and -interfaces well with the implementations of physical units and astronomical -coordinate systems in the `Astropy` package [@astropy] (`astropy.units` and -`astropy.coordinates`). - -`Gala` was designed to be used by both astronomical researchers and by -students in courses on gravitational dynamics or astronomy. It has already been -used in a number of scientific publications [@Pearson:2017] and has also been -used in graduate courses on Galactic dynamics to, e.g., provide interactive -visualizations of textbook material [@Binney:2008]. The combination of speed, -design, and support for Astropy functionality in `Gala` will enable exciting -scientific explorations of forthcoming data releases from the *Gaia* mission -[@gaia] by students and experts alike. - -# Mathematics - -Single dollars ($) are required for inline mathematics e.g. $f(x) = e^{\pi/x}$ - -Double dollars make self-standing equations: - -$$\Theta(x) = \left\{\begin{array}{l} -0\textrm{ if } x < 0\cr -1\textrm{ else} -\end{array}\right.$$ - -You can also use plain \LaTeX for equations -\begin{equation}\label{eq:fourier} -\hat f(\omega) = \int_{-\infty}^{\infty} f(x) e^{i\omega x} dx -\end{equation} -and refer to \autoref{eq:fourier} from text. - -# Citations - -Citations to entries in paper.bib should be in -[rMarkdown](http://rmarkdown.rstudio.com/authoring_bibliographies_and_citations.html) -format. - -If you want to cite a software repository URL (e.g. something on GitHub without a preferred -citation) then you can do it with the example BibTeX entry below for @fidgit. - -For a quick reference, the following citation commands can be used: -- `@author:2001` -> "Author et al. (2001)" -- `[@author:2001]` -> "(Author et al., 2001)" -- `[@author1:2001; @author2:2001]` -> "(Author1 et al., 2001; Author2 et al., 2002)" - -# Figures - -Figures can be included like this: -![Caption for example figure.\label{fig:example}](figure.png) -and referenced from text using \autoref{fig:example}. - -Figure sizes can be customized by adding an optional second parameter: -![Caption for example figure.](figure.png){ width=20% } - -# Acknowledgements - -We acknowledge contributions from Brigitta Sipocz, Syrtis Major, and Semyeong -Oh, and support from Kathryn Johnston during the genesis of this project. - -# References~ \ No newline at end of file From a7d6592415576efe17dfaa5f7ce00bf528e13658 Mon Sep 17 00:00:00 2001 From: James Trayford Date: Wed, 15 Jan 2025 21:44:07 +0000 Subject: [PATCH 83/86] remove defunct md files for presets --- src/strauss/presets/sampler/default.md | 110 ----------------- src/strauss/presets/sampler/staccato.md | 16 --- src/strauss/presets/sampler/sustain.md | 10 -- src/strauss/presets/spec/default.md | 116 ------------------ src/strauss/presets/synth/default.md | 138 ---------------------- src/strauss/presets/synth/pitch_mapper.md | 22 ---- src/strauss/presets/synth/spectraliser.md | 16 --- src/strauss/presets/synth/windy.md | 22 ---- 8 files changed, 450 deletions(-) delete mode 100644 src/strauss/presets/sampler/default.md delete mode 100644 src/strauss/presets/sampler/staccato.md delete mode 100644 src/strauss/presets/sampler/sustain.md delete mode 100644 src/strauss/presets/spec/default.md delete mode 100644 src/strauss/presets/synth/default.md delete mode 100644 src/strauss/presets/synth/pitch_mapper.md delete mode 100644 src/strauss/presets/synth/spectraliser.md delete mode 100644 src/strauss/presets/synth/windy.md diff --git a/src/strauss/presets/sampler/default.md b/src/strauss/presets/sampler/default.md deleted file mode 100644 index f8568b2..0000000 --- a/src/strauss/presets/sampler/default.md +++ /dev/null @@ -1,110 +0,0 @@ -## Name -preset name -## Description -full description -## Note_Length -Numerical note length in s or "sample" for the sample length or "none" to last to the end of the -## Looping -"off" for no looping "forward" to loop forward "forwardback" to loop back and forth -## Loop_Start -If looping, start and end point of loop in seconds. -## Loop_End -If loop_end is longer than the sample, clip to end of the sample. -## Volume_Envelope -### A -Attack -### D -Decay -### S -Sustain -### R -Release -### Ac - -### Dc - -### Rc - -### Level - - -## Filter -Do we apply a filter? -## Filter_Type -filter type -## Cutoff -filter cutoff -## Pitch_Lfo -### Use -switch feature on or off -### Wave -type of waveform -### Amount -amount -### Freq -frequency -### Freq_Shift -frequency shift -### Phase -random -### A -Attack -### D -Decay -### S -Sustain -### R -Release -### Ac - -### Dc - -### Rc - -### Level - - -## Volume_Lfo -### Use -switch feature on or off -### Wave -type of waveform -### Amount -amount -### Freq -frequency -### Freq_Shift -frequency shift -### Phase -random -### A -Attack -### D -Decay -### S -Sustain -### R -Release -### Ac - -### Dc - -### Rc - -### Level - - -## Volume -Master Volume -## Pitch -Default pitch selection -## Azimuth -azimuth coordinate for center panning -## Polar -polar coordinate for center panning -## Pitch_Hi -pitch range maximum in semitones -## Pitch_Lo -pitch range minimum in semitones -## Pitch_Shift -default shift in semitones diff --git a/src/strauss/presets/sampler/staccato.md b/src/strauss/presets/sampler/staccato.md deleted file mode 100644 index e5ef052..0000000 --- a/src/strauss/presets/sampler/staccato.md +++ /dev/null @@ -1,16 +0,0 @@ -## Name -preset name -## Description -full description -## Note_Length -Numerical note length in s or "sample" for the sample length or "none" to last to the end of the -## Volume_Envelope -### A -Attack -### D -Decay -### S -Sustain -### R -Release - diff --git a/src/strauss/presets/sampler/sustain.md b/src/strauss/presets/sampler/sustain.md deleted file mode 100644 index f947ed3..0000000 --- a/src/strauss/presets/sampler/sustain.md +++ /dev/null @@ -1,10 +0,0 @@ -## Name -preset name -## Description -full description -## Looping -use the 'forwardback' looping mode by default, between 0.2 and 0.5 seconds for each sample -## Loop_Start -start of the loop in seconds -## Loop_End -end of the loop in seconds diff --git a/src/strauss/presets/spec/default.md b/src/strauss/presets/spec/default.md deleted file mode 100644 index f8485c8..0000000 --- a/src/strauss/presets/spec/default.md +++ /dev/null @@ -1,116 +0,0 @@ -## _Meta -### Name -preset name -### Description -full description -### Note_Length -Numerical note length in s -### Volume_Envelope -#### A -Attack -#### D -Decay -#### S -Sustain -#### R -Release -#### Ac - -#### Dc - -#### Rc - -#### Level - - -### Filter -Do we apply a filter? -### Filter_Type -filter type -### Cutoff -filter cutoff -### Pitch_Lfo -#### Use -switch feature on or off -#### Wave -type of waveform -#### Amount -amount -#### Freq -frequency -#### Freq_Shift -frequency shift -#### Phase -random -#### A -Attack -#### D -Decay -#### S -Sustain -#### R -Release -#### Ac - -#### Dc - -#### Rc - -#### Level - - -### Volume_Lfo -#### Use -switch feature on or off -#### Wave -type of waveform -#### Amount -amount -#### Freq -frequency -#### Freq_Shift -frequency shift -#### Phase -random -#### A -Attack -#### D -Decay -#### S -Sustain -#### R -Release -#### Ac - -#### Dc - -#### Rc - -#### Level - - -### Volume -Master Volume -### Interpolation_Type -How to interpolate and resample in the spectrum. "sample": interpolate spectrum values directly; "preserve_power": integrate, interpolate then differentiate to avoid missing power in narrow features. -### Regen_Phases -For an evolving spectrum, do we regenerate phases for each buffer, or keep the same? These have differing effects. -### Fit_Spec_Multiples -Whether or not to generate IFFT such that the spectrum sample points are hit exactly. -### Min_Freq -Minimum frequency in Hz -### Max_Freq -Maximum frequency in Hz -### Pitch -Default pitch selection -### Azimuth -azimuth coordinate for center panning -### Polar -polar coordinate for center panning -### Pitch_Hi -pitch range maximum in semitones -### Pitch_Lo -pitch range minimum in semitones -### Pitch_Shift -default shift in semitones - diff --git a/src/strauss/presets/synth/default.md b/src/strauss/presets/synth/default.md deleted file mode 100644 index cf3eafd..0000000 --- a/src/strauss/presets/synth/default.md +++ /dev/null @@ -1,138 +0,0 @@ -## _Meta -### Name -preset name -### Description -full description -### Oscillators -#### Osc1 -##### Form -waveform, choose from ['saw', 'square', 'sine', 'tri', 'noise'] -##### Level -intrinsic volume -##### Detune -change in tuning as a percentage of the input frequency -##### Phase -phase - -#### Osc2 -##### Form -waveform, choose from ['saw', 'square', 'sine', 'tri', 'noise'] -##### Level -intrinsic volume -##### Detune -change in tuning as a percentage of the input frequency -##### Phase -phase - -#### Osc3 -##### Form -waveform, choose from ['saw', 'square', 'sine', 'tri', 'noise'] -##### Level -intrinsic volume -##### Detune -change in tuning as a percentage of the input frequency -##### Phase -phase - - -### Note_Length -Numerical note length in s -### Volume_Envelope -#### A -Attack -#### D -Decay -#### S -Sustain -#### R -Release -#### Ac - -#### Dc - -#### Rc - -#### Level - - -### Filter -Do we apply a filter? -### Filter_Type -filter type -### Cutoff -filter cutoff -### Pitch_Lfo -#### Use -switch feature on or off -#### Wave -type of waveform -#### Amount -amount -#### Freq -frequency -#### Freq_Shift -frequency shift -#### Phase -random -#### A -Attack -#### D -Decay -#### S -Sustain -#### R -Release -#### Ac - -#### Dc - -#### Rc - -#### Level - - -### Volume_Lfo -#### Use -switch feature on or off -#### Wave -type of waveform -#### Amount -amount -#### Freq -frequency -#### Freq_Shift -frequency shift -#### Phase -random -#### A -Attack -#### D -Decay -#### S -Sustain -#### R -Release -#### Ac - -#### Dc - -#### Rc - -#### Level - - -### Volume -Master Volume -### Pitch -Default pitch selection -### Azimuth -azimuth coordinate for center panning -### Polar -polar coordinate for center panning -### Pitch_Hi -pitch range maximum in semitones -### Pitch_Lo -pitch range minimum in semitones -### Pitch_Shift -default shift in semitones - diff --git a/src/strauss/presets/synth/pitch_mapper.md b/src/strauss/presets/synth/pitch_mapper.md deleted file mode 100644 index eb75c6b..0000000 --- a/src/strauss/presets/synth/pitch_mapper.md +++ /dev/null @@ -1,22 +0,0 @@ -## Name -preset name -## Description -full description -## Oscillators -### Osc1 -#### Form -waveform, choose from ['saw', 'square', 'sine', 'tri', 'noise'] -#### Level -intrinsic volume -#### Detune -change in tuning as a percentage of the input frequency -#### Phase -phase - - -## Pitch_Hi -pitch range maximum in semitones -## Pitch_Lo -pitch range minimum in semitones -## Pitch_Shift -default shift in semitones diff --git a/src/strauss/presets/synth/spectraliser.md b/src/strauss/presets/synth/spectraliser.md deleted file mode 100644 index cb53ecb..0000000 --- a/src/strauss/presets/synth/spectraliser.md +++ /dev/null @@ -1,16 +0,0 @@ -## Name -preset name -## Description -full description -## Oscillators -### Osc1 -#### Form -waveform, choose from ['saw', 'square', 'sine', 'tri', 'noise'] -#### Level -intrinsic volume -#### Detune -change in tuning as a percentage of the input frequency -#### Phase -phase - - diff --git a/src/strauss/presets/synth/windy.md b/src/strauss/presets/synth/windy.md deleted file mode 100644 index 60f909d..0000000 --- a/src/strauss/presets/synth/windy.md +++ /dev/null @@ -1,22 +0,0 @@ -## Name -preset name -## Description -full description -## Oscillators -### Osc1 -#### Form -waveform, choose from ['saw', 'square', 'sine', 'tri', 'noise'] -#### Level -intrinsic volume -#### Detune -change in tuning as a percentage of the input frequency -#### Phase -phase - - -## Filter -Do we apply a filter? -## Filter_Type -filter type -## Cutoff -filter cutoff From 2dda3f437a4054248df701096839232c6161997f Mon Sep 17 00:00:00 2001 From: James Trayford Date: Wed, 15 Jan 2025 22:04:18 +0000 Subject: [PATCH 84/86] remove more obsolete .md files --- src/strauss/presets/sampler/ranges/default.md | 62 ---------------- src/strauss/presets/spec/ranges/default.md | 70 ------------------- src/strauss/presets/synth/ranges/default.md | 62 ---------------- 3 files changed, 194 deletions(-) delete mode 100644 src/strauss/presets/sampler/ranges/default.md delete mode 100644 src/strauss/presets/spec/ranges/default.md delete mode 100644 src/strauss/presets/synth/ranges/default.md diff --git a/src/strauss/presets/sampler/ranges/default.md b/src/strauss/presets/sampler/ranges/default.md deleted file mode 100644 index f6caa40..0000000 --- a/src/strauss/presets/sampler/ranges/default.md +++ /dev/null @@ -1,62 +0,0 @@ -## Note_Length -Numerical note length -## Note_Length_Units -Units for numerical note length, e.g. seconds -## Volume_Envelope -### A -Attack -### D -Decay -### S -Sustain -### R -Release -### Ac - -### Dc - -### Rc - -### Level - -### A_Unit -units for Attack -### D_Unit -units for Decay -### S_Unit -units for Sustain -### R_Unit -units for Release -### Ac_Unit -units for Ac -### Dc_Unit -units for Dc -### Rc_Unit -units for Rc -### Level_Unit -units for level - -## Cutoff -filter cutoff -## Cutoff_Unit -units for filter cutoff -## Volume -Master volume -## Volume_Unit -Units for master volume -## Pitch -Default pitch selection -## Pitch_Unit -Units for Default pitch selection -## Azimuth -azimuth coordinate for center panning -## Azimuth_Unit -units for azimuth coordinate for center panning -## Polar -polar coordinate for center panning -## Polar_Unit -units for polar coordinate for center panning -## Pitch_Shift -default shift in semitones -## Pitch_Shift_Unit -units for default shift in semitones diff --git a/src/strauss/presets/spec/ranges/default.md b/src/strauss/presets/spec/ranges/default.md deleted file mode 100644 index 4c5bea3..0000000 --- a/src/strauss/presets/spec/ranges/default.md +++ /dev/null @@ -1,70 +0,0 @@ -## Note_Length -Numerical note length -## Note_Length_Units -Units for numerical note length, e.g. seconds -## Volume_Envelope -### A -Attack -### D -Decay -### S -Sustain -### R -Release -### Ac - -### Dc - -### Rc - -### Level - -### A_Unit -units for Attack -### D_Unit -units for Decay -### S_Unit -units for Sustain -### R_Unit -units for Release -### Ac_Unit -units for Ac -### Dc_Unit -units for Dc -### Rc_Unit -units for Rc -### Level_Unit -units for level - -## Cutoff -filter cutoff -## Cutoff_Unit -units for filter cutoff -## Volume -Master volume -## Volume_Unit -Units for master volume -## Min_Freq -Minimum frequency -## Max_Freq -Maximum frequency -## Min_Freq_Unit -Units for minimum frequency -## Max_Freq_Unit -Units for maximum frequency -## Pitch -Default pitch selection -## Pitch_Unit -Units for Default pitch selection -## Phi -phi coordinate for center panning -## Phi_Unit -units for phi coordinate for center panning -## Theta -theta coordinate for center panning -## Theta_Unit -units for theta coordinate for center panning -## Pitch_Shift -default shift in semitones -## Pitch_Shift_Unit -units for default shift in semitones diff --git a/src/strauss/presets/synth/ranges/default.md b/src/strauss/presets/synth/ranges/default.md deleted file mode 100644 index f6caa40..0000000 --- a/src/strauss/presets/synth/ranges/default.md +++ /dev/null @@ -1,62 +0,0 @@ -## Note_Length -Numerical note length -## Note_Length_Units -Units for numerical note length, e.g. seconds -## Volume_Envelope -### A -Attack -### D -Decay -### S -Sustain -### R -Release -### Ac - -### Dc - -### Rc - -### Level - -### A_Unit -units for Attack -### D_Unit -units for Decay -### S_Unit -units for Sustain -### R_Unit -units for Release -### Ac_Unit -units for Ac -### Dc_Unit -units for Dc -### Rc_Unit -units for Rc -### Level_Unit -units for level - -## Cutoff -filter cutoff -## Cutoff_Unit -units for filter cutoff -## Volume -Master volume -## Volume_Unit -Units for master volume -## Pitch -Default pitch selection -## Pitch_Unit -Units for Default pitch selection -## Azimuth -azimuth coordinate for center panning -## Azimuth_Unit -units for azimuth coordinate for center panning -## Polar -polar coordinate for center panning -## Polar_Unit -units for polar coordinate for center panning -## Pitch_Shift -default shift in semitones -## Pitch_Shift_Unit -units for default shift in semitones From 06f2429f03327d8ccfe5a787409a1f8368e65d0c Mon Sep 17 00:00:00 2001 From: James Trayford Date: Wed, 15 Jan 2025 22:06:39 +0000 Subject: [PATCH 85/86] get rid of old yaml to md script --- yamls_to_tables.py | 66 ---------------------------------------------- 1 file changed, 66 deletions(-) delete mode 100644 yamls_to_tables.py diff --git a/yamls_to_tables.py b/yamls_to_tables.py deleted file mode 100644 index a2f9221..0000000 --- a/yamls_to_tables.py +++ /dev/null @@ -1,66 +0,0 @@ - -import yaml -from glob import glob -from pathlib import Path - -generators = {'spec' : "`Spectraliser` Generator", - 'synth' : "`Synthesiser` Generator", - 'sampler' : "`Sampler` Generator"} - -# p = Path(__file__) -p = Path("src", "strauss", "presets", "*", "default.yml") - -def read_yaml(filename): - with filename.open(mode='r') as fdata: - # try: - yamldict = yaml.safe_load(fdata) - # except yaml.YAMLError as err: - # print(err) - return yamldict - -def yaml_traverse(metadict, valdict, rdict, headlev=1): - if hasattr(metadict, 'keys'): - topstr = '' - tabstr = '' - secstr = '' - tabstr += "\n| Parameter | Description | Default Value | Default Range | Unit |\n" - tabstr += "| ----------- | ----------- | ----------- | ----------- | ----------- |\n" - - for k in metadict.keys(): - # print (f">>>>>> {k}") - if hasattr(metadict[k], 'keys'): - secstr += '\n'+''.join(['#']*headlev) + f" `{k}` parameter group\n" - if not k in rdict: - rdict[k] = {} - secstr += yaml_traverse(metadict[k], valdict[k], rdict[k], headlev+1) - continue - if k == '_doc': - topstr += f"\n{metadict[k]}\n" - # print('\n',metadict[k]) - continue - if k not in rdict: - # unspecified => '-' - rdict[k] = '-' - else: - # lets avoid these line-breaking with special characters - rdict[k] = str(rdict[k]).replace(" ","").replace(",","\u2011").replace("-","\u2011") - if k+"_unit" not in rdict: - # unspecified => '-' - rdict[k+"_unit"] = '-' - # print(f"|`{k}` | _{metadict[k]}_ | `{str(valdict[k]).strip()}` | `{rdict[k]}` | {rdict[k+'_unit']}") - tabstr += f"| `{k}` | {str(metadict[k]).strip()} | {valdict[k]} | `{rdict[k]}` | {rdict[k+'_unit']}\n" - if k not in rdict: - rdict[k] = {} - return f'{topstr}{tabstr}{secstr}' - - else: - return - -for f in glob(str(p)): - p = Path(f) - ydat = read_yaml(p) - rdat = read_yaml(p.parents[0] / "ranges" / "default.yml") - print(f"\n# {generators[p.parents[0].name]}\n") - ystr = yaml_traverse(ydat['_meta'], ydat, rdat, 2) - print(ystr) - print('---') From 834eaa6d35980f6e85b4770b8486cd155d72fb2a Mon Sep 17 00:00:00 2001 From: James Trayford Date: Wed, 15 Jan 2025 22:47:09 +0000 Subject: [PATCH 86/86] update link --- docs/examples.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/examples.rst b/docs/examples.rst index 21c2c8c..e1b834f 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -25,7 +25,7 @@ Earth System (:code:`EarthSystem.ipynb`) **************************************** The *Earth System* sonification represents the ratio of ocean to land along lines of longitude as the Earth spins through three rotation cycles. We use the Strauss :code:`Synthesiser` to generate chords. A low pass filter is employed to generate a brighter sound to represent a high water fraction and a duller sound for high land fraction. -A video of this sequence is available starting at 2:17 in `this video `_. +A video of this sequence is available starting at 2:17 in `this video `_. The code is available in a `Google Colab notebook `_.