From ea59173a036343f92d48229501b04ee454a17fb1 Mon Sep 17 00:00:00 2001 From: Jacek Olszak Date: Sat, 2 Sep 2023 21:01:24 +0200 Subject: [PATCH] Change oscillator waveform func based on sfx note instrument --- audio/synth.go | 2 +- audio/synth_test.go | 52 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/audio/synth.go b/audio/synth.go index 360f180..796fb1b 100644 --- a/audio/synth.go +++ b/audio/synth.go @@ -51,7 +51,7 @@ func (s *Synthesizer) ReadSamples(buffer []float64) { if ch.noteNo == len(sfx.Notes) { ch.playing = false } else { - //ch.oscillator.Func = oscillatorFunc(sfx.Notes[ch.noteNo].Instrument) + ch.oscillator.Func = oscillatorFunc(sfx.Notes[ch.noteNo].Instrument) ch.oscillator.FreqHz = pitchToFreq(sfx.Notes[ch.noteNo].Pitch) } } diff --git a/audio/synth_test.go b/audio/synth_test.go index bba97de..1f8e5aa 100644 --- a/audio/synth_test.go +++ b/audio/synth_test.go @@ -479,6 +479,26 @@ func TestSynthesizer_Sfx(t *testing.T) { // then assert.True(t, dominantFrequency(buffer1) < dominantFrequency(buffer2), "frequency of pitch C1 must be smaller than D#5") }) + + t.Run("should generate different wave when second note has a different instrument", func(t *testing.T) { + e := audio.SoundEffect{ + Notes: [32]audio.Note{ + {Instrument: audio.InstrumentTriangle, Volume: audio.VolumeLoudest}, + {Instrument: audio.InstrumentSaw, Volume: audio.VolumeLoudest}, + }, + Speed: 32, + } + synth := audio.Synthesizer{} + synth.SetSfx(0, e) + synth.Sfx(0, 0, 0, 31) + buffer1 := make([]float64, 32*durationOfNoteWhenSpeedIsOne) + synth.ReadSamples(buffer1) + buffer2 := make([]float64, 32*durationOfNoteWhenSpeedIsOne) + // when + synth.ReadSamples(buffer2) + // then + assertDifferentShape(t, buffer1, buffer2) + }) } func clone(s []byte) []byte { @@ -562,3 +582,35 @@ func hfft(input []float64, freqs []complex128, n, step int) { freqs[k], freqs[k+h] = freqs[k]+e, freqs[k]-e } } + +func assertDifferentShape(t *testing.T, buffer1, buffer2 []float64) { + dtw := dtwDistance(buffer1, buffer2) + assert.Truef(t, dtw >= 30.00, "Waves should have different shape, but dtw distance = %f. Must be >= 30.00", dtw) +} + +// dtwDistance calculates dynamic time warping distance between two signals +func dtwDistance(signal1, signal2 []float64) float64 { + len1, len2 := len(signal1), len(signal2) + dtw := make([][]float64, len1+1) + for i := range dtw { + dtw[i] = make([]float64, len2+1) + } + + for i := 1; i <= len1; i++ { + for j := 1; j <= len2; j++ { + cost := math.Abs(signal1[i-1] - signal2[j-1]) + dtw[i][j] = cost + min3(dtw[i-1][j], dtw[i][j-1], dtw[i-1][j-1]) + } + } + + return dtw[len1][len2] +} + +func min3(a, b, c float64) float64 { + if a <= b && a <= c { + return a + } else if b <= a && b <= c { + return b + } + return c +}