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