The piano now has an internal ADSR
This commit is contained in:
parent
cfc2b5e130
commit
182cb1e35b
@ -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
|
||||
}
|
||||
|
Reference in New Issue
Block a user