1 /*
2  * datasette-sound.c - Sound of a cassette being played by the Datasette
3  *
4  * Written by
5  *  Fabrizio Gennari <fabrizio.ge@tiscali.it>
6  *
7  * This file is part of VICE, the Versatile Commodore Emulator.
8  * See README for copyright notice.
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 2 of the License, or
13  *  (at your option) any later version.
14  *
15  *  This program is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with this program; if not, write to the Free Software
22  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
23  *  02111-1307  USA.
24  *
25  */
26 
27 #include "vice.h"
28 
29 #include "sound.h"
30 #include "maincpu.h"
31 
32 static sound_chip_t datasette_sound;
33 
34 static CLOCK gap_circular_buffer[200];
35 static const int gap_circular_buffer_size =
36     sizeof(gap_circular_buffer) / sizeof(gap_circular_buffer[0]);
37 static int gap_circular_buffer_start = 0;
38 static int gap_circular_buffer_end = 0;
39 static char last_was_split_in_two = 0;
40 static CLOCK sound_start_maincpu_clk;
41 static char datasette_square_sign = 1;
42 static char datasette_halfwaves;
43 
44 
45 /* resources */
46 extern int datasette_sound_emulation;
47 extern int datasette_sound_emulation_volume;
48 
datasette_sound_flush_circular_buffer(void)49 static void datasette_sound_flush_circular_buffer(void)
50 {
51     datasette_sound.chip_enabled = 0;
52     gap_circular_buffer_end = gap_circular_buffer_start;
53 }
54 
datasette_sound_set_halfwaves(char halfwaves)55 void datasette_sound_set_halfwaves(char halfwaves)
56 {
57     datasette_halfwaves = halfwaves;
58 }
59 
datasette_sound_add_to_circular_buffer(CLOCK gap)60 void datasette_sound_add_to_circular_buffer(CLOCK gap)
61 {
62     if (!datasette_sound_emulation) {
63         datasette_sound_flush_circular_buffer();
64         return;
65     }
66     gap_circular_buffer[gap_circular_buffer_end] = gap;
67     if (datasette_sound.chip_enabled == 0) {
68         sound_start_maincpu_clk = maincpu_clk;
69         datasette_sound.chip_enabled = 1;
70     }
71     gap_circular_buffer_end =
72         (gap_circular_buffer_end + 1) % gap_circular_buffer_size;
73     if (gap_circular_buffer_end == gap_circular_buffer_start) {
74         gap_circular_buffer_start =
75             (gap_circular_buffer_start + 1) % gap_circular_buffer_size;
76     }
77 }
78 
datasette_sound_remove_from_circular_buffer(CLOCK max_amount_to_remove,char divide_by_two,char * must_flip)79 static CLOCK datasette_sound_remove_from_circular_buffer(
80     CLOCK max_amount_to_remove, char divide_by_two, char *must_flip)
81 {
82     CLOCK gap = 0;
83     *must_flip = 0;
84     if (gap_circular_buffer_end != gap_circular_buffer_start) {
85         char try_again;
86         do {
87             try_again = 0;
88             gap = gap_circular_buffer[gap_circular_buffer_start];
89             if (divide_by_two && !last_was_split_in_two) {
90                 gap /= 2;
91             }
92             if (gap > max_amount_to_remove) {
93                 if (divide_by_two && !last_was_split_in_two) {
94                     if (gap_circular_buffer_start == ((gap_circular_buffer_end + 1)
95                         % gap_circular_buffer_size)) {
96                         gap_circular_buffer_start = (gap_circular_buffer_start + 1)
97                             % gap_circular_buffer_size;
98                         try_again = 1;
99                         continue;
100                     }
101                     gap_circular_buffer[gap_circular_buffer_start] -= gap;
102                     gap_circular_buffer_start = gap_circular_buffer_start
103                         ? gap_circular_buffer_start - 1
104                         : gap_circular_buffer_size - 1;
105                     gap_circular_buffer[gap_circular_buffer_start] = gap;
106                     last_was_split_in_two = 1;
107                 }
108                 gap = max_amount_to_remove;
109             } else {
110                 *must_flip = 1;
111                 last_was_split_in_two = 0;
112             }
113         } while (try_again);
114         gap_circular_buffer[gap_circular_buffer_start] -= gap;
115         if (!gap_circular_buffer[gap_circular_buffer_start]) {
116             gap_circular_buffer_start = (gap_circular_buffer_start + 1)
117                 % gap_circular_buffer_size;
118             if (gap_circular_buffer_end == gap_circular_buffer_start) {
119                 datasette_sound.chip_enabled = 0;
120             }
121         }
122     }
123     return gap;
124 }
125 
datasette_sound_machine_calculate_samples(sound_t ** psid,int16_t * pbuf,int nr,int soc,int scc,int * delta_t)126 static int datasette_sound_machine_calculate_samples(sound_t **psid,
127     int16_t *pbuf, int nr, int soc, int scc, int *delta_t)
128 {
129     int i = 0, j, num_samples;
130     int cycles_to_be_consumed = *delta_t;
131     double factor = (double)cycles_to_be_consumed / nr;
132     char must_flip;
133 
134     if (sound_start_maincpu_clk) {
135         int initial_zero_samples =
136             (cycles_to_be_consumed + sound_start_maincpu_clk - maincpu_clk) / factor;
137         while (i < initial_zero_samples) {
138             pbuf[i++] = 0;
139         }
140         cycles_to_be_consumed = maincpu_clk - sound_start_maincpu_clk;
141         sound_start_maincpu_clk = 0;
142     }
143     while (cycles_to_be_consumed) {
144         CLOCK max_amount_to_consume = cycles_to_be_consumed;
145         CLOCK cycles_to_consume_now =
146             datasette_sound_remove_from_circular_buffer(max_amount_to_consume,
147                 datasette_square_sign == 1 && !datasette_halfwaves, &must_flip);
148         if (!cycles_to_consume_now) {
149             break;
150         }
151         cycles_to_be_consumed -= cycles_to_consume_now;
152         if (i < nr) {
153             if (cycles_to_be_consumed == 0) {
154                 num_samples = nr - i;
155             } else {
156                 num_samples = cycles_to_consume_now / factor;
157                 if (i + num_samples < nr - 1
158                     && cycles_to_be_consumed * 1.0 / (nr-i-num_samples) < factor) {
159                     num_samples++;
160                 }
161             }
162             for (j = 0; j < num_samples; j++) {
163                 pbuf[i++] =
164                     datasette_sound_emulation_volume * datasette_square_sign;
165             }
166         }
167         if (must_flip)
168             datasette_square_sign = -datasette_square_sign;
169     }
170     while (i < nr) {
171         pbuf[i++] = 0;
172     }
173     return nr;
174 }
175 
datasette_sound_machine_cycle_based(void)176 static int datasette_sound_machine_cycle_based(void)
177 {
178     return 0;
179 }
180 
datasette_sound_machine_channels(void)181 static int datasette_sound_machine_channels(void)
182 {
183     return 1;
184 }
185 
186 /* Drive sound 'chip', emulates the sound of a 1541 disk drive */
187 static sound_chip_t datasette_sound = {
188     NULL,                                      /* NO sound chip open function */
189     NULL,                                      /* NO sound chip init function */
190     NULL,                                      /* NO sound chip close function */
191     datasette_sound_machine_calculate_samples, /* sound chip calculate samples function */
192     NULL,                                      /* NO sound chip store function */
193     NULL,                                      /* NO sound chip read function */
194     NULL,                                      /* NO sound chip reset function */
195     datasette_sound_machine_cycle_based,       /* sound chip 'is_cycle_based()' function, chip is NOT cycle based */
196     datasette_sound_machine_channels,          /* sound chip 'get_amount_of_channels()' function, sound chip has 1 channel */
197     0                                          /* sound chip enabled flag, toggled upon device (de-)activation */
198 };
199 
datasette_sound_init(void)200 void datasette_sound_init(void)
201 {
202     sound_chip_register(&datasette_sound);
203 }
204