The piano now has an internal ADSR

This commit is contained in:
Sasha Koshka 2023-02-09 23:52:27 -05:00
parent cfc2b5e130
commit 182cb1e35b

View File

@ -1,6 +1,7 @@
package main
import "math"
import "time"
import "errors"
import "github.com/faiface/beep"
import "github.com/faiface/beep/speaker"
@ -15,7 +16,7 @@ const sampleRate = 44100
const bufferSize = 256
var tuning = music.EqualTemparment { A4: 440 }
var waveform = 0
var playing = map[music.Note] *beep.Ctrl { }
var playing = map[music.Note] *toneStreamer { }
func main () {
speaker.Init(sampleRate, bufferSize)
@ -59,17 +60,27 @@ func stopNote (note music.Note) {
if _, is := playing[note]; !is { return }
speaker.Lock()
playing[note].Streamer = nil
playing[note].Release()
delete(playing, note)
speaker.Unlock()
}
func playNote (note music.Note) {
streamer, _ := Tone(sampleRate, int(tuning.Tune(note)), waveform, 0.3)
streamer, _ := Tone (
sampleRate,
int(tuning.Tune(note)),
waveform,
0.3,
ADSR {
Attack: 100 * time.Millisecond,
Decay: 400 * time.Millisecond,
Sustain: 0.7,
Release: 500 * time.Millisecond,
})
stopNote(note)
speaker.Lock()
playing[note] = &beep.Ctrl { Streamer: streamer }
playing[note] = streamer
speaker.Unlock()
speaker.Play(playing[note])
}
@ -80,30 +91,62 @@ func playNote (note music.Note) {
type toneStreamer struct {
position float64
delta float64
waveform int
gain float64
adsr ADSR
released bool
complete bool
adsrPhase int
adsrPosition float64
adsrDeltas [4]float64
}
type ADSR struct {
Attack time.Duration
Decay time.Duration
Sustain float64
Release time.Duration
}
func Tone (
sr beep.SampleRate,
freq int,
sampleRate beep.SampleRate,
frequency int,
waveform int,
gain float64,
adsr ADSR,
) (
beep.Streamer,
*toneStreamer,
error,
) {
if int(sr) / freq < 2 {
if int(sampleRate) / frequency < 2 {
return nil, errors.New (
"tone generator: samplerate must be at least " +
"2 times greater then frequency")
}
tone := new(toneStreamer)
tone.position = 0.0
tone.waveform = waveform
steps := float64(sr) / float64(freq)
tone.position = 0.0
steps := float64(sampleRate) / float64(frequency)
tone.delta = 1.0 / steps
tone.gain = gain
if adsr.Attack < time.Millisecond { adsr.Attack = time.Millisecond }
if adsr.Decay < time.Millisecond { adsr.Decay = time.Millisecond }
if adsr.Release < time.Millisecond { adsr.Release = time.Millisecond }
tone.adsr = adsr
attackSteps := adsr.Attack.Seconds() * float64(sampleRate)
decaySteps := adsr.Decay.Seconds() * float64(sampleRate)
releaseSteps := adsr.Release.Seconds() * float64(sampleRate)
tone.adsrDeltas[0] = 1 / attackSteps
tone.adsrDeltas[1] = 1 / decaySteps
tone.adsrDeltas[2] = 0
tone.adsrDeltas[3] = 1 / releaseSteps
return tone, nil
}
@ -127,17 +170,59 @@ func (tone *toneStreamer) nextSample () (sample float64) {
28.32 * tone.position * tone.position +
15.62 * tone.position * tone.position * tone.position
}
adsrGain := 0.0
switch tone.adsrPhase {
case 0: adsrGain = tone.adsrPosition
if tone.adsrPosition > 1 {
tone.adsrPosition = 0
tone.adsrPhase = 1
}
case 1: adsrGain = 1 + tone.adsrPosition * (tone.adsr.Sustain - 1)
if tone.adsrPosition > 1 {
tone.adsrPosition = 0
tone.adsrPhase = 2
}
case 2: adsrGain = tone.adsr.Sustain
if tone.released {
tone.adsrPhase = 3
}
case 3: adsrGain = (1 - tone.adsrPosition) * tone.adsr.Sustain
if tone.adsrPosition > 1 {
tone.adsrPosition = 0
tone.complete = true
}
}
sample *= adsrGain * adsrGain
tone.adsrPosition += tone.adsrDeltas[tone.adsrPhase]
_, tone.position = math.Modf(tone.position + tone.delta)
return
}
func (tone *toneStreamer) Stream (buf [][2]float64) (int, bool) {
if tone.complete {
return 0, false
}
for i := 0; i < len(buf); i++ {
sample := tone.nextSample() * tone.gain
sample := 0.0
if !tone.complete {
sample = tone.nextSample() * tone.gain
}
buf[i] = [2]float64{sample, sample}
}
return len(buf), true
}
func (tone *toneStreamer) Err () error {
return nil
}
func (tone *toneStreamer) Release () {
tone.released = true
}