Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
alexbevi
GitHub Repository: alexbevi/BizHawk
Path: blob/master/libmeteor/source/audio/sound1.cpp
2 views
1
// Meteor - A Nintendo Gameboy Advance emulator
2
// Copyright (C) 2009-2011 Philippe Daouadi
3
//
4
// This program is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// This program is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17
#include "ameteor/audio/sound1.hpp"
18
#include "../globals.hpp"
19
#include <cmath>
20
21
namespace AMeteor
22
{
23
namespace Audio
24
{
25
Sound1::Sound1 (uint16_t& cntl, uint16_t& cnth, uint16_t& cntx,
26
uint16_t freq) :
27
m_cntl(cntl),
28
m_cnth(cnth),
29
m_cntx(cntx),
30
m_on(false),
31
m_posP(0),
32
m_posS(0),
33
m_posE(0),
34
m_sample(0),
35
m_speriod(16*1024*1024/freq),
36
m_envelope(0),
37
m_length(0),
38
m_timed(false)
39
{
40
}
41
42
void Sound1::Reset ()
43
{
44
m_on = false;
45
m_timed = false;
46
m_length = 0;
47
m_envelope = 0;
48
m_posP = m_posE = m_posS = 0;
49
m_sample = 0;
50
}
51
52
void Sound1::ResetSound ()
53
{
54
m_on = true;
55
m_timed = (m_cntx & (0x1 << 14));
56
m_length = (64 - (m_cnth & 0x3F)) * ((16*1024*1024)/256);
57
m_envelope = m_cnth >> 12;
58
m_posE = m_posS = 0;
59
}
60
61
void Sound1::SoundTick ()
62
{
63
// remember here that the processors runs at 16MHz = 16,777,216 cycles/s
64
// and this function is called normally at 44,100 Hz
65
66
m_posP += m_speriod;
67
m_posS += m_speriod;
68
m_posE += m_speriod;
69
if (m_length > m_speriod)
70
m_length -= m_speriod;
71
else
72
{
73
if (m_timed)
74
m_on = false;
75
m_length = 0;
76
}
77
78
// sweep time in cycles
79
// maximum is 917,504, so we need a 32 bits int
80
uint32_t sweeptime = ((m_cntl >> 4) & 0x7) * ((16*1024*1024)/128);
81
// period in cycles
82
// period = 16M/freq
83
// freq = 128K/(2048 - (SOUND1CNT_X & 0x7FF))
84
// maximum is 262,144, so we need a 32 bits int
85
uint32_t period =
86
((16*1024*1024) / (128*1024)) * (2048 - (m_cntx & 0x7FF));
87
// frequency as contained in SOUND1CNT_X
88
uint16_t freq = m_cntx & 0x7FF;
89
90
// we rewind posP
91
m_posP %= period;
92
93
// the envelope now
94
// envelope step time in cycles
95
uint32_t steptime = ((m_cnth >> 8) & 0x7) * ((16*1024*1024)/64);
96
// the envelope can't do two steps between to calls of SoundTick
97
if (steptime && m_posE > steptime)
98
{
99
if (m_cnth & (0x1 << 11))
100
{
101
if (m_envelope < 15)
102
++m_envelope;
103
}
104
else
105
{
106
if (m_envelope > 0)
107
--m_envelope;
108
}
109
110
m_posE -= steptime;
111
}
112
113
// if the envelope is null or the sound is finished, no need to calculate
114
// anything
115
if (m_on && m_envelope)
116
{
117
// we set the sample according to the position in the current period
118
// and the wave duty cycle
119
switch ((m_cnth >> 6) & 0x3)
120
{
121
case 0: // 12.5%
122
m_sample = m_posP < period/8 ? 112 : -16;
123
break;
124
case 1: // 25%
125
m_sample = m_posP < period/4 ? 96 : -32;
126
break;
127
case 2: // 50%
128
m_sample = m_posP < period/2 ? 64 : -64;
129
break;
130
case 3: // 75%
131
m_sample = m_posP < (3*period)/4 ? 32 : -96;
132
break;
133
}
134
135
m_sample = (((int16_t)m_sample) * m_envelope)/15;
136
}
137
else
138
m_sample = 0;
139
140
// there can't have been more than one sweep between two call of
141
// SoundTick since SoundTick is called at least at a frequency of 4,000Hz
142
// (alsa can't output at a lower samplerate on my sound card) and sweeps
143
// happen at maximum at a frequency of 128Hz
144
145
// if the channel is on and sweep is enabled and it's time to sweep
146
if (m_on && sweeptime && m_posS > sweeptime)
147
{
148
// n = sweep shifts (in SOUND1CNT_L)
149
if (m_cntl & (0x1 << 3))
150
// F(t+1) = F(t) - F(t) / 2^n
151
freq = freq - freq / (1 << (m_cntl & 0x7));
152
// freq won't go under 1 since when freq = 2, freq - freq / 2 (the
153
// minimum sweep shift) = 1 and then freq - freq / 2 = 1
154
// because 1/2 = 0
155
else
156
{
157
// F(t+1) = F(t) + F(t) / 2^n
158
freq = freq + freq / (1 << (m_cntl & 0x7));
159
if (freq > 2047)
160
{
161
m_on = false;
162
freq = 2047;
163
}
164
}
165
166
// we update the frequency in the cntx register
167
m_cntx = (m_cntx & 0xF800) | freq;
168
169
// now we rewind posS
170
m_posS -= sweeptime;
171
}
172
}
173
174
bool Sound1::SaveState (std::ostream& stream)
175
{
176
SS_WRITE_VAR(m_on);
177
SS_WRITE_VAR(m_posP);
178
SS_WRITE_VAR(m_posS);
179
SS_WRITE_VAR(m_posE);
180
SS_WRITE_VAR(m_sample);
181
SS_WRITE_VAR(m_envelope);
182
SS_WRITE_VAR(m_length);
183
SS_WRITE_VAR(m_timed);
184
185
return true;
186
}
187
188
bool Sound1::LoadState (std::istream& stream)
189
{
190
SS_READ_VAR(m_on);
191
SS_READ_VAR(m_posP);
192
SS_READ_VAR(m_posS);
193
SS_READ_VAR(m_posE);
194
SS_READ_VAR(m_sample);
195
SS_READ_VAR(m_envelope);
196
SS_READ_VAR(m_length);
197
SS_READ_VAR(m_timed);
198
199
return true;
200
}
201
}
202
}
203
204