/
audio.go
107 lines (92 loc) · 3.28 KB
/
audio.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
// This file is part of Gopher2600.
//
// Gopher2600 is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Gopher2600 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Gopher2600. If not, see <https://www.gnu.org/licenses/>.
package audio
import (
"strings"
)
// SampleFreq represents the number of samples generated per second. This is
// the 30Khz reference frequency desribed in the Stella Programmer's Guide.
const SampleFreq = 31403
// Audio is the implementation of the TIA audio sub-system, using Ron Fries'
// method. Reference source code here:
//
// https://raw.githubusercontent.com/alekmaul/stella/master/emucore/TIASound.c
type Audio struct {
// clock114 is so called because of the observation that the 30Khz
// reference frequency described in the Stella Programmer's Guide is
// generated from the 3.58Mhz clock divided by 114, giving a sample
// frequency of 31403Hz or 31Khz - close enough to the 30Khz referency
// frequency we need. Ron Fries' talks about this in his original
// documentation for TIASound.c
clock114 int
// similarly the 10Khz reference frequency can be generated by further
// dividing clock114 by three. each channel decides which reference
// frequency to use
clock342 int
// From the "Stella Programmer's Guide":
//
// "There are two audio circuits for generating sound. They are identical but
// completely independent and can be operated simultaneously [...]"
channel0 channel
channel1 channel
// the volume output for each channel
Vol0 uint8
Vol1 uint8
}
// NewAudio is the preferred method of initialisation for the Audio sub-system.
func NewAudio() *Audio {
return &Audio{}
}
// Snapshot creates a copy of the TIA Audio sub-system in its current state.
func (au *Audio) Snapshot() *Audio {
n := *au
return &n
}
func (au *Audio) String() string {
s := strings.Builder{}
s.WriteString("ch0: ")
s.WriteString(au.channel0.String())
s.WriteString(" ch1: ")
s.WriteString(au.channel1.String())
return s.String()
}
// Step the audio on one TIA clock. The step will be filtered to produce a
// 30Khz clock.
func (au *Audio) Step() bool {
// the reference frequency for all sound produced by the TIA is 30Khz. this
// is the 3.58Mhz clock, which the TIA operates at, divided by 114 (see
// declaration). Mix() is called every video cycle and we return
// immediately except on the 114th tick, whereupon we process the current
// audio registers and mix the two signals
if au.clock114 < 114 {
au.clock114++
return false
}
au.clock114 = 0
// sometimes an individual channel produces sound using a 10Khz reference
// frequency
if au.clock342 < 3 {
au.clock342++
}
if au.clock342 == 3 {
au.clock342 = 0
}
// process each channel before mixing
au.channel0.tick(au.clock342 == 0)
au.channel1.tick(au.clock342 == 0)
au.Vol0 = au.channel0.actualVol
au.Vol1 = au.channel1.actualVol
return true
}