apple2-go/audio/audio.go
Will Angenent 8284073beb Added very basic audio processing
Every frame sends a bunch of audio samples in a channel. This channel is
consumed by the audio Read() function which is called asynchronously. There's
all kinds of timing issues where the audio/video are not aligned.

Issues:
- There's a large delay between the audio being produced and it being played
- Something with the timing is wrong. The first not of lemonade stand and the
  system beep are both incorrect. Changing the CPU frequency fixes it for one
  but not for the other. This means something must be wrong in the cycle
  counting.

Also added FPS display that can be toggled with ctrl-alt-F.
2018-05-14 10:49:24 +01:00

109 lines
2.0 KiB
Go

package audio
import (
"errors"
"mos6502go/system"
ebiten_audio "github.com/hajimehoshi/ebiten/audio"
)
var (
audioContext *ebiten_audio.Context
player *ebiten_audio.Player
firstAudio bool
Mute bool
)
type stream struct{}
func (s *stream) Read(data []byte) (int, error) {
dataLen := len(data)
if firstAudio {
// The first time, drain the audio queue
firstAudio = false
for i := 0; i < len(system.AudioChannel); i++ {
<-system.AudioChannel
}
return dataLen, nil
}
if dataLen%4 != 0 {
return 0, errors.New("dataLen % 4 must be 0")
}
if Mute {
return dataLen, nil
}
samples := dataLen / 4
for i := 0; i < dataLen; i++ {
data[i] = 0
}
for i := 0; i < samples; i++ {
b := <-system.AudioChannel
data[4*i] = byte(b)
data[4*i+1] = byte(b >> 8)
data[4*i+2] = byte(b)
data[4*i+3] = byte(b >> 8)
}
return dataLen, nil
}
func (s *stream) Close() error {
return nil
}
func Init() {
system.AudioCycles = 0
firstAudio = true
Mute = false
var err error
audioContext, err = ebiten_audio.NewContext(system.AudioSampleRate)
if err != nil {
panic(err)
}
// Pass the (infinite) stream to audio.NewPlayer.
// After calling Play, the stream never ends as long as the player object lives.
// var err error
player, err = ebiten_audio.NewPlayer(audioContext, &stream{})
if err != nil {
panic(err)
}
player.Play()
}
func Click() {
ForwardToFrameCycle()
system.AudioAttenuationCounter = 4000
system.LastAudioValue = ^system.LastAudioValue
}
func attenuate(sample uint16) uint16 {
if system.AudioAttenuationCounter == 0 {
return 0
} else {
system.AudioAttenuationCounter--
return sample
}
}
func ForwardToFrameCycle() {
ratio := float64(system.AudioSampleRate) / system.CpuFrequency
samples := uint64(ratio * float64(system.FrameCycles-system.LastAudioCycles))
var i uint64
for i = 0; i < samples; i++ {
b := attenuate(system.LastAudioValue)
system.AudioChannel <- b
}
system.LastAudioCycles = system.FrameCycles
}