Skip to content

Commit a647141

Browse files
committed
corrected 114 divisor so that audio is correct for full speed emulation
better clipping and filling when audio buffer overruns and underruns
1 parent 5b4d60b commit a647141

File tree

3 files changed

+61
-16
lines changed

3 files changed

+61
-16
lines changed

gui/sdlaudio/audio.go

Lines changed: 58 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,22 @@ import (
2222
)
2323

2424
// the buffer length is important to get right. unfortunately, there's no
25-
// special way (that I know of) that can tells us what the ideal value is. we
26-
// don't want it to be long because we can introduce unnecessary lag between
27-
// the audio and video signal; by the same token we don't want it too short because
28-
// we will end up calling FlushAudio() too often - FlushAudio() is a
29-
// computationally expensive function.
25+
// special way (that I know of) that can tells us what the ideal value is
3026
//
31-
// the following value has been discovered through trial and error. the precise
32-
// value is not critical.
33-
const bufferLength = 512
27+
// the bufferLegnth value is the maximum size of the buffer. once the buffer is
28+
// full the audio will be queued
29+
const bufferLength = 1024
30+
31+
// if the audio queue is ever less than minQueueLength then the buffer
32+
// will be pushed to the queue immediately
33+
const minQueueLength = 256
34+
35+
// if audio queue is ever less than critQueueLength the the buffer is pushed to
36+
// the queue but the buffer is not reset
37+
const critQueueLength = 64
38+
39+
// if queued audio ever exceeds this value then clip the audio
40+
const maxQueueLength = 8192
3441

3542
// Audio outputs sound using SDL
3643
type Audio struct {
@@ -42,6 +49,7 @@ type Audio struct {
4249
// repeatAudio()
4350
buffer []uint8
4451
bufferCt int
52+
critCt int
4553

4654
// some ROMs do not output 0 as the silence value. silence is technically
4755
// caused by constant unchanging value so this shouldn't be a problem. the
@@ -85,6 +93,7 @@ func NewAudio() (*Audio, error) {
8593
}
8694

8795
aud.spec = actualSpec
96+
8897
aud.detectedSilenceValue = aud.spec.Silence
8998

9099
// fill buffers with silence
@@ -119,18 +128,53 @@ func (aud *Audio) SetAudio(audioData uint8) error {
119128
}
120129
aud.bufferCt++
121130

122-
remaining := int(sdl.GetQueuedAudioSize(aud.id))
123-
124131
if aud.bufferCt >= len(aud.buffer) {
132+
// if buffer is full then queue audio unconditionally
125133
err := sdl.QueueAudio(aud.id, aud.buffer)
126134
if err != nil {
127135
return err
128136
}
129137
aud.bufferCt = 0
130-
} else if remaining <= len(aud.buffer)/2 {
131-
err := sdl.QueueAudio(aud.id, aud.buffer)
132-
if err != nil {
133-
return err
138+
139+
} else {
140+
141+
remaining := int(sdl.GetQueuedAudioSize(aud.id))
142+
143+
if remaining < critQueueLength {
144+
// if we're running short of bits in the queue the queue what we have
145+
// in the buffer and NOT clearing the buffer
146+
//
147+
// condition valid when the frame rate is SIGNIFICANTLY LESS than 50/60fps
148+
err := sdl.QueueAudio(aud.id, aud.buffer)
149+
if err != nil {
150+
return err
151+
}
152+
153+
} else if remaining < minQueueLength && aud.bufferCt > 10 {
154+
// if we're running short of bits in the queue the queue what we have
155+
// in the buffer.
156+
//
157+
// condition valid when the frame rate is LESS than 50/60fps
158+
//
159+
// the additional condition makes sure we're not queueing a slice
160+
// that is too short. SDL has been known to hang with short audio
161+
// queues
162+
err := sdl.QueueAudio(aud.id, aud.buffer[:aud.bufferCt-1])
163+
if err != nil {
164+
return err
165+
}
166+
aud.bufferCt = 0
167+
168+
} else if remaining > maxQueueLength {
169+
// if length of SDL audio queue is getting too long then clear it
170+
//
171+
// condition valid when the frame rate is SIGNIFICANTLY MORE than 50/60fps
172+
//
173+
// if we don't do this the video will get ahead of the audio (ie. the audio
174+
// will lag)
175+
//
176+
// this is a brute force approach but it'll do for now
177+
sdl.ClearQueuedAudio(aud.id)
134178
}
135179
}
136180

hardware/tia/audio/audio.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ func (au *Audio) Mix() (bool, uint8) {
110110
// immediately except on the 114th tick, whereupon we process the current
111111
// audio registers and mix the two signals
112112
au.clock114++
113-
if au.clock114 < 114 {
113+
if au.clock114 < 115 {
114114
return false, 0
115115
}
116116

television/limiter.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,8 @@ func (lmtr *limiter) checkRate() {
116116
}
117117
}
118118

119-
// called every frame to calculate the actual frame rate being achieved
119+
// called every scanline (although internally limited) to calculate the actual
120+
// frame rate being achieved.
120121
func (lmtr *limiter) measureActual() {
121122
lmtr.actualCt++
122123
if lmtr.actualCt >= lmtr.actualCtTarget {

0 commit comments

Comments
 (0)