1 /*
2 * KCemu -- The emulator for the KC85 homecomputer series and much more.
3 * Copyright (C) 1997-2010 Torsten Paul
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include <math.h>
21 #include <unistd.h>
22 #include <string.h>
23 #include <sys/time.h>
24
25 #include "kc/system.h"
26
27 #include "kc/kc.h"
28 #include "kc/z80.h"
29 #include "kc/ctc.h"
30 #include "kc/sound3.h"
31
32 #include "libdbg/dbg.h"
33
34 #define SOUND_BUFFER_SIZE (1024)
35 #define SOUND_SAMPLE_FREQ (44100)
36 #define SYNC_COUNTER (10)
37
38 void
sound_callback(void * userdata,unsigned char * stream,int len)39 Sound3::sound_callback(void *userdata, unsigned char *stream, int len)
40 {
41 sndop *op = NULL;
42 Sound3 *self = (Sound3 *)userdata;
43 long long cur, diff, idx;
44 static long long counter = 0;
45 static int val = 1;
46 static int xxx = 0;
47
48 static int freq = 0;
49
50 cur = z80->getCounter();
51
52 if (counter == 0)
53 {
54 counter = cur;
55 memset(stream, 0, len);
56 return;
57 }
58
59 diff = cur - counter;
60
61 int a = 0;
62
63 if (!self->_sndop_list.empty())
64 op = *(self->_sndop_list.begin());
65
66 while (a < len)
67 {
68 idx = counter + (a * diff) / len;
69
70 if (op && (idx > op->_counter))
71 {
72 freq = (int)op->_freq;
73 if (freq > SOUND_SAMPLE_FREQ)
74 {
75 xxx = 0;
76 freq = 0;
77 val = -val;
78 }
79
80 //cout << a << " - " << op->_counter << " / " << op->_freq << endl;
81 self->_sndop_list.pop_front();
82 op = NULL;
83 if (self->_sndop_list.size() > 0)
84 op = *(self->_sndop_list.begin());
85
86 if (xxx == 0)
87 if (freq != 0)
88 xxx = SOUND_SAMPLE_FREQ / freq;
89 }
90
91 if (freq == 0)
92 stream[a] = 128;
93 else
94 stream[a] = 128 + 80 * val;
95
96 if (xxx > 0)
97 if (--xxx == 0)
98 {
99 val = -val;
100 if (freq != 0)
101 xxx = SOUND_SAMPLE_FREQ / freq;
102 }
103
104 a++;
105 }
106
107 counter = cur;
108 }
109
Sound3(void)110 Sound3::Sound3(void)
111 {
112 }
113
~Sound3(void)114 Sound3::~Sound3(void)
115 {
116 delete _dummy_sndop;
117 }
118
119 void
init()120 Sound3::init()
121 {
122 open_sound();
123
124 ctc->register_callback(0, this);
125 pio->register_callback_B_out(this);
126
127 _playing = true;
128 _dummy_sndop = new sndop(0, 0);
129 _last_sndop = _dummy_sndop;
130 }
131
132 void
start()133 Sound3::start()
134 {
135 double freq = (1750000.0 / 2) / ctc->getTimerValue(0);
136
137 if (_last_sndop->_freq != freq)
138 {
139 //cout << "sound: start [1]: " << z80->getCounter() << endl;
140 _last_sndop = new sndop(z80->getCounter(), freq);
141 _sndop_list.push_back(_last_sndop);
142 }
143 }
144
145 void
stop()146 Sound3::stop()
147 {
148 //cout << "sound: stop [1]: " << z80->getCounter() << endl;
149 if (_last_sndop->_freq != 0)
150 {
151 //cout << "sound: stop [2]: " << z80->getCounter() << endl;
152 _last_sndop = new sndop(z80->getCounter(), 0);
153 _sndop_list.push_back(_last_sndop);
154 }
155 }
156
157 void
callback_B_out(byte_t val)158 Sound3::callback_B_out(byte_t val)
159 {
160 if (((val & 1) == 1) && ((val & 0x0e) != 0x0e) && !ctc->isReset(0))
161 start();
162 else
163 stop();
164 }
165
166 void
ctc_callback_start(int channel)167 Sound3::ctc_callback_start(int channel)
168 {
169 if (channel != 0)
170 return;
171
172 if (ctc->getTimerValue(channel) == 0)
173 return;
174
175 //printf("Sound3::ctc_callback_start()\n");
176 start();
177 }
178
179 void
ctc_callback_stop(int channel)180 Sound3::ctc_callback_stop(int channel)
181 {
182 if (channel != 0)
183 return;
184
185 stop();
186 }
187
188 void
ctc_callback_TC(int channel,long tc)189 Sound3::ctc_callback_TC(int channel, long tc)
190 {
191 double freq;
192
193 switch (channel)
194 {
195 case 0:
196 freq = (1750000.0 / 2) / tc;
197 //printf("%04xh: Sound3::cb_TC() - %10Ld - %12.2f\n", z80->getPC(), z80->getCounter(), freq);
198 if (_last_sndop->_freq != freq)
199 {
200 _last_sndop = new sndop(z80->getCounter(), freq);
201 _sndop_list.push_back(_last_sndop);
202 }
203 break;
204 default:
205 break;
206 }
207 }
208