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