Revision 85571bc7
b/Makefile.target | ||
---|---|---|
1 | 1 |
include config.mak |
2 | 2 |
|
3 |
#After enabling Adlib and/or FMOD rebuild QEMU from scratch |
|
4 |
#Uncomment following for adlib support |
|
5 |
#USE_ADLIB=1 |
|
6 |
|
|
7 |
#Uncomment following and specify proper paths/names for FMOD support |
|
8 |
#USE_FMOD=1 |
|
9 |
#FMOD_INCLUDE=/net/include/fmod |
|
10 |
#FMOD_LIBPATH=/net/lib |
|
11 |
#FMOD_VERSION=3.74 |
|
12 |
|
|
3 | 13 |
TARGET_PATH=$(SRC_PATH)/target-$(TARGET_ARCH) |
4 |
VPATH=$(SRC_PATH):$(TARGET_PATH):$(SRC_PATH)/hw |
|
14 |
VPATH=$(SRC_PATH):$(TARGET_PATH):$(SRC_PATH)/hw:$(SRC_PATH)/audio
|
|
5 | 15 |
DEFINES=-I. -I$(TARGET_PATH) -I$(SRC_PATH) |
6 | 16 |
ifdef CONFIG_USER_ONLY |
7 | 17 |
VPATH+=:$(SRC_PATH)/linux-user |
... | ... | |
267 | 277 |
VL_OBJS=vl.o osdep.o block.o readline.o monitor.o pci.o console.o |
268 | 278 |
VL_OBJS+=block-cow.o block-qcow.o aes.o block-vmdk.o block-cloop.o |
269 | 279 |
|
280 |
SOUND_HW = sb16.o |
|
281 |
AUDIODRV = audio.o ossaudio.o sdlaudio.o wavaudio.o |
|
282 |
|
|
283 |
ifeq ($(USE_ADLIB),1) |
|
284 |
SOUND_HW += fmopl.o adlib.o |
|
285 |
audio.o: DEFINES := -DUSE_ADLIB $(DEFINES) |
|
286 |
endif |
|
287 |
|
|
288 |
ifeq ($(USE_FMOD),1) |
|
289 |
AUDIODRV += fmodaudio.o |
|
290 |
audio.o fmodaudio.o: DEFINES := -DUSE_FMOD_AUDIO -I$(FMOD_INCLUDE) $(DEFINES) |
|
291 |
LDFLAGS += -L$(FMOD_LIBPATH) -Wl,-rpath,$(FMOD_LIBPATH) |
|
292 |
LIBS += -lfmod-$(FMOD_VERSION) |
|
293 |
endif |
|
294 |
|
|
270 | 295 |
ifeq ($(TARGET_ARCH), i386) |
271 | 296 |
# Hardware support |
272 |
VL_OBJS+= ide.o ne2000.o pckbd.o vga.o sb16.o dma.o oss.o
|
|
273 |
VL_OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pc.o
|
|
274 |
VL_OBJS+= cirrus_vga.o |
|
297 |
VL_OBJS+= ide.o ne2000.o pckbd.o vga.o $(SOUND_HW) dma.o $(AUDIODRV)
|
|
298 |
VL_OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pc.o |
|
299 |
VL_OBJS+= cirrus_vga.o mixeng.o
|
|
275 | 300 |
endif |
276 | 301 |
ifeq ($(TARGET_ARCH), ppc) |
277 |
VL_OBJS+= ppc.o ide.o ne2000.o pckbd.o vga.o sb16.o dma.o oss.o
|
|
302 |
VL_OBJS+= ppc.o ide.o ne2000.o pckbd.o vga.o $(SOUND_HW) dma.o $(AUDIODRV)
|
|
278 | 303 |
VL_OBJS+= mc146818rtc.o serial.o i8259.o i8254.o fdc.o m48t59.o |
279 |
VL_OBJS+= ppc_prep.o ppc_chrp.o cuda.o adb.o openpic.o |
|
304 |
VL_OBJS+= ppc_prep.o ppc_chrp.o cuda.o adb.o openpic.o mixeng.o
|
|
280 | 305 |
endif |
281 | 306 |
ifeq ($(TARGET_ARCH), sparc) |
282 | 307 |
VL_OBJS+= sun4m.o tcx.o lance.o iommu.o sched.o m48t08.o magic-load.o timer.o |
... | ... | |
360 | 385 |
op_helper.o: op_helper_mem.h |
361 | 386 |
endif |
362 | 387 |
|
388 |
mixeng.o: mixeng.c mixeng.h mixeng_template.h |
|
389 |
|
|
363 | 390 |
%.o: %.c |
364 | 391 |
$(CC) $(CFLAGS) $(DEFINES) -c -o $@ $< |
365 | 392 |
|
b/audio/audio.c | ||
---|---|---|
1 |
/* |
|
2 |
* QEMU Audio subsystem |
|
3 |
* |
|
4 |
* Copyright (c) 2003-2004 Vassili Karpov (malc) |
|
5 |
* |
|
6 |
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|
7 |
* of this software and associated documentation files (the "Software"), to deal |
|
8 |
* in the Software without restriction, including without limitation the rights |
|
9 |
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
10 |
* copies of the Software, and to permit persons to whom the Software is |
|
11 |
* furnished to do so, subject to the following conditions: |
|
12 |
* |
|
13 |
* The above copyright notice and this permission notice shall be included in |
|
14 |
* all copies or substantial portions of the Software. |
|
15 |
* |
|
16 |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
17 |
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
18 |
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
|
19 |
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
20 |
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
21 |
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|
22 |
* THE SOFTWARE. |
|
23 |
*/ |
|
24 |
#include <assert.h> |
|
25 |
#include <limits.h> |
|
26 |
#include "vl.h" |
|
27 |
|
|
28 |
#define AUDIO_CAP "audio" |
|
29 |
#include "audio/audio.h" |
|
30 |
|
|
31 |
#define USE_SDL_AUDIO |
|
32 |
#define USE_WAV_AUDIO |
|
33 |
|
|
34 |
#if defined __linux__ || (defined _BSD && !defined __APPLE__) |
|
35 |
#define USE_OSS_AUDIO |
|
36 |
#endif |
|
37 |
|
|
38 |
#ifdef USE_OSS_AUDIO |
|
39 |
#include "audio/ossaudio.h" |
|
40 |
#endif |
|
41 |
|
|
42 |
#ifdef USE_SDL_AUDIO |
|
43 |
#include "audio/sdlaudio.h" |
|
44 |
#endif |
|
45 |
|
|
46 |
#ifdef USE_WAV_AUDIO |
|
47 |
#include "audio/wavaudio.h" |
|
48 |
#endif |
|
49 |
|
|
50 |
#ifdef USE_FMOD_AUDIO |
|
51 |
#include "audio/fmodaudio.h" |
|
52 |
#endif |
|
53 |
|
|
54 |
#define QC_AUDIO_DRV "QEMU_AUDIO_DRV" |
|
55 |
#define QC_VOICES "QEMU_VOICES" |
|
56 |
#define QC_FIXED_FORMAT "QEMU_FIXED_FORMAT" |
|
57 |
#define QC_FIXED_FREQ "QEMU_FIXED_FREQ" |
|
58 |
|
|
59 |
extern void SB16_init (void); |
|
60 |
|
|
61 |
#ifdef USE_ADLIB |
|
62 |
extern void Adlib_init (void); |
|
63 |
#endif |
|
64 |
|
|
65 |
#ifdef USE_GUS |
|
66 |
extern void GUS_init (void); |
|
67 |
#endif |
|
68 |
|
|
69 |
static void (*hw_ctors[]) (void) = { |
|
70 |
SB16_init, |
|
71 |
#ifdef USE_ADLIB |
|
72 |
Adlib_init, |
|
73 |
#endif |
|
74 |
#ifdef USE_GUS |
|
75 |
GUS_init, |
|
76 |
#endif |
|
77 |
NULL |
|
78 |
}; |
|
79 |
|
|
80 |
static HWVoice *hw_voice; |
|
81 |
|
|
82 |
AudioState audio_state = { |
|
83 |
1, /* use fixed settings */ |
|
84 |
44100, /* fixed frequency */ |
|
85 |
2, /* fixed channels */ |
|
86 |
AUD_FMT_S16, /* fixed format */ |
|
87 |
1, /* number of hw voices */ |
|
88 |
-1 /* voice size */ |
|
89 |
}; |
|
90 |
|
|
91 |
/* http://www.df.lth.se/~john_e/gems/gem002d.html */ |
|
92 |
/* http://www.multi-platforms.com/Tips/PopCount.htm */ |
|
93 |
uint32_t popcount (uint32_t u) |
|
94 |
{ |
|
95 |
u = ((u&0x55555555) + ((u>>1)&0x55555555)); |
|
96 |
u = ((u&0x33333333) + ((u>>2)&0x33333333)); |
|
97 |
u = ((u&0x0f0f0f0f) + ((u>>4)&0x0f0f0f0f)); |
|
98 |
u = ((u&0x00ff00ff) + ((u>>8)&0x00ff00ff)); |
|
99 |
u = ( u&0x0000ffff) + (u>>16); |
|
100 |
return u; |
|
101 |
} |
|
102 |
|
|
103 |
inline uint32_t lsbindex (uint32_t u) |
|
104 |
{ |
|
105 |
return popcount ((u&-u)-1); |
|
106 |
} |
|
107 |
|
|
108 |
int audio_get_conf_int (const char *key, int defval) |
|
109 |
{ |
|
110 |
int val = defval; |
|
111 |
char *strval; |
|
112 |
|
|
113 |
strval = getenv (key); |
|
114 |
if (strval) { |
|
115 |
val = atoi (strval); |
|
116 |
} |
|
117 |
|
|
118 |
return val; |
|
119 |
} |
|
120 |
|
|
121 |
const char *audio_get_conf_str (const char *key, const char *defval) |
|
122 |
{ |
|
123 |
const char *val = getenv (key); |
|
124 |
if (!val) |
|
125 |
return defval; |
|
126 |
else |
|
127 |
return val; |
|
128 |
} |
|
129 |
|
|
130 |
void audio_log (const char *fmt, ...) |
|
131 |
{ |
|
132 |
va_list ap; |
|
133 |
va_start (ap, fmt); |
|
134 |
vfprintf (stderr, fmt, ap); |
|
135 |
va_end (ap); |
|
136 |
} |
|
137 |
|
|
138 |
/* |
|
139 |
* Soft Voice |
|
140 |
*/ |
|
141 |
void pcm_sw_free_resources (SWVoice *sw) |
|
142 |
{ |
|
143 |
if (sw->buf) qemu_free (sw->buf); |
|
144 |
if (sw->rate) st_rate_stop (sw->rate); |
|
145 |
sw->buf = NULL; |
|
146 |
sw->rate = NULL; |
|
147 |
} |
|
148 |
|
|
149 |
int pcm_sw_alloc_resources (SWVoice *sw) |
|
150 |
{ |
|
151 |
sw->buf = qemu_mallocz (sw->hw->samples * sizeof (st_sample_t)); |
|
152 |
if (!sw->buf) |
|
153 |
return -1; |
|
154 |
|
|
155 |
sw->rate = st_rate_start (sw->freq, sw->hw->freq); |
|
156 |
if (!sw->rate) { |
|
157 |
qemu_free (sw->buf); |
|
158 |
sw->buf = NULL; |
|
159 |
return -1; |
|
160 |
} |
|
161 |
return 0; |
|
162 |
} |
|
163 |
|
|
164 |
void pcm_sw_fini (SWVoice *sw) |
|
165 |
{ |
|
166 |
pcm_sw_free_resources (sw); |
|
167 |
} |
|
168 |
|
|
169 |
int pcm_sw_init (SWVoice *sw, HWVoice *hw, int freq, |
|
170 |
int nchannels, audfmt_e fmt) |
|
171 |
{ |
|
172 |
int bits = 8, sign = 0; |
|
173 |
|
|
174 |
switch (fmt) { |
|
175 |
case AUD_FMT_S8: |
|
176 |
sign = 1; |
|
177 |
case AUD_FMT_U8: |
|
178 |
break; |
|
179 |
|
|
180 |
case AUD_FMT_S16: |
|
181 |
sign = 1; |
|
182 |
case AUD_FMT_U16: |
|
183 |
bits = 16; |
|
184 |
break; |
|
185 |
} |
|
186 |
|
|
187 |
sw->hw = hw; |
|
188 |
sw->freq = freq; |
|
189 |
sw->fmt = fmt; |
|
190 |
sw->nchannels = nchannels; |
|
191 |
sw->shift = (nchannels == 2) + (bits == 16); |
|
192 |
sw->align = (1 << sw->shift) - 1; |
|
193 |
sw->left = 0; |
|
194 |
sw->pos = 0; |
|
195 |
sw->wpos = 0; |
|
196 |
sw->live = 0; |
|
197 |
sw->ratio = (sw->hw->freq * ((int64_t) INT_MAX)) / sw->freq; |
|
198 |
sw->bytes_per_second = sw->freq << sw->shift; |
|
199 |
sw->conv = mixeng_conv[nchannels == 2][sign][bits == 16]; |
|
200 |
|
|
201 |
pcm_sw_free_resources (sw); |
|
202 |
return pcm_sw_alloc_resources (sw); |
|
203 |
} |
|
204 |
|
|
205 |
/* Hard voice */ |
|
206 |
void pcm_hw_free_resources (HWVoice *hw) |
|
207 |
{ |
|
208 |
if (hw->mix_buf) |
|
209 |
qemu_free (hw->mix_buf); |
|
210 |
hw->mix_buf = NULL; |
|
211 |
} |
|
212 |
|
|
213 |
int pcm_hw_alloc_resources (HWVoice *hw) |
|
214 |
{ |
|
215 |
hw->mix_buf = qemu_mallocz (hw->samples * sizeof (st_sample_t)); |
|
216 |
if (!hw->mix_buf) |
|
217 |
return -1; |
|
218 |
return 0; |
|
219 |
} |
|
220 |
|
|
221 |
|
|
222 |
void pcm_hw_fini (HWVoice *hw) |
|
223 |
{ |
|
224 |
if (hw->active) { |
|
225 |
ldebug ("pcm_hw_fini: %d %d %d\n", hw->freq, hw->nchannels, hw->fmt); |
|
226 |
pcm_hw_free_resources (hw); |
|
227 |
hw->pcm_ops->fini (hw); |
|
228 |
memset (hw, 0, audio_state.drv->voice_size); |
|
229 |
} |
|
230 |
} |
|
231 |
|
|
232 |
void pcm_hw_gc (HWVoice *hw) |
|
233 |
{ |
|
234 |
if (hw->nb_voices) |
|
235 |
return; |
|
236 |
|
|
237 |
pcm_hw_fini (hw); |
|
238 |
} |
|
239 |
|
|
240 |
int pcm_hw_get_live (HWVoice *hw) |
|
241 |
{ |
|
242 |
int i, alive = 0, live = hw->samples; |
|
243 |
|
|
244 |
for (i = 0; i < hw->nb_voices; i++) { |
|
245 |
if (hw->pvoice[i]->live) { |
|
246 |
live = audio_MIN (hw->pvoice[i]->live, live); |
|
247 |
alive += 1; |
|
248 |
} |
|
249 |
} |
|
250 |
|
|
251 |
if (alive) |
|
252 |
return live; |
|
253 |
else |
|
254 |
return -1; |
|
255 |
} |
|
256 |
|
|
257 |
int pcm_hw_get_live2 (HWVoice *hw, int *nb_active) |
|
258 |
{ |
|
259 |
int i, alive = 0, live = hw->samples; |
|
260 |
|
|
261 |
*nb_active = 0; |
|
262 |
for (i = 0; i < hw->nb_voices; i++) { |
|
263 |
if (hw->pvoice[i]->live) { |
|
264 |
if (hw->pvoice[i]->live < live) { |
|
265 |
*nb_active = hw->pvoice[i]->active != 0; |
|
266 |
live = hw->pvoice[i]->live; |
|
267 |
} |
|
268 |
alive += 1; |
|
269 |
} |
|
270 |
} |
|
271 |
|
|
272 |
if (alive) |
|
273 |
return live; |
|
274 |
else |
|
275 |
return -1; |
|
276 |
} |
|
277 |
|
|
278 |
void pcm_hw_dec_live (HWVoice *hw, int decr) |
|
279 |
{ |
|
280 |
int i; |
|
281 |
|
|
282 |
for (i = 0; i < hw->nb_voices; i++) { |
|
283 |
if (hw->pvoice[i]->live) { |
|
284 |
hw->pvoice[i]->live -= decr; |
|
285 |
} |
|
286 |
} |
|
287 |
} |
|
288 |
|
|
289 |
void pcm_hw_clear (HWVoice *hw, void *buf, int len) |
|
290 |
{ |
|
291 |
if (!len) |
|
292 |
return; |
|
293 |
|
|
294 |
switch (hw->fmt) { |
|
295 |
case AUD_FMT_S16: |
|
296 |
case AUD_FMT_S8: |
|
297 |
memset (buf, len << hw->shift, 0x00); |
|
298 |
break; |
|
299 |
|
|
300 |
case AUD_FMT_U8: |
|
301 |
memset (buf, len << hw->shift, 0x80); |
|
302 |
break; |
|
303 |
|
|
304 |
case AUD_FMT_U16: |
|
305 |
{ |
|
306 |
unsigned int i; |
|
307 |
uint16_t *p = buf; |
|
308 |
int shift = hw->nchannels - 1; |
|
309 |
|
|
310 |
for (i = 0; i < len << shift; i++) { |
|
311 |
p[i] = INT16_MAX; |
|
312 |
} |
|
313 |
} |
|
314 |
break; |
|
315 |
} |
|
316 |
} |
|
317 |
|
|
318 |
int pcm_hw_write (SWVoice *sw, void *buf, int size) |
|
319 |
{ |
|
320 |
int hwsamples, samples, isamp, osamp, wpos, live, dead, left, swlim, blck; |
|
321 |
int ret = 0, pos = 0; |
|
322 |
if (!sw) |
|
323 |
return size; |
|
324 |
|
|
325 |
hwsamples = sw->hw->samples; |
|
326 |
samples = size >> sw->shift; |
|
327 |
|
|
328 |
if (!sw->live) { |
|
329 |
sw->wpos = sw->hw->rpos; |
|
330 |
} |
|
331 |
wpos = sw->wpos; |
|
332 |
live = sw->live; |
|
333 |
dead = hwsamples - live; |
|
334 |
swlim = (dead * ((int64_t) INT_MAX)) / sw->ratio; |
|
335 |
swlim = audio_MIN (swlim, samples); |
|
336 |
|
|
337 |
ldebug ("size=%d live=%d dead=%d swlim=%d wpos=%d\n", |
|
338 |
size, live, dead, swlim, wpos); |
|
339 |
if (swlim) |
|
340 |
sw->conv (sw->buf, buf, swlim); |
|
341 |
|
|
342 |
while (swlim) { |
|
343 |
dead = hwsamples - live; |
|
344 |
left = hwsamples - wpos; |
|
345 |
blck = audio_MIN (dead, left); |
|
346 |
if (!blck) { |
|
347 |
/* dolog ("swlim=%d\n", swlim); */ |
|
348 |
break; |
|
349 |
} |
|
350 |
isamp = swlim; |
|
351 |
osamp = blck; |
|
352 |
st_rate_flow (sw->rate, sw->buf + pos, sw->hw->mix_buf + wpos, &isamp, &osamp); |
|
353 |
ret += isamp; |
|
354 |
swlim -= isamp; |
|
355 |
pos += isamp; |
|
356 |
live += osamp; |
|
357 |
wpos = (wpos + osamp) % hwsamples; |
|
358 |
} |
|
359 |
|
|
360 |
sw->wpos = wpos; |
|
361 |
sw->live = live; |
|
362 |
return ret << sw->shift; |
|
363 |
} |
|
364 |
|
|
365 |
int pcm_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt) |
|
366 |
{ |
|
367 |
int sign = 0, bits = 8; |
|
368 |
|
|
369 |
pcm_hw_fini (hw); |
|
370 |
ldebug ("pcm_hw_init: %d %d %d\n", freq, nchannels, fmt); |
|
371 |
if (hw->pcm_ops->init (hw, freq, nchannels, fmt)) { |
|
372 |
memset (hw, 0, audio_state.drv->voice_size); |
|
373 |
return -1; |
|
374 |
} |
|
375 |
|
|
376 |
switch (hw->fmt) { |
|
377 |
case AUD_FMT_S8: |
|
378 |
sign = 1; |
|
379 |
case AUD_FMT_U8: |
|
380 |
break; |
|
381 |
|
|
382 |
case AUD_FMT_S16: |
|
383 |
sign = 1; |
|
384 |
case AUD_FMT_U16: |
|
385 |
bits = 16; |
|
386 |
break; |
|
387 |
} |
|
388 |
|
|
389 |
hw->nb_voices = 0; |
|
390 |
hw->active = 1; |
|
391 |
hw->shift = (hw->nchannels == 2) + (bits == 16); |
|
392 |
hw->bytes_per_second = hw->freq << hw->shift; |
|
393 |
hw->align = (1 << hw->shift) - 1; |
|
394 |
hw->samples = hw->bufsize >> hw->shift; |
|
395 |
hw->clip = mixeng_clip[hw->nchannels == 2][sign][bits == 16]; |
|
396 |
if (pcm_hw_alloc_resources (hw)) { |
|
397 |
pcm_hw_fini (hw); |
|
398 |
return -1; |
|
399 |
} |
|
400 |
return 0; |
|
401 |
} |
|
402 |
|
|
403 |
static int dist (void *hw) |
|
404 |
{ |
|
405 |
if (hw) { |
|
406 |
return (((uint8_t *) hw - (uint8_t *) hw_voice) |
|
407 |
/ audio_state.voice_size) + 1; |
|
408 |
} |
|
409 |
else { |
|
410 |
return 0; |
|
411 |
} |
|
412 |
} |
|
413 |
|
|
414 |
#define ADVANCE(hw) hw ? advance (hw, audio_state.voice_size) : hw_voice |
|
415 |
|
|
416 |
HWVoice *pcm_hw_find_any (HWVoice *hw) |
|
417 |
{ |
|
418 |
int i = dist (hw); |
|
419 |
for (; i < audio_state.nb_hw_voices; i++) { |
|
420 |
hw = ADVANCE (hw); |
|
421 |
return hw; |
|
422 |
} |
|
423 |
return NULL; |
|
424 |
} |
|
425 |
|
|
426 |
HWVoice *pcm_hw_find_any_active (HWVoice *hw) |
|
427 |
{ |
|
428 |
int i = dist (hw); |
|
429 |
for (; i < audio_state.nb_hw_voices; i++) { |
|
430 |
hw = ADVANCE (hw); |
|
431 |
if (hw->active) |
|
432 |
return hw; |
|
433 |
} |
|
434 |
return NULL; |
|
435 |
} |
|
436 |
|
|
437 |
HWVoice *pcm_hw_find_any_active_enabled (HWVoice *hw) |
|
438 |
{ |
|
439 |
int i = dist (hw); |
|
440 |
for (; i < audio_state.nb_hw_voices; i++) { |
|
441 |
hw = ADVANCE (hw); |
|
442 |
if (hw->active && hw->enabled) |
|
443 |
return hw; |
|
444 |
} |
|
445 |
return NULL; |
|
446 |
} |
|
447 |
|
|
448 |
HWVoice *pcm_hw_find_any_passive (HWVoice *hw) |
|
449 |
{ |
|
450 |
int i = dist (hw); |
|
451 |
for (; i < audio_state.nb_hw_voices; i++) { |
|
452 |
hw = ADVANCE (hw); |
|
453 |
if (!hw->active) |
|
454 |
return hw; |
|
455 |
} |
|
456 |
return NULL; |
|
457 |
} |
|
458 |
|
|
459 |
HWVoice *pcm_hw_find_specific (HWVoice *hw, int freq, |
|
460 |
int nchannels, audfmt_e fmt) |
|
461 |
{ |
|
462 |
while ((hw = pcm_hw_find_any_active (hw))) { |
|
463 |
if (hw->freq == freq && |
|
464 |
hw->nchannels == nchannels && |
|
465 |
hw->fmt == fmt) |
|
466 |
return hw; |
|
467 |
} |
|
468 |
return NULL; |
|
469 |
} |
|
470 |
|
|
471 |
HWVoice *pcm_hw_add (int freq, int nchannels, audfmt_e fmt) |
|
472 |
{ |
|
473 |
HWVoice *hw; |
|
474 |
|
|
475 |
if (audio_state.fixed_format) { |
|
476 |
freq = audio_state.fixed_freq; |
|
477 |
nchannels = audio_state.fixed_channels; |
|
478 |
fmt = audio_state.fixed_fmt; |
|
479 |
} |
|
480 |
|
|
481 |
hw = pcm_hw_find_specific (NULL, freq, nchannels, fmt); |
|
482 |
|
|
483 |
if (hw) |
|
484 |
return hw; |
|
485 |
|
|
486 |
hw = pcm_hw_find_any_passive (NULL); |
|
487 |
if (hw) { |
|
488 |
hw->pcm_ops = audio_state.drv->pcm_ops; |
|
489 |
if (!hw->pcm_ops) |
|
490 |
return NULL; |
|
491 |
|
|
492 |
if (pcm_hw_init (hw, freq, nchannels, fmt)) { |
|
493 |
pcm_hw_gc (hw); |
|
494 |
return NULL; |
|
495 |
} |
|
496 |
else |
|
497 |
return hw; |
|
498 |
} |
|
499 |
|
|
500 |
return pcm_hw_find_any (NULL); |
|
501 |
} |
|
502 |
|
|
503 |
int pcm_hw_add_sw (HWVoice *hw, SWVoice *sw) |
|
504 |
{ |
|
505 |
SWVoice **pvoice = qemu_mallocz ((hw->nb_voices + 1) * sizeof (sw)); |
|
506 |
if (!pvoice) |
|
507 |
return -1; |
|
508 |
|
|
509 |
memcpy (pvoice, hw->pvoice, hw->nb_voices * sizeof (sw)); |
|
510 |
qemu_free (hw->pvoice); |
|
511 |
hw->pvoice = pvoice; |
|
512 |
hw->pvoice[hw->nb_voices++] = sw; |
|
513 |
return 0; |
|
514 |
} |
|
515 |
|
|
516 |
int pcm_hw_del_sw (HWVoice *hw, SWVoice *sw) |
|
517 |
{ |
|
518 |
int i, j; |
|
519 |
if (hw->nb_voices > 1) { |
|
520 |
SWVoice **pvoice = qemu_mallocz ((hw->nb_voices - 1) * sizeof (sw)); |
|
521 |
|
|
522 |
if (!pvoice) { |
|
523 |
dolog ("Can not maintain consistent state (not enough memory)\n"); |
|
524 |
return -1; |
|
525 |
} |
|
526 |
|
|
527 |
for (i = 0, j = 0; i < hw->nb_voices; i++) { |
|
528 |
if (j >= hw->nb_voices - 1) { |
|
529 |
dolog ("Can not maintain consistent state " |
|
530 |
"(invariant violated)\n"); |
|
531 |
return -1; |
|
532 |
} |
|
533 |
if (hw->pvoice[i] != sw) |
|
534 |
pvoice[j++] = hw->pvoice[i]; |
|
535 |
} |
|
536 |
qemu_free (hw->pvoice); |
|
537 |
hw->pvoice = pvoice; |
|
538 |
hw->nb_voices -= 1; |
|
539 |
} |
|
540 |
else { |
|
541 |
qemu_free (hw->pvoice); |
|
542 |
hw->pvoice = NULL; |
|
543 |
hw->nb_voices = 0; |
|
544 |
} |
|
545 |
return 0; |
|
546 |
} |
|
547 |
|
|
548 |
SWVoice *pcm_create_voice_pair (int freq, int nchannels, audfmt_e fmt) |
|
549 |
{ |
|
550 |
SWVoice *sw; |
|
551 |
HWVoice *hw; |
|
552 |
|
|
553 |
sw = qemu_mallocz (sizeof (*sw)); |
|
554 |
if (!sw) |
|
555 |
goto err1; |
|
556 |
|
|
557 |
hw = pcm_hw_add (freq, nchannels, fmt); |
|
558 |
if (!hw) |
|
559 |
goto err2; |
|
560 |
|
|
561 |
if (pcm_hw_add_sw (hw, sw)) |
|
562 |
goto err3; |
|
563 |
|
|
564 |
if (pcm_sw_init (sw, hw, freq, nchannels, fmt)) |
|
565 |
goto err4; |
|
566 |
|
|
567 |
return sw; |
|
568 |
|
|
569 |
err4: |
|
570 |
pcm_hw_del_sw (hw, sw); |
|
571 |
err3: |
|
572 |
pcm_hw_gc (hw); |
|
573 |
err2: |
|
574 |
qemu_free (sw); |
|
575 |
err1: |
|
576 |
return NULL; |
|
577 |
} |
|
578 |
|
|
579 |
SWVoice *AUD_open (SWVoice *sw, const char *name, |
|
580 |
int freq, int nchannels, audfmt_e fmt) |
|
581 |
{ |
|
582 |
if (!audio_state.drv) { |
|
583 |
return NULL; |
|
584 |
} |
|
585 |
|
|
586 |
if (sw && freq == sw->freq && sw->nchannels == nchannels && sw->fmt == fmt) { |
|
587 |
return sw; |
|
588 |
} |
|
589 |
|
|
590 |
if (sw) { |
|
591 |
ldebug ("Different format %s %d %d %d\n", |
|
592 |
name, |
|
593 |
sw->freq == freq, |
|
594 |
sw->nchannels == nchannels, |
|
595 |
sw->fmt == fmt); |
|
596 |
} |
|
597 |
|
|
598 |
if (nchannels != 1 && nchannels != 2) { |
|
599 |
dolog ("Bogus channel count %d for voice %s\n", nchannels, name); |
|
600 |
return NULL; |
|
601 |
} |
|
602 |
|
|
603 |
if (!audio_state.fixed_format && sw) { |
|
604 |
pcm_sw_fini (sw); |
|
605 |
pcm_hw_del_sw (sw->hw, sw); |
|
606 |
pcm_hw_gc (sw->hw); |
|
607 |
if (sw->name) { |
|
608 |
qemu_free (sw->name); |
|
609 |
sw->name = NULL; |
|
610 |
} |
|
611 |
qemu_free (sw); |
|
612 |
sw = NULL; |
|
613 |
} |
|
614 |
|
|
615 |
if (sw) { |
|
616 |
HWVoice *hw = sw->hw; |
|
617 |
if (!hw) { |
|
618 |
dolog ("Internal logic error voice %s has no hardware store\n", |
|
619 |
name); |
|
620 |
return sw; |
|
621 |
} |
|
622 |
|
|
623 |
if (pcm_sw_init (sw, hw, freq, nchannels, fmt)) { |
|
624 |
pcm_sw_fini (sw); |
|
625 |
pcm_hw_del_sw (hw, sw); |
|
626 |
pcm_hw_gc (hw); |
|
627 |
if (sw->name) { |
|
628 |
qemu_free (sw->name); |
|
629 |
sw->name = NULL; |
|
630 |
} |
|
631 |
qemu_free (sw); |
|
632 |
return NULL; |
|
633 |
} |
|
634 |
} |
|
635 |
else { |
|
636 |
sw = pcm_create_voice_pair (freq, nchannels, fmt); |
|
637 |
if (!sw) { |
|
638 |
dolog ("Failed to create voice %s\n", name); |
|
639 |
return NULL; |
|
640 |
} |
|
641 |
} |
|
642 |
|
|
643 |
if (sw->name) { |
|
644 |
qemu_free (sw->name); |
|
645 |
sw->name = NULL; |
|
646 |
} |
|
647 |
sw->name = qemu_strdup (name); |
|
648 |
return sw; |
|
649 |
} |
|
650 |
|
|
651 |
int AUD_write (SWVoice *sw, void *buf, int size) |
|
652 |
{ |
|
653 |
int bytes; |
|
654 |
|
|
655 |
if (!sw->hw->enabled) |
|
656 |
dolog ("Writing to disabled voice %s\n", sw->name); |
|
657 |
bytes = sw->hw->pcm_ops->write (sw, buf, size); |
|
658 |
return bytes; |
|
659 |
} |
|
660 |
|
|
661 |
void AUD_run (void) |
|
662 |
{ |
|
663 |
HWVoice *hw = NULL; |
|
664 |
|
|
665 |
while ((hw = pcm_hw_find_any_active_enabled (hw))) { |
|
666 |
int i; |
|
667 |
if (hw->pending_disable && pcm_hw_get_live (hw) <= 0) { |
|
668 |
hw->enabled = 0; |
|
669 |
hw->pcm_ops->ctl (hw, VOICE_DISABLE); |
|
670 |
for (i = 0; i < hw->nb_voices; i++) { |
|
671 |
hw->pvoice[i]->live = 0; |
|
672 |
/* hw->pvoice[i]->old_ticks = 0; */ |
|
673 |
} |
|
674 |
continue; |
|
675 |
} |
|
676 |
|
|
677 |
hw->pcm_ops->run (hw); |
|
678 |
assert (hw->rpos < hw->samples); |
|
679 |
for (i = 0; i < hw->nb_voices; i++) { |
|
680 |
SWVoice *sw = hw->pvoice[i]; |
|
681 |
if (!sw->active && !sw->live && sw->old_ticks) { |
|
682 |
int64_t delta = qemu_get_clock (vm_clock) - sw->old_ticks; |
|
683 |
if (delta > audio_state.ticks_threshold) { |
|
684 |
ldebug ("resetting old_ticks for %s\n", sw->name); |
|
685 |
sw->old_ticks = 0; |
|
686 |
} |
|
687 |
} |
|
688 |
} |
|
689 |
} |
|
690 |
} |
|
691 |
|
|
692 |
int AUD_get_free (SWVoice *sw) |
|
693 |
{ |
|
694 |
int free; |
|
695 |
|
|
696 |
if (!sw) |
|
697 |
return 4096; |
|
698 |
|
|
699 |
free = ((sw->hw->samples - sw->live) << sw->hw->shift) * sw->ratio |
|
700 |
/ INT_MAX; |
|
701 |
|
|
702 |
free &= ~sw->hw->align; |
|
703 |
if (!free) return 0; |
|
704 |
|
|
705 |
return free; |
|
706 |
} |
|
707 |
|
|
708 |
int AUD_get_buffer_size (SWVoice *sw) |
|
709 |
{ |
|
710 |
return sw->hw->bufsize; |
|
711 |
} |
|
712 |
|
|
713 |
void AUD_adjust (SWVoice *sw, int bytes) |
|
714 |
{ |
|
715 |
if (!sw) |
|
716 |
return; |
|
717 |
sw->old_ticks += (ticks_per_sec * (int64_t) bytes) / sw->bytes_per_second; |
|
718 |
} |
|
719 |
|
|
720 |
void AUD_reset (SWVoice *sw) |
|
721 |
{ |
|
722 |
sw->active = 0; |
|
723 |
sw->old_ticks = 0; |
|
724 |
} |
|
725 |
|
|
726 |
int AUD_calc_elapsed (SWVoice *sw) |
|
727 |
{ |
|
728 |
int64_t now, delta, bytes; |
|
729 |
int dead, swlim; |
|
730 |
|
|
731 |
if (!sw) |
|
732 |
return 0; |
|
733 |
|
|
734 |
now = qemu_get_clock (vm_clock); |
|
735 |
delta = now - sw->old_ticks; |
|
736 |
bytes = (delta * sw->bytes_per_second) / ticks_per_sec; |
|
737 |
if (delta < 0) { |
|
738 |
dolog ("whoops delta(<0)=%lld\n", delta); |
|
739 |
return 0; |
|
740 |
} |
|
741 |
|
|
742 |
dead = sw->hw->samples - sw->live; |
|
743 |
swlim = ((dead * (int64_t) INT_MAX) / sw->ratio); |
|
744 |
|
|
745 |
if (bytes > swlim) { |
|
746 |
return swlim; |
|
747 |
} |
|
748 |
else { |
|
749 |
return bytes; |
|
750 |
} |
|
751 |
} |
|
752 |
|
|
753 |
void AUD_enable (SWVoice *sw, int on) |
|
754 |
{ |
|
755 |
int i; |
|
756 |
HWVoice *hw; |
|
757 |
|
|
758 |
if (!sw) |
|
759 |
return; |
|
760 |
|
|
761 |
hw = sw->hw; |
|
762 |
if (on) { |
|
763 |
if (!sw->live) |
|
764 |
sw->wpos = sw->hw->rpos; |
|
765 |
if (!sw->old_ticks) { |
|
766 |
sw->old_ticks = qemu_get_clock (vm_clock); |
|
767 |
} |
|
768 |
} |
|
769 |
|
|
770 |
if (sw->active != on) { |
|
771 |
if (on) { |
|
772 |
hw->pending_disable = 0; |
|
773 |
if (!hw->enabled) { |
|
774 |
hw->enabled = 1; |
|
775 |
for (i = 0; i < hw->nb_voices; i++) { |
|
776 |
ldebug ("resetting voice\n"); |
|
777 |
sw = hw->pvoice[i]; |
|
778 |
sw->old_ticks = qemu_get_clock (vm_clock); |
|
779 |
} |
|
780 |
hw->pcm_ops->ctl (hw, VOICE_ENABLE); |
|
781 |
} |
|
782 |
} |
|
783 |
else { |
|
784 |
if (hw->enabled && !hw->pending_disable) { |
|
785 |
int nb_active = 0; |
|
786 |
for (i = 0; i < hw->nb_voices; i++) { |
|
787 |
nb_active += hw->pvoice[i]->active != 0; |
|
788 |
} |
|
789 |
|
|
790 |
if (nb_active == 1) { |
|
791 |
hw->pending_disable = 1; |
|
792 |
} |
|
793 |
} |
|
794 |
} |
|
795 |
sw->active = on; |
|
796 |
} |
|
797 |
} |
|
798 |
|
|
799 |
static struct audio_output_driver *drvtab[] = { |
|
800 |
#ifdef USE_OSS_AUDIO |
|
801 |
&oss_output_driver, |
|
802 |
#endif |
|
803 |
#ifdef USE_FMOD_AUDIO |
|
804 |
&fmod_output_driver, |
|
805 |
#endif |
|
806 |
#ifdef USE_SDL_AUDIO |
|
807 |
&sdl_output_driver, |
|
808 |
#endif |
|
809 |
#ifdef USE_WAV_AUDIO |
|
810 |
&wav_output_driver, |
|
811 |
#endif |
|
812 |
}; |
|
813 |
|
|
814 |
static int voice_init (struct audio_output_driver *drv) |
|
815 |
{ |
|
816 |
audio_state.opaque = drv->init (); |
|
817 |
if (audio_state.opaque) { |
|
818 |
if (audio_state.nb_hw_voices > drv->max_voices) { |
|
819 |
dolog ("`%s' does not support %d multiple hardware channels\n" |
|
820 |
"Resetting to %d\n", |
|
821 |
drv->name, audio_state.nb_hw_voices, drv->max_voices); |
|
822 |
audio_state.nb_hw_voices = drv->max_voices; |
|
823 |
} |
|
824 |
hw_voice = qemu_mallocz (audio_state.nb_hw_voices * drv->voice_size); |
|
825 |
if (hw_voice) { |
|
826 |
audio_state.drv = drv; |
|
827 |
return 1; |
|
828 |
} |
|
829 |
else { |
|
830 |
dolog ("Not enough memory for %d `%s' voices (each %d bytes)\n", |
|
831 |
audio_state.nb_hw_voices, drv->name, drv->voice_size); |
|
832 |
drv->fini (audio_state.opaque); |
|
833 |
return 0; |
|
834 |
} |
|
835 |
} |
|
836 |
else { |
|
837 |
dolog ("Could not init `%s' audio\n", drv->name); |
|
838 |
return 0; |
|
839 |
} |
|
840 |
} |
|
841 |
|
|
842 |
static void audio_vm_stop_handler (void *opaque, int reason) |
|
843 |
{ |
|
844 |
HWVoice *hw = NULL; |
|
845 |
|
|
846 |
while ((hw = pcm_hw_find_any (hw))) { |
|
847 |
if (!hw->pcm_ops) |
|
848 |
continue; |
|
849 |
|
|
850 |
hw->pcm_ops->ctl (hw, reason ? VOICE_ENABLE : VOICE_DISABLE); |
|
851 |
} |
|
852 |
} |
|
853 |
|
|
854 |
static void audio_atexit (void) |
|
855 |
{ |
|
856 |
HWVoice *hw = NULL; |
|
857 |
|
|
858 |
while ((hw = pcm_hw_find_any (hw))) { |
|
859 |
if (!hw->pcm_ops) |
|
860 |
continue; |
|
861 |
|
|
862 |
hw->pcm_ops->ctl (hw, VOICE_DISABLE); |
|
863 |
hw->pcm_ops->fini (hw); |
|
864 |
} |
|
865 |
audio_state.drv->fini (audio_state.opaque); |
|
866 |
} |
|
867 |
|
|
868 |
static void audio_save (QEMUFile *f, void *opaque) |
|
869 |
{ |
|
870 |
} |
|
871 |
|
|
872 |
static int audio_load (QEMUFile *f, void *opaque, int version_id) |
|
873 |
{ |
|
874 |
if (version_id != 1) |
|
875 |
return -EINVAL; |
|
876 |
|
|
877 |
return 0; |
|
878 |
} |
|
879 |
|
|
880 |
void AUD_init (void) |
|
881 |
{ |
|
882 |
int i; |
|
883 |
int done = 0; |
|
884 |
const char *drvname; |
|
885 |
|
|
886 |
audio_state.fixed_format = |
|
887 |
!!audio_get_conf_int (QC_FIXED_FORMAT, audio_state.fixed_format); |
|
888 |
audio_state.fixed_freq = |
|
889 |
audio_get_conf_int (QC_FIXED_FREQ, audio_state.fixed_freq); |
|
890 |
audio_state.nb_hw_voices = |
|
891 |
audio_get_conf_int (QC_VOICES, audio_state.nb_hw_voices); |
|
892 |
|
|
893 |
if (audio_state.nb_hw_voices <= 0) { |
|
894 |
dolog ("Bogus number of voices %d, resetting to 1\n", |
|
895 |
audio_state.nb_hw_voices); |
|
896 |
} |
|
897 |
|
|
898 |
drvname = audio_get_conf_str (QC_AUDIO_DRV, NULL); |
|
899 |
if (drvname) { |
|
900 |
int found = 0; |
|
901 |
for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) { |
|
902 |
if (!strcmp (drvname, drvtab[i]->name)) { |
|
903 |
done = voice_init (drvtab[i]); |
|
904 |
found = 1; |
|
905 |
break; |
|
906 |
} |
|
907 |
} |
|
908 |
if (!found) { |
|
909 |
dolog ("Unknown audio driver `%s'\n", drvname); |
|
910 |
} |
|
911 |
} |
|
912 |
|
|
913 |
qemu_add_vm_stop_handler (audio_vm_stop_handler, NULL); |
|
914 |
atexit (audio_atexit); |
|
915 |
|
|
916 |
if (!done) { |
|
917 |
for (i = 0; !done && i < sizeof (drvtab) / sizeof (drvtab[0]); i++) { |
|
918 |
if (drvtab[i]->can_be_default) |
|
919 |
done = voice_init (drvtab[i]); |
|
920 |
} |
|
921 |
} |
|
922 |
|
|
923 |
audio_state.ticks_threshold = ticks_per_sec / 50; |
|
924 |
audio_state.freq_threshold = 100; |
|
925 |
|
|
926 |
register_savevm ("audio", 0, 1, audio_save, audio_load, NULL); |
|
927 |
if (!done) { |
|
928 |
dolog ("Can not initialize audio subsystem\n"); |
|
929 |
return; |
|
930 |
} |
|
931 |
|
|
932 |
for (i = 0; hw_ctors[i]; i++) { |
|
933 |
hw_ctors[i] (); |
|
934 |
} |
|
935 |
} |
b/audio/audio.h | ||
---|---|---|
1 |
/* |
|
2 |
* QEMU Audio subsystem header |
|
3 |
* |
|
4 |
* Copyright (c) 2003-2004 Vassili Karpov (malc) |
|
5 |
* |
|
6 |
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|
7 |
* of this software and associated documentation files (the "Software"), to deal |
|
8 |
* in the Software without restriction, including without limitation the rights |
|
9 |
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
10 |
* copies of the Software, and to permit persons to whom the Software is |
|
11 |
* furnished to do so, subject to the following conditions: |
|
12 |
* |
|
13 |
* The above copyright notice and this permission notice shall be included in |
|
14 |
* all copies or substantial portions of the Software. |
|
15 |
* |
|
16 |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
17 |
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
18 |
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
|
19 |
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
20 |
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
21 |
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|
22 |
* THE SOFTWARE. |
|
23 |
*/ |
|
24 |
#ifndef QEMU_AUDIO_H |
|
25 |
#define QEMU_AUDIO_H |
|
26 |
|
|
27 |
#include "mixeng.h" |
|
28 |
|
|
29 |
#define dolog(...) fprintf (stderr, AUDIO_CAP ": " __VA_ARGS__) |
|
30 |
#ifdef DEBUG |
|
31 |
#define ldebug(...) dolog (__VA_ARGS__) |
|
32 |
#else |
|
33 |
#define ldebug(...) |
|
34 |
#endif |
|
35 |
|
|
36 |
typedef enum { |
|
37 |
AUD_FMT_U8, |
|
38 |
AUD_FMT_S8, |
|
39 |
AUD_FMT_U16, |
|
40 |
AUD_FMT_S16 |
|
41 |
} audfmt_e; |
|
42 |
|
|
43 |
typedef struct HWVoice HWVoice; |
|
44 |
struct audio_output_driver; |
|
45 |
|
|
46 |
typedef struct AudioState { |
|
47 |
int fixed_format; |
|
48 |
int fixed_freq; |
|
49 |
int fixed_channels; |
|
50 |
int fixed_fmt; |
|
51 |
int nb_hw_voices; |
|
52 |
int voice_size; |
|
53 |
int64_t ticks_threshold; |
|
54 |
int freq_threshold; |
|
55 |
void *opaque; |
|
56 |
struct audio_output_driver *drv; |
|
57 |
} AudioState; |
|
58 |
|
|
59 |
extern AudioState audio_state; |
|
60 |
|
|
61 |
typedef struct SWVoice { |
|
62 |
int freq; |
|
63 |
audfmt_e fmt; |
|
64 |
int nchannels; |
|
65 |
|
|
66 |
int shift; |
|
67 |
int align; |
|
68 |
|
|
69 |
t_sample *conv; |
|
70 |
|
|
71 |
int left; |
|
72 |
int pos; |
|
73 |
int bytes_per_second; |
|
74 |
int64_t ratio; |
|
75 |
st_sample_t *buf; |
|
76 |
void *rate; |
|
77 |
|
|
78 |
int wpos; |
|
79 |
int live; |
|
80 |
int active; |
|
81 |
int64_t old_ticks; |
|
82 |
HWVoice *hw; |
|
83 |
char *name; |
|
84 |
} SWVoice; |
|
85 |
|
|
86 |
#define VOICE_ENABLE 1 |
|
87 |
#define VOICE_DISABLE 2 |
|
88 |
|
|
89 |
struct pcm_ops { |
|
90 |
int (*init) (HWVoice *hw, int freq, int nchannels, audfmt_e fmt); |
|
91 |
void (*fini) (HWVoice *hw); |
|
92 |
void (*run) (HWVoice *hw); |
|
93 |
int (*write) (SWVoice *sw, void *buf, int size); |
|
94 |
int (*ctl) (HWVoice *hw, int cmd, ...); |
|
95 |
}; |
|
96 |
|
|
97 |
struct audio_output_driver { |
|
98 |
const char *name; |
|
99 |
void *(*init) (void); |
|
100 |
void (*fini) (void *); |
|
101 |
struct pcm_ops *pcm_ops; |
|
102 |
int can_be_default; |
|
103 |
int max_voices; |
|
104 |
int voice_size; |
|
105 |
}; |
|
106 |
|
|
107 |
struct HWVoice { |
|
108 |
int active; |
|
109 |
int enabled; |
|
110 |
int pending_disable; |
|
111 |
int valid; |
|
112 |
int freq; |
|
113 |
|
|
114 |
f_sample *clip; |
|
115 |
audfmt_e fmt; |
|
116 |
int nchannels; |
|
117 |
|
|
118 |
int align; |
|
119 |
int shift; |
|
120 |
|
|
121 |
int rpos; |
|
122 |
int bufsize; |
|
123 |
|
|
124 |
int bytes_per_second; |
|
125 |
st_sample_t *mix_buf; |
|
126 |
|
|
127 |
int samples; |
|
128 |
int64_t old_ticks; |
|
129 |
int nb_voices; |
|
130 |
struct SWVoice **pvoice; |
|
131 |
struct pcm_ops *pcm_ops; |
|
132 |
}; |
|
133 |
|
|
134 |
void audio_log (const char *fmt, ...); |
|
135 |
void pcm_sw_free_resources (SWVoice *sw); |
|
136 |
int pcm_sw_alloc_resources (SWVoice *sw); |
|
137 |
void pcm_sw_fini (SWVoice *sw); |
|
138 |
int pcm_sw_init (SWVoice *sw, HWVoice *hw, int freq, |
|
139 |
int nchannels, audfmt_e fmt); |
|
140 |
|
|
141 |
void pcm_hw_clear (HWVoice *hw, void *buf, int len); |
|
142 |
HWVoice * pcm_hw_find_any (HWVoice *hw); |
|
143 |
HWVoice * pcm_hw_find_any_active (HWVoice *hw); |
|
144 |
HWVoice * pcm_hw_find_any_passive (HWVoice *hw); |
|
145 |
HWVoice * pcm_hw_find_specific (HWVoice *hw, int freq, |
|
146 |
int nchannels, audfmt_e fmt); |
|
147 |
HWVoice * pcm_hw_add (int freq, int nchannels, audfmt_e fmt); |
|
148 |
int pcm_hw_add_sw (HWVoice *hw, SWVoice *sw); |
|
149 |
int pcm_hw_del_sw (HWVoice *hw, SWVoice *sw); |
|
150 |
SWVoice * pcm_create_voice_pair (int freq, int nchannels, audfmt_e fmt); |
|
151 |
|
|
152 |
void pcm_hw_free_resources (HWVoice *hw); |
|
153 |
int pcm_hw_alloc_resources (HWVoice *hw); |
|
154 |
void pcm_hw_fini (HWVoice *hw); |
|
155 |
void pcm_hw_gc (HWVoice *hw); |
|
156 |
int pcm_hw_get_live (HWVoice *hw); |
|
157 |
int pcm_hw_get_live2 (HWVoice *hw, int *nb_active); |
|
158 |
void pcm_hw_dec_live (HWVoice *hw, int decr); |
|
159 |
int pcm_hw_write (SWVoice *sw, void *buf, int len); |
|
160 |
|
|
161 |
int audio_get_conf_int (const char *key, int defval); |
|
162 |
const char *audio_get_conf_str (const char *key, const char *defval); |
|
163 |
|
|
164 |
/* Public API */ |
|
165 |
SWVoice * AUD_open (SWVoice *sw, const char *name, int freq, |
|
166 |
int nchannels, audfmt_e fmt); |
|
167 |
int AUD_write (SWVoice *sw, void *pcm_buf, int size); |
|
168 |
void AUD_adjust (SWVoice *sw, int leftover); |
|
169 |
void AUD_reset (SWVoice *sw); |
|
170 |
int AUD_get_free (SWVoice *sw); |
|
171 |
int AUD_get_buffer_size (SWVoice *sw); |
|
172 |
void AUD_run (void); |
|
173 |
void AUD_enable (SWVoice *sw, int on); |
|
174 |
int AUD_calc_elapsed (SWVoice *sw); |
|
175 |
|
|
176 |
static inline void *advance (void *p, int incr) |
|
177 |
{ |
|
178 |
uint8_t *d = p; |
|
179 |
return (d + incr); |
|
180 |
} |
|
181 |
|
|
182 |
uint32_t popcount (uint32_t u); |
|
183 |
inline uint32_t lsbindex (uint32_t u); |
|
184 |
|
|
185 |
#define audio_MIN(a, b) ((a)>(b)?(b):(a)) |
|
186 |
#define audio_MAX(a, b) ((a)<(b)?(b):(a)) |
|
187 |
|
|
188 |
#endif /* audio.h */ |
b/audio/fmodaudio.c | ||
---|---|---|
1 |
/* |
|
2 |
* QEMU FMOD audio output driver |
|
3 |
* |
|
4 |
* Copyright (c) 2004 Vassili Karpov (malc) |
|
5 |
* |
|
6 |
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|
7 |
* of this software and associated documentation files (the "Software"), to deal |
|
8 |
* in the Software without restriction, including without limitation the rights |
|
9 |
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
10 |
* copies of the Software, and to permit persons to whom the Software is |
|
11 |
* furnished to do so, subject to the following conditions: |
|
12 |
* |
|
13 |
* The above copyright notice and this permission notice shall be included in |
|
14 |
* all copies or substantial portions of the Software. |
|
15 |
* |
|
16 |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
17 |
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
18 |
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
|
19 |
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
20 |
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
21 |
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|
22 |
* THE SOFTWARE. |
|
23 |
*/ |
|
24 |
#include <fmod.h> |
|
25 |
#include <fmod_errors.h> |
|
26 |
#include "vl.h" |
|
27 |
|
|
28 |
#define AUDIO_CAP "fmod" |
|
29 |
#include "audio/audio.h" |
|
30 |
#include "audio/fmodaudio.h" |
|
31 |
|
|
32 |
#define QC_FMOD_DRV "QEMU_FMOD_DRV" |
|
33 |
#define QC_FMOD_FREQ "QEMU_FMOD_FREQ" |
|
34 |
#define QC_FMOD_SAMPLES "QEMU_FMOD_SAMPLES" |
|
35 |
#define QC_FMOD_CHANNELS "QEMU_FMOD_CHANNELS" |
|
36 |
#define QC_FMOD_BUFSIZE "QEMU_FMOD_BUFSIZE" |
|
37 |
#define QC_FMOD_THRESHOLD "QEMU_FMOD_THRESHOLD" |
|
38 |
|
|
39 |
static struct { |
|
40 |
int nb_samples; |
|
41 |
int freq; |
|
42 |
int nb_channels; |
|
43 |
int bufsize; |
|
44 |
int threshold; |
|
45 |
} conf = { |
|
46 |
2048, |
|
47 |
44100, |
|
48 |
1, |
|
49 |
0, |
|
50 |
128 |
|
51 |
}; |
|
52 |
|
|
53 |
#define errstr() FMOD_ErrorString (FSOUND_GetError ()) |
|
54 |
|
|
55 |
static int fmod_hw_write (SWVoice *sw, void *buf, int len) |
|
56 |
{ |
|
57 |
return pcm_hw_write (sw, buf, len); |
|
58 |
} |
|
59 |
|
|
60 |
static void fmod_clear_sample (FMODVoice *fmd) |
|
61 |
{ |
|
62 |
HWVoice *hw = &fmd->hw; |
|
63 |
int status; |
|
64 |
void *p1 = 0, *p2 = 0; |
|
65 |
unsigned int len1 = 0, len2 = 0; |
|
66 |
|
|
67 |
status = FSOUND_Sample_Lock ( |
|
68 |
fmd->fmod_sample, |
|
69 |
0, |
|
70 |
hw->samples << hw->shift, |
|
71 |
&p1, |
|
72 |
&p2, |
|
73 |
&len1, |
|
74 |
&len2 |
|
75 |
); |
|
76 |
|
|
77 |
if (!status) { |
|
78 |
dolog ("Failed to lock sample\nReason: %s\n", errstr ()); |
|
79 |
return; |
|
80 |
} |
|
81 |
|
|
82 |
if ((len1 & hw->align) || (len2 & hw->align)) { |
|
83 |
dolog ("Locking sample returned unaligned length %d, %d\n", |
|
84 |
len1, len2); |
|
85 |
goto fail; |
|
86 |
} |
|
87 |
|
|
88 |
if (len1 + len2 != hw->samples << hw->shift) { |
|
89 |
dolog ("Locking sample returned incomplete length %d, %d\n", |
|
90 |
len1 + len2, hw->samples << hw->shift); |
|
91 |
goto fail; |
|
92 |
} |
|
93 |
pcm_hw_clear (hw, p1, hw->samples); |
|
94 |
|
|
95 |
fail: |
|
96 |
status = FSOUND_Sample_Unlock (fmd->fmod_sample, p1, p2, len1, len2); |
|
97 |
if (!status) { |
|
98 |
dolog ("Failed to unlock sample\nReason: %s\n", errstr ()); |
|
99 |
} |
|
100 |
} |
|
101 |
|
|
102 |
static int fmod_write_sample (HWVoice *hw, uint8_t *dst, st_sample_t *src, |
|
103 |
int src_size, int src_pos, int dst_len) |
|
104 |
{ |
|
105 |
int src_len1 = dst_len, src_len2 = 0, pos = src_pos + dst_len; |
|
106 |
st_sample_t *src1 = src + src_pos, *src2 = 0; |
|
107 |
|
|
108 |
if (src_pos + dst_len > src_size) { |
|
109 |
src_len1 = src_size - src_pos; |
|
110 |
src2 = src; |
|
111 |
src_len2 = dst_len - src_len1; |
|
112 |
pos = src_len2; |
|
113 |
} |
|
114 |
|
|
115 |
if (src_len1) { |
|
116 |
hw->clip (dst, src1, src_len1); |
|
117 |
memset (src1, 0, src_len1 * sizeof (st_sample_t)); |
|
118 |
advance (dst, src_len1); |
|
119 |
} |
|
120 |
|
|
121 |
if (src_len2) { |
|
122 |
hw->clip (dst, src2, src_len2); |
|
123 |
memset (src2, 0, src_len2 * sizeof (st_sample_t)); |
|
124 |
} |
|
125 |
return pos; |
|
126 |
} |
|
127 |
|
|
128 |
static int fmod_unlock_sample (FMODVoice *fmd, void *p1, void *p2, |
|
129 |
unsigned int blen1, unsigned int blen2) |
|
130 |
{ |
|
131 |
int status = FSOUND_Sample_Unlock (fmd->fmod_sample, p1, p2, blen1, blen2); |
|
132 |
if (!status) { |
|
133 |
dolog ("Failed to unlock sample\nReason: %s\n", errstr ()); |
|
134 |
return -1; |
|
135 |
} |
|
136 |
return 0; |
|
137 |
} |
|
138 |
|
|
139 |
static int fmod_lock_sample (FMODVoice *fmd, int pos, int len, |
|
140 |
void **p1, void **p2, |
|
141 |
unsigned int *blen1, unsigned int *blen2) |
|
142 |
{ |
|
143 |
HWVoice *hw = &fmd->hw; |
|
144 |
int status; |
|
145 |
|
|
146 |
status = FSOUND_Sample_Lock ( |
|
147 |
fmd->fmod_sample, |
|
148 |
pos << hw->shift, |
|
149 |
len << hw->shift, |
|
150 |
p1, |
|
151 |
p2, |
|
152 |
blen1, |
|
153 |
blen2 |
|
154 |
); |
|
155 |
|
|
156 |
if (!status) { |
|
157 |
dolog ("Failed to lock sample\nReason: %s\n", errstr ()); |
|
158 |
return -1; |
|
159 |
} |
|
160 |
|
|
161 |
if ((*blen1 & hw->align) || (*blen2 & hw->align)) { |
|
162 |
dolog ("Locking sample returned unaligned length %d, %d\n", |
|
163 |
*blen1, *blen2); |
|
164 |
fmod_unlock_sample (fmd, *p1, *p2, *blen1, *blen2); |
|
165 |
return -1; |
|
166 |
} |
|
167 |
return 0; |
|
168 |
} |
|
169 |
|
|
170 |
static void fmod_hw_run (HWVoice *hw) |
|
171 |
{ |
|
172 |
FMODVoice *fmd = (FMODVoice *) hw; |
|
173 |
int rpos, live, decr; |
|
174 |
void *p1 = 0, *p2 = 0; |
|
175 |
unsigned int blen1 = 0, blen2 = 0; |
|
176 |
unsigned int len1 = 0, len2 = 0; |
|
177 |
int nb_active; |
|
178 |
|
|
179 |
live = pcm_hw_get_live2 (hw, &nb_active); |
|
180 |
if (live <= 0) { |
|
181 |
return; |
|
182 |
} |
|
183 |
|
|
184 |
if (!hw->pending_disable |
|
185 |
&& nb_active |
|
186 |
&& conf.threshold |
|
187 |
&& live <= conf.threshold) { |
|
188 |
ldebug ("live=%d nb_active=%d\n", live, nb_active); |
|
189 |
return; |
|
190 |
} |
|
191 |
|
|
192 |
decr = live; |
|
193 |
|
|
194 |
#if 1 |
|
195 |
if (fmd->channel >= 0) { |
|
196 |
int pos2 = (fmd->old_pos + decr) % hw->samples; |
|
197 |
int pos = FSOUND_GetCurrentPosition (fmd->channel); |
|
198 |
|
|
199 |
if (fmd->old_pos < pos && pos2 >= pos) { |
|
200 |
decr = pos - fmd->old_pos - (pos2 == pos) - 1; |
|
201 |
} |
|
202 |
else if (fmd->old_pos > pos && pos2 >= pos && pos2 < fmd->old_pos) { |
|
203 |
decr = (hw->samples - fmd->old_pos) + pos - (pos2 == pos) - 1; |
|
204 |
} |
|
205 |
/* ldebug ("pos=%d pos2=%d old=%d live=%d decr=%d\n", */ |
|
206 |
/* pos, pos2, fmd->old_pos, live, decr); */ |
|
207 |
} |
|
208 |
#endif |
|
209 |
|
|
210 |
if (decr <= 0) { |
|
211 |
return; |
|
212 |
} |
|
213 |
|
|
214 |
if (fmod_lock_sample (fmd, fmd->old_pos, decr, &p1, &p2, &blen1, &blen2)) { |
|
215 |
return; |
|
216 |
} |
|
217 |
|
|
218 |
len1 = blen1 >> hw->shift; |
|
219 |
len2 = blen2 >> hw->shift; |
|
220 |
ldebug ("%p %p %d %d %d %d\n", p1, p2, len1, len2, blen1, blen2); |
|
221 |
decr = len1 + len2; |
|
222 |
rpos = hw->rpos; |
|
223 |
|
|
224 |
if (len1) { |
|
225 |
rpos = fmod_write_sample (hw, p1, hw->mix_buf, hw->samples, rpos, len1); |
|
226 |
} |
|
227 |
|
|
228 |
if (len2) { |
|
229 |
rpos = fmod_write_sample (hw, p2, hw->mix_buf, hw->samples, rpos, len2); |
|
230 |
} |
|
231 |
|
|
232 |
fmod_unlock_sample (fmd, p1, p2, blen1, blen2); |
|
233 |
|
|
234 |
pcm_hw_dec_live (hw, decr); |
|
235 |
hw->rpos = rpos % hw->samples; |
|
236 |
fmd->old_pos = (fmd->old_pos + decr) % hw->samples; |
|
237 |
} |
|
238 |
|
|
239 |
static int AUD_to_fmodfmt (audfmt_e fmt, int stereo) |
|
240 |
{ |
|
241 |
int mode = FSOUND_LOOP_NORMAL; |
|
242 |
|
|
243 |
switch (fmt) { |
|
244 |
case AUD_FMT_S8: |
|
245 |
mode |= FSOUND_SIGNED | FSOUND_8BITS; |
|
246 |
break; |
|
247 |
|
|
248 |
case AUD_FMT_U8: |
|
249 |
mode |= FSOUND_UNSIGNED | FSOUND_8BITS; |
|
250 |
break; |
|
251 |
|
|
252 |
case AUD_FMT_S16: |
|
253 |
mode |= FSOUND_SIGNED | FSOUND_16BITS; |
|
254 |
break; |
|
255 |
|
|
256 |
case AUD_FMT_U16: |
|
257 |
mode |= FSOUND_UNSIGNED | FSOUND_16BITS; |
|
258 |
break; |
|
259 |
|
|
260 |
default: |
|
261 |
dolog ("Internal logic error: Bad audio format %d\nAborting\n", fmt); |
|
262 |
exit (EXIT_FAILURE); |
|
263 |
} |
|
264 |
mode |= stereo ? FSOUND_STEREO : FSOUND_MONO; |
|
265 |
return mode; |
|
266 |
} |
|
267 |
|
|
268 |
static void fmod_hw_fini (HWVoice *hw) |
|
269 |
{ |
|
270 |
FMODVoice *fmd = (FMODVoice *) hw; |
|
271 |
|
|
272 |
if (fmd->fmod_sample) { |
|
273 |
FSOUND_Sample_Free (fmd->fmod_sample); |
|
274 |
fmd->fmod_sample = 0; |
|
275 |
|
|
276 |
if (fmd->channel >= 0) { |
|
277 |
FSOUND_StopSound (fmd->channel); |
|
278 |
} |
|
279 |
} |
|
280 |
} |
|
281 |
|
|
282 |
static int fmod_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt) |
Also available in: Unified diff