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