root / audio / fmodaudio.c @ 1b2b0af5
History | View | Annotate | Download (16.8 kB)
1 | 85571bc7 | bellard | /*
|
---|---|---|---|
2 | 1d14ffa9 | bellard | * QEMU FMOD audio driver
|
3 | 1d14ffa9 | bellard | *
|
4 | 1d14ffa9 | bellard | * Copyright (c) 2004-2005 Vassili Karpov (malc)
|
5 | 1d14ffa9 | bellard | *
|
6 | 85571bc7 | bellard | * Permission is hereby granted, free of charge, to any person obtaining a copy
|
7 | 85571bc7 | bellard | * of this software and associated documentation files (the "Software"), to deal
|
8 | 85571bc7 | bellard | * in the Software without restriction, including without limitation the rights
|
9 | 85571bc7 | bellard | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10 | 85571bc7 | bellard | * copies of the Software, and to permit persons to whom the Software is
|
11 | 85571bc7 | bellard | * furnished to do so, subject to the following conditions:
|
12 | 85571bc7 | bellard | *
|
13 | 85571bc7 | bellard | * The above copyright notice and this permission notice shall be included in
|
14 | 85571bc7 | bellard | * all copies or substantial portions of the Software.
|
15 | 85571bc7 | bellard | *
|
16 | 85571bc7 | bellard | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17 | 85571bc7 | bellard | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18 | 85571bc7 | bellard | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
19 | 85571bc7 | bellard | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20 | 85571bc7 | bellard | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21 | 85571bc7 | bellard | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22 | 85571bc7 | bellard | * THE SOFTWARE.
|
23 | 85571bc7 | bellard | */
|
24 | 85571bc7 | bellard | #include <fmod.h> |
25 | 85571bc7 | bellard | #include <fmod_errors.h> |
26 | 85571bc7 | bellard | #include "vl.h" |
27 | 85571bc7 | bellard | |
28 | 1d14ffa9 | bellard | #define AUDIO_CAP "fmod" |
29 | 1d14ffa9 | bellard | #include "audio_int.h" |
30 | fb065187 | bellard | |
31 | 1d14ffa9 | bellard | typedef struct FMODVoiceOut { |
32 | 1d14ffa9 | bellard | HWVoiceOut hw; |
33 | fb065187 | bellard | unsigned int old_pos; |
34 | fb065187 | bellard | FSOUND_SAMPLE *fmod_sample; |
35 | fb065187 | bellard | int channel;
|
36 | 1d14ffa9 | bellard | } FMODVoiceOut; |
37 | 85571bc7 | bellard | |
38 | 1d14ffa9 | bellard | typedef struct FMODVoiceIn { |
39 | 1d14ffa9 | bellard | HWVoiceIn hw; |
40 | 1d14ffa9 | bellard | FSOUND_SAMPLE *fmod_sample; |
41 | 1d14ffa9 | bellard | } FMODVoiceIn; |
42 | 85571bc7 | bellard | |
43 | 85571bc7 | bellard | static struct { |
44 | 1d14ffa9 | bellard | const char *drvname; |
45 | 85571bc7 | bellard | int nb_samples;
|
46 | 85571bc7 | bellard | int freq;
|
47 | 85571bc7 | bellard | int nb_channels;
|
48 | 85571bc7 | bellard | int bufsize;
|
49 | 85571bc7 | bellard | int threshold;
|
50 | 1d14ffa9 | bellard | int broken_adc;
|
51 | 85571bc7 | bellard | } conf = { |
52 | 1d14ffa9 | bellard | NULL,
|
53 | 1d14ffa9 | bellard | 2048 * 2, |
54 | 85571bc7 | bellard | 44100,
|
55 | 1d14ffa9 | bellard | 2,
|
56 | 1d14ffa9 | bellard | 0,
|
57 | 85571bc7 | bellard | 0,
|
58 | 1d14ffa9 | bellard | 0
|
59 | 85571bc7 | bellard | }; |
60 | 85571bc7 | bellard | |
61 | 1d14ffa9 | bellard | static void GCC_FMT_ATTR (1, 2) fmod_logerr (const char *fmt, ...) |
62 | 1d14ffa9 | bellard | { |
63 | 1d14ffa9 | bellard | va_list ap; |
64 | 1d14ffa9 | bellard | |
65 | 1d14ffa9 | bellard | va_start (ap, fmt); |
66 | 1d14ffa9 | bellard | AUD_vlog (AUDIO_CAP, fmt, ap); |
67 | 1d14ffa9 | bellard | va_end (ap); |
68 | 1d14ffa9 | bellard | |
69 | 1d14ffa9 | bellard | AUD_log (AUDIO_CAP, "Reason: %s\n",
|
70 | 1d14ffa9 | bellard | FMOD_ErrorString (FSOUND_GetError ())); |
71 | 1d14ffa9 | bellard | } |
72 | 1d14ffa9 | bellard | |
73 | 1d14ffa9 | bellard | static void GCC_FMT_ATTR (2, 3) fmod_logerr2 ( |
74 | 1d14ffa9 | bellard | const char *typ, |
75 | 1d14ffa9 | bellard | const char *fmt, |
76 | 1d14ffa9 | bellard | ... |
77 | 1d14ffa9 | bellard | ) |
78 | 1d14ffa9 | bellard | { |
79 | 1d14ffa9 | bellard | va_list ap; |
80 | 1d14ffa9 | bellard | |
81 | c0fe3827 | bellard | AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
|
82 | 1d14ffa9 | bellard | |
83 | 1d14ffa9 | bellard | va_start (ap, fmt); |
84 | 1d14ffa9 | bellard | AUD_vlog (AUDIO_CAP, fmt, ap); |
85 | 1d14ffa9 | bellard | va_end (ap); |
86 | 1d14ffa9 | bellard | |
87 | 1d14ffa9 | bellard | AUD_log (AUDIO_CAP, "Reason: %s\n",
|
88 | 1d14ffa9 | bellard | FMOD_ErrorString (FSOUND_GetError ())); |
89 | 1d14ffa9 | bellard | } |
90 | 85571bc7 | bellard | |
91 | 1d14ffa9 | bellard | static int fmod_write (SWVoiceOut *sw, void *buf, int len) |
92 | 85571bc7 | bellard | { |
93 | 1d14ffa9 | bellard | return audio_pcm_sw_write (sw, buf, len);
|
94 | 85571bc7 | bellard | } |
95 | 85571bc7 | bellard | |
96 | 1d14ffa9 | bellard | static void fmod_clear_sample (FMODVoiceOut *fmd) |
97 | 85571bc7 | bellard | { |
98 | 1d14ffa9 | bellard | HWVoiceOut *hw = &fmd->hw; |
99 | 85571bc7 | bellard | int status;
|
100 | 85571bc7 | bellard | void *p1 = 0, *p2 = 0; |
101 | 85571bc7 | bellard | unsigned int len1 = 0, len2 = 0; |
102 | 85571bc7 | bellard | |
103 | 85571bc7 | bellard | status = FSOUND_Sample_Lock ( |
104 | 85571bc7 | bellard | fmd->fmod_sample, |
105 | 85571bc7 | bellard | 0,
|
106 | 1d14ffa9 | bellard | hw->samples << hw->info.shift, |
107 | 85571bc7 | bellard | &p1, |
108 | 85571bc7 | bellard | &p2, |
109 | 85571bc7 | bellard | &len1, |
110 | 85571bc7 | bellard | &len2 |
111 | 85571bc7 | bellard | ); |
112 | 85571bc7 | bellard | |
113 | 85571bc7 | bellard | if (!status) {
|
114 | 1d14ffa9 | bellard | fmod_logerr ("Failed to lock sample\n");
|
115 | 85571bc7 | bellard | return;
|
116 | 85571bc7 | bellard | } |
117 | 85571bc7 | bellard | |
118 | 1d14ffa9 | bellard | if ((len1 & hw->info.align) || (len2 & hw->info.align)) {
|
119 | 1d14ffa9 | bellard | dolog ("Lock returned misaligned length %d, %d, alignment %d\n",
|
120 | 1d14ffa9 | bellard | len1, len2, hw->info.align + 1);
|
121 | 85571bc7 | bellard | goto fail;
|
122 | 85571bc7 | bellard | } |
123 | 85571bc7 | bellard | |
124 | 1d14ffa9 | bellard | if ((len1 + len2) - (hw->samples << hw->info.shift)) {
|
125 | 1d14ffa9 | bellard | dolog ("Lock returned incomplete length %d, %d\n",
|
126 | 1d14ffa9 | bellard | len1 + len2, hw->samples << hw->info.shift); |
127 | 85571bc7 | bellard | goto fail;
|
128 | 85571bc7 | bellard | } |
129 | 1d14ffa9 | bellard | |
130 | 1d14ffa9 | bellard | audio_pcm_info_clear_buf (&hw->info, p1, hw->samples); |
131 | 85571bc7 | bellard | |
132 | 85571bc7 | bellard | fail:
|
133 | 85571bc7 | bellard | status = FSOUND_Sample_Unlock (fmd->fmod_sample, p1, p2, len1, len2); |
134 | 85571bc7 | bellard | if (!status) {
|
135 | 1d14ffa9 | bellard | fmod_logerr ("Failed to unlock sample\n");
|
136 | 85571bc7 | bellard | } |
137 | 85571bc7 | bellard | } |
138 | 85571bc7 | bellard | |
139 | 1d14ffa9 | bellard | static void fmod_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len) |
140 | 85571bc7 | bellard | { |
141 | 1d14ffa9 | bellard | int src_len1 = dst_len;
|
142 | 1d14ffa9 | bellard | int src_len2 = 0; |
143 | 1d14ffa9 | bellard | int pos = hw->rpos + dst_len;
|
144 | 1d14ffa9 | bellard | st_sample_t *src1 = hw->mix_buf + hw->rpos; |
145 | 1d14ffa9 | bellard | st_sample_t *src2 = NULL;
|
146 | 1d14ffa9 | bellard | |
147 | 1d14ffa9 | bellard | if (pos > hw->samples) {
|
148 | 1d14ffa9 | bellard | src_len1 = hw->samples - hw->rpos; |
149 | 1d14ffa9 | bellard | src2 = hw->mix_buf; |
150 | 85571bc7 | bellard | src_len2 = dst_len - src_len1; |
151 | 85571bc7 | bellard | pos = src_len2; |
152 | 85571bc7 | bellard | } |
153 | 85571bc7 | bellard | |
154 | 85571bc7 | bellard | if (src_len1) {
|
155 | 85571bc7 | bellard | hw->clip (dst, src1, src_len1); |
156 | 1d14ffa9 | bellard | mixeng_clear (src1, src_len1); |
157 | 85571bc7 | bellard | } |
158 | 85571bc7 | bellard | |
159 | 85571bc7 | bellard | if (src_len2) {
|
160 | 1d14ffa9 | bellard | dst = advance (dst, src_len1 << hw->info.shift); |
161 | 85571bc7 | bellard | hw->clip (dst, src2, src_len2); |
162 | 1d14ffa9 | bellard | mixeng_clear (src2, src_len2); |
163 | 85571bc7 | bellard | } |
164 | 1d14ffa9 | bellard | |
165 | 1d14ffa9 | bellard | hw->rpos = pos % hw->samples; |
166 | 85571bc7 | bellard | } |
167 | 85571bc7 | bellard | |
168 | 1d14ffa9 | bellard | static int fmod_unlock_sample (FSOUND_SAMPLE *sample, void *p1, void *p2, |
169 | 85571bc7 | bellard | unsigned int blen1, unsigned int blen2) |
170 | 85571bc7 | bellard | { |
171 | 1d14ffa9 | bellard | int status = FSOUND_Sample_Unlock (sample, p1, p2, blen1, blen2);
|
172 | 85571bc7 | bellard | if (!status) {
|
173 | 1d14ffa9 | bellard | fmod_logerr ("Failed to unlock sample\n");
|
174 | 85571bc7 | bellard | return -1; |
175 | 85571bc7 | bellard | } |
176 | 85571bc7 | bellard | return 0; |
177 | 85571bc7 | bellard | } |
178 | 85571bc7 | bellard | |
179 | 1d14ffa9 | bellard | static int fmod_lock_sample ( |
180 | 1d14ffa9 | bellard | FSOUND_SAMPLE *sample, |
181 | 1d14ffa9 | bellard | struct audio_pcm_info *info,
|
182 | 1d14ffa9 | bellard | int pos,
|
183 | 1d14ffa9 | bellard | int len,
|
184 | 1d14ffa9 | bellard | void **p1,
|
185 | 1d14ffa9 | bellard | void **p2,
|
186 | 1d14ffa9 | bellard | unsigned int *blen1, |
187 | 1d14ffa9 | bellard | unsigned int *blen2 |
188 | 1d14ffa9 | bellard | ) |
189 | 85571bc7 | bellard | { |
190 | 85571bc7 | bellard | int status;
|
191 | 85571bc7 | bellard | |
192 | 85571bc7 | bellard | status = FSOUND_Sample_Lock ( |
193 | 1d14ffa9 | bellard | sample, |
194 | 1d14ffa9 | bellard | pos << info->shift, |
195 | 1d14ffa9 | bellard | len << info->shift, |
196 | 85571bc7 | bellard | p1, |
197 | 85571bc7 | bellard | p2, |
198 | 85571bc7 | bellard | blen1, |
199 | 85571bc7 | bellard | blen2 |
200 | 85571bc7 | bellard | ); |
201 | 85571bc7 | bellard | |
202 | 85571bc7 | bellard | if (!status) {
|
203 | 1d14ffa9 | bellard | fmod_logerr ("Failed to lock sample\n");
|
204 | 85571bc7 | bellard | return -1; |
205 | 85571bc7 | bellard | } |
206 | 85571bc7 | bellard | |
207 | 1d14ffa9 | bellard | if ((*blen1 & info->align) || (*blen2 & info->align)) {
|
208 | 1d14ffa9 | bellard | dolog ("Lock returned misaligned length %d, %d, alignment %d\n",
|
209 | 1d14ffa9 | bellard | *blen1, *blen2, info->align + 1);
|
210 | 1d14ffa9 | bellard | |
211 | 1d14ffa9 | bellard | fmod_unlock_sample (sample, *p1, *p2, *blen1, *blen2); |
212 | 1d14ffa9 | bellard | |
213 | 1d14ffa9 | bellard | *p1 = NULL - 1; |
214 | 1d14ffa9 | bellard | *p2 = NULL - 1; |
215 | 1d14ffa9 | bellard | *blen1 = ~0U;
|
216 | 1d14ffa9 | bellard | *blen2 = ~0U;
|
217 | 85571bc7 | bellard | return -1; |
218 | 85571bc7 | bellard | } |
219 | 1d14ffa9 | bellard | |
220 | 1d14ffa9 | bellard | if (!*p1 && *blen1) {
|
221 | 1d14ffa9 | bellard | dolog ("warning: !p1 && blen1=%d\n", *blen1);
|
222 | 1d14ffa9 | bellard | *blen1 = 0;
|
223 | 1d14ffa9 | bellard | } |
224 | 1d14ffa9 | bellard | |
225 | 1d14ffa9 | bellard | if (!p2 && *blen2) {
|
226 | 1d14ffa9 | bellard | dolog ("warning: !p2 && blen2=%d\n", *blen2);
|
227 | 1d14ffa9 | bellard | *blen2 = 0;
|
228 | 1d14ffa9 | bellard | } |
229 | 1d14ffa9 | bellard | |
230 | 85571bc7 | bellard | return 0; |
231 | 85571bc7 | bellard | } |
232 | 85571bc7 | bellard | |
233 | 1d14ffa9 | bellard | static int fmod_run_out (HWVoiceOut *hw) |
234 | 85571bc7 | bellard | { |
235 | 1d14ffa9 | bellard | FMODVoiceOut *fmd = (FMODVoiceOut *) hw; |
236 | 1d14ffa9 | bellard | int live, decr;
|
237 | 85571bc7 | bellard | void *p1 = 0, *p2 = 0; |
238 | 85571bc7 | bellard | unsigned int blen1 = 0, blen2 = 0; |
239 | 85571bc7 | bellard | unsigned int len1 = 0, len2 = 0; |
240 | 1d14ffa9 | bellard | int nb_live;
|
241 | 85571bc7 | bellard | |
242 | 1d14ffa9 | bellard | live = audio_pcm_hw_get_live_out2 (hw, &nb_live); |
243 | 1d14ffa9 | bellard | if (!live) {
|
244 | 1d14ffa9 | bellard | return 0; |
245 | 85571bc7 | bellard | } |
246 | 85571bc7 | bellard | |
247 | 85571bc7 | bellard | if (!hw->pending_disable
|
248 | 1d14ffa9 | bellard | && nb_live |
249 | 1d14ffa9 | bellard | && (conf.threshold && live <= conf.threshold)) { |
250 | 1d14ffa9 | bellard | ldebug ("live=%d nb_live=%d\n", live, nb_live);
|
251 | 1d14ffa9 | bellard | return 0; |
252 | 85571bc7 | bellard | } |
253 | 85571bc7 | bellard | |
254 | 85571bc7 | bellard | decr = live; |
255 | 85571bc7 | bellard | |
256 | 85571bc7 | bellard | if (fmd->channel >= 0) { |
257 | 1d14ffa9 | bellard | int len = decr;
|
258 | 1d14ffa9 | bellard | int old_pos = fmd->old_pos;
|
259 | 1d14ffa9 | bellard | int ppos = FSOUND_GetCurrentPosition (fmd->channel);
|
260 | 85571bc7 | bellard | |
261 | 1d14ffa9 | bellard | if (ppos == old_pos || !ppos) {
|
262 | 1d14ffa9 | bellard | return 0; |
263 | 85571bc7 | bellard | } |
264 | 1d14ffa9 | bellard | |
265 | 1d14ffa9 | bellard | if ((old_pos < ppos) && ((old_pos + len) > ppos)) {
|
266 | 1d14ffa9 | bellard | len = ppos - old_pos; |
267 | 1d14ffa9 | bellard | } |
268 | 1d14ffa9 | bellard | else {
|
269 | 1d14ffa9 | bellard | if ((old_pos > ppos) && ((old_pos + len) > (ppos + hw->samples))) {
|
270 | 1d14ffa9 | bellard | len = hw->samples - old_pos + ppos; |
271 | 1d14ffa9 | bellard | } |
272 | 1d14ffa9 | bellard | } |
273 | 1d14ffa9 | bellard | decr = len; |
274 | 1d14ffa9 | bellard | |
275 | 1d14ffa9 | bellard | if (audio_bug (AUDIO_FUNC, decr < 0)) { |
276 | 1d14ffa9 | bellard | dolog ("decr=%d live=%d ppos=%d old_pos=%d len=%d\n",
|
277 | 1d14ffa9 | bellard | decr, live, ppos, old_pos, len); |
278 | 1d14ffa9 | bellard | return 0; |
279 | 85571bc7 | bellard | } |
280 | 85571bc7 | bellard | } |
281 | 85571bc7 | bellard | |
282 | 1d14ffa9 | bellard | |
283 | 1d14ffa9 | bellard | if (!decr) {
|
284 | 1d14ffa9 | bellard | return 0; |
285 | 85571bc7 | bellard | } |
286 | 85571bc7 | bellard | |
287 | 1d14ffa9 | bellard | if (fmod_lock_sample (fmd->fmod_sample, &fmd->hw.info,
|
288 | 1d14ffa9 | bellard | fmd->old_pos, decr, |
289 | 1d14ffa9 | bellard | &p1, &p2, |
290 | 1d14ffa9 | bellard | &blen1, &blen2)) { |
291 | 1d14ffa9 | bellard | return 0; |
292 | 85571bc7 | bellard | } |
293 | 85571bc7 | bellard | |
294 | 1d14ffa9 | bellard | len1 = blen1 >> hw->info.shift; |
295 | 1d14ffa9 | bellard | len2 = blen2 >> hw->info.shift; |
296 | 85571bc7 | bellard | ldebug ("%p %p %d %d %d %d\n", p1, p2, len1, len2, blen1, blen2);
|
297 | 85571bc7 | bellard | decr = len1 + len2; |
298 | 85571bc7 | bellard | |
299 | 1d14ffa9 | bellard | if (p1 && len1) {
|
300 | 1d14ffa9 | bellard | fmod_write_sample (hw, p1, len1); |
301 | 85571bc7 | bellard | } |
302 | 85571bc7 | bellard | |
303 | 1d14ffa9 | bellard | if (p2 && len2) {
|
304 | 1d14ffa9 | bellard | fmod_write_sample (hw, p2, len2); |
305 | 85571bc7 | bellard | } |
306 | 85571bc7 | bellard | |
307 | 1d14ffa9 | bellard | fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2); |
308 | 85571bc7 | bellard | |
309 | 85571bc7 | bellard | fmd->old_pos = (fmd->old_pos + decr) % hw->samples; |
310 | 1d14ffa9 | bellard | return decr;
|
311 | 85571bc7 | bellard | } |
312 | 85571bc7 | bellard | |
313 | 1d14ffa9 | bellard | static int aud_to_fmodfmt (audfmt_e fmt, int stereo) |
314 | 85571bc7 | bellard | { |
315 | 85571bc7 | bellard | int mode = FSOUND_LOOP_NORMAL;
|
316 | 85571bc7 | bellard | |
317 | 85571bc7 | bellard | switch (fmt) {
|
318 | 85571bc7 | bellard | case AUD_FMT_S8:
|
319 | 85571bc7 | bellard | mode |= FSOUND_SIGNED | FSOUND_8BITS; |
320 | 85571bc7 | bellard | break;
|
321 | 85571bc7 | bellard | |
322 | 85571bc7 | bellard | case AUD_FMT_U8:
|
323 | 85571bc7 | bellard | mode |= FSOUND_UNSIGNED | FSOUND_8BITS; |
324 | 85571bc7 | bellard | break;
|
325 | 85571bc7 | bellard | |
326 | 85571bc7 | bellard | case AUD_FMT_S16:
|
327 | 85571bc7 | bellard | mode |= FSOUND_SIGNED | FSOUND_16BITS; |
328 | 85571bc7 | bellard | break;
|
329 | 85571bc7 | bellard | |
330 | 85571bc7 | bellard | case AUD_FMT_U16:
|
331 | 85571bc7 | bellard | mode |= FSOUND_UNSIGNED | FSOUND_16BITS; |
332 | 85571bc7 | bellard | break;
|
333 | 85571bc7 | bellard | |
334 | 85571bc7 | bellard | default:
|
335 | 1d14ffa9 | bellard | dolog ("Internal logic error: Bad audio format %d\n", fmt);
|
336 | 1d14ffa9 | bellard | #ifdef DEBUG_FMOD
|
337 | 1d14ffa9 | bellard | abort (); |
338 | 1d14ffa9 | bellard | #endif
|
339 | 1d14ffa9 | bellard | mode |= FSOUND_8BITS; |
340 | 85571bc7 | bellard | } |
341 | 85571bc7 | bellard | mode |= stereo ? FSOUND_STEREO : FSOUND_MONO; |
342 | 85571bc7 | bellard | return mode;
|
343 | 85571bc7 | bellard | } |
344 | 85571bc7 | bellard | |
345 | 1d14ffa9 | bellard | static void fmod_fini_out (HWVoiceOut *hw) |
346 | 85571bc7 | bellard | { |
347 | 1d14ffa9 | bellard | FMODVoiceOut *fmd = (FMODVoiceOut *) hw; |
348 | 85571bc7 | bellard | |
349 | 85571bc7 | bellard | if (fmd->fmod_sample) {
|
350 | 85571bc7 | bellard | FSOUND_Sample_Free (fmd->fmod_sample); |
351 | 85571bc7 | bellard | fmd->fmod_sample = 0;
|
352 | 85571bc7 | bellard | |
353 | 85571bc7 | bellard | if (fmd->channel >= 0) { |
354 | 85571bc7 | bellard | FSOUND_StopSound (fmd->channel); |
355 | 85571bc7 | bellard | } |
356 | 85571bc7 | bellard | } |
357 | 85571bc7 | bellard | } |
358 | 85571bc7 | bellard | |
359 | c0fe3827 | bellard | static int fmod_init_out (HWVoiceOut *hw, audsettings_t *as) |
360 | 85571bc7 | bellard | { |
361 | 85571bc7 | bellard | int bits16, mode, channel;
|
362 | 1d14ffa9 | bellard | FMODVoiceOut *fmd = (FMODVoiceOut *) hw; |
363 | 85571bc7 | bellard | |
364 | c0fe3827 | bellard | mode = aud_to_fmodfmt (as->fmt, as->nchannels == 2 ? 1 : 0); |
365 | 85571bc7 | bellard | fmd->fmod_sample = FSOUND_Sample_Alloc ( |
366 | 85571bc7 | bellard | FSOUND_FREE, /* index */
|
367 | 85571bc7 | bellard | conf.nb_samples, /* length */
|
368 | 85571bc7 | bellard | mode, /* mode */
|
369 | c0fe3827 | bellard | as->freq, /* freq */
|
370 | 85571bc7 | bellard | 255, /* volume */ |
371 | 85571bc7 | bellard | 128, /* pan */ |
372 | 85571bc7 | bellard | 255 /* priority */ |
373 | 85571bc7 | bellard | ); |
374 | 85571bc7 | bellard | |
375 | 85571bc7 | bellard | if (!fmd->fmod_sample) {
|
376 | 1d14ffa9 | bellard | fmod_logerr2 ("DAC", "Failed to allocate FMOD sample\n"); |
377 | 85571bc7 | bellard | return -1; |
378 | 85571bc7 | bellard | } |
379 | 85571bc7 | bellard | |
380 | 85571bc7 | bellard | channel = FSOUND_PlaySoundEx (FSOUND_FREE, fmd->fmod_sample, 0, 1); |
381 | 85571bc7 | bellard | if (channel < 0) { |
382 | 1d14ffa9 | bellard | fmod_logerr2 ("DAC", "Failed to start playing sound\n"); |
383 | 85571bc7 | bellard | FSOUND_Sample_Free (fmd->fmod_sample); |
384 | 85571bc7 | bellard | return -1; |
385 | 85571bc7 | bellard | } |
386 | 85571bc7 | bellard | fmd->channel = channel; |
387 | 85571bc7 | bellard | |
388 | 1d14ffa9 | bellard | /* FMOD always operates on little endian frames? */
|
389 | c0fe3827 | bellard | audio_pcm_init_info (&hw->info, as, audio_need_to_swap_endian (0));
|
390 | 1d14ffa9 | bellard | bits16 = (mode & FSOUND_16BITS) != 0;
|
391 | c0fe3827 | bellard | hw->samples = conf.nb_samples; |
392 | 85571bc7 | bellard | return 0; |
393 | 85571bc7 | bellard | } |
394 | 85571bc7 | bellard | |
395 | 1d14ffa9 | bellard | static int fmod_ctl_out (HWVoiceOut *hw, int cmd, ...) |
396 | 85571bc7 | bellard | { |
397 | 85571bc7 | bellard | int status;
|
398 | 1d14ffa9 | bellard | FMODVoiceOut *fmd = (FMODVoiceOut *) hw; |
399 | 85571bc7 | bellard | |
400 | 85571bc7 | bellard | switch (cmd) {
|
401 | 85571bc7 | bellard | case VOICE_ENABLE:
|
402 | 85571bc7 | bellard | fmod_clear_sample (fmd); |
403 | 85571bc7 | bellard | status = FSOUND_SetPaused (fmd->channel, 0);
|
404 | 85571bc7 | bellard | if (!status) {
|
405 | 1d14ffa9 | bellard | fmod_logerr ("Failed to resume channel %d\n", fmd->channel);
|
406 | 85571bc7 | bellard | } |
407 | 85571bc7 | bellard | break;
|
408 | 85571bc7 | bellard | |
409 | 85571bc7 | bellard | case VOICE_DISABLE:
|
410 | 85571bc7 | bellard | status = FSOUND_SetPaused (fmd->channel, 1);
|
411 | 85571bc7 | bellard | if (!status) {
|
412 | 1d14ffa9 | bellard | fmod_logerr ("Failed to pause channel %d\n", fmd->channel);
|
413 | 85571bc7 | bellard | } |
414 | 85571bc7 | bellard | break;
|
415 | 85571bc7 | bellard | } |
416 | 85571bc7 | bellard | return 0; |
417 | 85571bc7 | bellard | } |
418 | 85571bc7 | bellard | |
419 | c0fe3827 | bellard | static int fmod_init_in (HWVoiceIn *hw, audsettings_t *as) |
420 | 1d14ffa9 | bellard | { |
421 | 1d14ffa9 | bellard | int bits16, mode;
|
422 | 1d14ffa9 | bellard | FMODVoiceIn *fmd = (FMODVoiceIn *) hw; |
423 | 1d14ffa9 | bellard | |
424 | 1d14ffa9 | bellard | if (conf.broken_adc) {
|
425 | 1d14ffa9 | bellard | return -1; |
426 | 1d14ffa9 | bellard | } |
427 | 1d14ffa9 | bellard | |
428 | c0fe3827 | bellard | mode = aud_to_fmodfmt (as->fmt, as->nchannels == 2 ? 1 : 0); |
429 | 1d14ffa9 | bellard | fmd->fmod_sample = FSOUND_Sample_Alloc ( |
430 | 1d14ffa9 | bellard | FSOUND_FREE, /* index */
|
431 | 1d14ffa9 | bellard | conf.nb_samples, /* length */
|
432 | 1d14ffa9 | bellard | mode, /* mode */
|
433 | c0fe3827 | bellard | as->freq, /* freq */
|
434 | 1d14ffa9 | bellard | 255, /* volume */ |
435 | 1d14ffa9 | bellard | 128, /* pan */ |
436 | 1d14ffa9 | bellard | 255 /* priority */ |
437 | 1d14ffa9 | bellard | ); |
438 | 1d14ffa9 | bellard | |
439 | 1d14ffa9 | bellard | if (!fmd->fmod_sample) {
|
440 | 1d14ffa9 | bellard | fmod_logerr2 ("ADC", "Failed to allocate FMOD sample\n"); |
441 | 1d14ffa9 | bellard | return -1; |
442 | 1d14ffa9 | bellard | } |
443 | 1d14ffa9 | bellard | |
444 | 1d14ffa9 | bellard | /* FMOD always operates on little endian frames? */
|
445 | c0fe3827 | bellard | audio_pcm_init_info (&hw->info, as, audio_need_to_swap_endian (0));
|
446 | 1d14ffa9 | bellard | bits16 = (mode & FSOUND_16BITS) != 0;
|
447 | c0fe3827 | bellard | hw->samples = conf.nb_samples; |
448 | 1d14ffa9 | bellard | return 0; |
449 | 1d14ffa9 | bellard | } |
450 | 1d14ffa9 | bellard | |
451 | 1d14ffa9 | bellard | static void fmod_fini_in (HWVoiceIn *hw) |
452 | 1d14ffa9 | bellard | { |
453 | 1d14ffa9 | bellard | FMODVoiceIn *fmd = (FMODVoiceIn *) hw; |
454 | 1d14ffa9 | bellard | |
455 | 1d14ffa9 | bellard | if (fmd->fmod_sample) {
|
456 | 1d14ffa9 | bellard | FSOUND_Record_Stop (); |
457 | 1d14ffa9 | bellard | FSOUND_Sample_Free (fmd->fmod_sample); |
458 | 1d14ffa9 | bellard | fmd->fmod_sample = 0;
|
459 | 1d14ffa9 | bellard | } |
460 | 1d14ffa9 | bellard | } |
461 | 1d14ffa9 | bellard | |
462 | 1d14ffa9 | bellard | static int fmod_run_in (HWVoiceIn *hw) |
463 | 1d14ffa9 | bellard | { |
464 | 1d14ffa9 | bellard | FMODVoiceIn *fmd = (FMODVoiceIn *) hw; |
465 | 1d14ffa9 | bellard | int hwshift = hw->info.shift;
|
466 | 1d14ffa9 | bellard | int live, dead, new_pos, len;
|
467 | 1d14ffa9 | bellard | unsigned int blen1 = 0, blen2 = 0; |
468 | 1d14ffa9 | bellard | unsigned int len1, len2; |
469 | 1d14ffa9 | bellard | unsigned int decr; |
470 | 1d14ffa9 | bellard | void *p1, *p2;
|
471 | 1d14ffa9 | bellard | |
472 | 1d14ffa9 | bellard | live = audio_pcm_hw_get_live_in (hw); |
473 | 1d14ffa9 | bellard | dead = hw->samples - live; |
474 | 1d14ffa9 | bellard | if (!dead) {
|
475 | 1d14ffa9 | bellard | return 0; |
476 | 1d14ffa9 | bellard | } |
477 | 1d14ffa9 | bellard | |
478 | 1d14ffa9 | bellard | new_pos = FSOUND_Record_GetPosition (); |
479 | 1d14ffa9 | bellard | if (new_pos < 0) { |
480 | c0fe3827 | bellard | fmod_logerr ("Could not get recording position\n");
|
481 | 1d14ffa9 | bellard | return 0; |
482 | 1d14ffa9 | bellard | } |
483 | 1d14ffa9 | bellard | |
484 | 1d14ffa9 | bellard | len = audio_ring_dist (new_pos, hw->wpos, hw->samples); |
485 | 1d14ffa9 | bellard | if (!len) {
|
486 | 1d14ffa9 | bellard | return 0; |
487 | 1d14ffa9 | bellard | } |
488 | 1d14ffa9 | bellard | len = audio_MIN (len, dead); |
489 | 1d14ffa9 | bellard | |
490 | 1d14ffa9 | bellard | if (fmod_lock_sample (fmd->fmod_sample, &fmd->hw.info,
|
491 | 1d14ffa9 | bellard | hw->wpos, len, |
492 | 1d14ffa9 | bellard | &p1, &p2, |
493 | 1d14ffa9 | bellard | &blen1, &blen2)) { |
494 | 1d14ffa9 | bellard | return 0; |
495 | 1d14ffa9 | bellard | } |
496 | 1d14ffa9 | bellard | |
497 | 1d14ffa9 | bellard | len1 = blen1 >> hwshift; |
498 | 1d14ffa9 | bellard | len2 = blen2 >> hwshift; |
499 | 1d14ffa9 | bellard | decr = len1 + len2; |
500 | 1d14ffa9 | bellard | |
501 | 1d14ffa9 | bellard | if (p1 && blen1) {
|
502 | 1d14ffa9 | bellard | hw->conv (hw->conv_buf + hw->wpos, p1, len1, &nominal_volume); |
503 | 1d14ffa9 | bellard | } |
504 | 1d14ffa9 | bellard | if (p2 && len2) {
|
505 | 1d14ffa9 | bellard | hw->conv (hw->conv_buf, p2, len2, &nominal_volume); |
506 | 1d14ffa9 | bellard | } |
507 | 1d14ffa9 | bellard | |
508 | 1d14ffa9 | bellard | fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2); |
509 | 1d14ffa9 | bellard | hw->wpos = (hw->wpos + decr) % hw->samples; |
510 | 1d14ffa9 | bellard | return decr;
|
511 | 1d14ffa9 | bellard | } |
512 | 1d14ffa9 | bellard | |
513 | 85571bc7 | bellard | static struct { |
514 | 85571bc7 | bellard | const char *name; |
515 | 85571bc7 | bellard | int type;
|
516 | 85571bc7 | bellard | } drvtab[] = { |
517 | 85571bc7 | bellard | {"none", FSOUND_OUTPUT_NOSOUND},
|
518 | 85571bc7 | bellard | #ifdef _WIN32
|
519 | 85571bc7 | bellard | {"winmm", FSOUND_OUTPUT_WINMM},
|
520 | 85571bc7 | bellard | {"dsound", FSOUND_OUTPUT_DSOUND},
|
521 | 85571bc7 | bellard | {"a3d", FSOUND_OUTPUT_A3D},
|
522 | 85571bc7 | bellard | {"asio", FSOUND_OUTPUT_ASIO},
|
523 | 85571bc7 | bellard | #endif
|
524 | 85571bc7 | bellard | #ifdef __linux__
|
525 | 85571bc7 | bellard | {"oss", FSOUND_OUTPUT_OSS},
|
526 | 85571bc7 | bellard | {"alsa", FSOUND_OUTPUT_ALSA},
|
527 | 85571bc7 | bellard | {"esd", FSOUND_OUTPUT_ESD},
|
528 | 85571bc7 | bellard | #endif
|
529 | 85571bc7 | bellard | #ifdef __APPLE__
|
530 | 85571bc7 | bellard | {"mac", FSOUND_OUTPUT_MAC},
|
531 | 85571bc7 | bellard | #endif
|
532 | 85571bc7 | bellard | #if 0
|
533 | 85571bc7 | bellard | {"xbox", FSOUND_OUTPUT_XBOX},
|
534 | 85571bc7 | bellard | {"ps2", FSOUND_OUTPUT_PS2},
|
535 | 85571bc7 | bellard | {"gcube", FSOUND_OUTPUT_GC},
|
536 | 85571bc7 | bellard | #endif
|
537 | 1d14ffa9 | bellard | {"none-realtime", FSOUND_OUTPUT_NOSOUND_NONREALTIME}
|
538 | 85571bc7 | bellard | }; |
539 | 85571bc7 | bellard | |
540 | 85571bc7 | bellard | static void *fmod_audio_init (void) |
541 | 85571bc7 | bellard | { |
542 | 1d14ffa9 | bellard | size_t i; |
543 | 85571bc7 | bellard | double ver;
|
544 | 85571bc7 | bellard | int status;
|
545 | 85571bc7 | bellard | int output_type = -1; |
546 | 1d14ffa9 | bellard | const char *drv = conf.drvname; |
547 | 85571bc7 | bellard | |
548 | 85571bc7 | bellard | ver = FSOUND_GetVersion (); |
549 | 85571bc7 | bellard | if (ver < FMOD_VERSION) {
|
550 | 85571bc7 | bellard | dolog ("Wrong FMOD version %f, need at least %f\n", ver, FMOD_VERSION);
|
551 | 85571bc7 | bellard | return NULL; |
552 | 85571bc7 | bellard | } |
553 | 85571bc7 | bellard | |
554 | 1d14ffa9 | bellard | #ifdef __linux__
|
555 | 1d14ffa9 | bellard | if (ver < 3.75) { |
556 | 1d14ffa9 | bellard | dolog ("FMOD before 3.75 has bug preventing ADC from working\n"
|
557 | 1d14ffa9 | bellard | "ADC will be disabled.\n");
|
558 | 1d14ffa9 | bellard | conf.broken_adc = 1;
|
559 | 1d14ffa9 | bellard | } |
560 | 1d14ffa9 | bellard | #endif
|
561 | 1d14ffa9 | bellard | |
562 | 85571bc7 | bellard | if (drv) {
|
563 | 85571bc7 | bellard | int found = 0; |
564 | 85571bc7 | bellard | for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) { |
565 | 85571bc7 | bellard | if (!strcmp (drv, drvtab[i].name)) {
|
566 | 85571bc7 | bellard | output_type = drvtab[i].type; |
567 | 85571bc7 | bellard | found = 1;
|
568 | 85571bc7 | bellard | break;
|
569 | 85571bc7 | bellard | } |
570 | 85571bc7 | bellard | } |
571 | 85571bc7 | bellard | if (!found) {
|
572 | 1d14ffa9 | bellard | dolog ("Unknown FMOD driver `%s'\n", drv);
|
573 | 1d14ffa9 | bellard | dolog ("Valid drivers:\n");
|
574 | 1d14ffa9 | bellard | for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) { |
575 | 1d14ffa9 | bellard | dolog (" %s\n", drvtab[i].name);
|
576 | 1d14ffa9 | bellard | } |
577 | 85571bc7 | bellard | } |
578 | 85571bc7 | bellard | } |
579 | 85571bc7 | bellard | |
580 | 85571bc7 | bellard | if (output_type != -1) { |
581 | 85571bc7 | bellard | status = FSOUND_SetOutput (output_type); |
582 | 85571bc7 | bellard | if (!status) {
|
583 | 1d14ffa9 | bellard | fmod_logerr ("FSOUND_SetOutput(%d) failed\n", output_type);
|
584 | 85571bc7 | bellard | return NULL; |
585 | 85571bc7 | bellard | } |
586 | 85571bc7 | bellard | } |
587 | 85571bc7 | bellard | |
588 | 85571bc7 | bellard | if (conf.bufsize) {
|
589 | 85571bc7 | bellard | status = FSOUND_SetBufferSize (conf.bufsize); |
590 | 85571bc7 | bellard | if (!status) {
|
591 | 1d14ffa9 | bellard | fmod_logerr ("FSOUND_SetBufferSize (%d) failed\n", conf.bufsize);
|
592 | 85571bc7 | bellard | } |
593 | 85571bc7 | bellard | } |
594 | 85571bc7 | bellard | |
595 | 85571bc7 | bellard | status = FSOUND_Init (conf.freq, conf.nb_channels, 0);
|
596 | 85571bc7 | bellard | if (!status) {
|
597 | 1d14ffa9 | bellard | fmod_logerr ("FSOUND_Init failed\n");
|
598 | 85571bc7 | bellard | return NULL; |
599 | 85571bc7 | bellard | } |
600 | 85571bc7 | bellard | |
601 | 85571bc7 | bellard | return &conf;
|
602 | 85571bc7 | bellard | } |
603 | 85571bc7 | bellard | |
604 | 1d14ffa9 | bellard | static int fmod_read (SWVoiceIn *sw, void *buf, int size) |
605 | 1d14ffa9 | bellard | { |
606 | 1d14ffa9 | bellard | return audio_pcm_sw_read (sw, buf, size);
|
607 | 1d14ffa9 | bellard | } |
608 | 1d14ffa9 | bellard | |
609 | 1d14ffa9 | bellard | static int fmod_ctl_in (HWVoiceIn *hw, int cmd, ...) |
610 | 1d14ffa9 | bellard | { |
611 | 1d14ffa9 | bellard | int status;
|
612 | 1d14ffa9 | bellard | FMODVoiceIn *fmd = (FMODVoiceIn *) hw; |
613 | 1d14ffa9 | bellard | |
614 | 1d14ffa9 | bellard | switch (cmd) {
|
615 | 1d14ffa9 | bellard | case VOICE_ENABLE:
|
616 | 1d14ffa9 | bellard | status = FSOUND_Record_StartSample (fmd->fmod_sample, 1);
|
617 | 1d14ffa9 | bellard | if (!status) {
|
618 | 1d14ffa9 | bellard | fmod_logerr ("Failed to start recording\n");
|
619 | 1d14ffa9 | bellard | } |
620 | 1d14ffa9 | bellard | break;
|
621 | 1d14ffa9 | bellard | |
622 | 1d14ffa9 | bellard | case VOICE_DISABLE:
|
623 | 1d14ffa9 | bellard | status = FSOUND_Record_Stop (); |
624 | 1d14ffa9 | bellard | if (!status) {
|
625 | 1d14ffa9 | bellard | fmod_logerr ("Failed to stop recording\n");
|
626 | 1d14ffa9 | bellard | } |
627 | 1d14ffa9 | bellard | break;
|
628 | 1d14ffa9 | bellard | } |
629 | 1d14ffa9 | bellard | return 0; |
630 | 1d14ffa9 | bellard | } |
631 | 1d14ffa9 | bellard | |
632 | 85571bc7 | bellard | static void fmod_audio_fini (void *opaque) |
633 | 85571bc7 | bellard | { |
634 | 1d14ffa9 | bellard | (void) opaque;
|
635 | 85571bc7 | bellard | FSOUND_Close (); |
636 | 85571bc7 | bellard | } |
637 | 85571bc7 | bellard | |
638 | 1d14ffa9 | bellard | static struct audio_option fmod_options[] = { |
639 | 1d14ffa9 | bellard | {"DRV", AUD_OPT_STR, &conf.drvname,
|
640 | 1d14ffa9 | bellard | "FMOD driver", NULL, 0}, |
641 | 1d14ffa9 | bellard | {"FREQ", AUD_OPT_INT, &conf.freq,
|
642 | 1d14ffa9 | bellard | "Default frequency", NULL, 0}, |
643 | 1d14ffa9 | bellard | {"SAMPLES", AUD_OPT_INT, &conf.nb_samples,
|
644 | 1d14ffa9 | bellard | "Buffer size in samples", NULL, 0}, |
645 | 1d14ffa9 | bellard | {"CHANNELS", AUD_OPT_INT, &conf.nb_channels,
|
646 | 1d14ffa9 | bellard | "Number of default channels (1 - mono, 2 - stereo)", NULL, 0}, |
647 | 1d14ffa9 | bellard | {"BUFSIZE", AUD_OPT_INT, &conf.bufsize,
|
648 | 1d14ffa9 | bellard | "(undocumented)", NULL, 0}, |
649 | 1d14ffa9 | bellard | #if 0
|
650 | 1d14ffa9 | bellard | {"THRESHOLD", AUD_OPT_INT, &conf.threshold,
|
651 | 1d14ffa9 | bellard | "(undocumented)"},
|
652 | 1d14ffa9 | bellard | #endif
|
653 | 1d14ffa9 | bellard | |
654 | 1d14ffa9 | bellard | {NULL, 0, NULL, NULL, NULL, 0} |
655 | 1d14ffa9 | bellard | }; |
656 | 1d14ffa9 | bellard | |
657 | 1d14ffa9 | bellard | static struct audio_pcm_ops fmod_pcm_ops = { |
658 | 1d14ffa9 | bellard | fmod_init_out, |
659 | 1d14ffa9 | bellard | fmod_fini_out, |
660 | 1d14ffa9 | bellard | fmod_run_out, |
661 | 1d14ffa9 | bellard | fmod_write, |
662 | 1d14ffa9 | bellard | fmod_ctl_out, |
663 | 1d14ffa9 | bellard | |
664 | 1d14ffa9 | bellard | fmod_init_in, |
665 | 1d14ffa9 | bellard | fmod_fini_in, |
666 | 1d14ffa9 | bellard | fmod_run_in, |
667 | 1d14ffa9 | bellard | fmod_read, |
668 | 1d14ffa9 | bellard | fmod_ctl_in |
669 | 85571bc7 | bellard | }; |
670 | 85571bc7 | bellard | |
671 | 1d14ffa9 | bellard | struct audio_driver fmod_audio_driver = {
|
672 | 1d14ffa9 | bellard | INIT_FIELD (name = ) "fmod",
|
673 | 1d14ffa9 | bellard | INIT_FIELD (descr = ) "FMOD 3.xx http://www.fmod.org",
|
674 | 1d14ffa9 | bellard | INIT_FIELD (options = ) fmod_options, |
675 | 1d14ffa9 | bellard | INIT_FIELD (init = ) fmod_audio_init, |
676 | 1d14ffa9 | bellard | INIT_FIELD (fini = ) fmod_audio_fini, |
677 | 1d14ffa9 | bellard | INIT_FIELD (pcm_ops = ) &fmod_pcm_ops, |
678 | 1d14ffa9 | bellard | INIT_FIELD (can_be_default = ) 1,
|
679 | 1d14ffa9 | bellard | INIT_FIELD (max_voices_out = ) INT_MAX, |
680 | 1d14ffa9 | bellard | INIT_FIELD (max_voices_in = ) INT_MAX, |
681 | 1d14ffa9 | bellard | INIT_FIELD (voice_size_out = ) sizeof (FMODVoiceOut),
|
682 | 1d14ffa9 | bellard | INIT_FIELD (voice_size_in = ) sizeof (FMODVoiceIn)
|
683 | 85571bc7 | bellard | }; |