root / audio / fmodaudio.c @ cbeb0857
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 | 85571bc7 | bellard | } |
157 | 85571bc7 | bellard | |
158 | 85571bc7 | bellard | if (src_len2) {
|
159 | 1d14ffa9 | bellard | dst = advance (dst, src_len1 << hw->info.shift); |
160 | 85571bc7 | bellard | hw->clip (dst, src2, src_len2); |
161 | 85571bc7 | bellard | } |
162 | 1d14ffa9 | bellard | |
163 | 1d14ffa9 | bellard | hw->rpos = pos % hw->samples; |
164 | 85571bc7 | bellard | } |
165 | 85571bc7 | bellard | |
166 | 1d14ffa9 | bellard | static int fmod_unlock_sample (FSOUND_SAMPLE *sample, void *p1, void *p2, |
167 | 85571bc7 | bellard | unsigned int blen1, unsigned int blen2) |
168 | 85571bc7 | bellard | { |
169 | 1d14ffa9 | bellard | int status = FSOUND_Sample_Unlock (sample, p1, p2, blen1, blen2);
|
170 | 85571bc7 | bellard | if (!status) {
|
171 | 1d14ffa9 | bellard | fmod_logerr ("Failed to unlock sample\n");
|
172 | 85571bc7 | bellard | return -1; |
173 | 85571bc7 | bellard | } |
174 | 85571bc7 | bellard | return 0; |
175 | 85571bc7 | bellard | } |
176 | 85571bc7 | bellard | |
177 | 1d14ffa9 | bellard | static int fmod_lock_sample ( |
178 | 1d14ffa9 | bellard | FSOUND_SAMPLE *sample, |
179 | 1d14ffa9 | bellard | struct audio_pcm_info *info,
|
180 | 1d14ffa9 | bellard | int pos,
|
181 | 1d14ffa9 | bellard | int len,
|
182 | 1d14ffa9 | bellard | void **p1,
|
183 | 1d14ffa9 | bellard | void **p2,
|
184 | 1d14ffa9 | bellard | unsigned int *blen1, |
185 | 1d14ffa9 | bellard | unsigned int *blen2 |
186 | 1d14ffa9 | bellard | ) |
187 | 85571bc7 | bellard | { |
188 | 85571bc7 | bellard | int status;
|
189 | 85571bc7 | bellard | |
190 | 85571bc7 | bellard | status = FSOUND_Sample_Lock ( |
191 | 1d14ffa9 | bellard | sample, |
192 | 1d14ffa9 | bellard | pos << info->shift, |
193 | 1d14ffa9 | bellard | len << info->shift, |
194 | 85571bc7 | bellard | p1, |
195 | 85571bc7 | bellard | p2, |
196 | 85571bc7 | bellard | blen1, |
197 | 85571bc7 | bellard | blen2 |
198 | 85571bc7 | bellard | ); |
199 | 85571bc7 | bellard | |
200 | 85571bc7 | bellard | if (!status) {
|
201 | 1d14ffa9 | bellard | fmod_logerr ("Failed to lock sample\n");
|
202 | 85571bc7 | bellard | return -1; |
203 | 85571bc7 | bellard | } |
204 | 85571bc7 | bellard | |
205 | 1d14ffa9 | bellard | if ((*blen1 & info->align) || (*blen2 & info->align)) {
|
206 | 1d14ffa9 | bellard | dolog ("Lock returned misaligned length %d, %d, alignment %d\n",
|
207 | 1d14ffa9 | bellard | *blen1, *blen2, info->align + 1);
|
208 | 1d14ffa9 | bellard | |
209 | 1d14ffa9 | bellard | fmod_unlock_sample (sample, *p1, *p2, *blen1, *blen2); |
210 | 1d14ffa9 | bellard | |
211 | 1d14ffa9 | bellard | *p1 = NULL - 1; |
212 | 1d14ffa9 | bellard | *p2 = NULL - 1; |
213 | 1d14ffa9 | bellard | *blen1 = ~0U;
|
214 | 1d14ffa9 | bellard | *blen2 = ~0U;
|
215 | 85571bc7 | bellard | return -1; |
216 | 85571bc7 | bellard | } |
217 | 1d14ffa9 | bellard | |
218 | 1d14ffa9 | bellard | if (!*p1 && *blen1) {
|
219 | 1d14ffa9 | bellard | dolog ("warning: !p1 && blen1=%d\n", *blen1);
|
220 | 1d14ffa9 | bellard | *blen1 = 0;
|
221 | 1d14ffa9 | bellard | } |
222 | 1d14ffa9 | bellard | |
223 | 1d14ffa9 | bellard | if (!p2 && *blen2) {
|
224 | 1d14ffa9 | bellard | dolog ("warning: !p2 && blen2=%d\n", *blen2);
|
225 | 1d14ffa9 | bellard | *blen2 = 0;
|
226 | 1d14ffa9 | bellard | } |
227 | 1d14ffa9 | bellard | |
228 | 85571bc7 | bellard | return 0; |
229 | 85571bc7 | bellard | } |
230 | 85571bc7 | bellard | |
231 | 1d14ffa9 | bellard | static int fmod_run_out (HWVoiceOut *hw) |
232 | 85571bc7 | bellard | { |
233 | 1d14ffa9 | bellard | FMODVoiceOut *fmd = (FMODVoiceOut *) hw; |
234 | 1d14ffa9 | bellard | int live, decr;
|
235 | 85571bc7 | bellard | void *p1 = 0, *p2 = 0; |
236 | 85571bc7 | bellard | unsigned int blen1 = 0, blen2 = 0; |
237 | 85571bc7 | bellard | unsigned int len1 = 0, len2 = 0; |
238 | 1d14ffa9 | bellard | int nb_live;
|
239 | 85571bc7 | bellard | |
240 | 1d14ffa9 | bellard | live = audio_pcm_hw_get_live_out2 (hw, &nb_live); |
241 | 1d14ffa9 | bellard | if (!live) {
|
242 | 1d14ffa9 | bellard | return 0; |
243 | 85571bc7 | bellard | } |
244 | 85571bc7 | bellard | |
245 | 85571bc7 | bellard | if (!hw->pending_disable
|
246 | 1d14ffa9 | bellard | && nb_live |
247 | 1d14ffa9 | bellard | && (conf.threshold && live <= conf.threshold)) { |
248 | 1d14ffa9 | bellard | ldebug ("live=%d nb_live=%d\n", live, nb_live);
|
249 | 1d14ffa9 | bellard | return 0; |
250 | 85571bc7 | bellard | } |
251 | 85571bc7 | bellard | |
252 | 85571bc7 | bellard | decr = live; |
253 | 85571bc7 | bellard | |
254 | 85571bc7 | bellard | if (fmd->channel >= 0) { |
255 | 1d14ffa9 | bellard | int len = decr;
|
256 | 1d14ffa9 | bellard | int old_pos = fmd->old_pos;
|
257 | 1d14ffa9 | bellard | int ppos = FSOUND_GetCurrentPosition (fmd->channel);
|
258 | 85571bc7 | bellard | |
259 | 1d14ffa9 | bellard | if (ppos == old_pos || !ppos) {
|
260 | 1d14ffa9 | bellard | return 0; |
261 | 85571bc7 | bellard | } |
262 | 1d14ffa9 | bellard | |
263 | 1d14ffa9 | bellard | if ((old_pos < ppos) && ((old_pos + len) > ppos)) {
|
264 | 1d14ffa9 | bellard | len = ppos - old_pos; |
265 | 1d14ffa9 | bellard | } |
266 | 1d14ffa9 | bellard | else {
|
267 | 1d14ffa9 | bellard | if ((old_pos > ppos) && ((old_pos + len) > (ppos + hw->samples))) {
|
268 | 1d14ffa9 | bellard | len = hw->samples - old_pos + ppos; |
269 | 1d14ffa9 | bellard | } |
270 | 1d14ffa9 | bellard | } |
271 | 1d14ffa9 | bellard | decr = len; |
272 | 1d14ffa9 | bellard | |
273 | 1d14ffa9 | bellard | if (audio_bug (AUDIO_FUNC, decr < 0)) { |
274 | 1d14ffa9 | bellard | dolog ("decr=%d live=%d ppos=%d old_pos=%d len=%d\n",
|
275 | 1d14ffa9 | bellard | decr, live, ppos, old_pos, len); |
276 | 1d14ffa9 | bellard | return 0; |
277 | 85571bc7 | bellard | } |
278 | 85571bc7 | bellard | } |
279 | 85571bc7 | bellard | |
280 | 1d14ffa9 | bellard | |
281 | 1d14ffa9 | bellard | if (!decr) {
|
282 | 1d14ffa9 | bellard | return 0; |
283 | 85571bc7 | bellard | } |
284 | 85571bc7 | bellard | |
285 | 1d14ffa9 | bellard | if (fmod_lock_sample (fmd->fmod_sample, &fmd->hw.info,
|
286 | 1d14ffa9 | bellard | fmd->old_pos, decr, |
287 | 1d14ffa9 | bellard | &p1, &p2, |
288 | 1d14ffa9 | bellard | &blen1, &blen2)) { |
289 | 1d14ffa9 | bellard | return 0; |
290 | 85571bc7 | bellard | } |
291 | 85571bc7 | bellard | |
292 | 1d14ffa9 | bellard | len1 = blen1 >> hw->info.shift; |
293 | 1d14ffa9 | bellard | len2 = blen2 >> hw->info.shift; |
294 | 85571bc7 | bellard | ldebug ("%p %p %d %d %d %d\n", p1, p2, len1, len2, blen1, blen2);
|
295 | 85571bc7 | bellard | decr = len1 + len2; |
296 | 85571bc7 | bellard | |
297 | 1d14ffa9 | bellard | if (p1 && len1) {
|
298 | 1d14ffa9 | bellard | fmod_write_sample (hw, p1, len1); |
299 | 85571bc7 | bellard | } |
300 | 85571bc7 | bellard | |
301 | 1d14ffa9 | bellard | if (p2 && len2) {
|
302 | 1d14ffa9 | bellard | fmod_write_sample (hw, p2, len2); |
303 | 85571bc7 | bellard | } |
304 | 85571bc7 | bellard | |
305 | 1d14ffa9 | bellard | fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2); |
306 | 85571bc7 | bellard | |
307 | 85571bc7 | bellard | fmd->old_pos = (fmd->old_pos + decr) % hw->samples; |
308 | 1d14ffa9 | bellard | return decr;
|
309 | 85571bc7 | bellard | } |
310 | 85571bc7 | bellard | |
311 | 1d14ffa9 | bellard | static int aud_to_fmodfmt (audfmt_e fmt, int stereo) |
312 | 85571bc7 | bellard | { |
313 | 85571bc7 | bellard | int mode = FSOUND_LOOP_NORMAL;
|
314 | 85571bc7 | bellard | |
315 | 85571bc7 | bellard | switch (fmt) {
|
316 | 85571bc7 | bellard | case AUD_FMT_S8:
|
317 | 85571bc7 | bellard | mode |= FSOUND_SIGNED | FSOUND_8BITS; |
318 | 85571bc7 | bellard | break;
|
319 | 85571bc7 | bellard | |
320 | 85571bc7 | bellard | case AUD_FMT_U8:
|
321 | 85571bc7 | bellard | mode |= FSOUND_UNSIGNED | FSOUND_8BITS; |
322 | 85571bc7 | bellard | break;
|
323 | 85571bc7 | bellard | |
324 | 85571bc7 | bellard | case AUD_FMT_S16:
|
325 | 85571bc7 | bellard | mode |= FSOUND_SIGNED | FSOUND_16BITS; |
326 | 85571bc7 | bellard | break;
|
327 | 85571bc7 | bellard | |
328 | 85571bc7 | bellard | case AUD_FMT_U16:
|
329 | 85571bc7 | bellard | mode |= FSOUND_UNSIGNED | FSOUND_16BITS; |
330 | 85571bc7 | bellard | break;
|
331 | 85571bc7 | bellard | |
332 | 85571bc7 | bellard | default:
|
333 | 1d14ffa9 | bellard | dolog ("Internal logic error: Bad audio format %d\n", fmt);
|
334 | 1d14ffa9 | bellard | #ifdef DEBUG_FMOD
|
335 | 1d14ffa9 | bellard | abort (); |
336 | 1d14ffa9 | bellard | #endif
|
337 | 1d14ffa9 | bellard | mode |= FSOUND_8BITS; |
338 | 85571bc7 | bellard | } |
339 | 85571bc7 | bellard | mode |= stereo ? FSOUND_STEREO : FSOUND_MONO; |
340 | 85571bc7 | bellard | return mode;
|
341 | 85571bc7 | bellard | } |
342 | 85571bc7 | bellard | |
343 | 1d14ffa9 | bellard | static void fmod_fini_out (HWVoiceOut *hw) |
344 | 85571bc7 | bellard | { |
345 | 1d14ffa9 | bellard | FMODVoiceOut *fmd = (FMODVoiceOut *) hw; |
346 | 85571bc7 | bellard | |
347 | 85571bc7 | bellard | if (fmd->fmod_sample) {
|
348 | 85571bc7 | bellard | FSOUND_Sample_Free (fmd->fmod_sample); |
349 | 85571bc7 | bellard | fmd->fmod_sample = 0;
|
350 | 85571bc7 | bellard | |
351 | 85571bc7 | bellard | if (fmd->channel >= 0) { |
352 | 85571bc7 | bellard | FSOUND_StopSound (fmd->channel); |
353 | 85571bc7 | bellard | } |
354 | 85571bc7 | bellard | } |
355 | 85571bc7 | bellard | } |
356 | 85571bc7 | bellard | |
357 | c0fe3827 | bellard | static int fmod_init_out (HWVoiceOut *hw, audsettings_t *as) |
358 | 85571bc7 | bellard | { |
359 | 85571bc7 | bellard | int bits16, mode, channel;
|
360 | 1d14ffa9 | bellard | FMODVoiceOut *fmd = (FMODVoiceOut *) hw; |
361 | d929eba5 | bellard | audsettings_t obt_as = *as; |
362 | 85571bc7 | bellard | |
363 | c0fe3827 | bellard | mode = aud_to_fmodfmt (as->fmt, as->nchannels == 2 ? 1 : 0); |
364 | 85571bc7 | bellard | fmd->fmod_sample = FSOUND_Sample_Alloc ( |
365 | 85571bc7 | bellard | FSOUND_FREE, /* index */
|
366 | 85571bc7 | bellard | conf.nb_samples, /* length */
|
367 | 85571bc7 | bellard | mode, /* mode */
|
368 | c0fe3827 | bellard | as->freq, /* freq */
|
369 | 85571bc7 | bellard | 255, /* volume */ |
370 | 85571bc7 | bellard | 128, /* pan */ |
371 | 85571bc7 | bellard | 255 /* priority */ |
372 | 85571bc7 | bellard | ); |
373 | 85571bc7 | bellard | |
374 | 85571bc7 | bellard | if (!fmd->fmod_sample) {
|
375 | 1d14ffa9 | bellard | fmod_logerr2 ("DAC", "Failed to allocate FMOD sample\n"); |
376 | 85571bc7 | bellard | return -1; |
377 | 85571bc7 | bellard | } |
378 | 85571bc7 | bellard | |
379 | 85571bc7 | bellard | channel = FSOUND_PlaySoundEx (FSOUND_FREE, fmd->fmod_sample, 0, 1); |
380 | 85571bc7 | bellard | if (channel < 0) { |
381 | 1d14ffa9 | bellard | fmod_logerr2 ("DAC", "Failed to start playing sound\n"); |
382 | 85571bc7 | bellard | FSOUND_Sample_Free (fmd->fmod_sample); |
383 | 85571bc7 | bellard | return -1; |
384 | 85571bc7 | bellard | } |
385 | 85571bc7 | bellard | fmd->channel = channel; |
386 | 85571bc7 | bellard | |
387 | 1d14ffa9 | bellard | /* FMOD always operates on little endian frames? */
|
388 | d929eba5 | bellard | obt_as.endianness = 0;
|
389 | d929eba5 | bellard | audio_pcm_init_info (&hw->info, &obt_as); |
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 | d929eba5 | bellard | audsettings_t obt_as = *as; |
424 | 1d14ffa9 | bellard | |
425 | 1d14ffa9 | bellard | if (conf.broken_adc) {
|
426 | 1d14ffa9 | bellard | return -1; |
427 | 1d14ffa9 | bellard | } |
428 | 1d14ffa9 | bellard | |
429 | c0fe3827 | bellard | mode = aud_to_fmodfmt (as->fmt, as->nchannels == 2 ? 1 : 0); |
430 | 1d14ffa9 | bellard | fmd->fmod_sample = FSOUND_Sample_Alloc ( |
431 | 1d14ffa9 | bellard | FSOUND_FREE, /* index */
|
432 | 1d14ffa9 | bellard | conf.nb_samples, /* length */
|
433 | 1d14ffa9 | bellard | mode, /* mode */
|
434 | c0fe3827 | bellard | as->freq, /* freq */
|
435 | 1d14ffa9 | bellard | 255, /* volume */ |
436 | 1d14ffa9 | bellard | 128, /* pan */ |
437 | 1d14ffa9 | bellard | 255 /* priority */ |
438 | 1d14ffa9 | bellard | ); |
439 | 1d14ffa9 | bellard | |
440 | 1d14ffa9 | bellard | if (!fmd->fmod_sample) {
|
441 | 1d14ffa9 | bellard | fmod_logerr2 ("ADC", "Failed to allocate FMOD sample\n"); |
442 | 1d14ffa9 | bellard | return -1; |
443 | 1d14ffa9 | bellard | } |
444 | 1d14ffa9 | bellard | |
445 | 1d14ffa9 | bellard | /* FMOD always operates on little endian frames? */
|
446 | d929eba5 | bellard | obt_as.endianness = 0;
|
447 | d929eba5 | bellard | audio_pcm_init_info (&hw->info, &obt_as); |
448 | 1d14ffa9 | bellard | bits16 = (mode & FSOUND_16BITS) != 0;
|
449 | c0fe3827 | bellard | hw->samples = conf.nb_samples; |
450 | 1d14ffa9 | bellard | return 0; |
451 | 1d14ffa9 | bellard | } |
452 | 1d14ffa9 | bellard | |
453 | 1d14ffa9 | bellard | static void fmod_fini_in (HWVoiceIn *hw) |
454 | 1d14ffa9 | bellard | { |
455 | 1d14ffa9 | bellard | FMODVoiceIn *fmd = (FMODVoiceIn *) hw; |
456 | 1d14ffa9 | bellard | |
457 | 1d14ffa9 | bellard | if (fmd->fmod_sample) {
|
458 | 1d14ffa9 | bellard | FSOUND_Record_Stop (); |
459 | 1d14ffa9 | bellard | FSOUND_Sample_Free (fmd->fmod_sample); |
460 | 1d14ffa9 | bellard | fmd->fmod_sample = 0;
|
461 | 1d14ffa9 | bellard | } |
462 | 1d14ffa9 | bellard | } |
463 | 1d14ffa9 | bellard | |
464 | 1d14ffa9 | bellard | static int fmod_run_in (HWVoiceIn *hw) |
465 | 1d14ffa9 | bellard | { |
466 | 1d14ffa9 | bellard | FMODVoiceIn *fmd = (FMODVoiceIn *) hw; |
467 | 1d14ffa9 | bellard | int hwshift = hw->info.shift;
|
468 | 1d14ffa9 | bellard | int live, dead, new_pos, len;
|
469 | 1d14ffa9 | bellard | unsigned int blen1 = 0, blen2 = 0; |
470 | 1d14ffa9 | bellard | unsigned int len1, len2; |
471 | 1d14ffa9 | bellard | unsigned int decr; |
472 | 1d14ffa9 | bellard | void *p1, *p2;
|
473 | 1d14ffa9 | bellard | |
474 | 1d14ffa9 | bellard | live = audio_pcm_hw_get_live_in (hw); |
475 | 1d14ffa9 | bellard | dead = hw->samples - live; |
476 | 1d14ffa9 | bellard | if (!dead) {
|
477 | 1d14ffa9 | bellard | return 0; |
478 | 1d14ffa9 | bellard | } |
479 | 1d14ffa9 | bellard | |
480 | 1d14ffa9 | bellard | new_pos = FSOUND_Record_GetPosition (); |
481 | 1d14ffa9 | bellard | if (new_pos < 0) { |
482 | c0fe3827 | bellard | fmod_logerr ("Could not get recording position\n");
|
483 | 1d14ffa9 | bellard | return 0; |
484 | 1d14ffa9 | bellard | } |
485 | 1d14ffa9 | bellard | |
486 | 1d14ffa9 | bellard | len = audio_ring_dist (new_pos, hw->wpos, hw->samples); |
487 | 1d14ffa9 | bellard | if (!len) {
|
488 | 1d14ffa9 | bellard | return 0; |
489 | 1d14ffa9 | bellard | } |
490 | 1d14ffa9 | bellard | len = audio_MIN (len, dead); |
491 | 1d14ffa9 | bellard | |
492 | 1d14ffa9 | bellard | if (fmod_lock_sample (fmd->fmod_sample, &fmd->hw.info,
|
493 | 1d14ffa9 | bellard | hw->wpos, len, |
494 | 1d14ffa9 | bellard | &p1, &p2, |
495 | 1d14ffa9 | bellard | &blen1, &blen2)) { |
496 | 1d14ffa9 | bellard | return 0; |
497 | 1d14ffa9 | bellard | } |
498 | 1d14ffa9 | bellard | |
499 | 1d14ffa9 | bellard | len1 = blen1 >> hwshift; |
500 | 1d14ffa9 | bellard | len2 = blen2 >> hwshift; |
501 | 1d14ffa9 | bellard | decr = len1 + len2; |
502 | 1d14ffa9 | bellard | |
503 | 1d14ffa9 | bellard | if (p1 && blen1) {
|
504 | 1d14ffa9 | bellard | hw->conv (hw->conv_buf + hw->wpos, p1, len1, &nominal_volume); |
505 | 1d14ffa9 | bellard | } |
506 | 1d14ffa9 | bellard | if (p2 && len2) {
|
507 | 1d14ffa9 | bellard | hw->conv (hw->conv_buf, p2, len2, &nominal_volume); |
508 | 1d14ffa9 | bellard | } |
509 | 1d14ffa9 | bellard | |
510 | 1d14ffa9 | bellard | fmod_unlock_sample (fmd->fmod_sample, p1, p2, blen1, blen2); |
511 | 1d14ffa9 | bellard | hw->wpos = (hw->wpos + decr) % hw->samples; |
512 | 1d14ffa9 | bellard | return decr;
|
513 | 1d14ffa9 | bellard | } |
514 | 1d14ffa9 | bellard | |
515 | 85571bc7 | bellard | static struct { |
516 | 85571bc7 | bellard | const char *name; |
517 | 85571bc7 | bellard | int type;
|
518 | 85571bc7 | bellard | } drvtab[] = { |
519 | 85571bc7 | bellard | {"none", FSOUND_OUTPUT_NOSOUND},
|
520 | 85571bc7 | bellard | #ifdef _WIN32
|
521 | 85571bc7 | bellard | {"winmm", FSOUND_OUTPUT_WINMM},
|
522 | 85571bc7 | bellard | {"dsound", FSOUND_OUTPUT_DSOUND},
|
523 | 85571bc7 | bellard | {"a3d", FSOUND_OUTPUT_A3D},
|
524 | 85571bc7 | bellard | {"asio", FSOUND_OUTPUT_ASIO},
|
525 | 85571bc7 | bellard | #endif
|
526 | 85571bc7 | bellard | #ifdef __linux__
|
527 | 85571bc7 | bellard | {"oss", FSOUND_OUTPUT_OSS},
|
528 | 85571bc7 | bellard | {"alsa", FSOUND_OUTPUT_ALSA},
|
529 | 85571bc7 | bellard | {"esd", FSOUND_OUTPUT_ESD},
|
530 | 85571bc7 | bellard | #endif
|
531 | 85571bc7 | bellard | #ifdef __APPLE__
|
532 | 85571bc7 | bellard | {"mac", FSOUND_OUTPUT_MAC},
|
533 | 85571bc7 | bellard | #endif
|
534 | 85571bc7 | bellard | #if 0
|
535 | 85571bc7 | bellard | {"xbox", FSOUND_OUTPUT_XBOX},
|
536 | 85571bc7 | bellard | {"ps2", FSOUND_OUTPUT_PS2},
|
537 | 85571bc7 | bellard | {"gcube", FSOUND_OUTPUT_GC},
|
538 | 85571bc7 | bellard | #endif
|
539 | 1d14ffa9 | bellard | {"none-realtime", FSOUND_OUTPUT_NOSOUND_NONREALTIME}
|
540 | 85571bc7 | bellard | }; |
541 | 85571bc7 | bellard | |
542 | 85571bc7 | bellard | static void *fmod_audio_init (void) |
543 | 85571bc7 | bellard | { |
544 | 1d14ffa9 | bellard | size_t i; |
545 | 85571bc7 | bellard | double ver;
|
546 | 85571bc7 | bellard | int status;
|
547 | 85571bc7 | bellard | int output_type = -1; |
548 | 1d14ffa9 | bellard | const char *drv = conf.drvname; |
549 | 85571bc7 | bellard | |
550 | 85571bc7 | bellard | ver = FSOUND_GetVersion (); |
551 | 85571bc7 | bellard | if (ver < FMOD_VERSION) {
|
552 | 85571bc7 | bellard | dolog ("Wrong FMOD version %f, need at least %f\n", ver, FMOD_VERSION);
|
553 | 85571bc7 | bellard | return NULL; |
554 | 85571bc7 | bellard | } |
555 | 85571bc7 | bellard | |
556 | 1d14ffa9 | bellard | #ifdef __linux__
|
557 | 1d14ffa9 | bellard | if (ver < 3.75) { |
558 | 1d14ffa9 | bellard | dolog ("FMOD before 3.75 has bug preventing ADC from working\n"
|
559 | 1d14ffa9 | bellard | "ADC will be disabled.\n");
|
560 | 1d14ffa9 | bellard | conf.broken_adc = 1;
|
561 | 1d14ffa9 | bellard | } |
562 | 1d14ffa9 | bellard | #endif
|
563 | 1d14ffa9 | bellard | |
564 | 85571bc7 | bellard | if (drv) {
|
565 | 85571bc7 | bellard | int found = 0; |
566 | 85571bc7 | bellard | for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) { |
567 | 85571bc7 | bellard | if (!strcmp (drv, drvtab[i].name)) {
|
568 | 85571bc7 | bellard | output_type = drvtab[i].type; |
569 | 85571bc7 | bellard | found = 1;
|
570 | 85571bc7 | bellard | break;
|
571 | 85571bc7 | bellard | } |
572 | 85571bc7 | bellard | } |
573 | 85571bc7 | bellard | if (!found) {
|
574 | 1d14ffa9 | bellard | dolog ("Unknown FMOD driver `%s'\n", drv);
|
575 | 1d14ffa9 | bellard | dolog ("Valid drivers:\n");
|
576 | 1d14ffa9 | bellard | for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) { |
577 | 1d14ffa9 | bellard | dolog (" %s\n", drvtab[i].name);
|
578 | 1d14ffa9 | bellard | } |
579 | 85571bc7 | bellard | } |
580 | 85571bc7 | bellard | } |
581 | 85571bc7 | bellard | |
582 | 85571bc7 | bellard | if (output_type != -1) { |
583 | 85571bc7 | bellard | status = FSOUND_SetOutput (output_type); |
584 | 85571bc7 | bellard | if (!status) {
|
585 | 1d14ffa9 | bellard | fmod_logerr ("FSOUND_SetOutput(%d) failed\n", output_type);
|
586 | 85571bc7 | bellard | return NULL; |
587 | 85571bc7 | bellard | } |
588 | 85571bc7 | bellard | } |
589 | 85571bc7 | bellard | |
590 | 85571bc7 | bellard | if (conf.bufsize) {
|
591 | 85571bc7 | bellard | status = FSOUND_SetBufferSize (conf.bufsize); |
592 | 85571bc7 | bellard | if (!status) {
|
593 | 1d14ffa9 | bellard | fmod_logerr ("FSOUND_SetBufferSize (%d) failed\n", conf.bufsize);
|
594 | 85571bc7 | bellard | } |
595 | 85571bc7 | bellard | } |
596 | 85571bc7 | bellard | |
597 | 85571bc7 | bellard | status = FSOUND_Init (conf.freq, conf.nb_channels, 0);
|
598 | 85571bc7 | bellard | if (!status) {
|
599 | 1d14ffa9 | bellard | fmod_logerr ("FSOUND_Init failed\n");
|
600 | 85571bc7 | bellard | return NULL; |
601 | 85571bc7 | bellard | } |
602 | 85571bc7 | bellard | |
603 | 85571bc7 | bellard | return &conf;
|
604 | 85571bc7 | bellard | } |
605 | 85571bc7 | bellard | |
606 | 1d14ffa9 | bellard | static int fmod_read (SWVoiceIn *sw, void *buf, int size) |
607 | 1d14ffa9 | bellard | { |
608 | 1d14ffa9 | bellard | return audio_pcm_sw_read (sw, buf, size);
|
609 | 1d14ffa9 | bellard | } |
610 | 1d14ffa9 | bellard | |
611 | 1d14ffa9 | bellard | static int fmod_ctl_in (HWVoiceIn *hw, int cmd, ...) |
612 | 1d14ffa9 | bellard | { |
613 | 1d14ffa9 | bellard | int status;
|
614 | 1d14ffa9 | bellard | FMODVoiceIn *fmd = (FMODVoiceIn *) hw; |
615 | 1d14ffa9 | bellard | |
616 | 1d14ffa9 | bellard | switch (cmd) {
|
617 | 1d14ffa9 | bellard | case VOICE_ENABLE:
|
618 | 1d14ffa9 | bellard | status = FSOUND_Record_StartSample (fmd->fmod_sample, 1);
|
619 | 1d14ffa9 | bellard | if (!status) {
|
620 | 1d14ffa9 | bellard | fmod_logerr ("Failed to start recording\n");
|
621 | 1d14ffa9 | bellard | } |
622 | 1d14ffa9 | bellard | break;
|
623 | 1d14ffa9 | bellard | |
624 | 1d14ffa9 | bellard | case VOICE_DISABLE:
|
625 | 1d14ffa9 | bellard | status = FSOUND_Record_Stop (); |
626 | 1d14ffa9 | bellard | if (!status) {
|
627 | 1d14ffa9 | bellard | fmod_logerr ("Failed to stop recording\n");
|
628 | 1d14ffa9 | bellard | } |
629 | 1d14ffa9 | bellard | break;
|
630 | 1d14ffa9 | bellard | } |
631 | 1d14ffa9 | bellard | return 0; |
632 | 1d14ffa9 | bellard | } |
633 | 1d14ffa9 | bellard | |
634 | 85571bc7 | bellard | static void fmod_audio_fini (void *opaque) |
635 | 85571bc7 | bellard | { |
636 | 1d14ffa9 | bellard | (void) opaque;
|
637 | 85571bc7 | bellard | FSOUND_Close (); |
638 | 85571bc7 | bellard | } |
639 | 85571bc7 | bellard | |
640 | 1d14ffa9 | bellard | static struct audio_option fmod_options[] = { |
641 | 1d14ffa9 | bellard | {"DRV", AUD_OPT_STR, &conf.drvname,
|
642 | 1d14ffa9 | bellard | "FMOD driver", NULL, 0}, |
643 | 1d14ffa9 | bellard | {"FREQ", AUD_OPT_INT, &conf.freq,
|
644 | 1d14ffa9 | bellard | "Default frequency", NULL, 0}, |
645 | 1d14ffa9 | bellard | {"SAMPLES", AUD_OPT_INT, &conf.nb_samples,
|
646 | 1d14ffa9 | bellard | "Buffer size in samples", NULL, 0}, |
647 | 1d14ffa9 | bellard | {"CHANNELS", AUD_OPT_INT, &conf.nb_channels,
|
648 | 1d14ffa9 | bellard | "Number of default channels (1 - mono, 2 - stereo)", NULL, 0}, |
649 | 1d14ffa9 | bellard | {"BUFSIZE", AUD_OPT_INT, &conf.bufsize,
|
650 | 1d14ffa9 | bellard | "(undocumented)", NULL, 0}, |
651 | 1d14ffa9 | bellard | #if 0
|
652 | 1d14ffa9 | bellard | {"THRESHOLD", AUD_OPT_INT, &conf.threshold,
|
653 | 1d14ffa9 | bellard | "(undocumented)"},
|
654 | 1d14ffa9 | bellard | #endif
|
655 | 1d14ffa9 | bellard | |
656 | 1d14ffa9 | bellard | {NULL, 0, NULL, NULL, NULL, 0} |
657 | 1d14ffa9 | bellard | }; |
658 | 1d14ffa9 | bellard | |
659 | 1d14ffa9 | bellard | static struct audio_pcm_ops fmod_pcm_ops = { |
660 | 1d14ffa9 | bellard | fmod_init_out, |
661 | 1d14ffa9 | bellard | fmod_fini_out, |
662 | 1d14ffa9 | bellard | fmod_run_out, |
663 | 1d14ffa9 | bellard | fmod_write, |
664 | 1d14ffa9 | bellard | fmod_ctl_out, |
665 | 1d14ffa9 | bellard | |
666 | 1d14ffa9 | bellard | fmod_init_in, |
667 | 1d14ffa9 | bellard | fmod_fini_in, |
668 | 1d14ffa9 | bellard | fmod_run_in, |
669 | 1d14ffa9 | bellard | fmod_read, |
670 | 1d14ffa9 | bellard | fmod_ctl_in |
671 | 85571bc7 | bellard | }; |
672 | 85571bc7 | bellard | |
673 | 1d14ffa9 | bellard | struct audio_driver fmod_audio_driver = {
|
674 | 1d14ffa9 | bellard | INIT_FIELD (name = ) "fmod",
|
675 | 1d14ffa9 | bellard | INIT_FIELD (descr = ) "FMOD 3.xx http://www.fmod.org",
|
676 | 1d14ffa9 | bellard | INIT_FIELD (options = ) fmod_options, |
677 | 1d14ffa9 | bellard | INIT_FIELD (init = ) fmod_audio_init, |
678 | 1d14ffa9 | bellard | INIT_FIELD (fini = ) fmod_audio_fini, |
679 | 1d14ffa9 | bellard | INIT_FIELD (pcm_ops = ) &fmod_pcm_ops, |
680 | 1d14ffa9 | bellard | INIT_FIELD (can_be_default = ) 1,
|
681 | 1d14ffa9 | bellard | INIT_FIELD (max_voices_out = ) INT_MAX, |
682 | 1d14ffa9 | bellard | INIT_FIELD (max_voices_in = ) INT_MAX, |
683 | 1d14ffa9 | bellard | INIT_FIELD (voice_size_out = ) sizeof (FMODVoiceOut),
|
684 | 1d14ffa9 | bellard | INIT_FIELD (voice_size_in = ) sizeof (FMODVoiceIn)
|
685 | 85571bc7 | bellard | }; |