The piano now has an internal ADSR
This commit is contained in:
parent
cfc2b5e130
commit
182cb1e35b
@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import "math"
|
import "math"
|
||||||
|
import "time"
|
||||||
import "errors"
|
import "errors"
|
||||||
import "github.com/faiface/beep"
|
import "github.com/faiface/beep"
|
||||||
import "github.com/faiface/beep/speaker"
|
import "github.com/faiface/beep/speaker"
|
||||||
@ -15,7 +16,7 @@ const sampleRate = 44100
|
|||||||
const bufferSize = 256
|
const bufferSize = 256
|
||||||
var tuning = music.EqualTemparment { A4: 440 }
|
var tuning = music.EqualTemparment { A4: 440 }
|
||||||
var waveform = 0
|
var waveform = 0
|
||||||
var playing = map[music.Note] *beep.Ctrl { }
|
var playing = map[music.Note] *toneStreamer { }
|
||||||
|
|
||||||
func main () {
|
func main () {
|
||||||
speaker.Init(sampleRate, bufferSize)
|
speaker.Init(sampleRate, bufferSize)
|
||||||
@ -59,17 +60,27 @@ func stopNote (note music.Note) {
|
|||||||
if _, is := playing[note]; !is { return }
|
if _, is := playing[note]; !is { return }
|
||||||
|
|
||||||
speaker.Lock()
|
speaker.Lock()
|
||||||
playing[note].Streamer = nil
|
playing[note].Release()
|
||||||
delete(playing, note)
|
delete(playing, note)
|
||||||
speaker.Unlock()
|
speaker.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func playNote (note music.Note) {
|
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)
|
stopNote(note)
|
||||||
speaker.Lock()
|
speaker.Lock()
|
||||||
playing[note] = &beep.Ctrl { Streamer: streamer }
|
playing[note] = streamer
|
||||||
speaker.Unlock()
|
speaker.Unlock()
|
||||||
speaker.Play(playing[note])
|
speaker.Play(playing[note])
|
||||||
}
|
}
|
||||||
@ -80,30 +91,62 @@ func playNote (note music.Note) {
|
|||||||
type toneStreamer struct {
|
type toneStreamer struct {
|
||||||
position float64
|
position float64
|
||||||
delta float64
|
delta float64
|
||||||
|
|
||||||
waveform int
|
waveform int
|
||||||
gain float64
|
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 (
|
func Tone (
|
||||||
sr beep.SampleRate,
|
sampleRate beep.SampleRate,
|
||||||
freq int,
|
frequency int,
|
||||||
waveform int,
|
waveform int,
|
||||||
gain float64,
|
gain float64,
|
||||||
|
adsr ADSR,
|
||||||
) (
|
) (
|
||||||
beep.Streamer,
|
*toneStreamer,
|
||||||
error,
|
error,
|
||||||
) {
|
) {
|
||||||
if int(sr) / freq < 2 {
|
if int(sampleRate) / frequency < 2 {
|
||||||
return nil, errors.New (
|
return nil, errors.New (
|
||||||
"tone generator: samplerate must be at least " +
|
"tone generator: samplerate must be at least " +
|
||||||
"2 times greater then frequency")
|
"2 times greater then frequency")
|
||||||
}
|
}
|
||||||
|
|
||||||
tone := new(toneStreamer)
|
tone := new(toneStreamer)
|
||||||
tone.position = 0.0
|
|
||||||
tone.waveform = waveform
|
tone.waveform = waveform
|
||||||
steps := float64(sr) / float64(freq)
|
tone.position = 0.0
|
||||||
|
steps := float64(sampleRate) / float64(frequency)
|
||||||
tone.delta = 1.0 / steps
|
tone.delta = 1.0 / steps
|
||||||
tone.gain = gain
|
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
|
return tone, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,17 +170,59 @@ func (tone *toneStreamer) nextSample () (sample float64) {
|
|||||||
28.32 * tone.position * tone.position +
|
28.32 * tone.position * tone.position +
|
||||||
15.62 * tone.position * 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)
|
_, tone.position = math.Modf(tone.position + tone.delta)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tone *toneStreamer) Stream (buf [][2]float64) (int, bool) {
|
func (tone *toneStreamer) Stream (buf [][2]float64) (int, bool) {
|
||||||
|
if tone.complete {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
for i := 0; i < len(buf); i++ {
|
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}
|
buf[i] = [2]float64{sample, sample}
|
||||||
}
|
}
|
||||||
return len(buf), true
|
return len(buf), true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tone *toneStreamer) Err () error {
|
func (tone *toneStreamer) Err () error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tone *toneStreamer) Release () {
|
||||||
|
tone.released = true
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user