diff --git a/src/synth.ts b/src/synth.ts index 12c7e5a2..03540ad8 100644 --- a/src/synth.ts +++ b/src/synth.ts @@ -8,10 +8,11 @@ import { computed, type ComputedRef } from 'vue' import TIMBRES from '@/timbres.json' type Spectrum = number[] +type Timbre = { spectrum: Spectrum; amplitudes: number[]; source?: string } type Timbres = { plainSpectra: { [key: string]: Spectrum } - // TODO: Migrate inharmonic metallic and some other timbral data + timbres: { [key: string]: Timbre } } function getPlainSpectrum(id: string): Spectrum { @@ -19,7 +20,15 @@ function getPlainSpectrum(id: string): Spectrum { } function getPlainSpectraWaveformNames(): string[] { - return Object.keys((TIMBRES as unknown as Timbres).plainSpectra) + return Object.keys((TIMBRES as unknown as Timbres).plainSpectra).sort() +} + +function getTimbre(id: string): Timbre { + return (TIMBRES as unknown as Timbres).timbres[id] +} + +function getTimbreWaveformNames(): string[] { + return Object.keys((TIMBRES as unknown as Timbres).timbres).sort() } export const BASIC_WAVEFORMS = ['sine', 'square', 'sawtooth', 'triangle'] @@ -49,6 +58,7 @@ export const CUSTOM_WAVEFORMS = [ export const WAVEFORMS = BASIC_WAVEFORMS.concat(CUSTOM_WAVEFORMS) export const PERIODIC_WAVES: Record> = {} +// Some of these have entries in timbres.json, but we preserve the old UI order. export const APERIODIC_WAVEFORMS = [ 'jegogan', 'jublag', @@ -332,55 +342,6 @@ function initializeAperiodic(audioContext: BaseAudioContext) { return new AperiodicWave(audioContext, spectrum, amplitudes, maxNumberOfVoices, tolerance) }) - // https://pubs.aip.org/asa/jasa/article/127/5/EL197/783208/Vibrational-characteristics-of-Balinese-gamelan - APERIODIC_WAVES['jublag'] = computed(() => { - // Spectrum is from literature - const spectrum = [1, 2.77, 5.18, 5.33] - // Made up stuff to round it off - spectrum.push(9.1, 18.9, 23) - // Add shimmer - spectrum[0] = 1.01 - spectrum.unshift(1 / spectrum[0]) - spectrum.push(2.76) - - // Amplitudes are made up - const amplitudes = [1, 0.5, 0.5, 0.3, 0.2, 0.15, 0.1, 0.09, 0.2].map((a) => 0.45 * a) - - return new AperiodicWave(audioContext, spectrum, amplitudes, maxNumberOfVoices, tolerance) - }) - - // https://pubs.aip.org/asa/jasa/article/127/5/EL197/783208/Vibrational-characteristics-of-Balinese-gamelan - APERIODIC_WAVES['ugal'] = computed(() => { - // Spectrum is from literature - const spectrum = [1, 2.61, 4.8, 4.94, 6.32] - // Made up stuff to round it off - spectrum.push(9.9, 17, 24.1) - // Add shimmer - spectrum[0] = 1.008 - spectrum.unshift(1 / spectrum[0]) - spectrum.push(2.605) - spectrum.push(4.81) - - // Amplitudes are made up - const amplitudes = [0.6, 1, 0.45, 0.3, 0.15, 0.2, 0.07, 0.08, 0.05, 0.1, 0.1].map( - (a) => 0.45 * a - ) - - return new AperiodicWave(audioContext, spectrum, amplitudes, maxNumberOfVoices, tolerance) - }) - - // https://pubs.aip.org/asa/jasa/article/127/5/EL197/783208/Vibrational-characteristics-of-Balinese-gamelan - APERIODIC_WAVES['jegogan'] = computed(() => { - // Spectrum is from literature - const spectrum = [ - 1, 2.8, 5.5, 9, 16.7, 17.8, 20.5, 22.9, 24.9, 27, 28.1, 29.2, 29.5, 30, 31.8, 33.3, 36, 36.9, - 40.6, 41.4 - ] - // Amplitudes are made up - const amplitudes = spectrum.map((i) => (0.7 * (Math.cos(0.3 * i * i) + 1.6)) / (i ** 1.4 + 1.6)) - return new AperiodicWave(audioContext, spectrum, amplitudes, maxNumberOfVoices, tolerance) - }) - APERIODIC_WAVES['12-TET'] = computed(() => { const twelveSpectrumCents: number[] = [] const twelveAmplitudes: number[] = [] @@ -405,26 +366,6 @@ function initializeAperiodic(audioContext: BaseAudioContext) { ) }) - APERIODIC_WAVES['piano'] = computed(() => { - const spectrum = [ - 0.998711340392508, 1.0012886596074921, 2.000000001915048, 3.0077319605175252, - 4.024484537329971, 4.028350517109971, 5.052835053482418, 6.0953608281898495, - 6.100515466619817, 7.149484540322233, 7.158505158532202, 8.221649488874554, 9.326030932401801, - 9.3298969121818, 9.33891753039177, 10.449742272914, 10.457474232474, 10.466494850683969, - 11.597938150756168 - ] - - const amps = [ - 0.9123120265773679, 0.7281477301038842, 0.5078045641543809, 0.8061314224800064, - 0.3177244232370868, 0.15135058363038334, 0.12440135191000032, 0.045007651288955175, - 0.050804443667738106, 0.029671376885221354, 0.023841125287306208, 0.01853341284211317, - 0.02380292893502422, 0.024761095205029133, 0.020866326567241505, 0.0017670624571622458, - 0.0024893662658642206, 0.0012043792096897129, 0.0014228119365412375 - ].map((a) => a * 0.38) - - return new AperiodicWave(audioContext, spectrum, amps, maxNumberOfVoices, tolerance) - }) - getPlainSpectraWaveformNames().forEach((id) => { APERIODIC_WAVES[id] = computed(() => { const spectrum = getPlainSpectrum(id) @@ -436,6 +377,13 @@ function initializeAperiodic(audioContext: BaseAudioContext) { return new AperiodicWave(audioContext, spectrum, amps, maxNumberOfVoices, tolerance) }) }) + + getTimbreWaveformNames().forEach((id) => { + APERIODIC_WAVES[id] = computed(() => { + const { spectrum, amplitudes } = getTimbre(id) + return new AperiodicWave(audioContext, spectrum, amplitudes, maxNumberOfVoices, tolerance) + }) + }) } export function initializeCustomWaves(audioContext: BaseAudioContext) { diff --git a/src/timbres.json b/src/timbres.json index 58329003..276c0b9a 100644 --- a/src/timbres.json +++ b/src/timbres.json @@ -65,5 +65,47 @@ 47.864014, 48.864014, 50.329585, 51.329585, 52.477484, 53.477484, 54.625383, 55.625383, 56.625383, 57.773282, 58.773282 ] + }, + "timbres": { + "jegogan": { + "spectrum": [ + 1, 2.8, 5.5, 9, 16.7, 17.8, 20.5, 22.9, 24.9, 27, 28.1, 29.2, 29.5, 30, 31.8, 33.3, 36, + 36.9, 40.6, 41.4 + ], + "amplitudes": [ + 0.6879752086107401, 0.10762266937637699, 0.03705915578882312, 0.06835927457535572, + 0.01577978118293504, 0.027725128514115686, 0.025086932797794458, 0.022017323971778323, + 0.0061329366021215925, 0.013327650595532958, 0.008386387560340483, 0.008303976140444933, + 0.0039384178057395615, 0.015261008676063096, 0.007590483984245218, 0.01299488244216124, + 0.010676041856564743, 0.011516704413238312, 0.005093296889963593, 0.007984328157770058 + ], + "source": "https://pubs.aip.org/asa/jasa/article/127/5/EL197/783208/Vibrational-characteristics-of-Balinese-gamelan (amplitudes are made up)" + }, + "jublag": { + "spectrum": [0.9901, 1.01, 2.76, 2.77, 5.18, 5.33, 9.1, 18.9, 23], + "amplitudes": [0.45, 0.225, 0.09, 0.225, 0.135, 0.09, 0.0675, 0.045, 0.0405], + "source": "https://pubs.aip.org/asa/jasa/article/127/5/EL197/783208/Vibrational-characteristics-of-Balinese-gamelan (shimmer and amplitudes are made up)" + }, + "ugal": { + "spectrum": [0.9921, 1.008, 2.605, 2.61, 4.8, 4.81, 4.94, 6.32, 9.9, 17, 24.1], + "amplitudes": [0.27, 0.45, 0.045, 0.2025, 0.135, 0.045, 0.0675, 0.09, 0.0315, 0.036, 0.0225], + "source": "https://pubs.aip.org/asa/jasa/article/127/5/EL197/783208/Vibrational-characteristics-of-Balinese-gamelan (shimmer and amplitudes are made up)" + }, + "piano": { + "spectrum": [ + 0.998711340392508, 1.0012886596074921, 2.000000001915048, 3.0077319605175252, + 4.024484537329971, 4.028350517109971, 5.052835053482418, 6.0953608281898495, + 6.100515466619817, 7.149484540322233, 7.158505158532202, 8.221649488874554, + 9.326030932401801, 9.3298969121818, 9.33891753039177, 10.449742272914, 10.457474232474, + 10.466494850683969, 11.597938150756168 + ], + "amplitudes": [ + 0.3466785700993998, 0.276696137439476, 0.19296573437866474, 0.3063299405424024, + 0.12073528083009298, 0.05751322177954567, 0.047272513725800124, 0.017102907489802966, + 0.019305688593740482, 0.011275123216384115, 0.00905962760917636, 0.007042696880003005, + 0.009045112995309204, 0.009409216177911071, 0.007929204095551772, 0.0006714837337216534, + 0.0009459591810284039, 0.0004576640996820909, 0.0005406685358856703 + ] + } } }