diff options
author | David 'Digit' Turner <digit@android.com> | 2011-01-02 13:51:44 -0800 |
---|---|---|
committer | Android Code Review <code-review@android.com> | 2011-01-02 13:51:44 -0800 |
commit | 49186a9370ff139eda36db0b251b9844884ecf7d (patch) | |
tree | ed261be80a75ad5c2508a154f27a3101a7752d16 | |
parent | 1d08551ecbc0e900e87c4508aa65cb2251a2cde3 (diff) | |
parent | ca6a2e034bce665a08d9d748ac11d6a7cfcd7c48 (diff) | |
download | external_qemu-49186a9370ff139eda36db0b251b9844884ecf7d.zip external_qemu-49186a9370ff139eda36db0b251b9844884ecf7d.tar.gz external_qemu-49186a9370ff139eda36db0b251b9844884ecf7d.tar.bz2 |
Merge changes I30b39053,If7896d65,I364390a7,Ibd4321ed,Ifc00b124,I805c1a24,Ie9442676,Ia9496943,Ic3e35ac0,I060300b4,Id6c6c93f,I71c837e4,I37c21672
* changes:
qemu-sockets-android.c: Add inet_dgram_opts
qemu-sockets-android.c: Add support for socket=<number> option.
monitor.h: cleanup
upstream: Move bottom-half handlers to async.c
sockets.h: Add SOCKET_LIST_DGRAM
upstream: qemu-io.c
upstream: update hxtool
upstream: minor json-lexer.c integration.
upstream: minor integration
Add -audio-test-out option to the core.
Fix upstream audio bug: can't play with two soft voices.
Remove obsolete -audio-in and -audio-out options.
upstream: audio sub-system improvements.
40 files changed, 2464 insertions, 1544 deletions
diff --git a/Makefile.android b/Makefile.android index 7849775..c995f27 100644 --- a/Makefile.android +++ b/Makefile.android @@ -642,6 +642,7 @@ endif # misc. sources # CORE_MISC_SOURCES = vl-android.c \ + async.c \ console.c \ qemu-malloc.c \ cutils.c \ @@ -735,6 +736,8 @@ CORE_SOURCES = $(CORE_BLOCK_SOURCES) $(CORE_HW_SOURCES) CORE_SOURCES += $(CORE_MIGRATION_SOURCES) $(CORE_MISC_SOURCES) CORE_SOURCES += $(CORE_UPSTREAM_SOURCES) +CORE_SOURCES += android/audio-test.c + ############################################################################## # lists of source files used to build the emulator UI # diff --git a/android/audio-test.c b/android/audio-test.c new file mode 100644 index 0000000..e10b987 --- /dev/null +++ b/android/audio-test.c @@ -0,0 +1,103 @@ +/* Copyright (C) 2010 The Android Open Source Project +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +*/ +#include "audio/audio.h" +#include "android/utils/debug.h" + +/* This source file contains a small test audio virtual device that + * can be used to check that the emulator properly plays sound on + * the host system without having to boot a full system. + */ + +#define SAMPLE_SIZE 16384 + +typedef struct { + QEMUSoundCard card; + SWVoiceOut *voice; + int pos; + short sample[SAMPLE_SIZE]; +} TestAudio; + +static void +testAudio_audio_callback(void* opaque, int free) +{ + TestAudio* ta = opaque; + + //printf("%s: pos=%d free=%d\n", __FUNCTION__, ta->pos, free); + + while (free > 0) { + int avail = SAMPLE_SIZE - ta->pos; + if (avail > free) + avail = free; + + AUD_write(ta->voice, ta->sample + ta->pos, avail); + ta->pos += avail; + if (ta->pos >= SAMPLE_SIZE) + ta->pos = 0; + + free -= avail; + } +} + +static int +testAudio_init( TestAudio* ta ) +{ + struct audsettings as; + + AUD_register_card("test_audio", &ta->card); + + as.freq = 16000; + as.nchannels = 1; + as.fmt = AUD_FMT_S16; + as.endianness = AUDIO_HOST_ENDIANNESS; + + ta->voice = AUD_open_out( + &ta->card, + ta->voice, + "test_audio", + ta, + testAudio_audio_callback, + &as); + + if (!ta->voice) { + dprint("Cannot open test audio!"); + return -1; + } + ta->pos = 0; + + /* Initialize samples */ + int nn; + for (nn = 0; nn < SAMPLE_SIZE; nn++) { + ta->sample[nn] = (short)(((nn % (SAMPLE_SIZE/4))*65536/(SAMPLE_SIZE/4)) & 0xffff); + } + + AUD_set_active_out(ta->voice, 1); + return 0; +} + +static TestAudio* testAudio; + +int +android_audio_test_start_out(void) +{ + if (!testAudio) { + testAudio = malloc(sizeof(*testAudio)); + if (testAudio_init(testAudio) < 0) { + free(testAudio); + testAudio = NULL; + fprintf(stderr, "Could not start audio test!\n"); + return -1; + } else { + printf("Audio test started!\n"); + } + } + return 0; +} diff --git a/android/audio-test.h b/android/audio-test.h new file mode 100644 index 0000000..8113aae --- /dev/null +++ b/android/audio-test.h @@ -0,0 +1,22 @@ +/* Copyright (C) 2010 The Android Open Source Project +** +** This software is licensed under the terms of the GNU General Public +** License version 2, as published by the Free Software Foundation, and +** may be copied, distributed, and modified under those terms. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +*/ +#ifndef ANDROID_AUDIO_TEST_H +#define ANDROID_AUDIO_TEST_H + +/* Start the audio test output, this will register a virtual sound + * device that sends a saw signal to the audio sub-system. This is + * used to test that the audio backend works without having to boot + * a full system and launch a music application. + */ +extern int android_audio_test_start_out(void); + +#endif /* ANDROID_AUDIO_TEST_H */ diff --git a/android/cmdline-options.h b/android/cmdline-options.h index 3ee5f45..104de7e 100644 --- a/android/cmdline-options.h +++ b/android/cmdline-options.h @@ -108,8 +108,6 @@ OPT_PARAM( logcat, "<tags>", "enable logcat output with given tags" ) OPT_FLAG ( no_audio, "disable audio support" ) OPT_FLAG ( noaudio, "same as -no-audio" ) OPT_PARAM( audio, "<backend>", "use specific audio backend" ) -OPT_PARAM( audio_in, "<backend>", "use specific audio input backend" ) -OPT_PARAM( audio_out,"<backend>", "use specific audio output backend" ) OPT_FLAG ( raw_keys, "disable Unicode keyboard reverse-mapping" ) OPT_PARAM( radio, "<device>", "redirect radio modem interface to character device" ) diff --git a/android/help.c b/android/help.c index f226cf6..c49f3ff 100644 --- a/android/help.c +++ b/android/help.c @@ -889,71 +889,11 @@ help_audio(stralloc_t* out) " the '-audio <backend>' option allows you to select a specific backend\n" " to be used to both play and record audio in the Android emulator.\n\n" - " this is equivalent to calling both '-audio-in <backend>' and\n" - " '-audio-out <backend>' at the same time.\n\n" - - " use '-help-audio-out' to see a list of valid output <backend> values.\n" - " use '-help-audio-in' to see a list of valid input <backend> values.\n" " use '-audio none' to disable audio completely.\n\n" ); } static void -help_audio_out(stralloc_t* out) -{ - int nn; - - PRINTF( - " the '-audio-out <backend>' option allows you to select a specific\n" - " backend to play audio in the Android emulator. this is mostly useful\n" - " on Linux\n\n" - - " on this system, output <backend> can be one of the following:\n\n" - ); - for ( nn = 0; ; nn++ ) { - char name[512]; - char descr[4096]; - if (android_core_audio_get_backend_name(0, nn, name, sizeof(name), - descr, sizeof(descr))) { - break; - } - PRINTF( " %-10s %s\n", name, descr ); - } - PRINTF( "\n" ); -} - -static void -help_audio_in(stralloc_t* out) -{ - int nn; - - PRINTF( - " the '-audio-in <backend>' option allows you to select a specific\n" - " backend to play audio in the Android emulator. this is mostly useful\n" - " on Linux\n\n" - - " IMPORTANT NOTE:\n" - " on some Linux systems, broken Esd/ALSA/driver implementations will\n" - " make your emulator freeze and become totally unresponsive when\n" - " using audio recording. the only way to avoid this is to use\n" - " '-audio-in none' to disable it\n\n" - - " on this system, input <backend> can be one of:\n\n" - ); - for ( nn = 0; ; nn++ ) { - char name[512]; - char descr[4096]; - if (android_core_audio_get_backend_name(1, nn, name, sizeof(name), - descr, sizeof(descr))) { - break; - } - PRINTF( " %-10s %s\n", name, descr ); - } - PRINTF( "\n" ); -} - - -static void help_scale(stralloc_t* out) { PRINTF( diff --git a/android/main-ui.c b/android/main-ui.c index 043a9a8..688c529 100644 --- a/android/main-ui.c +++ b/android/main-ui.c @@ -1472,15 +1472,6 @@ int main(int argc, char **argv) args[n++] = opts->audio; } - if (opts->audio_in) { - args[n++] = "-audio-in"; - args[n++] = opts->audio_in; - } - if (opts->audio_out) { - args[n++] = "-audio-out"; - args[n++] = opts->audio_out; - } - if (opts->cpu_delay) { args[n++] = "-cpu-delay"; args[n++] = opts->cpu_delay; diff --git a/android/main.c b/android/main.c index 0eebffb..1244c13 100644 --- a/android/main.c +++ b/android/main.c @@ -1240,15 +1240,6 @@ int main(int argc, char **argv) args[n++] = opts->audio; } - if (opts->audio_in) { - args[n++] = "-audio-in"; - args[n++] = opts->audio_in; - } - if (opts->audio_out) { - args[n++] = "-audio-out"; - args[n++] = opts->audio_out; - } - if (opts->cpu_delay) { args[n++] = "-cpu-delay"; args[n++] = opts->cpu_delay; @@ -0,0 +1,216 @@ +/* + * QEMU System Emulator + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu-common.h" +#include "qemu-aio.h" + +/* + * An AsyncContext protects the callbacks of AIO requests and Bottom Halves + * against interfering with each other. A typical example is qcow2 that accepts + * asynchronous requests, but relies for manipulation of its metadata on + * synchronous bdrv_read/write that doesn't trigger any callbacks. + * + * However, these functions are often emulated using AIO which means that AIO + * callbacks must be run - but at the same time we must not run callbacks of + * other requests as they might start to modify metadata and corrupt the + * internal state of the caller of bdrv_read/write. + * + * To achieve the desired semantics we switch into a new AsyncContext. + * Callbacks must only be run if they belong to the current AsyncContext. + * Otherwise they need to be queued until their own context is active again. + * This is how you can make qemu_aio_wait() wait only for your own callbacks. + * + * The AsyncContexts form a stack. When you leave a AsyncContexts, you always + * return to the old ("parent") context. + */ +struct AsyncContext { + /* Consecutive number of the AsyncContext (position in the stack) */ + int id; + + /* Anchor of the list of Bottom Halves belonging to the context */ + struct QEMUBH *first_bh; + + /* Link to parent context */ + struct AsyncContext *parent; +}; + +/* The currently active AsyncContext */ +static struct AsyncContext *async_context = &(struct AsyncContext) { 0 }; + +/* + * Enter a new AsyncContext. Already scheduled Bottom Halves and AIO callbacks + * won't be called until this context is left again. + */ +void async_context_push(void) +{ + struct AsyncContext *new = qemu_mallocz(sizeof(*new)); + new->parent = async_context; + new->id = async_context->id + 1; + async_context = new; +} + +/* Run queued AIO completions and destroy Bottom Half */ +static void bh_run_aio_completions(void *opaque) +{ + QEMUBH **bh = opaque; + qemu_bh_delete(*bh); + qemu_free(bh); + qemu_aio_process_queue(); +} +/* + * Leave the currently active AsyncContext. All Bottom Halves belonging to the + * old context are executed before changing the context. + */ +void async_context_pop(void) +{ + struct AsyncContext *old = async_context; + QEMUBH **bh; + + /* Flush the bottom halves, we don't want to lose them */ + while (qemu_bh_poll()); + + /* Switch back to the parent context */ + async_context = async_context->parent; + qemu_free(old); + + if (async_context == NULL) { + abort(); + } + + /* Schedule BH to run any queued AIO completions as soon as possible */ + bh = qemu_malloc(sizeof(*bh)); + *bh = qemu_bh_new(bh_run_aio_completions, bh); + qemu_bh_schedule(*bh); +} + +/* + * Returns the ID of the currently active AsyncContext + */ +int get_async_context_id(void) +{ + return async_context->id; +} + +/***********************************************************/ +/* bottom halves (can be seen as timers which expire ASAP) */ + +struct QEMUBH { + QEMUBHFunc *cb; + void *opaque; + int scheduled; + int idle; + int deleted; + QEMUBH *next; +}; + +QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque) +{ + QEMUBH *bh; + bh = qemu_mallocz(sizeof(QEMUBH)); + bh->cb = cb; + bh->opaque = opaque; + bh->next = async_context->first_bh; + async_context->first_bh = bh; + return bh; +} + +int qemu_bh_poll(void) +{ + QEMUBH *bh, **bhp; + int ret; + + ret = 0; + for (bh = async_context->first_bh; bh; bh = bh->next) { + if (!bh->deleted && bh->scheduled) { + bh->scheduled = 0; + if (!bh->idle) + ret = 1; + bh->idle = 0; + bh->cb(bh->opaque); + } + } + + /* remove deleted bhs */ + bhp = &async_context->first_bh; + while (*bhp) { + bh = *bhp; + if (bh->deleted) { + *bhp = bh->next; + qemu_free(bh); + } else + bhp = &bh->next; + } + + return ret; +} + +void qemu_bh_schedule_idle(QEMUBH *bh) +{ + if (bh->scheduled) + return; + bh->scheduled = 1; + bh->idle = 1; +} + +void qemu_bh_schedule(QEMUBH *bh) +{ + if (bh->scheduled) + return; + bh->scheduled = 1; + bh->idle = 0; + /* stop the currently executing CPU to execute the BH ASAP */ + qemu_notify_event(); +} + +void qemu_bh_cancel(QEMUBH *bh) +{ + bh->scheduled = 0; +} + +void qemu_bh_delete(QEMUBH *bh) +{ + bh->scheduled = 0; + bh->deleted = 1; +} + +void qemu_bh_update_timeout(int *timeout) +{ + QEMUBH *bh; + + for (bh = async_context->first_bh; bh; bh = bh->next) { + if (!bh->deleted && bh->scheduled) { + if (bh->idle) { + /* idle bottom halves will be polled at least + * every 10ms */ + *timeout = MIN(10, *timeout); + } else { + /* non-idle bottom halves will be executed + * immediately */ + *timeout = 0; + break; + } + } + } +} + diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c index 0f5ee9e..1cbbaa4 100644 --- a/audio/alsaaudio.c +++ b/audio/alsaaudio.c @@ -1,7 +1,7 @@ /* * QEMU ALSA audio driver * - * Copyright (c) 2008 The Android Open Source Project + * Copyright (c) 2008-2010 The Android Open Source Project * Copyright (c) 2005 Vassili Karpov (malc) * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -24,8 +24,13 @@ */ #include <alsa/asoundlib.h> #include "qemu-common.h" +#include "qemu-char.h" #include "audio.h" +#if QEMU_GNUC_PREREQ(4, 3) +#pragma GCC diagnostic ignored "-Waddress" +#endif + #define AUDIO_CAP "alsa" #include "audio_int.h" #include <dlfcn.h> @@ -81,6 +86,10 @@ DYNLINK_FUNC(int,snd_pcm_hw_params_set_buffer_size_near,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val)) \ DYNLINK_FUNC(int,snd_pcm_hw_params_set_period_size_near,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val, int *dir)) \ DYNLINK_FUNC(int,snd_pcm_hw_params_get_format,(const snd_pcm_hw_params_t *params, snd_pcm_format_t *val)) \ + DYNLINK_FUNC(int,snd_pcm_resume,(snd_pcm_t *pcm)) \ + DYNLINK_FUNC(int,snd_pcm_poll_descriptors_revents,(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)) \ + DYNLINK_FUNC(int,snd_pcm_poll_descriptors_count,(snd_pcm_t *pcm)) \ + DYNLINK_FUNC(int,snd_pcm_poll_descriptors,(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space)) \ #define DYNLINK_FUNCTIONS_INIT \ alsa_dynlink_init @@ -96,16 +105,27 @@ static void* alsa_lib; +struct pollhlp { + snd_pcm_t *handle; + struct pollfd *pfds; + int count; + int mask; +}; + typedef struct ALSAVoiceOut { HWVoiceOut hw; + int wpos; + int pending; void *pcm_buf; snd_pcm_t *handle; + struct pollhlp pollhlp; } ALSAVoiceOut; typedef struct ALSAVoiceIn { HWVoiceIn hw; snd_pcm_t *handle; void *pcm_buf; + struct pollhlp pollhlp; } ALSAVoiceIn; static struct { @@ -126,7 +146,8 @@ static struct { int period_size_out_overridden; int verbose; } conf = { - .buffer_size_out = 1024, + .buffer_size_out = 4096, + .period_size_out = 1024, .pcm_name_out = "default", .pcm_name_in = "default", }; @@ -178,7 +199,23 @@ static void GCC_FMT_ATTR (3, 4) alsa_logerr2 ( AUD_log (AUDIO_CAP, "Reason: %s\n", FF(snd_strerror) (err)); } -static void alsa_anal_close (snd_pcm_t **handlep) +static void alsa_fini_poll (struct pollhlp *hlp) +{ + int i; + struct pollfd *pfds = hlp->pfds; + + if (pfds) { + for (i = 0; i < hlp->count; ++i) { + qemu_set_fd_handler (pfds[i].fd, NULL, NULL, NULL); + } + qemu_free (pfds); + } + hlp->pfds = NULL; + hlp->count = 0; + hlp->handle = NULL; +} + +static void alsa_anal_close1 (snd_pcm_t **handlep) { int err = FF(snd_pcm_close) (*handlep); if (err) { @@ -187,6 +224,167 @@ static void alsa_anal_close (snd_pcm_t **handlep) *handlep = NULL; } +static void alsa_anal_close (snd_pcm_t **handlep, struct pollhlp *hlp) +{ + alsa_fini_poll (hlp); + alsa_anal_close1 (handlep); +} + +static int alsa_recover (snd_pcm_t *handle) +{ + int err = FF(snd_pcm_prepare) (handle); + if (err < 0) { + alsa_logerr (err, "Failed to prepare handle %p\n", handle); + return -1; + } + return 0; +} + +static int alsa_resume (snd_pcm_t *handle) +{ + int err = FF(snd_pcm_resume) (handle); + if (err < 0) { + alsa_logerr (err, "Failed to resume handle %p\n", handle); + return -1; + } + return 0; +} + +static void alsa_poll_handler (void *opaque) +{ + int err, count; + snd_pcm_state_t state; + struct pollhlp *hlp = opaque; + unsigned short revents; + + count = poll (hlp->pfds, hlp->count, 0); + if (count < 0) { + dolog ("alsa_poll_handler: poll %s\n", strerror (errno)); + return; + } + + if (!count) { + return; + } + + /* XXX: ALSA example uses initial count, not the one returned by + poll, correct? */ + err = FF(snd_pcm_poll_descriptors_revents) (hlp->handle, hlp->pfds, + hlp->count, &revents); + if (err < 0) { + alsa_logerr (err, "snd_pcm_poll_descriptors_revents"); + return; + } + + if (!(revents & hlp->mask)) { + if (conf.verbose) { + dolog ("revents = %d\n", revents); + } + return; + } + + state = FF(snd_pcm_state) (hlp->handle); + switch (state) { + case SND_PCM_STATE_SETUP: + alsa_recover (hlp->handle); + break; + + case SND_PCM_STATE_XRUN: + alsa_recover (hlp->handle); + break; + + case SND_PCM_STATE_SUSPENDED: + alsa_resume (hlp->handle); + break; + + case SND_PCM_STATE_PREPARED: + audio_run ("alsa run (prepared)"); + break; + + case SND_PCM_STATE_RUNNING: + audio_run ("alsa run (running)"); + break; + + default: + dolog ("Unexpected state %d\n", state); + } +} + +static int alsa_poll_helper (snd_pcm_t *handle, struct pollhlp *hlp, int mask) +{ + int i, count, err; + struct pollfd *pfds; + + count = FF(snd_pcm_poll_descriptors_count) (handle); + if (count <= 0) { + dolog ("Could not initialize poll mode\n" + "Invalid number of poll descriptors %d\n", count); + return -1; + } + + pfds = audio_calloc ("alsa_poll_helper", count, sizeof (*pfds)); + if (!pfds) { + dolog ("Could not initialize poll mode\n"); + return -1; + } + + err = FF(snd_pcm_poll_descriptors) (handle, pfds, count); + if (err < 0) { + alsa_logerr (err, "Could not initialize poll mode\n" + "Could not obtain poll descriptors\n"); + qemu_free (pfds); + return -1; + } + + for (i = 0; i < count; ++i) { + if (pfds[i].events & POLLIN) { + err = qemu_set_fd_handler (pfds[i].fd, alsa_poll_handler, + NULL, hlp); + } + if (pfds[i].events & POLLOUT) { + if (conf.verbose) { + dolog ("POLLOUT %d %d\n", i, pfds[i].fd); + } + err = qemu_set_fd_handler (pfds[i].fd, NULL, + alsa_poll_handler, hlp); + } + if (conf.verbose) { + dolog ("Set handler events=%#x index=%d fd=%d err=%d\n", + pfds[i].events, i, pfds[i].fd, err); + } + + if (err) { + dolog ("Failed to set handler events=%#x index=%d fd=%d err=%d\n", + pfds[i].events, i, pfds[i].fd, err); + + while (i--) { + qemu_set_fd_handler (pfds[i].fd, NULL, NULL, NULL); + } + qemu_free (pfds); + return -1; + } + } + hlp->pfds = pfds; + hlp->count = count; + hlp->handle = handle; + hlp->mask = mask; + return 0; +} + +static int alsa_poll_out (HWVoiceOut *hw) +{ + ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; + + return alsa_poll_helper (alsa->handle, &alsa->pollhlp, POLLOUT); +} + +static int alsa_poll_in (HWVoiceIn *hw) +{ + ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; + + return alsa_poll_helper (alsa->handle, &alsa->pollhlp, POLLIN); +} + static int alsa_write (SWVoiceOut *sw, void *buf, int len) { return audio_pcm_sw_write (sw, buf, len); @@ -285,10 +483,11 @@ static int alsa_to_audfmt (snd_pcm_format_t alsafmt, audfmt_e *fmt, } static void alsa_dump_info (struct alsa_params_req *req, - struct alsa_params_obt *obt) + struct alsa_params_obt *obt, + snd_pcm_format_t obtfmt) { dolog ("parameter | requested value | obtained value\n"); - dolog ("format | %10d | %10d\n", req->fmt, obt->fmt); + dolog ("format | %10d | %10d\n", req->fmt, obtfmt); dolog ("channels | %10d | %10d\n", req->nchannels, obt->nchannels); dolog ("frequency | %10d | %10d\n", req->freq, obt->freq); @@ -474,7 +673,7 @@ static int alsa_open (int in, struct alsa_params_req *req, goto err; } - if ((req->override_mask & 1) && (obt - req->period_size)) + if (((req->override_mask & 1) && (obt - req->period_size))) dolog ("Requested period %s %u was rejected, using %lu\n", size_in_usec ? "time" : "size", req->period_size, obt); } @@ -491,7 +690,6 @@ static int alsa_open (int in, struct alsa_params_req *req, goto err; } - err = FF(snd_pcm_hw_params_get_format)(hw_params, &obtfmt); err = FF(snd_pcm_hw_params_get_format) (hw_params, &obtfmt); if (err < 0) { alsa_logerr2 (err, typ, "Failed to get format\n"); @@ -542,33 +740,23 @@ static int alsa_open (int in, struct alsa_params_req *req, *handlep = handle; if (conf.verbose && - (obt->fmt != req->fmt || + (obtfmt != req->fmt || obt->nchannels != req->nchannels || obt->freq != req->freq)) { - dolog ("Audio paramters for %s\n", typ); - alsa_dump_info (req, obt); + dolog ("Audio parameters for %s\n", typ); + alsa_dump_info (req, obt, obtfmt); } #ifdef DEBUG - alsa_dump_info (req, obt); + alsa_dump_info (req, obt, obtfmt); #endif return 0; err: - alsa_anal_close (&handle); + alsa_anal_close1 (&handle); return -1; } -static int alsa_recover (snd_pcm_t *handle) -{ - int err = FF(snd_pcm_prepare) (handle); - if (err < 0) { - alsa_logerr (err, "Failed to prepare handle %p\n", handle); - return -1; - } - return 0; -} - static snd_pcm_sframes_t alsa_get_avail (snd_pcm_t *handle) { snd_pcm_sframes_t avail; @@ -591,41 +779,19 @@ static snd_pcm_sframes_t alsa_get_avail (snd_pcm_t *handle) return avail; } -static int alsa_run_out (HWVoiceOut *hw) +static void alsa_write_pending (ALSAVoiceOut *alsa) { - ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; - int rpos, live, decr; - int samples; - uint8_t *dst; - struct st_sample *src; - snd_pcm_sframes_t avail; - - live = audio_pcm_hw_get_live_out (hw); - if (!live) { - return 0; - } - - avail = alsa_get_avail (alsa->handle); - if (avail < 0) { - dolog ("Could not get number of available playback frames\n"); - return 0; - } - - decr = audio_MIN (live, avail); - samples = decr; - rpos = hw->rpos; - while (samples) { - int left_till_end_samples = hw->samples - rpos; - int len = audio_MIN (samples, left_till_end_samples); - snd_pcm_sframes_t written; - - src = hw->mix_buf + rpos; - dst = advance (alsa->pcm_buf, rpos << hw->info.shift); + HWVoiceOut *hw = &alsa->hw; - hw->clip (dst, src, len); + while (alsa->pending) { + int left_till_end_samples = hw->samples - alsa->wpos; + int len = audio_MIN (alsa->pending, left_till_end_samples); + char *src = advance (alsa->pcm_buf, alsa->wpos << hw->info.shift); while (len) { - written = FF(snd_pcm_writei) (alsa->handle, dst, len); + snd_pcm_sframes_t written; + + written = FF(snd_pcm_writei) (alsa->handle, src, len); if (written <= 0) { switch (written) { @@ -633,39 +799,65 @@ static int alsa_run_out (HWVoiceOut *hw) if (conf.verbose) { dolog ("Failed to write %d frames (wrote zero)\n", len); } - goto exit; + return; case -EPIPE: if (alsa_recover (alsa->handle)) { alsa_logerr (written, "Failed to write %d frames\n", len); - goto exit; + return; } if (conf.verbose) { dolog ("Recovering from playback xrun\n"); } continue; + case -ESTRPIPE: + /* stream is suspended and waiting for an + application recovery */ + if (alsa_resume (alsa->handle)) { + alsa_logerr (written, "Failed to write %d frames\n", + len); + return; + } + if (conf.verbose) { + dolog ("Resuming suspended output stream\n"); + } + continue; + case -EAGAIN: - goto exit; + return; default: - alsa_logerr (written, "Failed to write %d frames to %p\n", - len, dst); - goto exit; + alsa_logerr (written, "Failed to write %d frames from %p\n", + len, src); + return; } } - rpos = (rpos + written) % hw->samples; - samples -= written; + alsa->wpos = (alsa->wpos + written) % hw->samples; + alsa->pending -= written; len -= written; - dst = advance (dst, written << hw->info.shift); - src += written; } } +} - exit: - hw->rpos = rpos; +static int alsa_run_out (HWVoiceOut *hw, int live) +{ + ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; + int decr; + snd_pcm_sframes_t avail; + + avail = alsa_get_avail (alsa->handle); + if (avail < 0) { + dolog ("Could not get number of available playback frames\n"); + return 0; + } + + decr = audio_MIN (live, avail); + decr = audio_pcm_hw_clip_out (hw, alsa->pcm_buf, decr, alsa->pending); + alsa->pending += decr; + alsa_write_pending (alsa); return decr; } @@ -674,7 +866,7 @@ static void alsa_fini_out (HWVoiceOut *hw) ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; ldebug ("alsa_fini\n"); - alsa_anal_close (&alsa->handle); + alsa_anal_close (&alsa->handle, &alsa->pollhlp); if (alsa->pcm_buf) { qemu_free (alsa->pcm_buf); @@ -701,8 +893,9 @@ static int alsa_init_out (HWVoiceOut *hw, struct audsettings *as) req.period_size = conf.period_size_out; req.buffer_size = conf.buffer_size_out; req.size_in_usec = conf.size_in_usec_out; - req.override_mask = !!conf.period_size_out_overridden - | (!!conf.buffer_size_out_overridden << 1); + req.override_mask = + (conf.period_size_out_overridden ? 1 : 0) | + (conf.buffer_size_out_overridden ? 2 : 0); if (alsa_open (0, &req, &obt, &handle)) { goto Exit; @@ -720,7 +913,7 @@ static int alsa_init_out (HWVoiceOut *hw, struct audsettings *as) if (!alsa->pcm_buf) { dolog ("Could not allocate DAC buffer (%d samples, each %d bytes)\n", hw->samples, 1 << hw->info.shift); - alsa_anal_close (&handle); + alsa_anal_close1 (&handle); goto Exit; } @@ -762,8 +955,21 @@ static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...) switch (cmd) { case VOICE_ENABLE: - ldebug ("enabling voice\n"); - return alsa_voice_ctl (alsa->handle, "playback", 0); + { + va_list ap; + int poll_mode; + + va_start (ap, cmd); + poll_mode = va_arg (ap, int); + va_end (ap); + + ldebug ("enabling voice\n"); + if (poll_mode && alsa_poll_out (hw)) { + poll_mode = 0; + } + hw->poll_mode = poll_mode; + return alsa_voice_ctl (alsa->handle, "playback", 0); + } case VOICE_DISABLE: ldebug ("disabling voice\n"); @@ -792,8 +998,9 @@ static int alsa_init_in (HWVoiceIn *hw, struct audsettings *as) req.period_size = conf.period_size_in; req.buffer_size = conf.buffer_size_in; req.size_in_usec = conf.size_in_usec_in; - req.override_mask = !!conf.period_size_in_overridden - | (!!conf.buffer_size_in_overridden << 1); + req.override_mask = + (conf.period_size_in_overridden ? 1 : 0) | + (conf.buffer_size_in_overridden ? 2 : 0); if (alsa_open (1, &req, &obt, &handle)) { goto Exit; @@ -811,7 +1018,7 @@ static int alsa_init_in (HWVoiceIn *hw, struct audsettings *as) if (!alsa->pcm_buf) { dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n", hw->samples, 1 << hw->info.shift); - alsa_anal_close (&handle); + alsa_anal_close1 (&handle); goto Exit; } @@ -829,7 +1036,7 @@ static void alsa_fini_in (HWVoiceIn *hw) { ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; - alsa_anal_close (&alsa->handle); + alsa_anal_close (&alsa->handle, &alsa->pollhlp); if (alsa->pcm_buf) { qemu_free (alsa->pcm_buf); @@ -849,8 +1056,8 @@ static int alsa_run_in (HWVoiceIn *hw) int add; int len; } bufs[2] = { - { hw->wpos, 0 }, - { 0, 0 } + { .add = hw->wpos, .len = 0 }, + { .add = 0, .len = 0 } }; snd_pcm_sframes_t avail; snd_pcm_uframes_t read_samples = 0; @@ -865,8 +1072,30 @@ static int alsa_run_in (HWVoiceIn *hw) return 0; } - if (!avail && (FF(snd_pcm_state) (alsa->handle) == SND_PCM_STATE_PREPARED)) { - avail = hw->samples; + if (!avail) { + snd_pcm_state_t state; + + state = FF(snd_pcm_state) (alsa->handle); + switch (state) { + case SND_PCM_STATE_PREPARED: + avail = hw->samples; + break; + case SND_PCM_STATE_SUSPENDED: + /* stream is suspended and waiting for an application recovery */ + if (alsa_resume (alsa->handle)) { + dolog ("Failed to resume suspended input stream\n"); + return 0; + } + if (conf.verbose) { + dolog ("Resuming suspended input stream\n"); + } + break; + default: + if (conf.verbose) { + dolog ("No frames available and ALSA state is %d\n", state); + } + return 0; + } } decr = audio_MIN (dead, avail); @@ -954,11 +1183,29 @@ static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...) switch (cmd) { case VOICE_ENABLE: - ldebug ("enabling voice\n"); - return alsa_voice_ctl (alsa->handle, "capture", 0); + { + va_list ap; + int poll_mode; + + va_start (ap, cmd); + poll_mode = va_arg (ap, int); + va_end (ap); + + ldebug ("enabling voice\n"); + if (poll_mode && alsa_poll_in (hw)) { + poll_mode = 0; + } + hw->poll_mode = poll_mode; + + return alsa_voice_ctl (alsa->handle, "capture", 0); + } case VOICE_DISABLE: ldebug ("disabling voice\n"); + if (hw->poll_mode) { + hw->poll_mode = 0; + alsa_fini_poll (&alsa->pollhlp); + } return alsa_voice_ctl (alsa->handle, "capture", 1); } @@ -1002,63 +1249,98 @@ static void alsa_audio_fini (void *opaque) } static struct audio_option alsa_options[] = { - {"DAC_SIZE_IN_USEC", AUD_OPT_BOOL, &conf.size_in_usec_out, - "DAC period/buffer size in microseconds (otherwise in frames)", NULL, 0}, - {"DAC_PERIOD_SIZE", AUD_OPT_INT, &conf.period_size_out, - "DAC period size (0 to go with system default)", - &conf.period_size_out_overridden, 0}, - {"DAC_BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_size_out, - "DAC buffer size (0 to go with system default)", - &conf.buffer_size_out_overridden, 0}, - - {"ADC_SIZE_IN_USEC", AUD_OPT_BOOL, &conf.size_in_usec_in, - "ADC period/buffer size in microseconds (otherwise in frames)", NULL, 0}, - {"ADC_PERIOD_SIZE", AUD_OPT_INT, &conf.period_size_in, - "ADC period size (0 to go with system default)", - &conf.period_size_in_overridden, 0}, - {"ADC_BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_size_in, - "ADC buffer size (0 to go with system default)", - &conf.buffer_size_in_overridden, 0}, - - {"THRESHOLD", AUD_OPT_INT, &conf.threshold, - "(undocumented)", NULL, 0}, - - {"DAC_DEV", AUD_OPT_STR, &conf.pcm_name_out, - "DAC device name (for instance dmix)", NULL, 0}, - - {"ADC_DEV", AUD_OPT_STR, &conf.pcm_name_in, - "ADC device name", NULL, 0}, - - {"VERBOSE", AUD_OPT_BOOL, &conf.verbose, - "Behave in a more verbose way", NULL, 0}, - - {NULL, 0, NULL, NULL, NULL, 0} + { + .name = "DAC_SIZE_IN_USEC", + .tag = AUD_OPT_BOOL, + .valp = &conf.size_in_usec_out, + .descr = "DAC period/buffer size in microseconds (otherwise in frames)" + }, + { + .name = "DAC_PERIOD_SIZE", + .tag = AUD_OPT_INT, + .valp = &conf.period_size_out, + .descr = "DAC period size (0 to go with system default)", + .overriddenp = &conf.period_size_out_overridden + }, + { + .name = "DAC_BUFFER_SIZE", + .tag = AUD_OPT_INT, + .valp = &conf.buffer_size_out, + .descr = "DAC buffer size (0 to go with system default)", + .overriddenp = &conf.buffer_size_out_overridden + }, + { + .name = "ADC_SIZE_IN_USEC", + .tag = AUD_OPT_BOOL, + .valp = &conf.size_in_usec_in, + .descr = + "ADC period/buffer size in microseconds (otherwise in frames)" + }, + { + .name = "ADC_PERIOD_SIZE", + .tag = AUD_OPT_INT, + .valp = &conf.period_size_in, + .descr = "ADC period size (0 to go with system default)", + .overriddenp = &conf.period_size_in_overridden + }, + { + .name = "ADC_BUFFER_SIZE", + .tag = AUD_OPT_INT, + .valp = &conf.buffer_size_in, + .descr = "ADC buffer size (0 to go with system default)", + .overriddenp = &conf.buffer_size_in_overridden + }, + { + .name = "THRESHOLD", + .tag = AUD_OPT_INT, + .valp = &conf.threshold, + .descr = "(undocumented)" + }, + { + .name = "DAC_DEV", + .tag = AUD_OPT_STR, + .valp = &conf.pcm_name_out, + .descr = "DAC device name (for instance dmix)" + }, + { + .name = "ADC_DEV", + .tag = AUD_OPT_STR, + .valp = &conf.pcm_name_in, + .descr = "ADC device name" + }, + { + .name = "VERBOSE", + .tag = AUD_OPT_BOOL, + .valp = &conf.verbose, + .descr = "Behave in a more verbose way" + }, + { /* End of list */ } }; static struct audio_pcm_ops alsa_pcm_ops = { - alsa_init_out, - alsa_fini_out, - alsa_run_out, - alsa_write, - alsa_ctl_out, - - alsa_init_in, - alsa_fini_in, - alsa_run_in, - alsa_read, - alsa_ctl_in + .init_out = alsa_init_out, + .fini_out = alsa_fini_out, + .run_out = alsa_run_out, + .write = alsa_write, + .ctl_out = alsa_ctl_out, + + .init_in = alsa_init_in, + .fini_in = alsa_fini_in, + .run_in = alsa_run_in, + .read = alsa_read, + .ctl_in = alsa_ctl_in, }; struct audio_driver alsa_audio_driver = { - INIT_FIELD (name = ) "alsa", - INIT_FIELD (descr = ) "ALSA audio (www.alsa-project.org)", - INIT_FIELD (options = ) alsa_options, - INIT_FIELD (init = ) alsa_audio_init, - INIT_FIELD (fini = ) alsa_audio_fini, - INIT_FIELD (pcm_ops = ) &alsa_pcm_ops, - INIT_FIELD (can_be_default = ) 1, - INIT_FIELD (max_voices_out = ) INT_MAX, - INIT_FIELD (max_voices_in = ) INT_MAX, - INIT_FIELD (voice_size_out = ) sizeof (ALSAVoiceOut), - INIT_FIELD (voice_size_in = ) sizeof (ALSAVoiceIn) + .name = "alsa", + .descr = "ALSA http://www.alsa-project.org", + .options = alsa_options, + .init = alsa_audio_init, + .fini = alsa_audio_fini, + .pcm_ops = &alsa_pcm_ops, + .can_be_default = 1, + .max_voices_out = INT_MAX, + .max_voices_in = INT_MAX, + .voice_size_out = sizeof (ALSAVoiceOut), + .voice_size_in = sizeof (ALSAVoiceIn) }; diff --git a/audio/audio.c b/audio/audio.c index 6f107dc..3e60c12 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -158,71 +158,59 @@ static struct { } period; int plive; int log_to_monitor; + int try_poll_in; + int try_poll_out; } conf = { - { /* DAC fixed settings */ - 1, /* enabled */ - 1, /* nb_voices */ - 1, /* greedy */ - { - 44100, /* freq */ - 2, /* nchannels */ - AUD_FMT_S16, /* fmt */ - AUDIO_HOST_ENDIANNESS + .fixed_out = { /* DAC fixed settings */ + .enabled = 1, + .nb_voices = 1, + .greedy = 1, + .settings = { + .freq = 44100, + .nchannels = 2, + .fmt = AUD_FMT_S16, + .endianness = AUDIO_HOST_ENDIANNESS, } }, - { /* ADC fixed settings */ - 1, /* enabled */ - 1, /* nb_voices */ - 1, /* greedy */ - { - 44100, /* freq */ - 2, /* nchannels */ - AUD_FMT_S16, /* fmt */ - AUDIO_HOST_ENDIANNESS + .fixed_in = { /* ADC fixed settings */ + .enabled = 1, + .nb_voices = 1, + .greedy = 1, + .settings = { + .freq = 44100, + .nchannels = 2, + .fmt = AUD_FMT_S16, + .endianness = AUDIO_HOST_ENDIANNESS, } }, - { 250 }, /* period */ - 0, /* plive */ - 0 /* log_to_monitor */ + .period = { .hertz = 250 }, + .plive = 0, + .log_to_monitor = 0, + .try_poll_in = 1, + .try_poll_out = 1, }; static AudioState glob_audio_state; struct mixeng_volume nominal_volume = { - 0, + .mute = 0, #ifdef FLOAT_MIXENG - 1.0, - 1.0 + .r = 1.0, + .l = 1.0, #else - 1ULL << 32, - 1ULL << 32 + .r = 1ULL << 32, + .l = 1ULL << 32, #endif }; -#if 0 -/* http://www.df.lth.se/~john_e/gems/gem002d.html */ -/* http://www.multi-platforms.com/Tips/PopCount.htm */ -uint32_t popcount (uint32_t u) -{ - u = ((u&0x55555555) + ((u>>1)&0x55555555)); - u = ((u&0x33333333) + ((u>>2)&0x33333333)); - u = ((u&0x0f0f0f0f) + ((u>>4)&0x0f0f0f0f)); - u = ((u&0x00ff00ff) + ((u>>8)&0x00ff00ff)); - u = ( u&0x0000ffff) + (u>>16); - return u; -} - -inline uint32_t lsbindex (uint32_t u) -{ - return popcount ((u&-u)-1); -} -#endif - #ifdef AUDIO_IS_FLAWLESS_AND_NO_CHECKS_ARE_REQURIED #error No its not #else +static void audio_print_options (const char *prefix, + struct audio_option *opt); + int audio_bug (const char *funcname, int cond) { if (cond) { @@ -230,10 +218,16 @@ int audio_bug (const char *funcname, int cond) AUD_log (NULL, "A bug was just triggered in %s\n", funcname); if (!shown) { + struct audio_driver *d; + shown = 1; AUD_log (NULL, "Save all your work and restart without audio\n"); - AUD_log (NULL, "Please send bug report to malc@pulsesoft.com\n"); + AUD_log (NULL, "Please send bug report to av1474@comtv.ru\n"); AUD_log (NULL, "I am sorry\n"); + d = glob_audio_state.drv; + if (d) { + audio_print_options (d->name, d->options); + } } AUD_log (NULL, "Context:\n"); @@ -966,6 +960,28 @@ int audio_pcm_hw_get_live_in (HWVoiceIn *hw) return live; } +int audio_pcm_hw_clip_out (HWVoiceOut *hw, void *pcm_buf, + int live, int pending) +{ + int left = hw->samples - pending; + int len = audio_MIN (left, live); + int clipped = 0; + + while (len) { + struct st_sample *src = hw->mix_buf + hw->rpos; + uint8_t *dst = advance (pcm_buf, hw->rpos << hw->info.shift); + int samples_till_end_of_buf = hw->samples - hw->rpos; + int samples_to_clip = audio_MIN (len, samples_till_end_of_buf); + + hw->clip (dst, src, samples_to_clip); + + hw->rpos = (hw->rpos + samples_to_clip) % hw->samples; + len -= samples_to_clip; + clipped += samples_to_clip; + } + return clipped; +} + /* * Soft voice (capture) */ @@ -1052,7 +1068,7 @@ static int audio_pcm_hw_find_min_out (HWVoiceOut *hw, int *nb_livep) int nb_live = 0; for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { - if (sw->active || !sw->empty) { + if (sw->active && !sw->empty) { m = audio_MIN (m, sw->total_hw_samples_mixed); nb_live += 1; } @@ -1062,16 +1078,17 @@ static int audio_pcm_hw_find_min_out (HWVoiceOut *hw, int *nb_livep) return m; } -int audio_pcm_hw_get_live_out2 (HWVoiceOut *hw, int *nb_live) +static int audio_pcm_hw_get_live_out (HWVoiceOut *hw, int *nb_live) { int smin; + int nb_live1; - smin = audio_pcm_hw_find_min_out (hw, nb_live); - - if (!*nb_live) { - return 0; + smin = audio_pcm_hw_find_min_out (hw, &nb_live1); + if (nb_live) { + *nb_live = nb_live1; } - else { + + if (nb_live1) { int live = smin; if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) { @@ -1080,19 +1097,7 @@ int audio_pcm_hw_get_live_out2 (HWVoiceOut *hw, int *nb_live) } return live; } -} - -int audio_pcm_hw_get_live_out (HWVoiceOut *hw) -{ - int nb_live; - int live; - - live = audio_pcm_hw_get_live_out2 (hw, &nb_live); - if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) { - dolog ("live=%d hw->samples=%d\n", live, hw->samples); - return 0; - } - return live; + return 0; } /* @@ -1185,6 +1190,76 @@ static void audio_pcm_print_info (const char *cap, struct audio_pcm_info *info) #undef DAC #include "audio_template.h" +/* + * Timer + */ +static void audio_timer (void *opaque) +{ + AudioState *s = opaque; +#if 0 +#define MAX_DIFFS 100 + int64_t now = qemu_get_clock(vm_clock); + static int64_t last = 0; + static float diffs[MAX_DIFFS]; + static int num_diffs; + + if (last == 0) + last = now; + else { + diffs[num_diffs] = (float)((now-last)/1e6); /* last diff in ms */ + if (++num_diffs == MAX_DIFFS) { + double min_diff = 1e6, max_diff = -1e6; + double all_diff = 0.; + int nn; + + for (nn = 0; nn < num_diffs; nn++) { + if (diffs[nn] < min_diff) min_diff = diffs[nn]; + if (diffs[nn] > max_diff) max_diff = diffs[nn]; + all_diff += diffs[nn]; + } + all_diff *= 1.0/num_diffs; + printf("audio timer: min_diff=%6.2g max_diff=%6.2g avg_diff=%6.2g samples=%d\n", + min_diff, max_diff, all_diff, num_diffs); + num_diffs = 0; + } + } + last = now; +#endif + + audio_run ("timer"); + qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + conf.period.ticks); +} + + +static int audio_is_timer_needed (void) +{ + HWVoiceIn *hwi = NULL; + HWVoiceOut *hwo = NULL; + + while ((hwo = audio_pcm_hw_find_any_enabled_out (hwo))) { + if (!hwo->poll_mode) return 1; + } + while ((hwi = audio_pcm_hw_find_any_enabled_in (hwi))) { + if (!hwi->poll_mode) return 1; + } + return 0; +} + +static void audio_reset_timer (void) +{ + AudioState *s = &glob_audio_state; + + if (audio_is_timer_needed ()) { + qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + 1); + } + else { + qemu_del_timer (s->ts); + } +} + +/* + * Public API + */ int AUD_write (SWVoiceOut *sw, void *buf, int size) { int bytes; @@ -1199,9 +1274,7 @@ int AUD_write (SWVoiceOut *sw, void *buf, int size) return 0; } - BEGIN_NOSIGALRM - bytes = sw->hw->pcm_ops->write (sw, buf, size); - END_NOSIGALRM + bytes = sw->hw->pcm_ops->write (sw, buf, size); return bytes; } @@ -1219,9 +1292,7 @@ int AUD_read (SWVoiceIn *sw, void *buf, int size) return 0; } - BEGIN_NOSIGALRM - bytes = sw->hw->pcm_ops->read (sw, buf, size); - END_NOSIGALRM + bytes = sw->hw->pcm_ops->read (sw, buf, size); return bytes; } @@ -1249,9 +1320,8 @@ void AUD_set_active_out (SWVoiceOut *sw, int on) if (!hw->enabled) { hw->enabled = 1; if (s->vm_running) { - BEGIN_NOSIGALRM - hw->pcm_ops->ctl_out (hw, VOICE_ENABLE); - END_NOSIGALRM + hw->pcm_ops->ctl_out (hw, VOICE_ENABLE, conf.try_poll_out); + audio_reset_timer (); } } } @@ -1295,9 +1365,7 @@ void AUD_set_active_in (SWVoiceIn *sw, int on) if (!hw->enabled) { hw->enabled = 1; if (s->vm_running) { - BEGIN_NOSIGALRM - hw->pcm_ops->ctl_in (hw, VOICE_ENABLE); - END_NOSIGALRM + hw->pcm_ops->ctl_in (hw, VOICE_ENABLE, conf.try_poll_in); } } sw->total_hw_samples_acquired = hw->total_samples_captured; @@ -1313,9 +1381,7 @@ void AUD_set_active_in (SWVoiceIn *sw, int on) if (nb_active == 1) { hw->enabled = 0; - BEGIN_NOSIGALRM - hw->pcm_ops->ctl_in (hw, VOICE_DISABLE); - END_NOSIGALRM + hw->pcm_ops->ctl_in (hw, VOICE_DISABLE); } } } @@ -1418,7 +1484,7 @@ static void audio_run_out (AudioState *s) int played; int live, free, nb_live, cleanup_required, prev_rpos; - live = audio_pcm_hw_get_live_out2 (hw, &nb_live); + live = audio_pcm_hw_get_live_out (hw, &nb_live); if (!nb_live) { live = 0; } @@ -1435,9 +1501,7 @@ static void audio_run_out (AudioState *s) #endif hw->enabled = 0; hw->pending_disable = 0; - BEGIN_NOSIGALRM - hw->pcm_ops->ctl_out (hw, VOICE_DISABLE); - END_NOSIGALRM + hw->pcm_ops->ctl_out (hw, VOICE_DISABLE); for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) { sc->sw.active = 0; audio_recalc_and_notify_capture (sc->cap); @@ -1458,9 +1522,7 @@ static void audio_run_out (AudioState *s) } prev_rpos = hw->rpos; - BEGIN_NOSIGALRM - played = hw->pcm_ops->run_out (hw); - END_NOSIGALRM + played = hw->pcm_ops->run_out (hw, live); if (audio_bug (AUDIO_FUNC, hw->rpos >= hw->samples)) { dolog ("hw->rpos=%d hw->samples=%d played=%d\n", hw->rpos, hw->samples, played); @@ -1478,7 +1540,7 @@ static void audio_run_out (AudioState *s) cleanup_required = 0; for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { - if (!sw->active && sw->empty) { + if (!sw->active || sw->empty) { continue; } @@ -1529,9 +1591,7 @@ static void audio_run_in (AudioState *s) SWVoiceIn *sw; int captured, min; - BEGIN_NOSIGALRM - captured = hw->pcm_ops->run_in (hw); - END_NOSIGALRM + captured = hw->pcm_ops->run_in (hw); min = audio_pcm_hw_find_min_in (hw); hw->total_samples_captured += captured - min; @@ -1561,7 +1621,7 @@ static void audio_run_capture (AudioState *s) HWVoiceOut *hw = &cap->hw; SWVoiceOut *sw; - captured = live = audio_pcm_hw_get_live_out (hw); + captured = live = audio_pcm_hw_get_live_out (hw, NULL); rpos = hw->rpos; while (live) { int left = hw->samples - rpos; @@ -1599,89 +1659,126 @@ static void audio_run_capture (AudioState *s) } } -static void audio_timer (void *opaque) +void audio_run (const char *msg) { - AudioState *s = opaque; -#if 0 -#define MAX_DIFFS 1000 - int64_t now = qemu_get_clock(vm_clock); - static int64_t last = 0; - static float diffs[MAX_DIFFS]; - static int num_diffs; + AudioState *s = &glob_audio_state; - if (last == 0) - last = now; - else { - diffs[num_diffs] = (float)((now-last)/1e6); /* last diff in ms */ - if (++num_diffs == MAX_DIFFS) { - double min_diff = 1e6, max_diff = -1e6; - double all_diff = 0.; - int nn; - - for (nn = 0; nn < num_diffs; nn++) { - if (diffs[nn] < min_diff) min_diff = diffs[nn]; - if (diffs[nn] > max_diff) max_diff = diffs[nn]; - all_diff += diffs[nn]; - } - all_diff *= 1.0/num_diffs; - printf("audio timer: min_diff=%6.2g max_diff=%6.2g avg_diff=%6.2g samples=%d\n", - min_diff, max_diff, all_diff, num_diffs); - num_diffs = 0; - } - } - last = now; -#endif audio_run_out (s); audio_run_in (s); audio_run_capture (s); +#ifdef DEBUG_POLL + { + static double prevtime; + double currtime; + struct timeval tv; - qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + conf.period.ticks); + if (gettimeofday (&tv, NULL)) { + perror ("audio_run: gettimeofday"); + return; + } + + currtime = tv.tv_sec + tv.tv_usec * 1e-6; + dolog ("Elapsed since last %s: %f\n", msg, currtime - prevtime); + prevtime = currtime; + } +#endif } static struct audio_option audio_options[] = { /* DAC */ - {"DAC_FIXED_SETTINGS", AUD_OPT_BOOL, &conf.fixed_out.enabled, - "Use fixed settings for host DAC", NULL, 0}, - - {"DAC_FIXED_FREQ", AUD_OPT_INT, &conf.fixed_out.settings.freq, - "Frequency for fixed host DAC", NULL, 0}, - - {"DAC_FIXED_FMT", AUD_OPT_FMT, &conf.fixed_out.settings.fmt, - "Format for fixed host DAC", NULL, 0}, - - {"DAC_FIXED_CHANNELS", AUD_OPT_INT, &conf.fixed_out.settings.nchannels, - "Number of channels for fixed DAC (1 - mono, 2 - stereo)", NULL, 0}, - - {"DAC_VOICES", AUD_OPT_INT, &conf.fixed_out.nb_voices, - "Number of voices for DAC", NULL, 0}, - + { + .name = "DAC_FIXED_SETTINGS", + .tag = AUD_OPT_BOOL, + .valp = &conf.fixed_out.enabled, + .descr = "Use fixed settings for host DAC" + }, + { + .name = "DAC_FIXED_FREQ", + .tag = AUD_OPT_INT, + .valp = &conf.fixed_out.settings.freq, + .descr = "Frequency for fixed host DAC" + }, + { + .name = "DAC_FIXED_FMT", + .tag = AUD_OPT_FMT, + .valp = &conf.fixed_out.settings.fmt, + .descr = "Format for fixed host DAC" + }, + { + .name = "DAC_FIXED_CHANNELS", + .tag = AUD_OPT_INT, + .valp = &conf.fixed_out.settings.nchannels, + .descr = "Number of channels for fixed DAC (1 - mono, 2 - stereo)" + }, + { + .name = "DAC_VOICES", + .tag = AUD_OPT_INT, + .valp = &conf.fixed_out.nb_voices, + .descr = "Number of voices for DAC" + }, + { + .name = "DAC_TRY_POLL", + .tag = AUD_OPT_BOOL, + .valp = &conf.try_poll_out, + .descr = "Attempt using poll mode for DAC" + }, /* ADC */ - {"ADC_FIXED_SETTINGS", AUD_OPT_BOOL, &conf.fixed_in.enabled, - "Use fixed settings for host ADC", NULL, 0}, - - {"ADC_FIXED_FREQ", AUD_OPT_INT, &conf.fixed_in.settings.freq, - "Frequency for fixed host ADC", NULL, 0}, - - {"ADC_FIXED_FMT", AUD_OPT_FMT, &conf.fixed_in.settings.fmt, - "Format for fixed host ADC", NULL, 0}, - - {"ADC_FIXED_CHANNELS", AUD_OPT_INT, &conf.fixed_in.settings.nchannels, - "Number of channels for fixed ADC (1 - mono, 2 - stereo)", NULL, 0}, - - {"ADC_VOICES", AUD_OPT_INT, &conf.fixed_in.nb_voices, - "Number of voices for ADC", NULL, 0}, - + { + .name = "ADC_FIXED_SETTINGS", + .tag = AUD_OPT_BOOL, + .valp = &conf.fixed_in.enabled, + .descr = "Use fixed settings for host ADC" + }, + { + .name = "ADC_FIXED_FREQ", + .tag = AUD_OPT_INT, + .valp = &conf.fixed_in.settings.freq, + .descr = "Frequency for fixed host ADC" + }, + { + .name = "ADC_FIXED_FMT", + .tag = AUD_OPT_FMT, + .valp = &conf.fixed_in.settings.fmt, + .descr = "Format for fixed host ADC" + }, + { + .name = "ADC_FIXED_CHANNELS", + .tag = AUD_OPT_INT, + .valp = &conf.fixed_in.settings.nchannels, + .descr = "Number of channels for fixed ADC (1 - mono, 2 - stereo)" + }, + { + .name = "ADC_VOICES", + .tag = AUD_OPT_INT, + .valp = &conf.fixed_in.nb_voices, + .descr = "Number of voices for ADC" + }, + { + .name = "ADC_TRY_POLL", + .tag = AUD_OPT_BOOL, + .valp = &conf.try_poll_in, + .descr = "Attempt using poll mode for ADC" + }, /* Misc */ - {"TIMER_PERIOD", AUD_OPT_INT, &conf.period.hertz, - "Timer period in HZ (0 - use lowest possible)", NULL, 0}, - - {"PLIVE", AUD_OPT_BOOL, &conf.plive, - "(undocumented)", NULL, 0}, - - {"LOG_TO_MONITOR", AUD_OPT_BOOL, &conf.log_to_monitor, - "print logging messages to monitor instead of stderr", NULL, 0}, - - {NULL, 0, NULL, NULL, NULL, 0} + { + .name = "TIMER_PERIOD", + .tag = AUD_OPT_INT, + .valp = &conf.period.hertz, + .descr = "Timer period in HZ (0 - use lowest possible)" + }, + { + .name = "PLIVE", + .tag = AUD_OPT_BOOL, + .valp = &conf.plive, + .descr = "(undocumented)" + }, + { + .name = "LOG_TO_MONITOR", + .tag = AUD_OPT_BOOL, + .valp = &conf.log_to_monitor, + .descr = "Print logging messages to monitor instead of stderr" + }, + { /* End of list */ } }; static void audio_pp_nb_voices (const char *typ, int nb) @@ -1755,43 +1852,17 @@ void AUD_help (void) ); } -static int audio_driver_init (AudioState *s, struct audio_driver *drv, int out) +static int audio_driver_init (AudioState *s, struct audio_driver *drv) { - void* opaque; - if (drv->options) { audio_process_options (drv->name, drv->options); } + s->drv_opaque = drv->init (); - /* is the driver already initialized ? */ - if (out) { - if (drv == s->drv_in) { - s->drv_out = drv; - s->drv_out_opaque = s->drv_in_opaque; - return 0; - } - } else { - if (drv == s->drv_out) { - s->drv_in = drv; - s->drv_in_opaque = s->drv_out_opaque; - return 0; - } - } - - BEGIN_NOSIGALRM - opaque = drv->init(); - END_NOSIGALRM - - if (opaque != NULL) { + if (s->drv_opaque) { audio_init_nb_voices_out (drv); audio_init_nb_voices_in (drv); - if (out) { - s->drv_out = drv; - s->drv_out_opaque = opaque; - } else { - s->drv_in = drv; - s->drv_in_opaque = opaque; - } + s->drv = drv; return 0; } else { @@ -1809,19 +1880,17 @@ static void audio_vm_change_state_handler (void *opaque, int running, int op = running ? VOICE_ENABLE : VOICE_DISABLE; s->vm_running = running; - BEGIN_NOSIGALRM - while ((hwo = audio_pcm_hw_find_any_enabled_out (hwo))) { - hwo->pcm_ops->ctl_out (hwo, op); - } + while ((hwo = audio_pcm_hw_find_any_enabled_out (hwo))) { + hwo->pcm_ops->ctl_out (hwo, op, conf.try_poll_out); + } - while ((hwi = audio_pcm_hw_find_any_enabled_in (hwi))) { - hwi->pcm_ops->ctl_in (hwi, op); - } - END_NOSIGALRM + while ((hwi = audio_pcm_hw_find_any_enabled_in (hwi))) { + hwi->pcm_ops->ctl_in (hwi, op, conf.try_poll_in); + } + audio_reset_timer (); } -// to make sure audio_atexit() is only called once -static int initialized = 0; +static int initialized; static void audio_atexit (void) { @@ -1832,7 +1901,6 @@ static void audio_atexit (void) if (!initialized) return; initialized = 0; - BEGIN_NOSIGALRM while ((hwo = audio_pcm_hw_find_any_enabled_out (hwo))) { SWVoiceCap *sc; @@ -1854,13 +1922,9 @@ static void audio_atexit (void) hwi->pcm_ops->fini_in (hwi); } - if (s->drv_in) { - s->drv_in->fini (s->drv_in_opaque); + if (s->drv) { + s->drv->fini (s->drv_opaque); } - if (s->drv_out) { - s->drv_out->fini (s->drv_out_opaque); - } - END_NOSIGALRM } static void audio_save (QEMUFile *f, void *opaque) @@ -1881,73 +1945,17 @@ static int audio_load (QEMUFile *f, void *opaque, int version_id) return 0; } -static int -find_audio_driver( AudioState* s, int out ) -{ - int i, done = 0, def; - const char* envname; - const char* drvname; - struct audio_driver* drv = NULL; - const char* drvtype = out ? "output" : "input"; - - envname = out ? "QEMU_AUDIO_OUT_DRV" : "QEMU_AUDIO_IN_DRV"; - drvname = audio_get_conf_str(envname, NULL, &def); - if (drvname == NULL) { - drvname = audio_get_conf_str("QEMU_AUDIO_DRV", NULL, &def); - } - - if (drvname != NULL) { /* look for a specific driver */ - for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) { - if (!strcmp (drvname, drvtab[i]->name)) { - drv = drvtab[i]; - break; - } - } - } - - if (drv != NULL) { - done = !audio_driver_init (s, drv, out); - if (!done) { - dolog ("Could not initialize '%s' %s audio backend, trying default one.\n", - drvname, drvtype); - dolog ("Run with -qemu -audio-help to list available backends\n"); - drv = NULL; - } - } - - if (!drv) { - for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) { - if (drvtab[i]->can_be_default) { - drv = drvtab[i]; - done = !audio_driver_init (s, drv, out); - if (done) - break; - } - } - } - - if (!done) { - drv = &no_audio_driver; - done = !audio_driver_init (s, drv, out); - if (!done) { - /* this should never happen */ - dolog ("Could not initialize audio subsystem\n"); - return -1; - } - dolog ("warning: Could not find suitable audio %s backend\n", drvtype); - } - - if (VERBOSE_CHECK(init)) - dprint("using '%s' audio %s backend", drv->name, drvtype ); - return 0; -} static void audio_init (void) { + size_t i; + int done = 0; + const char *drvname; + VMChangeStateEntry *e; AudioState *s = &glob_audio_state; - if (s->drv_out && s->drv_in) { + if (s->drv) { return; } @@ -1979,13 +1987,45 @@ static void audio_init (void) s->nb_hw_voices_in = 0; } - if ( find_audio_driver (s, 0) != 0 || - find_audio_driver (s, 1) != 0 ) { - qemu_del_timer (s->ts); - return; + { + int def; + drvname = audio_get_conf_str ("QEMU_AUDIO_DRV", NULL, &def); } - VMChangeStateEntry *e; + if (drvname) { + int found = 0; + + for (i = 0; i < ARRAY_SIZE (drvtab); i++) { + if (!strcmp (drvname, drvtab[i]->name)) { + done = !audio_driver_init (s, drvtab[i]); + found = 1; + break; + } + } + + if (!found) { + dolog ("Unknown audio driver `%s'\n", drvname); + dolog ("Run with -audio-help to list available drivers\n"); + } + } + + if (!done) { + for (i = 0; !done && i < ARRAY_SIZE (drvtab); i++) { + if (drvtab[i]->can_be_default) { + done = !audio_driver_init (s, drvtab[i]); + } + } + } + + if (!done) { + done = !audio_driver_init (s, &no_audio_driver); + if (!done) { + hw_error("Could not initialize audio subsystem\n"); + } + else { + dolog ("warning: Using timer based audio emulation\n"); + } + } if (conf.period.hertz <= 0) { if (conf.period.hertz < 0) { @@ -2004,12 +2044,11 @@ static void audio_init (void) dolog ("warning: Could not register change state handler\n" "(Audio can continue looping even after stopping the VM)\n"); } - initialized = 1; QLIST_INIT (&s->card_head); register_savevm ("audio", 0, 1, audio_save, audio_load, s); - qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + conf.period.ticks); + audio_reset_timer(); } void AUD_register_card (const char *name, QEMUSoundCard *card) @@ -2026,11 +2065,6 @@ void AUD_remove_card (QEMUSoundCard *card) qemu_free (card->name); } -// this was added to work around a deadlock in SDL when quitting -void AUD_cleanup() -{ - audio_atexit(); -} CaptureVoiceOut *AUD_add_capture ( struct audsettings *as, diff --git a/audio/audio_int.h b/audio/audio_int.h index a78f394..c6afe81 100644 --- a/audio/audio_int.h +++ b/audio/audio_int.h @@ -70,6 +70,7 @@ typedef struct SWVoiceCap SWVoiceCap; typedef struct HWVoiceOut { int enabled; + int poll_mode; int pending_disable; struct audio_pcm_info info; @@ -89,6 +90,7 @@ typedef struct HWVoiceOut { typedef struct HWVoiceIn { int enabled; + int poll_mode; struct audio_pcm_info info; t_sample *conv; @@ -155,7 +157,7 @@ struct audio_driver { struct audio_pcm_ops { int (*init_out)(HWVoiceOut *hw, struct audsettings *as); void (*fini_out)(HWVoiceOut *hw); - int (*run_out) (HWVoiceOut *hw); + int (*run_out) (HWVoiceOut *hw, int live); int (*write) (SWVoiceOut *sw, void *buf, int size); int (*ctl_out) (HWVoiceOut *hw, int cmd, ...); @@ -186,10 +188,8 @@ struct SWVoiceCap { }; struct AudioState { - struct audio_driver* drv_in; - void* drv_in_opaque; - struct audio_driver* drv_out; - void* drv_out_opaque; + struct audio_driver *drv; + void *drv_opaque; QEMUTimer *ts; QLIST_HEAD (card_listhead, QEMUSoundCard) card_head; @@ -203,6 +203,7 @@ struct AudioState { extern struct audio_driver no_audio_driver; extern struct audio_driver oss_audio_driver; +extern struct audio_driver sdl_audio_driver; extern struct audio_driver win_audio_driver; extern struct audio_driver wav_audio_driver; extern struct audio_driver fmod_audio_driver; @@ -211,6 +212,7 @@ extern struct audio_driver coreaudio_audio_driver; extern struct audio_driver dsound_audio_driver; extern struct audio_driver esd_audio_driver; extern struct audio_driver pa_audio_driver; +extern struct audio_driver winwave_audio_driver; extern struct mixeng_volume nominal_volume; void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as); @@ -220,12 +222,15 @@ int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int len); int audio_pcm_hw_get_live_in (HWVoiceIn *hw); int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int len); -int audio_pcm_hw_get_live_out (HWVoiceOut *hw); -int audio_pcm_hw_get_live_out2 (HWVoiceOut *hw, int *nb_live); + +int audio_pcm_hw_clip_out (HWVoiceOut *hw, void *pcm_buf, + int live, int pending); int audio_bug (const char *funcname, int cond); void *audio_calloc (const char *funcname, int nmemb, size_t size); +void audio_run (const char *msg); + #define VOICE_ENABLE 1 #define VOICE_DISABLE 2 diff --git a/audio/audio_pt_int.c b/audio/audio_pt_int.c index e889a98..f15cc70 100644 --- a/audio/audio_pt_int.c +++ b/audio/audio_pt_int.c @@ -6,6 +6,8 @@ #include "audio_int.h" #include "audio_pt_int.h" +#include <signal.h> + static void logerr (struct audio_pt *pt, int err, const char *fmt, ...) { va_list ap; @@ -23,9 +25,16 @@ int audio_pt_init (struct audio_pt *p, void *(*func) (void *), { int err, err2; const char *efunc; + sigset_t set, old_set; p->drv = drv; + err = sigfillset (&set); + if (err) { + logerr (p, errno, "%s(%s): sigfillset failed", cap, AUDIO_FUNC); + return -1; + } + err = pthread_mutex_init (&p->mutex, NULL); if (err) { efunc = "pthread_mutex_init"; @@ -38,7 +47,23 @@ int audio_pt_init (struct audio_pt *p, void *(*func) (void *), goto err1; } + err = pthread_sigmask (SIG_BLOCK, &set, &old_set); + if (err) { + efunc = "pthread_sigmask"; + goto err2; + } + err = pthread_create (&p->thread, NULL, func, opaque); + + err2 = pthread_sigmask (SIG_SETMASK, &old_set, NULL); + if (err2) { + logerr (p, err2, "%s(%s): pthread_sigmask (restore) failed", + cap, AUDIO_FUNC); + /* We have failed to restore original signal mask, all bets are off, + so terminate the process */ + exit (EXIT_FAILURE); + } + if (err) { efunc = "pthread_create"; goto err2; diff --git a/audio/audio_template.h b/audio/audio_template.h index 9f75f19..c0d07e5 100644 --- a/audio/audio_template.h +++ b/audio/audio_template.h @@ -245,8 +245,8 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as) { HW *hw; AudioState *s = &glob_audio_state; - struct audio_driver *drv = glue(s->drv_, TYPE); - int err; + struct audio_driver *drv = s->drv; + int err; if (!glue (s->nb_hw_voices_, TYPE)) { return NULL; @@ -433,7 +433,7 @@ SW *glue (AUD_open_, TYPE) ( goto fail; } - if (audio_bug (AUDIO_FUNC, !glue (s->drv_, TYPE))) { + if (audio_bug (AUDIO_FUNC, !s->drv)) { dolog ("Can not open `%s' (no host audio driver)\n", name); goto fail; } @@ -452,9 +452,9 @@ SW *glue (AUD_open_, TYPE) ( SW_NAME (sw), sw->info.freq, sw->info.bits, sw->info.nchannels); dolog ("New %s freq %d, bits %d, channels %d\n", name, - freq, - (fmt == AUD_FMT_S16 || fmt == AUD_FMT_U16) ? 16 : 8, - nchannels); + as->freq, + (as->fmt == AUD_FMT_S16 || as->fmt == AUD_FMT_U16) ? 16 : 8, + as->nchannels); #endif if (live) { @@ -496,6 +496,9 @@ SW *glue (AUD_open_, TYPE) ( sw->vol = nominal_volume; sw->callback.fn = callback_fn; sw->callback.opaque = callback_opaque; +#ifdef DAC + sw->empty = 1; +#endif #ifdef DAC if (live) { @@ -548,7 +551,7 @@ uint64_t glue (AUD_get_elapsed_usec_, TYPE) (SW *sw, QEMUAudioTimeStamp *ts) cur_ts = sw->hw->ts_helper; old_ts = ts->old_ts; - /* dolog ("cur %lld old %lld\n", cur_ts, old_ts); */ + /* dolog ("cur %" PRId64 " old %" PRId64 "\n", cur_ts, old_ts); */ if (cur_ts >= old_ts) { delta = cur_ts - old_ts; diff --git a/audio/audio_win_int.c b/audio/audio_win_int.c new file mode 100644 index 0000000..5869052 --- /dev/null +++ b/audio/audio_win_int.c @@ -0,0 +1,108 @@ +/* public domain */ + +#include "qemu-common.h" +#include "audio.h" + +#define AUDIO_CAP "win-int" +#include <windows.h> +#include <mmsystem.h> + +#include "audio.h" +#include "audio_int.h" +#include "audio_win_int.h" + +int waveformat_from_audio_settings (WAVEFORMATEX *wfx, + struct audsettings *as) +{ + memset (wfx, 0, sizeof (*wfx)); + + wfx->wFormatTag = WAVE_FORMAT_PCM; + wfx->nChannels = as->nchannels; + wfx->nSamplesPerSec = as->freq; + wfx->nAvgBytesPerSec = as->freq << (as->nchannels == 2); + wfx->nBlockAlign = 1 << (as->nchannels == 2); + wfx->cbSize = 0; + + switch (as->fmt) { + case AUD_FMT_S8: + case AUD_FMT_U8: + wfx->wBitsPerSample = 8; + break; + + case AUD_FMT_S16: + case AUD_FMT_U16: + wfx->wBitsPerSample = 16; + wfx->nAvgBytesPerSec <<= 1; + wfx->nBlockAlign <<= 1; + break; + + case AUD_FMT_S32: + case AUD_FMT_U32: + wfx->wBitsPerSample = 32; + wfx->nAvgBytesPerSec <<= 2; + wfx->nBlockAlign <<= 2; + break; + + default: + dolog ("Internal logic error: Bad audio format %d\n", as->freq); + return -1; + } + + return 0; +} + +int waveformat_to_audio_settings (WAVEFORMATEX *wfx, + struct audsettings *as) +{ + if (wfx->wFormatTag != WAVE_FORMAT_PCM) { + dolog ("Invalid wave format, tag is not PCM, but %d\n", + wfx->wFormatTag); + return -1; + } + + if (!wfx->nSamplesPerSec) { + dolog ("Invalid wave format, frequency is zero\n"); + return -1; + } + as->freq = wfx->nSamplesPerSec; + + switch (wfx->nChannels) { + case 1: + as->nchannels = 1; + break; + + case 2: + as->nchannels = 2; + break; + + default: + dolog ( + "Invalid wave format, number of channels is not 1 or 2, but %d\n", + wfx->nChannels + ); + return -1; + } + + switch (wfx->wBitsPerSample) { + case 8: + as->fmt = AUD_FMT_U8; + break; + + case 16: + as->fmt = AUD_FMT_S16; + break; + + case 32: + as->fmt = AUD_FMT_S32; + break; + + default: + dolog ("Invalid wave format, bits per sample is not " + "8, 16 or 32, but %d\n", + wfx->wBitsPerSample); + return -1; + } + + return 0; +} + diff --git a/audio/audio_win_int.h b/audio/audio_win_int.h new file mode 100644 index 0000000..fa5b3cb --- /dev/null +++ b/audio/audio_win_int.h @@ -0,0 +1,10 @@ +#ifndef AUDIO_WIN_INT_H +#define AUDIO_WIN_INT_H + +int waveformat_from_audio_settings (WAVEFORMATEX *wfx, + struct audsettings *as); + +int waveformat_to_audio_settings (WAVEFORMATEX *wfx, + struct audsettings *as); + +#endif /* AUDIO_WIN_INT_H */ diff --git a/audio/coreaudio.c b/audio/coreaudio.c index 8abe0c4..26631df 100644 --- a/audio/coreaudio.c +++ b/audio/coreaudio.c @@ -27,13 +27,12 @@ #include <string.h> /* strerror */ #include <pthread.h> /* pthread_X */ +#include "qemu-common.h" #include "audio.h" #define AUDIO_CAP "coreaudio" #include "audio_int.h" -#define ENABLE_IN 1 - #if 0 # define D(...) fprintf(stderr, __VA_ARGS__) #else @@ -152,11 +151,6 @@ static void GCC_FMT_ATTR (3, 4) coreaudio_logerr2 ( coreaudio_logstatus (status); } -static void coreaudio_atexit (void) -{ - conf.isAtexit = 1; -} - /***************************************************************************************/ /***************************************************************************************/ /*** ***/ @@ -177,9 +171,8 @@ typedef struct coreAudioVoice { int pos; } coreaudioVoice; - static inline UInt32 -coreaudio_voice_isPlaying (coreaudioVoice* core) +coreaudio_voice_isPlaying (coreaudioVoice *core) { OSStatus status; UInt32 result = 0; @@ -194,8 +187,12 @@ coreaudio_voice_isPlaying (coreaudioVoice* core) return result; } -static int -coreaudio_voice_lock (coreaudioVoice* core, const char *fn_name) +static void coreaudio_atexit (void) +{ + conf.isAtexit = 1; +} + +static int coreaudio_voice_lock (coreaudioVoice *core, const char *fn_name) { int err; @@ -209,7 +206,7 @@ coreaudio_voice_lock (coreaudioVoice* core, const char *fn_name) } static int -coreaudio_voice_unlock (coreaudioVoice* core, const char *fn_name) +coreaudio_voice_unlock (coreaudioVoice *core, const char *fn_name) { int err; @@ -465,18 +462,15 @@ typedef struct coreaudioVoiceOut { #define CORE_OUT(hw) ((coreaudioVoiceOut*)(hw))->core -static int -coreaudio_run_out (HWVoiceOut *hw) +static int coreaudio_run_out (HWVoiceOut *hw, int live) { - int live, decr; - coreaudioVoice *core = CORE_OUT(hw); + int decr; + coreaudioVoice *core = CORE_OUT(hw); if (coreaudio_voice_lock (core, "coreaudio_run_out")) { return 0; } - live = audio_pcm_hw_get_live_out (hw); - if (core->decr > live) { ldebug ("core->decr %d live %d core->live %d\n", core->decr, @@ -484,19 +478,18 @@ coreaudio_run_out (HWVoiceOut *hw) core->live); } - decr = audio_MIN (core->decr, live); + decr = audio_MIN (core->decr, live); core->decr -= decr; - core->live = live - decr; - hw->rpos = core->pos; + + core->live = live - decr; + hw->rpos = core->pos; coreaudio_voice_unlock (core, "coreaudio_run_out"); return decr; } - /* callback to feed audiooutput buffer */ -static OSStatus -audioOutDeviceIOProc( +static OSStatus audioOutDeviceIOProc( AudioDeviceID inDevice, const AudioTimeStamp* inNow, const AudioBufferList* inInputData, @@ -555,27 +548,25 @@ audioOutDeviceIOProc( rpos = (rpos + frameCount) % hw->samples; core->decr += frameCount; - core->pos = rpos; + core->pos = rpos; coreaudio_voice_unlock (core, "audioDeviceIOProc"); return 0; } -static int -coreaudio_write (SWVoiceOut *sw, void *buf, int len) +static int coreaudio_write (SWVoiceOut *sw, void *buf, int len) { return audio_pcm_sw_write (sw, buf, len); } -static int -coreaudio_init_out (HWVoiceOut *hw, struct audsettings *as) +static int coreaudio_init_out (HWVoiceOut *hw, struct audsettings *as) { coreaudioVoice* core = CORE_OUT(hw); - int err; + int err; audio_pcm_init_info (&hw->info, as); - err = coreaudio_voice_init( core, as, conf.out_buffer_frames, audioOutDeviceIOProc, hw, 0 ); + err = coreaudio_voice_init (core, as, conf.out_buffer_frames, audioOutDeviceIOProc, hw, 0); if (err < 0) return err; @@ -583,21 +574,19 @@ coreaudio_init_out (HWVoiceOut *hw, struct audsettings *as) return 0; } -static void -coreaudio_fini_out (HWVoiceOut *hw) +static void coreaudio_fini_out (HWVoiceOut *hw) { + coreaudioVoice *core = CORE_OUT(hw); - coreaudioVoice* core = CORE_OUT(hw); - - coreaudio_voice_fini(core); + coreaudio_voice_fini (core); } static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...) { - coreaudioVoice* core = CORE_OUT(hw); + coreaudioVoice *core = CORE_OUT(hw); - return coreaudio_voice_ctl(core, cmd); + return coreaudio_voice_ctl (core, cmd); } /***************************************************************************************/ @@ -615,15 +604,14 @@ typedef struct coreaudioVoiceIn { coreaudioVoice core[1]; } coreaudioVoiceIn; -#define CORE_IN(hw) ((coreaudioVoiceIn*)(hw))->core +#define CORE_IN(hw) ((coreaudioVoiceIn *) (hw))->core -static int -coreaudio_run_in (HWVoiceIn *hw) +static int coreaudio_run_in (HWVoiceIn *hw, int live) { int decr; - coreaudioVoice *core = CORE_IN(hw); + coreaudioVoice *core = CORE_IN(hw); if (coreaudio_voice_lock (core, "coreaudio_run_in")) { return 0; @@ -639,8 +627,7 @@ coreaudio_run_in (HWVoiceIn *hw) /* callback to feed audiooutput buffer */ -static OSStatus -audioInDeviceIOProc( +static OSStatus audioInDeviceIOProc( AudioDeviceID inDevice, const AudioTimeStamp* inNow, const AudioBufferList* inInputData, @@ -712,7 +699,7 @@ audioInDeviceIOProc( static int coreaudio_read (SWVoiceIn *sw, void *buf, int len) { - int result = audio_pcm_sw_read(sw, buf, len); + int result = audio_pcm_sw_read (sw, buf, len); D("%s: audio_pcm_sw_read(%d) returned %d\n", __FUNCTION__, len, result); return result; } @@ -725,7 +712,7 @@ coreaudio_init_in (HWVoiceIn *hw, struct audsettings *as) audio_pcm_init_info (&hw->info, as); - err = coreaudio_voice_init( core, as, conf.in_buffer_frames, audioInDeviceIOProc, hw, 1 ); + err = coreaudio_voice_init (core, as, conf.in_buffer_frames, audioInDeviceIOProc, hw, 1); if (err < 0) { return err; } @@ -751,71 +738,69 @@ coreaudio_ctl_in (HWVoiceIn *hw, int cmd, ...) return coreaudio_voice_ctl(core, cmd); } -static void* -coreaudio_audio_init (void) +static void *coreaudio_audio_init (void) { atexit(coreaudio_atexit); return &coreaudio_audio_init; } -static void -coreaudio_audio_fini (void *opaque) +static void coreaudio_audio_fini (void *opaque) { (void) opaque; } static struct audio_option coreaudio_options[] = { - {"OUT_BUFFER_SIZE", AUD_OPT_INT, &conf.out_buffer_frames, - "Size of the output buffer in frames", NULL, 0}, - {"OUT_BUFFER_COUNT", AUD_OPT_INT, &conf.out_nbuffers, - "Number of output buffers", NULL, 0}, - {"IN_BUFFER_SIZE", AUD_OPT_INT, &conf.in_buffer_frames, - "Size of the input buffer in frames", NULL, 0}, - {"IN_BUFFER_COUNT", AUD_OPT_INT, &conf.in_nbuffers, - "Number of input buffers", NULL, 0}, - {NULL, 0, NULL, NULL, NULL, 0} + { + .name = "OUT_BUFFER_SIZE", + .tag = AUD_OPT_INT, + .valp = &conf.out_buffer_frames, + .descr = "Size of the output buffer in frames" + }, + { + .name = "OUT_BUFFER_COUNT", + .tag = AUD_OPT_INT, + .valp = &conf.out_nbuffers, + .descr = "Number of output buffers" + }, + { + .name = "IN_BUFFER_SIZE", + .tag = AUD_OPT_INT, + .valp = &conf.in_buffer_frames, + .descr = "Size of the input buffer in frames" + }, + { + .name = "IN_BUFFER_COUNT", + .tag = AUD_OPT_INT, + .valp = &conf.in_nbuffers, + .descr = "Number of input buffers" + }, + { /* End of list */ } }; static struct audio_pcm_ops coreaudio_pcm_ops = { - coreaudio_init_out, - coreaudio_fini_out, - coreaudio_run_out, - coreaudio_write, - coreaudio_ctl_out, - -#if ENABLE_IN - coreaudio_init_in, - coreaudio_fini_in, - coreaudio_run_in, - coreaudio_read, - coreaudio_ctl_in -#else - NULL, - NULL, - NULL, - NULL, - NULL -#endif + .init_out = coreaudio_init_out, + .fini_out = coreaudio_fini_out, + .run_out = coreaudio_run_out, + .write = coreaudio_write, + .ctl_out = coreaudio_ctl_out + + .init_in = coreaudio_init_in, + .fini_in = coreaudio_fini_in, + .run_in = coreaudio_run_in, + .read = coreaudio_read, + .ctl_in = coreaudio_ctl_in }; struct audio_driver coreaudio_audio_driver = { - INIT_FIELD (name = ) "coreaudio", - INIT_FIELD (descr = ) - "CoreAudio (developer.apple.com/audio/coreaudio.html)", - INIT_FIELD (options = ) coreaudio_options, - INIT_FIELD (init = ) coreaudio_audio_init, - INIT_FIELD (fini = ) coreaudio_audio_fini, - INIT_FIELD (pcm_ops = ) &coreaudio_pcm_ops, - INIT_FIELD (can_be_default = ) 1, -#if ENABLE_IN - INIT_FIELD (max_voices_out = ) 1, - INIT_FIELD (max_voices_in = ) 1, - INIT_FIELD (voice_size_out = ) sizeof (coreaudioVoiceOut), - INIT_FIELD (voice_size_in = ) sizeof (coreaudioVoiceIn), -#else - INIT_FIELD (max_voices_out = ) 1, - INIT_FIELD (max_voices_in = ) 0, - INIT_FIELD (voice_size_out = ) sizeof (coreaudioVoiceOut), - INIT_FIELD (voice_size_in = ) 0, -#endif + .name = "coreaudio", + .descr = "CoreAudio http://developer.apple.com/audio/coreaudio.html", + .options = coreaudio_options, + .init = coreaudio_audio_init, + .fini = coreaudio_audio_fini, + .pcm_ops = &coreaudio_pcm_ops, + .can_be_default = 1, + .max_voices_out = 1, + .max_voices_in = 1, + .voice_size_out = sizeof (coreaudioVoiceOut), + .voice_size_in = sizeof (coreaudioVoiceIn), }; diff --git a/audio/dsound_template.h b/audio/dsound_template.h index 9cc0b9d..8b37d16 100644 --- a/audio/dsound_template.h +++ b/audio/dsound_template.h @@ -174,16 +174,16 @@ static void dsound_fini_out (HWVoiceOut *hw) } #ifdef DSBTYPE_IN -static int dsound_init_in (HWVoiceIn *hw, audsettings_t *as) +static int dsound_init_in (HWVoiceIn *hw, struct audsettings *as) #else -static int dsound_init_out (HWVoiceOut *hw, audsettings_t *as) +static int dsound_init_out (HWVoiceOut *hw, struct audsettings *as) #endif { int err; HRESULT hr; dsound *s = &glob_dsound; WAVEFORMATEX wfx; - audsettings_t obt_as; + struct audsettings obt_as; #ifdef DSBTYPE_IN const char *typ = "ADC"; DSoundVoiceIn *ds = (DSoundVoiceIn *) hw; @@ -285,7 +285,9 @@ static int dsound_init_out (HWVoiceOut *hw, audsettings_t *as) } #undef NAME +#undef NAME2 #undef TYPE #undef IFACE #undef BUFPTR #undef FIELD +#undef FIELD2 diff --git a/audio/dsoundaudio.c b/audio/dsoundaudio.c index 8284067..e547955 100644 --- a/audio/dsoundaudio.c +++ b/audio/dsoundaudio.c @@ -26,17 +26,19 @@ * SEAL 1.07 by Carlos 'pel' Hasan was used as documentation */ +#include "qemu-common.h" #include "audio.h" #define AUDIO_CAP "dsound" #include "audio_int.h" -#define WIN32_LEAN_AND_MEAN #include <windows.h> #include <mmsystem.h> #include <objbase.h> #include <dsound.h> +#include "audio_win_int.h" + /* #define DEBUG_DSOUND */ static struct { @@ -46,28 +48,26 @@ static struct { int set_primary; int bufsize_in; int bufsize_out; - audsettings_t settings; + struct audsettings settings; int latency_millis; } conf = { - 1, - 1, - 1, - 0, - 16384, - 16384, - { - 44100, - 2, - AUD_FMT_S16 - }, - 10 + .lock_retries = 1, + .restore_retries = 1, + .getstatus_retries = 1, + .set_primary = 0, + .bufsize_in = 16384, + .bufsize_out = 16384, + .settings.freq = 44100, + .settings.nchannels = 2, + .settings.fmt = AUD_FMT_S16, + .latency_millis = 10 }; typedef struct { LPDIRECTSOUND dsound; LPDIRECTSOUNDCAPTURE dsound_capture; LPDIRECTSOUNDBUFFER dsound_primary_buffer; - audsettings_t settings; + struct audsettings settings; } dsound; static dsound glob_dsound; @@ -306,99 +306,6 @@ static int dsound_restore_out (LPDIRECTSOUNDBUFFER dsb) return -1; } -static int waveformat_from_audio_settings (WAVEFORMATEX *wfx, audsettings_t *as) -{ - memset (wfx, 0, sizeof (*wfx)); - - wfx->wFormatTag = WAVE_FORMAT_PCM; - wfx->nChannels = as->nchannels; - wfx->nSamplesPerSec = as->freq; - wfx->nAvgBytesPerSec = as->freq << (as->nchannels == 2); - wfx->nBlockAlign = 1 << (as->nchannels == 2); - wfx->cbSize = 0; - - switch (as->fmt) { - case AUD_FMT_S8: - case AUD_FMT_U8: - wfx->wBitsPerSample = 8; - break; - - case AUD_FMT_S16: - case AUD_FMT_U16: - wfx->wBitsPerSample = 16; - wfx->nAvgBytesPerSec <<= 1; - wfx->nBlockAlign <<= 1; - break; - - case AUD_FMT_S32: - case AUD_FMT_U32: - wfx->wBitsPerSample = 32; - wfx->nAvgBytesPerSec <<= 2; - wfx->nBlockAlign <<= 2; - break; - - default: - dolog ("Internal logic error: Bad audio format %d\n", as->freq); - return -1; - } - - return 0; -} - -static int waveformat_to_audio_settings (WAVEFORMATEX *wfx, audsettings_t *as) -{ - if (wfx->wFormatTag != WAVE_FORMAT_PCM) { - dolog ("Invalid wave format, tag is not PCM, but %d\n", - wfx->wFormatTag); - return -1; - } - - if (!wfx->nSamplesPerSec) { - dolog ("Invalid wave format, frequency is zero\n"); - return -1; - } - as->freq = wfx->nSamplesPerSec; - - switch (wfx->nChannels) { - case 1: - as->nchannels = 1; - break; - - case 2: - as->nchannels = 2; - break; - - default: - dolog ( - "Invalid wave format, number of channels is not 1 or 2, but %d\n", - wfx->nChannels - ); - return -1; - } - - switch (wfx->wBitsPerSample) { - case 8: - as->fmt = AUD_FMT_U8; - break; - - case 16: - as->fmt = AUD_FMT_S16; - break; - - case 32: - as->fmt = AUD_FMT_S32; - break; - - default: - dolog ("Invalid wave format, bits per sample is not " - "8, 16 or 32, but %d\n", - wfx->wBitsPerSample); - return -1; - } - - return 0; -} - #include "dsound_template.h" #define DSBTYPE_IN #include "dsound_template.h" @@ -447,8 +354,8 @@ static void dsound_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len) int src_len1 = dst_len; int src_len2 = 0; int pos = hw->rpos + dst_len; - st_sample_t *src1 = hw->mix_buf + hw->rpos; - st_sample_t *src2 = NULL; + struct st_sample *src1 = hw->mix_buf + hw->rpos; + struct st_sample *src2 = NULL; if (pos > hw->samples) { src_len1 = hw->samples - hw->rpos; @@ -658,13 +565,13 @@ static int dsound_write (SWVoiceOut *sw, void *buf, int len) return audio_pcm_sw_write (sw, buf, len); } -static int dsound_run_out (HWVoiceOut *hw) +static int dsound_run_out (HWVoiceOut *hw, int live) { int err; HRESULT hr; DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; LPDIRECTSOUNDBUFFER dsb = ds->dsound_buffer; - int live, len, hwshift; + int len, hwshift; DWORD blen1, blen2; DWORD len1, len2; DWORD decr; @@ -680,8 +587,6 @@ static int dsound_run_out (HWVoiceOut *hw) hwshift = hw->info.shift; bufsize = hw->samples << hwshift; - live = audio_pcm_hw_get_live_out (hw); - hr = IDirectSoundBuffer_GetCurrentPosition ( dsb, &ppos, @@ -1033,54 +938,93 @@ static void *dsound_audio_init (void) } static struct audio_option dsound_options[] = { - {"LOCK_RETRIES", AUD_OPT_INT, &conf.lock_retries, - "Number of times to attempt locking the buffer", NULL, 0}, - {"RESTOURE_RETRIES", AUD_OPT_INT, &conf.restore_retries, - "Number of times to attempt restoring the buffer", NULL, 0}, - {"GETSTATUS_RETRIES", AUD_OPT_INT, &conf.getstatus_retries, - "Number of times to attempt getting status of the buffer", NULL, 0}, - {"SET_PRIMARY", AUD_OPT_BOOL, &conf.set_primary, - "Set the parameters of primary buffer", NULL, 0}, - {"LATENCY_MILLIS", AUD_OPT_INT, &conf.latency_millis, - "(undocumented)", NULL, 0}, - {"PRIMARY_FREQ", AUD_OPT_INT, &conf.settings.freq, - "Primary buffer frequency", NULL, 0}, - {"PRIMARY_CHANNELS", AUD_OPT_INT, &conf.settings.nchannels, - "Primary buffer number of channels (1 - mono, 2 - stereo)", NULL, 0}, - {"PRIMARY_FMT", AUD_OPT_FMT, &conf.settings.fmt, - "Primary buffer format", NULL, 0}, - {"BUFSIZE_OUT", AUD_OPT_INT, &conf.bufsize_out, - "(undocumented)", NULL, 0}, - {"BUFSIZE_IN", AUD_OPT_INT, &conf.bufsize_in, - "(undocumented)", NULL, 0}, - {NULL, 0, NULL, NULL, NULL, 0} + { + .name = "LOCK_RETRIES", + .tag = AUD_OPT_INT, + .valp = &conf.lock_retries, + .descr = "Number of times to attempt locking the buffer" + }, + { + .name = "RESTOURE_RETRIES", + .tag = AUD_OPT_INT, + .valp = &conf.restore_retries, + .descr = "Number of times to attempt restoring the buffer" + }, + { + .name = "GETSTATUS_RETRIES", + .tag = AUD_OPT_INT, + .valp = &conf.getstatus_retries, + .descr = "Number of times to attempt getting status of the buffer" + }, + { + .name = "SET_PRIMARY", + .tag = AUD_OPT_BOOL, + .valp = &conf.set_primary, + .descr = "Set the parameters of primary buffer" + }, + { + .name = "LATENCY_MILLIS", + .tag = AUD_OPT_INT, + .valp = &conf.latency_millis, + .descr = "(undocumented)" + }, + { + .name = "PRIMARY_FREQ", + .tag = AUD_OPT_INT, + .valp = &conf.settings.freq, + .descr = "Primary buffer frequency" + }, + { + .name = "PRIMARY_CHANNELS", + .tag = AUD_OPT_INT, + .valp = &conf.settings.nchannels, + .descr = "Primary buffer number of channels (1 - mono, 2 - stereo)" + }, + { + .name = "PRIMARY_FMT", + .tag = AUD_OPT_FMT, + .valp = &conf.settings.fmt, + .descr = "Primary buffer format" + }, + { + .name = "BUFSIZE_OUT", + .tag = AUD_OPT_INT, + .valp = &conf.bufsize_out, + .descr = "(undocumented)" + }, + { + .name = "BUFSIZE_IN", + .tag = AUD_OPT_INT, + .valp = &conf.bufsize_in, + .descr = "(undocumented)" + }, + { /* End of list */ } }; static struct audio_pcm_ops dsound_pcm_ops = { - dsound_init_out, - dsound_fini_out, - dsound_run_out, - dsound_write, - dsound_ctl_out, - - dsound_init_in, - dsound_fini_in, - dsound_run_in, - dsound_read, - dsound_ctl_in + .init_out = dsound_init_out, + .fini_out = dsound_fini_out, + .run_out = dsound_run_out, + .write = dsound_write, + .ctl_out = dsound_ctl_out, + + .init_in = dsound_init_in, + .fini_in = dsound_fini_in, + .run_in = dsound_run_in, + .read = dsound_read, + .ctl_in = dsound_ctl_in }; struct audio_driver dsound_audio_driver = { - INIT_FIELD (name = ) "dsound", - INIT_FIELD (descr = ) - "DirectSound audio (www.wikipedia.org/wiki/DirectSound)", - INIT_FIELD (options = ) dsound_options, - INIT_FIELD (init = ) dsound_audio_init, - INIT_FIELD (fini = ) dsound_audio_fini, - INIT_FIELD (pcm_ops = ) &dsound_pcm_ops, - INIT_FIELD (can_be_default = ) 1, - INIT_FIELD (max_voices_out = ) INT_MAX, - INIT_FIELD (max_voices_in = ) 1, - INIT_FIELD (voice_size_out = ) sizeof (DSoundVoiceOut), - INIT_FIELD (voice_size_in = ) sizeof (DSoundVoiceIn) + .name = "dsound", + .descr = "DirectSound http://wikipedia.org/wiki/DirectSound", + .options = dsound_options, + .init = dsound_audio_init, + .fini = dsound_audio_fini, + .pcm_ops = &dsound_pcm_ops, + .can_be_default = 1, + .max_voices_out = INT_MAX, + .max_voices_in = 1, + .voice_size_out = sizeof (DSoundVoiceOut), + .voice_size_in = sizeof (DSoundVoiceIn) }; diff --git a/audio/esdaudio.c b/audio/esdaudio.c index 1d72125..84cbcb4 100644 --- a/audio/esdaudio.c +++ b/audio/esdaudio.c @@ -25,11 +25,10 @@ #include <esd.h> #include "qemu-common.h" #include "audio.h" -#include <signal.h> #define AUDIO_CAP "esd" #include "audio_int.h" -#include <dlfcn.h> +#include "audio_pt_int.h" #include "qemu_debug.h" @@ -51,6 +50,23 @@ #define STRINGIFY_(x) #x #define STRINGIFY(x) STRINGIFY_(x) +#include <dlfcn.h> +/* link dynamically to the libesd.so */ + +#define DYNLINK_FUNCTIONS \ + DYNLINK_FUNC(int,esd_play_stream,(esd_format_t,int,const char*,const char*)) \ + DYNLINK_FUNC(int,esd_record_stream,(esd_format_t,int,const char*,const char*)) \ + DYNLINK_FUNC(int,esd_open_sound,( const char *host )) \ + DYNLINK_FUNC(int,esd_close,(int)) \ + +#define DYNLINK_FUNCTIONS_INIT \ + esd_dynlink_init + +#include "dynlink.h" + +static void* esd_lib; + + typedef struct { HWVoiceOut hw; int done; @@ -59,6 +75,7 @@ typedef struct { int rpos; void *pcm_buf; int fd; + struct audio_pt pt; } ESDVoiceOut; typedef struct { @@ -69,6 +86,7 @@ typedef struct { int wpos; void *pcm_buf; int fd; + struct audio_pt pt; } ESDVoiceIn; static struct { @@ -77,27 +95,10 @@ static struct { char *dac_host; char *adc_host; } conf = { - 1024, - 2, - NULL, - NULL + .samples = 1024, + .divisor = 2, }; -/* link dynamically to the libesd.so */ - -#define DYNLINK_FUNCTIONS \ - DYNLINK_FUNC(int,esd_play_stream,(esd_format_t,int,const char*,const char*)) \ - DYNLINK_FUNC(int,esd_record_stream,(esd_format_t,int,const char*,const char*)) \ - DYNLINK_FUNC(int,esd_open_sound,( const char *host )) \ - DYNLINK_FUNC(int,esd_close,(int)) \ - -#define DYNLINK_FUNCTIONS_INIT \ - esd_dynlink_init - -#include "dynlink.h" - -static void* esd_lib; - static void GCC_FMT_ATTR (2, 3) qesd_logerr (int err, const char *fmt, ...) { va_list ap; @@ -109,50 +110,111 @@ static void GCC_FMT_ATTR (2, 3) qesd_logerr (int err, const char *fmt, ...) AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err)); } -static int qesd_run_out (HWVoiceOut *hw) +/* playback */ +static void *qesd_thread_out (void *arg) { - ESDVoiceOut *esd = (ESDVoiceOut *) hw; - int liveSamples, totalSamples; - int rpos, nwrite, writeSamples, writeBytes; - - liveSamples = audio_pcm_hw_get_live_out (hw); - rpos = hw->rpos; - totalSamples = 0; - - while (liveSamples > 0) { - int chunkSamples = audio_MIN (liveSamples, hw->samples - rpos); - int chunkBytes = chunkSamples << hw->info.shift; - struct st_sample *src = hw->mix_buf + rpos; - - hw->clip (esd->pcm_buf, src, chunkSamples); - - AGAIN: - nwrite = write (esd->fd, esd->pcm_buf, chunkBytes); - if (nwrite == -1) { - if (errno == EINTR) - goto AGAIN; - if (errno == EAGAIN || errno == EWOULDBLOCK) + ESDVoiceOut *esd = arg; + HWVoiceOut *hw = &esd->hw; + int threshold; + + threshold = conf.divisor ? hw->samples / conf.divisor : 0; + + if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) { + return NULL; + } + + for (;;) { + int decr, to_mix, rpos; + + for (;;) { + if (esd->done) { + goto exit; + } + + if (esd->live > threshold) { + break; + } + + if (audio_pt_wait (&esd->pt, AUDIO_FUNC)) { + goto exit; + } + } + + decr = to_mix = esd->live; + rpos = hw->rpos; + + if (audio_pt_unlock (&esd->pt, AUDIO_FUNC)) { + return NULL; + } + + while (to_mix) { + ssize_t written; + int chunk = audio_MIN (to_mix, hw->samples - rpos); + struct st_sample *src = hw->mix_buf + rpos; + + hw->clip (esd->pcm_buf, src, chunk); + + again: + written = write (esd->fd, esd->pcm_buf, chunk << hw->info.shift); + if (written == -1) { + if (errno == EINTR || errno == EAGAIN) { + goto again; + } + qesd_logerr (errno, "write failed\n"); + return NULL; + } + + if (written != chunk << hw->info.shift) { + int wsamples = written >> hw->info.shift; + int wbytes = wsamples << hw->info.shift; + if (wbytes != written) { + dolog ("warning: Misaligned write %d (requested %zd), " + "alignment %d\n", + wbytes, written, hw->info.align + 1); + } + to_mix -= wsamples; + rpos = (rpos + wsamples) % hw->samples; break; - qesd_logerr (errno, "write failed: %s\n", strerror(errno)); - O("EsounD output thread write error: %s", strerror(errno)); - break; + } + + rpos = (rpos + chunk) % hw->samples; + to_mix -= chunk; } - if (nwrite == 0) - break; - - writeSamples = nwrite >> hw->info.shift; - writeBytes = writeSamples << hw->info.shift; - if (writeBytes != nwrite) { - dolog ("warning: Misaligned write %d (requested %d), " - "alignment %d\n", - nwrite, writeBytes, hw->info.align + 1); + + if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) { + return NULL; } - rpos = (rpos + writeSamples) % hw->samples; - totalSamples += writeSamples; - liveSamples -= writeSamples; + + esd->rpos = rpos; + esd->live -= decr; + esd->decr += decr; + } + + exit: + audio_pt_unlock (&esd->pt, AUDIO_FUNC); + return NULL; +} + +static int qesd_run_out (HWVoiceOut *hw, int live) +{ + int decr; + ESDVoiceOut *esd = (ESDVoiceOut *) hw; + + if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) { + return 0; + } + + decr = audio_MIN (live, esd->decr); + esd->decr -= decr; + esd->live = live - decr; + hw->rpos = esd->rpos; + if (esd->live > 0) { + audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC); + } + else { + audio_pt_unlock (&esd->pt, AUDIO_FUNC); } - hw->rpos = rpos; - return totalSamples; + return decr; } static int qesd_write (SWVoiceOut *sw, void *buf, int len) @@ -165,13 +227,7 @@ static int qesd_init_out (HWVoiceOut *hw, struct audsettings *as) ESDVoiceOut *esd = (ESDVoiceOut *) hw; struct audsettings obt_as = *as; int esdfmt = ESD_STREAM | ESD_PLAY; - int result = -1; - /* shut down verbose debug spew */ - if (!D_ACTIVE) - stdio_disable(); - - O("initializing EsoundD audio output"); esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO; switch (as->fmt) { case AUD_FMT_S8: @@ -179,11 +235,11 @@ static int qesd_init_out (HWVoiceOut *hw, struct audsettings *as) esdfmt |= ESD_BITS8; obt_as.fmt = AUD_FMT_U8; break; -#if 0 + case AUD_FMT_S32: case AUD_FMT_U32: dolog ("Will use 16 instead of 32 bit samples\n"); -#endif + case AUD_FMT_S16: case AUD_FMT_U16: deffmt: @@ -205,50 +261,53 @@ static int qesd_init_out (HWVoiceOut *hw, struct audsettings *as) if (!esd->pcm_buf) { dolog ("Could not allocate buffer (%d bytes)\n", hw->samples << hw->info.shift); - goto exit; + return -1; } esd->fd = FF(esd_play_stream) (esdfmt, as->freq, conf.dac_host, NULL); if (esd->fd < 0) { - if (conf.dac_host == NULL) { - esd->fd = FF(esd_play_stream) (esdfmt, as->freq, "localhost", NULL); - } - if (esd->fd < 0) { - qesd_logerr (errno, "esd_play_stream failed\n"); - goto fail2; - } + qesd_logerr (errno, "esd_play_stream failed\n"); + goto fail1; } - { - int flags; - flags = fcntl(esd->fd, F_GETFL); - fcntl(esd->fd, F_SETFL, flags | O_NONBLOCK); + if (audio_pt_init (&esd->pt, qesd_thread_out, esd, AUDIO_CAP, AUDIO_FUNC)) { + goto fail2; } - result = 0; /* success */ - goto exit; + return 0; fail2: + if (close (esd->fd)) { + qesd_logerr (errno, "%s: close on esd socket(%d) failed\n", + AUDIO_FUNC, esd->fd); + } + esd->fd = -1; + + fail1: qemu_free (esd->pcm_buf); esd->pcm_buf = NULL; - - exit: - if (!D_ACTIVE) - stdio_enable(); - - return result; + return -1; } static void qesd_fini_out (HWVoiceOut *hw) { + void *ret; ESDVoiceOut *esd = (ESDVoiceOut *) hw; + audio_pt_lock (&esd->pt, AUDIO_FUNC); + esd->done = 1; + audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC); + audio_pt_join (&esd->pt, &ret, AUDIO_FUNC); + if (esd->fd >= 0) { if (close (esd->fd)) { qesd_logerr (errno, "failed to close esd socket\n"); } esd->fd = -1; } + + audio_pt_fini (&esd->pt, AUDIO_FUNC); + qemu_free (esd->pcm_buf); esd->pcm_buf = NULL; } @@ -261,56 +320,112 @@ static int qesd_ctl_out (HWVoiceOut *hw, int cmd, ...) } /* capture */ -static int qesd_run_in (HWVoiceIn *hw) +static void *qesd_thread_in (void *arg) { - int wpos, liveSamples, totalSamples; - int grabSamples; - ESDVoiceIn *esd = (ESDVoiceIn *) hw; + ESDVoiceIn *esd = arg; + HWVoiceIn *hw = &esd->hw; + int threshold; + + threshold = conf.divisor ? hw->samples / conf.divisor : 0; + + if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) { + return NULL; + } + + for (;;) { + int incr, to_grab, wpos; - wpos = hw->wpos; - liveSamples = audio_pcm_hw_get_live_in (hw); - grabSamples = hw->samples - liveSamples; - totalSamples = 0; - - while (grabSamples > 0) { - ssize_t nread; - int chunkSamples = audio_MIN (grabSamples, hw->samples - wpos); - int chunkBytes = chunkSamples << hw->info.shift; - int readSamples, readBytes; - void* buf = advance (esd->pcm_buf, wpos); - - AGAIN: - nread = read (esd->fd, buf, chunkBytes); - if (nread == -1) { - if (errno == EINTR) - goto AGAIN; - if (errno == EAGAIN || errno == EWOULDBLOCK) + for (;;) { + if (esd->done) { + goto exit; + } + + if (esd->dead > threshold) { break; + } - qesd_logerr (errno, "read failed: %s\n", strerror(errno)); - break; + if (audio_pt_wait (&esd->pt, AUDIO_FUNC)) { + goto exit; + } } - if (nread == 0) - break; - readSamples = nread >> hw->info.shift; - readBytes = readSamples << hw->info.shift; + incr = to_grab = esd->dead; + wpos = hw->wpos; - if (readBytes != nread) { - dolog ("warning: Misaligned read %d (requested %d), " - "alignment %d\n", - nread, readBytes, hw->info.align + 1); + if (audio_pt_unlock (&esd->pt, AUDIO_FUNC)) { + return NULL; } - hw->conv (hw->conv_buf + wpos, buf, readSamples, - &nominal_volume); + while (to_grab) { + ssize_t nread; + int chunk = audio_MIN (to_grab, hw->samples - wpos); + void *buf = advance (esd->pcm_buf, wpos); + + again: + nread = read (esd->fd, buf, chunk << hw->info.shift); + if (nread == -1) { + if (errno == EINTR || errno == EAGAIN) { + goto again; + } + qesd_logerr (errno, "read failed\n"); + return NULL; + } + + if (nread != chunk << hw->info.shift) { + int rsamples = nread >> hw->info.shift; + int rbytes = rsamples << hw->info.shift; + if (rbytes != nread) { + dolog ("warning: Misaligned write %d (requested %zd), " + "alignment %d\n", + rbytes, nread, hw->info.align + 1); + } + to_grab -= rsamples; + wpos = (wpos + rsamples) % hw->samples; + break; + } + + hw->conv (hw->conv_buf + wpos, buf, nread >> hw->info.shift, + &nominal_volume); + wpos = (wpos + chunk) % hw->samples; + to_grab -= chunk; + } + + if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) { + return NULL; + } - wpos = (wpos + readSamples) % hw->samples; - grabSamples -= readSamples; - totalSamples += readSamples; + esd->wpos = wpos; + esd->dead -= incr; + esd->incr += incr; } - hw->wpos = wpos; - return totalSamples; + + exit: + audio_pt_unlock (&esd->pt, AUDIO_FUNC); + return NULL; +} + +static int qesd_run_in (HWVoiceIn *hw) +{ + int live, incr, dead; + ESDVoiceIn *esd = (ESDVoiceIn *) hw; + + if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) { + return 0; + } + + live = audio_pcm_hw_get_live_in (hw); + dead = hw->samples - live; + incr = audio_MIN (dead, esd->incr); + esd->incr -= incr; + esd->dead = dead - incr; + hw->wpos = esd->wpos; + if (esd->dead > 0) { + audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC); + } + else { + audio_pt_unlock (&esd->pt, AUDIO_FUNC); + } + return incr; } static int qesd_read (SWVoiceIn *sw, void *buf, int len) @@ -323,11 +438,6 @@ static int qesd_init_in (HWVoiceIn *hw, struct audsettings *as) ESDVoiceIn *esd = (ESDVoiceIn *) hw; struct audsettings obt_as = *as; int esdfmt = ESD_STREAM | ESD_RECORD; - int result = -1; - - /* shut down verbose debug spew */ - if (!D_ACTIVE) - stdio_disable(); esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO; switch (as->fmt) { @@ -359,50 +469,53 @@ static int qesd_init_in (HWVoiceIn *hw, struct audsettings *as) if (!esd->pcm_buf) { dolog ("Could not allocate buffer (%d bytes)\n", hw->samples << hw->info.shift); - goto exit; + return -1; } esd->fd = FF(esd_record_stream) (esdfmt, as->freq, conf.adc_host, NULL); if (esd->fd < 0) { - if (conf.adc_host == NULL) { - esd->fd = FF(esd_record_stream) (esdfmt, as->freq, "localhost", NULL); - } - if (esd->fd < 0) { - qesd_logerr (errno, "esd_record_stream failed\n"); - goto fail2; - } + qesd_logerr (errno, "esd_record_stream failed\n"); + goto fail1; } - { - int flags; - flags = fcntl(esd->fd, F_GETFL); - fcntl(esd->fd, F_SETFL, flags | O_NONBLOCK); + if (audio_pt_init (&esd->pt, qesd_thread_in, esd, AUDIO_CAP, AUDIO_FUNC)) { + goto fail2; } - result = 0; /* success */ - goto exit; + return 0; fail2: + if (close (esd->fd)) { + qesd_logerr (errno, "%s: close on esd socket(%d) failed\n", + AUDIO_FUNC, esd->fd); + } + esd->fd = -1; + + fail1: qemu_free (esd->pcm_buf); esd->pcm_buf = NULL; - - exit: - if (!D_ACTIVE) - stdio_enable(); - - return result; + return -1; } static void qesd_fini_in (HWVoiceIn *hw) { + void *ret; ESDVoiceIn *esd = (ESDVoiceIn *) hw; + audio_pt_lock (&esd->pt, AUDIO_FUNC); + esd->done = 1; + audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC); + audio_pt_join (&esd->pt, &ret, AUDIO_FUNC); + if (esd->fd >= 0) { if (close (esd->fd)) { qesd_logerr (errno, "failed to close esd socket\n"); } esd->fd = -1; } + + audio_pt_fini (&esd->pt, AUDIO_FUNC); + qemu_free (esd->pcm_buf); esd->pcm_buf = NULL; } @@ -473,46 +586,57 @@ static void qesd_audio_fini (void *opaque) } struct audio_option qesd_options[] = { - {"SAMPLES", AUD_OPT_INT, &conf.samples, - "buffer size in samples", NULL, 0}, - - {"DIVISOR", AUD_OPT_INT, &conf.divisor, - "threshold divisor", NULL, 0}, - - {"DAC_HOST", AUD_OPT_STR, &conf.dac_host, - "playback host", NULL, 0}, - - {"ADC_HOST", AUD_OPT_STR, &conf.adc_host, - "capture host", NULL, 0}, - - {NULL, 0, NULL, NULL, NULL, 0} + { + .name = "SAMPLES", + .tag = AUD_OPT_INT, + .valp = &conf.samples, + .descr = "buffer size in samples" + }, + { + .name = "DIVISOR", + .tag = AUD_OPT_INT, + .valp = &conf.divisor, + .descr = "threshold divisor" + }, + { + .name = "DAC_HOST", + .tag = AUD_OPT_STR, + .valp = &conf.dac_host, + .descr = "playback host" + }, + { + .name = "ADC_HOST", + .tag = AUD_OPT_STR, + .valp = &conf.adc_host, + .descr = "capture host" + }, + { /* End of list */ } }; static struct audio_pcm_ops qesd_pcm_ops = { - qesd_init_out, - qesd_fini_out, - qesd_run_out, - qesd_write, - qesd_ctl_out, - - qesd_init_in, - qesd_fini_in, - qesd_run_in, - qesd_read, - qesd_ctl_in, + .init_out = qesd_init_out, + .fini_out = qesd_fini_out, + .run_out = qesd_run_out, + .write = qesd_write, + .ctl_out = qesd_ctl_out, + + .init_in = qesd_init_in, + .fini_in = qesd_fini_in, + .run_in = qesd_run_in, + .read = qesd_read, + .ctl_in = qesd_ctl_in, }; struct audio_driver esd_audio_driver = { - INIT_FIELD (name = ) "esd", - INIT_FIELD (descr = ) - "EsounD audio (en.wikipedia.org/wiki/Esound)", - INIT_FIELD (options = ) qesd_options, - INIT_FIELD (init = ) qesd_audio_init, - INIT_FIELD (fini = ) qesd_audio_fini, - INIT_FIELD (pcm_ops = ) &qesd_pcm_ops, - INIT_FIELD (can_be_default = ) 1, - INIT_FIELD (max_voices_out = ) INT_MAX, - INIT_FIELD (max_voices_in = ) 1, - INIT_FIELD (voice_size_out = ) sizeof (ESDVoiceOut), - INIT_FIELD (voice_size_in = ) sizeof (ESDVoiceIn) + .name = "esd", + .descr = "http://en.wikipedia.org/wiki/Esound", + .options = qesd_options, + .init = qesd_audio_init, + .fini = qesd_audio_fini, + .pcm_ops = &qesd_pcm_ops, + .can_be_default = 0, + .max_voices_out = INT_MAX, + .max_voices_in = INT_MAX, + .voice_size_out = sizeof (ESDVoiceOut), + .voice_size_in = sizeof (ESDVoiceIn) }; diff --git a/audio/fmodaudio.c b/audio/fmodaudio.c index 0becd3b..7f08e14 100644 --- a/audio/fmodaudio.c +++ b/audio/fmodaudio.c @@ -47,16 +47,11 @@ static struct { int freq; int nb_channels; int bufsize; - int threshold; int broken_adc; } conf = { - NULL, - 2048 * 2, - 44100, - 2, - 0, - 0, - 0 + .nb_samples = 2048 * 2, + .freq = 44100, + .nb_channels = 2, }; static void GCC_FMT_ATTR (1, 2) fmod_logerr (const char *fmt, ...) @@ -229,24 +224,15 @@ static int fmod_lock_sample ( return 0; } -static int fmod_run_out (HWVoiceOut *hw) +static int fmod_run_out (HWVoiceOut *hw, int live) { FMODVoiceOut *fmd = (FMODVoiceOut *) hw; - int live, decr; + int decr; void *p1 = 0, *p2 = 0; unsigned int blen1 = 0, blen2 = 0; unsigned int len1 = 0, len2 = 0; - int nb_live; - live = audio_pcm_hw_get_live_out2 (hw, &nb_live); - if (!live) { - return 0; - } - - if (!hw->pending_disable - && nb_live - && (conf.threshold && live <= conf.threshold)) { - ldebug ("live=%d nb_live=%d\n", live, nb_live); + if (!hw->pending_disable) { return 0; } @@ -517,27 +503,27 @@ static struct { const char *name; int type; } drvtab[] = { - {"none", FSOUND_OUTPUT_NOSOUND}, + { .name = "none", .type = FSOUND_OUTPUT_NOSOUND }, #ifdef _WIN32 - {"winmm", FSOUND_OUTPUT_WINMM}, - {"dsound", FSOUND_OUTPUT_DSOUND}, - {"a3d", FSOUND_OUTPUT_A3D}, - {"asio", FSOUND_OUTPUT_ASIO}, + { .name = "winmm", .type = FSOUND_OUTPUT_WINMM }, + { .name = "dsound", .type = FSOUND_OUTPUT_DSOUND }, + { .name = "a3d", .type = FSOUND_OUTPUT_A3D }, + { .name = "asio", .type = FSOUND_OUTPUT_ASIO }, #endif #ifdef __linux__ - {"oss", FSOUND_OUTPUT_OSS}, - {"alsa", FSOUND_OUTPUT_ALSA}, - {"esd", FSOUND_OUTPUT_ESD}, + { .name = "oss", .type = FSOUND_OUTPUT_OSS }, + { .name = "alsa", .type = FSOUND_OUTPUT_ALSA }, + { .name = "esd", .type = FSOUND_OUTPUT_ESD }, #endif #ifdef __APPLE__ - {"mac", FSOUND_OUTPUT_MAC}, + { .name = "mac", .type = FSOUND_OUTPUT_MAC }, #endif #if 0 - {"xbox", FSOUND_OUTPUT_XBOX}, - {"ps2", FSOUND_OUTPUT_PS2}, - {"gcube", FSOUND_OUTPUT_GC}, + { .name = "xbox", .type = FSOUND_OUTPUT_XBOX }, + { .name = "ps2", .type = FSOUND_OUTPUT_PS2 }, + { .name = "gcube", .type = FSOUND_OUTPUT_GC }, #endif - {"none-realtime", FSOUND_OUTPUT_NOSOUND_NONREALTIME} + { .name = "none-realtime", .type = FSOUND_OUTPUT_NOSOUND_NONREALTIME } }; static void *fmod_audio_init (void) @@ -639,48 +625,63 @@ static void fmod_audio_fini (void *opaque) } static struct audio_option fmod_options[] = { - {"DRV", AUD_OPT_STR, &conf.drvname, - "FMOD driver", NULL, 0}, - {"FREQ", AUD_OPT_INT, &conf.freq, - "Default frequency", NULL, 0}, - {"SAMPLES", AUD_OPT_INT, &conf.nb_samples, - "Buffer size in samples", NULL, 0}, - {"CHANNELS", AUD_OPT_INT, &conf.nb_channels, - "Number of default channels (1 - mono, 2 - stereo)", NULL, 0}, - {"BUFSIZE", AUD_OPT_INT, &conf.bufsize, - "(undocumented)", NULL, 0}, -#if 0 - {"THRESHOLD", AUD_OPT_INT, &conf.threshold, - "(undocumented)"}, -#endif - - {NULL, 0, NULL, NULL, NULL, 0} + { + .name = "DRV", + .tag = AUD_OPT_STR, + .valp = &conf.drvname, + .descr = "FMOD driver" + }, + { + .name = "FREQ", + .tag = AUD_OPT_INT, + .valp = &conf.freq, + .descr = "Default frequency" + }, + { + .name = "SAMPLES", + .tag = AUD_OPT_INT, + .valp = &conf.nb_samples, + .descr = "Buffer size in samples" + }, + { + .name = "CHANNELS", + .tag = AUD_OPT_INT, + .valp = &conf.nb_channels, + .descr = "Number of default channels (1 - mono, 2 - stereo)" + }, + { + .name = "BUFSIZE", + .tag = AUD_OPT_INT, + .valp = &conf.bufsize, + .descr = "(undocumented)" + }, + { /* End of list */ } }; static struct audio_pcm_ops fmod_pcm_ops = { - fmod_init_out, - fmod_fini_out, - fmod_run_out, - fmod_write, - fmod_ctl_out, - - fmod_init_in, - fmod_fini_in, - fmod_run_in, - fmod_read, - fmod_ctl_in + .init_out = fmod_init_out, + .fini_out = fmod_fini_out, + .run_out = fmod_run_out, + .write = fmod_write, + .ctl_out = fmod_ctl_out, + + .init_in = fmod_init_in, + .fini_in = fmod_fini_in, + .run_in = fmod_run_in, + .read = fmod_read, + .ctl_in = fmod_ctl_in }; struct audio_driver fmod_audio_driver = { - INIT_FIELD (name = ) "fmod", - INIT_FIELD (descr = ) "FMOD 3.xx http://www.fmod.org", - INIT_FIELD (options = ) fmod_options, - INIT_FIELD (init = ) fmod_audio_init, - INIT_FIELD (fini = ) fmod_audio_fini, - INIT_FIELD (pcm_ops = ) &fmod_pcm_ops, - INIT_FIELD (can_be_default = ) 1, - INIT_FIELD (max_voices_out = ) INT_MAX, - INIT_FIELD (max_voices_in = ) INT_MAX, - INIT_FIELD (voice_size_out = ) sizeof (FMODVoiceOut), - INIT_FIELD (voice_size_in = ) sizeof (FMODVoiceIn) + .name = "fmod", + .descr = "FMOD 3.xx http://www.fmod.org", + .options = fmod_options, + .init = fmod_audio_init, + .fini = fmod_audio_fini, + .pcm_ops = &fmod_pcm_ops, + .can_be_default = 1, + .max_voices_out = INT_MAX, + .max_voices_in = INT_MAX, + .voice_size_out = sizeof (FMODVoiceOut), + .voice_size_in = sizeof (FMODVoiceIn) }; diff --git a/audio/mixeng.c b/audio/mixeng.c index 8ce942e..9f1d93f 100644 --- a/audio/mixeng.c +++ b/audio/mixeng.c @@ -123,7 +123,7 @@ #undef IN_T #undef SHIFT -/* Unsigned 16 bit */ +/* Unsigned 32 bit */ #define IN_T uint32_t #define IN_MIN 0 #define IN_MAX UINT32_MAX diff --git a/audio/noaudio.c b/audio/noaudio.c index 0209edb..4925234 100644 --- a/audio/noaudio.c +++ b/audio/noaudio.c @@ -38,19 +38,14 @@ typedef struct NoVoiceIn { int64_t old_ticks; } NoVoiceIn; -static int no_run_out (HWVoiceOut *hw) +static int no_run_out (HWVoiceOut *hw, int live) { NoVoiceOut *no = (NoVoiceOut *) hw; - int live, decr, samples; + int decr, samples; int64_t now; int64_t ticks; int64_t bytes; - live = audio_pcm_hw_get_live_out (&no->hw); - if (!live) { - return 0; - } - now = qemu_get_clock (vm_clock); ticks = now - no->old_ticks; bytes = muldiv64 (ticks, hw->info.bytes_per_second, get_ticks_per_sec ()); @@ -147,29 +142,29 @@ static void no_audio_fini (void *opaque) } static struct audio_pcm_ops no_pcm_ops = { - no_init_out, - no_fini_out, - no_run_out, - no_write, - no_ctl_out, - - no_init_in, - no_fini_in, - no_run_in, - no_read, - no_ctl_in + .init_out = no_init_out, + .fini_out = no_fini_out, + .run_out = no_run_out, + .write = no_write, + .ctl_out = no_ctl_out, + + .init_in = no_init_in, + .fini_in = no_fini_in, + .run_in = no_run_in, + .read = no_read, + .ctl_in = no_ctl_in }; struct audio_driver no_audio_driver = { - INIT_FIELD (name = ) "none", - INIT_FIELD (descr = ) "disabled audio", - INIT_FIELD (options = ) NULL, - INIT_FIELD (init = ) no_audio_init, - INIT_FIELD (fini = ) no_audio_fini, - INIT_FIELD (pcm_ops = ) &no_pcm_ops, - INIT_FIELD (can_be_default = ) 1, - INIT_FIELD (max_voices_out = ) INT_MAX, - INIT_FIELD (max_voices_in = ) INT_MAX, - INIT_FIELD (voice_size_out = ) sizeof (NoVoiceOut), - INIT_FIELD (voice_size_in = ) sizeof (NoVoiceIn) + .name = "none", + .descr = "Timer based audio emulation", + .options = NULL, + .init = no_audio_init, + .fini = no_audio_fini, + .pcm_ops = &no_pcm_ops, + .can_be_default = 1, + .max_voices_out = INT_MAX, + .max_voices_in = INT_MAX, + .voice_size_out = sizeof (NoVoiceOut), + .voice_size_in = sizeof (NoVoiceIn) }; diff --git a/audio/ossaudio.c b/audio/ossaudio.c index ceb81ce..42bffae 100644 --- a/audio/ossaudio.c +++ b/audio/ossaudio.c @@ -31,36 +31,26 @@ #include <sys/soundcard.h> #endif #include "qemu-common.h" +#include "host-utils.h" +#include "qemu-char.h" #include "audio.h" #define AUDIO_CAP "oss" #include "audio_int.h" -/* http://www.df.lth.se/~john_e/gems/gem002d.html */ -/* http://www.multi-platforms.com/Tips/PopCount.htm */ -uint32_t popcount (uint32_t u) -{ - u = ((u&0x55555555) + ((u>>1)&0x55555555)); - u = ((u&0x33333333) + ((u>>2)&0x33333333)); - u = ((u&0x0f0f0f0f) + ((u>>4)&0x0f0f0f0f)); - u = ((u&0x00ff00ff) + ((u>>8)&0x00ff00ff)); - u = ( u&0x0000ffff) + (u>>16); - return u; -} - -inline uint32_t lsbindex (uint32_t u) -{ - return popcount ((u&-u)-1); -} +#if defined OSS_GETVERSION && defined SNDCTL_DSP_POLICY +#define USE_DSP_POLICY +#endif typedef struct OSSVoiceOut { HWVoiceOut hw; void *pcm_buf; int fd; + int wpos; int nfrags; int fragsize; int mmapped; - int old_optr; + int pending; } OSSVoiceOut; typedef struct OSSVoiceIn { @@ -69,7 +59,6 @@ typedef struct OSSVoiceIn { int fd; int nfrags; int fragsize; - int old_optr; } OSSVoiceIn; static struct { @@ -79,13 +68,17 @@ static struct { const char *devpath_out; const char *devpath_in; int debug; + int exclusive; + int policy; } conf = { .try_mmap = 0, .nfrags = 4, .fragsize = 4096, .devpath_out = "/dev/dsp", .devpath_in = "/dev/dsp", - .debug = 0 + .debug = 0, + .exclusive = 0, + .policy = 5 }; struct oss_params { @@ -127,13 +120,42 @@ static void GCC_FMT_ATTR (3, 4) oss_logerr2 ( static void oss_anal_close (int *fdp) { - int err = close (*fdp); + int err; + + qemu_set_fd_handler (*fdp, NULL, NULL, NULL); + err = close (*fdp); if (err) { oss_logerr (errno, "Failed to close file(fd=%d)\n", *fdp); } *fdp = -1; } +static void oss_helper_poll_out (void *opaque) +{ + (void) opaque; + audio_run ("oss_poll_out"); +} + +static void oss_helper_poll_in (void *opaque) +{ + (void) opaque; + audio_run ("oss_poll_in"); +} + +static int oss_poll_out (HWVoiceOut *hw) +{ + OSSVoiceOut *oss = (OSSVoiceOut *) hw; + + return qemu_set_fd_handler (oss->fd, NULL, oss_helper_poll_out, NULL); +} + +static int oss_poll_in (HWVoiceIn *hw) +{ + OSSVoiceIn *oss = (OSSVoiceIn *) hw; + + return qemu_set_fd_handler (oss->fd, oss_helper_poll_in, NULL, NULL); +} + static int oss_write (SWVoiceOut *sw, void *buf, int len) { return audio_pcm_sw_write (sw, buf, len); @@ -218,17 +240,46 @@ static void oss_dump_info (struct oss_params *req, struct oss_params *obt) } #endif +#ifdef USE_DSP_POLICY +static int oss_get_version (int fd, int *version, const char *typ) +{ + if (ioctl (fd, OSS_GETVERSION, &version)) { +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) + /* + * Looks like atm (20100109) FreeBSD knows OSS_GETVERSION + * since 7.x, but currently only on the mixer device (or in + * the Linuxolator), and in the native version that part of + * the code is in fact never reached so the ioctl fails anyway. + * Until this is fixed, just check the errno and if its what + * FreeBSD's sound drivers return atm assume they are new enough. + */ + if (errno == EINVAL) { + *version = 0x040000; + return 0; + } +#endif + oss_logerr2 (errno, typ, "Failed to get OSS version\n"); + return -1; + } + return 0; +} +#endif + static int oss_open (int in, struct oss_params *req, struct oss_params *obt, int *pfd) { int fd; - int mmmmssss; + int oflags = conf.exclusive ? O_EXCL : 0; audio_buf_info abinfo; int fmt, freq, nchannels; + int setfragment = 1; const char *dspname = in ? conf.devpath_in : conf.devpath_out; const char *typ = in ? "ADC" : "DAC"; - fd = open (dspname, (in ? O_RDONLY : O_WRONLY) | O_NONBLOCK); + /* Kludge needed to have working mmap on Linux */ + oflags |= conf.try_mmap ? O_RDWR : (in ? O_RDONLY : O_WRONLY); + + fd = open (dspname, oflags | O_NONBLOCK); if (-1 == fd) { oss_logerr2 (errno, typ, "Failed to open `%s'\n", dspname); return -1; @@ -259,11 +310,36 @@ static int oss_open (int in, struct oss_params *req, goto err; } - mmmmssss = (req->nfrags << 16) | lsbindex (req->fragsize); - if (ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &mmmmssss)) { - oss_logerr2 (errno, typ, "Failed to set buffer length (%d, %d)\n", - req->nfrags, req->fragsize); - goto err; +#ifdef USE_DSP_POLICY + if (conf.policy >= 0) { + int version; + + if (!oss_get_version (fd, &version, typ)) { + if (conf.debug) { + dolog ("OSS version = %#x\n", version); + } + + if (version >= 0x040000) { + int policy = conf.policy; + if (ioctl (fd, SNDCTL_DSP_POLICY, &policy)) { + oss_logerr2 (errno, typ, + "Failed to set timing policy to %d\n", + conf.policy); + goto err; + } + setfragment = 0; + } + } + } +#endif + + if (setfragment) { + int mmmmssss = (req->nfrags << 16) | ctz32 (req->fragsize); + if (ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &mmmmssss)) { + oss_logerr2 (errno, typ, "Failed to set buffer length (%d, %d)\n", + req->nfrags, req->fragsize); + goto err; + } } if (ioctl (fd, in ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE, &abinfo)) { @@ -305,26 +381,58 @@ static int oss_open (int in, struct oss_params *req, return -1; } -static int oss_run_out (HWVoiceOut *hw) +static void oss_write_pending (OSSVoiceOut *oss) +{ + HWVoiceOut *hw = &oss->hw; + + if (oss->mmapped) { + return; + } + + while (oss->pending) { + int samples_written; + ssize_t bytes_written; + int samples_till_end = hw->samples - oss->wpos; + int samples_to_write = audio_MIN (oss->pending, samples_till_end); + int bytes_to_write = samples_to_write << hw->info.shift; + void *pcm = advance (oss->pcm_buf, oss->wpos << hw->info.shift); + + bytes_written = write (oss->fd, pcm, bytes_to_write); + if (bytes_written < 0) { + if (errno != EAGAIN) { + oss_logerr (errno, "failed to write %d bytes\n", + bytes_to_write); + } + break; + } + + if (bytes_written & hw->info.align) { + dolog ("misaligned write asked for %d, but got %zd\n", + bytes_to_write, bytes_written); + return; + } + + samples_written = bytes_written >> hw->info.shift; + oss->pending -= samples_written; + oss->wpos = (oss->wpos + samples_written) % hw->samples; + if (bytes_written - bytes_to_write) { + break; + } + } +} + +static int oss_run_out (HWVoiceOut *hw, int live) { OSSVoiceOut *oss = (OSSVoiceOut *) hw; - int err, rpos, live, decr; - int samples; - uint8_t *dst; - struct st_sample *src; + int err, decr; struct audio_buf_info abinfo; struct count_info cntinfo; int bufsize; - live = audio_pcm_hw_get_live_out (hw); - if (!live) { - return 0; - } - bufsize = hw->samples << hw->info.shift; if (oss->mmapped) { - int bytes; + int bytes, pos; err = ioctl (oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo); if (err < 0) { @@ -332,20 +440,8 @@ static int oss_run_out (HWVoiceOut *hw) return 0; } - if (cntinfo.ptr == oss->old_optr) { - if (abs (hw->samples - live) < 64) { - dolog ("warning: Overrun\n"); - } - return 0; - } - - if (cntinfo.ptr > oss->old_optr) { - bytes = cntinfo.ptr - oss->old_optr; - } - else { - bytes = bufsize + cntinfo.ptr - oss->old_optr; - } - + pos = hw->rpos << hw->info.shift; + bytes = audio_ring_dist (cntinfo.ptr, pos, bufsize); decr = audio_MIN (bytes >> hw->info.shift, live); } else { @@ -358,7 +454,7 @@ static int oss_run_out (HWVoiceOut *hw) if (abinfo.bytes > bufsize) { if (conf.debug) { dolog ("warning: Invalid available size, size=%d bufsize=%d\n" - "please report your OS/audio hw to malc@pulsesoft.com\n", + "please report your OS/audio hw to av1474@comtv.ru\n", abinfo.bytes, bufsize); } abinfo.bytes = bufsize; @@ -378,53 +474,10 @@ static int oss_run_out (HWVoiceOut *hw) } } - samples = decr; - rpos = hw->rpos; - while (samples) { - int left_till_end_samples = hw->samples - rpos; - int convert_samples = audio_MIN (samples, left_till_end_samples); - - src = hw->mix_buf + rpos; - dst = advance (oss->pcm_buf, rpos << hw->info.shift); - - hw->clip (dst, src, convert_samples); - if (!oss->mmapped) { - int written; + decr = audio_pcm_hw_clip_out (hw, oss->pcm_buf, decr, oss->pending); + oss->pending += decr; + oss_write_pending (oss); - written = write (oss->fd, dst, convert_samples << hw->info.shift); - /* XXX: follow errno recommendations ? */ - if (written == -1) { - oss_logerr ( - errno, - "Failed to write %d bytes of audio data from %p\n", - convert_samples << hw->info.shift, - dst - ); - continue; - } - - if (written != convert_samples << hw->info.shift) { - int wsamples = written >> hw->info.shift; - int wbytes = wsamples << hw->info.shift; - if (wbytes != written) { - dolog ("warning: Misaligned write %d (requested %d), " - "alignment %d\n", - wbytes, written, hw->info.align + 1); - } - decr -= wsamples; - rpos = (rpos + wsamples) % hw->samples; - break; - } - } - - rpos = (rpos + convert_samples) % hw->samples; - samples -= convert_samples; - } - if (oss->mmapped) { - oss->old_optr = cntinfo.ptr; - } - - hw->rpos = rpos; return decr; } @@ -498,7 +551,7 @@ static int oss_init_out (HWVoiceOut *hw, struct audsettings *as) oss->mmapped = 0; if (conf.try_mmap) { oss->pcm_buf = mmap ( - 0, + NULL, hw->samples << hw->info.shift, PROT_READ | PROT_WRITE, MAP_SHARED, @@ -508,7 +561,8 @@ static int oss_init_out (HWVoiceOut *hw, struct audsettings *as) if (oss->pcm_buf == MAP_FAILED) { oss_logerr (errno, "Failed to map %d bytes of DAC\n", hw->samples << hw->info.shift); - } else { + } + else { int err; int trig = 0; if (ioctl (fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { @@ -563,25 +617,48 @@ static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...) int trig; OSSVoiceOut *oss = (OSSVoiceOut *) hw; - if (!oss->mmapped) { - return 0; - } - switch (cmd) { case VOICE_ENABLE: - ldebug ("enabling voice\n"); - audio_pcm_info_clear_buf (&hw->info, oss->pcm_buf, hw->samples); - trig = PCM_ENABLE_OUTPUT; - if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { - oss_logerr ( - errno, - "SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n" - ); - return -1; + { + va_list ap; + int poll_mode; + + va_start (ap, cmd); + poll_mode = va_arg (ap, int); + va_end (ap); + + ldebug ("enabling voice\n"); + if (poll_mode && oss_poll_out (hw)) { + poll_mode = 0; + } + hw->poll_mode = poll_mode; + + if (!oss->mmapped) { + return 0; + } + + audio_pcm_info_clear_buf (&hw->info, oss->pcm_buf, hw->samples); + trig = PCM_ENABLE_OUTPUT; + if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { + oss_logerr ( + errno, + "SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n" + ); + return -1; + } } break; case VOICE_DISABLE: + if (hw->poll_mode) { + qemu_set_fd_handler (oss->fd, NULL, NULL, NULL); + hw->poll_mode = 0; + } + + if (!oss->mmapped) { + return 0; + } + ldebug ("disabling voice\n"); trig = 0; if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) { @@ -671,8 +748,8 @@ static int oss_run_in (HWVoiceIn *hw) int add; int len; } bufs[2] = { - { hw->wpos, 0 }, - { 0, 0 } + { .add = hw->wpos, .len = 0 }, + { .add = 0, .len = 0 } }; if (!dead) { @@ -687,7 +764,6 @@ static int oss_run_in (HWVoiceIn *hw) bufs[0].len = dead << hwshift; } - for (i = 0; i < 2; ++i) { ssize_t nread; @@ -737,8 +813,32 @@ static int oss_read (SWVoiceIn *sw, void *buf, int size) static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...) { - (void) hw; - (void) cmd; + OSSVoiceIn *oss = (OSSVoiceIn *) hw; + + switch (cmd) { + case VOICE_ENABLE: + { + va_list ap; + int poll_mode; + + va_start (ap, cmd); + poll_mode = va_arg (ap, int); + va_end (ap); + + if (poll_mode && oss_poll_in (hw)) { + poll_mode = 0; + } + hw->poll_mode = poll_mode; + } + break; + + case VOICE_DISABLE: + if (hw->poll_mode) { + hw->poll_mode = 0; + qemu_set_fd_handler (oss->fd, NULL, NULL, NULL); + } + break; + } return 0; } @@ -753,45 +853,83 @@ static void oss_audio_fini (void *opaque) } static struct audio_option oss_options[] = { - {"FRAGSIZE", AUD_OPT_INT, &conf.fragsize, - "Fragment size in bytes", NULL, 0}, - {"NFRAGS", AUD_OPT_INT, &conf.nfrags, - "Number of fragments", NULL, 0}, - {"MMAP", AUD_OPT_BOOL, &conf.try_mmap, - "Try using memory mapped access", NULL, 0}, - {"DAC_DEV", AUD_OPT_STR, &conf.devpath_out, - "Path to DAC device", NULL, 0}, - {"ADC_DEV", AUD_OPT_STR, &conf.devpath_in, - "Path to ADC device", NULL, 0}, - {"DEBUG", AUD_OPT_BOOL, &conf.debug, - "Turn on some debugging messages", NULL, 0}, - {NULL, 0, NULL, NULL, NULL, 0} + { + .name = "FRAGSIZE", + .tag = AUD_OPT_INT, + .valp = &conf.fragsize, + .descr = "Fragment size in bytes" + }, + { + .name = "NFRAGS", + .tag = AUD_OPT_INT, + .valp = &conf.nfrags, + .descr = "Number of fragments" + }, + { + .name = "MMAP", + .tag = AUD_OPT_BOOL, + .valp = &conf.try_mmap, + .descr = "Try using memory mapped access" + }, + { + .name = "DAC_DEV", + .tag = AUD_OPT_STR, + .valp = &conf.devpath_out, + .descr = "Path to DAC device" + }, + { + .name = "ADC_DEV", + .tag = AUD_OPT_STR, + .valp = &conf.devpath_in, + .descr = "Path to ADC device" + }, + { + .name = "EXCLUSIVE", + .tag = AUD_OPT_BOOL, + .valp = &conf.exclusive, + .descr = "Open device in exclusive mode (vmix wont work)" + }, +#ifdef USE_DSP_POLICY + { + .name = "POLICY", + .tag = AUD_OPT_INT, + .valp = &conf.policy, + .descr = "Set the timing policy of the device, -1 to use fragment mode", + }, +#endif + { + .name = "DEBUG", + .tag = AUD_OPT_BOOL, + .valp = &conf.debug, + .descr = "Turn on some debugging messages" + }, + { /* End of list */ } }; static struct audio_pcm_ops oss_pcm_ops = { - oss_init_out, - oss_fini_out, - oss_run_out, - oss_write, - oss_ctl_out, - - oss_init_in, - oss_fini_in, - oss_run_in, - oss_read, - oss_ctl_in + .init_out = oss_init_out, + .fini_out = oss_fini_out, + .run_out = oss_run_out, + .write = oss_write, + .ctl_out = oss_ctl_out, + + .init_in = oss_init_in, + .fini_in = oss_fini_in, + .run_in = oss_run_in, + .read = oss_read, + .ctl_in = oss_ctl_in }; struct audio_driver oss_audio_driver = { - INIT_FIELD (name = ) "oss", - INIT_FIELD (descr = ) "OSS audio (www.opensound.com)", - INIT_FIELD (options = ) oss_options, - INIT_FIELD (init = ) oss_audio_init, - INIT_FIELD (fini = ) oss_audio_fini, - INIT_FIELD (pcm_ops = ) &oss_pcm_ops, - INIT_FIELD (can_be_default = ) 1, - INIT_FIELD (max_voices_out = ) INT_MAX, - INIT_FIELD (max_voices_in = ) INT_MAX, - INIT_FIELD (voice_size_out = ) sizeof (OSSVoiceOut), - INIT_FIELD (voice_size_in = ) sizeof (OSSVoiceIn) + .name = "oss", + .descr = "OSS http://www.opensound.com", + .options = oss_options, + .init = oss_audio_init, + .fini = oss_audio_fini, + .pcm_ops = &oss_pcm_ops, + .can_be_default = 1, + .max_voices_out = INT_MAX, + .max_voices_in = INT_MAX, + .voice_size_out = sizeof (OSSVoiceOut), + .voice_size_in = sizeof (OSSVoiceIn) }; diff --git a/audio/paaudio.c b/audio/paaudio.c index ed2c58c..c78353b 100644 --- a/audio/paaudio.c +++ b/audio/paaudio.c @@ -151,17 +151,11 @@ static void *qpa_thread_out (void *arg) return NULL; } -static int qpa_run_out (HWVoiceOut *hw) +static int qpa_run_out (HWVoiceOut *hw, int live) { int decr; - int live; PAVoiceOut *pa = (PAVoiceOut *) hw; - live = audio_pcm_hw_get_live_out (hw); - if (!live) { - return 0; - } - if (audio_pt_lock (&pa->pt, AUDIO_FUNC)) { return 0; } diff --git a/audio/wavaudio.c b/audio/wavaudio.c index a82997e..62ba42d 100644 --- a/audio/wavaudio.c +++ b/audio/wavaudio.c @@ -55,10 +55,10 @@ static struct { "qemu.wav" }; -static int wav_out_run (HWVoiceOut *hw) +static int wav_out_run (HWVoiceOut *hw, int live) { WAVVoiceOut *wav = (WAVVoiceOut *) hw; - int rpos, live, decr, samples; + int rpos, decr, samples; uint8_t *dst; struct st_sample *src; int64_t now = qemu_get_clock (vm_clock); @@ -73,11 +73,6 @@ static int wav_out_run (HWVoiceOut *hw) samples = bytes >> hw->info.shift; } - live = audio_pcm_hw_get_live_out (hw); - if (!live) { - return 0; - } - wav->old_ticks = now; decr = audio_MIN (live, samples); samples = decr; diff --git a/audio/winaudio.c b/audio/winaudio.c index ca6c487..75f6af2 100644 --- a/audio/winaudio.c +++ b/audio/winaudio.c @@ -251,12 +251,11 @@ winaudio_out_init (HWVoiceOut *hw, struct audsettings *as) static int
-winaudio_out_run (HWVoiceOut *hw)
+winaudio_out_run (HWVoiceOut *hw, int live)
{
WinAudioOut* s = (WinAudioOut*) hw;
int played = 0;
int has_buffer;
- int live = audio_pcm_hw_get_live_out (hw);
if (!live) {
return 0;
diff --git a/cpu-common.h b/cpu-common.h index d48ba5b..a422689 100644 --- a/cpu-common.h +++ b/cpu-common.h @@ -47,8 +47,8 @@ void *qemu_get_ram_ptr(ram_addr_t addr); /* This should not be used by devices. */ ram_addr_t qemu_ram_addr_from_host(void *ptr); -int cpu_register_io_memory(CPUReadMemoryFunc **mem_read, - CPUWriteMemoryFunc **mem_write, +int cpu_register_io_memory(CPUReadMemoryFunc * const *mem_read, + CPUWriteMemoryFunc * const *mem_write, void *opaque); void cpu_unregister_io_memory(int table_address); @@ -226,21 +226,21 @@ static void map_exec(void *addr, long size) DWORD old_protect; VirtualProtect(addr, size, PAGE_EXECUTE_READWRITE, &old_protect); - + } #else static void map_exec(void *addr, long size) { unsigned long start, end, page_size; - + page_size = getpagesize(); start = (unsigned long)addr; start &= ~(page_size - 1); - + end = (unsigned long)addr + size; end += page_size - 1; end &= ~(page_size - 1); - + mprotect((void *)start, end - start, PROT_READ | PROT_WRITE | PROT_EXEC); } @@ -440,7 +440,7 @@ static void code_gen_alloc(unsigned long tb_size) code_gen_buffer_size = MIN_CODE_GEN_BUFFER_SIZE; /* The code gen buffer location may have constraints depending on the host cpu and OS */ -#if defined(__linux__) +#if defined(__linux__) { int flags; void *start = NULL; @@ -463,6 +463,13 @@ static void code_gen_alloc(unsigned long tb_size) start = (void *) 0x01000000UL; if (code_gen_buffer_size > 16 * 1024 * 1024) code_gen_buffer_size = 16 * 1024 * 1024; +#elif defined(__s390x__) + /* Map the buffer so that we can use direct calls and branches. */ + /* We have a +- 4GB range on the branches; leave some slop. */ + if (code_gen_buffer_size > (3ul * 1024 * 1024 * 1024)) { + code_gen_buffer_size = 3ul * 1024 * 1024 * 1024; + } + start = (void *)0x90000000UL; #endif code_gen_buffer = mmap(start, code_gen_buffer_size, PROT_WRITE | PROT_READ | PROT_EXEC, @@ -487,7 +494,7 @@ static void code_gen_alloc(unsigned long tb_size) code_gen_buffer_size = (800 * 1024 * 1024); #endif code_gen_buffer = mmap(addr, code_gen_buffer_size, - PROT_WRITE | PROT_READ | PROT_EXEC, + PROT_WRITE | PROT_READ | PROT_EXEC, flags, -1, 0); if (code_gen_buffer == MAP_FAILED) { fprintf(stderr, "Could not allocate dynamic translator buffer\n"); @@ -1712,6 +1719,14 @@ void cpu_abort(CPUState *env, const char *fmt, ...) } va_end(ap2); va_end(ap); +#if defined(CONFIG_USER_ONLY) + { + struct sigaction act; + sigfillset(&act.sa_mask); + act.sa_handler = SIG_DFL; + sigaction(SIGABRT, &act, NULL); + } +#endif abort(); } @@ -1758,12 +1773,12 @@ static inline void tlb_flush_jmp_cache(CPUState *env, target_ulong addr) /* Discard jump cache entries for any tb which might potentially overlap the flushed page. */ i = tb_jmp_cache_hash_page(addr - TARGET_PAGE_SIZE); - memset (&env->tb_jmp_cache[i], 0, - TB_JMP_PAGE_SIZE * sizeof(TranslationBlock *)); + memset (&env->tb_jmp_cache[i], 0, + TB_JMP_PAGE_SIZE * sizeof(TranslationBlock *)); i = tb_jmp_cache_hash_page(addr); - memset (&env->tb_jmp_cache[i], 0, - TB_JMP_PAGE_SIZE * sizeof(TranslationBlock *)); + memset (&env->tb_jmp_cache[i], 0, + TB_JMP_PAGE_SIZE * sizeof(TranslationBlock *)); } /* NOTE: if flush_global is true, also flush global entries (not @@ -2198,9 +2213,9 @@ int page_get_flags(target_ulong address) return p->flags; } -/* modify the flags of a page and invalidate the code if - necessary. The flag PAGE_WRITE_ORG is positioned automatically - depending on PAGE_WRITE */ +/* Modify the flags of a page and invalidate the code if necessary. + The flag PAGE_WRITE_ORG is positioned automatically depending + on PAGE_WRITE. The mmap_lock should already be held. */ void page_set_flags(target_ulong start, target_ulong end, int flags) { PageDesc *p; @@ -2626,13 +2641,13 @@ static void unassigned_mem_writel(void *opaque, target_phys_addr_t addr, uint32_ #endif } -static CPUReadMemoryFunc *unassigned_mem_read[3] = { +static CPUReadMemoryFunc * const unassigned_mem_read[3] = { unassigned_mem_readb, unassigned_mem_readw, unassigned_mem_readl, }; -static CPUWriteMemoryFunc *unassigned_mem_write[3] = { +static CPUWriteMemoryFunc * const unassigned_mem_write[3] = { unassigned_mem_writeb, unassigned_mem_writew, unassigned_mem_writel, @@ -2698,13 +2713,13 @@ static void notdirty_mem_writel(void *opaque, target_phys_addr_t ram_addr, tlb_set_dirty(cpu_single_env, cpu_single_env->mem_io_vaddr); } -static CPUReadMemoryFunc *error_mem_read[3] = { +static CPUReadMemoryFunc * const error_mem_read[3] = { NULL, /* never used */ NULL, /* never used */ NULL, /* never used */ }; -static CPUWriteMemoryFunc *notdirty_mem_write[3] = { +static CPUWriteMemoryFunc * const notdirty_mem_write[3] = { notdirty_mem_writeb, notdirty_mem_writew, notdirty_mem_writel, @@ -2797,13 +2812,13 @@ static void watch_mem_writel(void *opaque, target_phys_addr_t addr, stl_phys(addr, val); } -static CPUReadMemoryFunc *watch_mem_read[3] = { +static CPUReadMemoryFunc * const watch_mem_read[3] = { watch_mem_readb, watch_mem_readw, watch_mem_readl, }; -static CPUWriteMemoryFunc *watch_mem_write[3] = { +static CPUWriteMemoryFunc * const watch_mem_write[3] = { watch_mem_writeb, watch_mem_writew, watch_mem_writel, @@ -2895,13 +2910,13 @@ static void subpage_writel (void *opaque, subpage_writelen(opaque, addr, value, 2); } -static CPUReadMemoryFunc *subpage_read[] = { +static CPUReadMemoryFunc * const subpage_read[] = { &subpage_readb, &subpage_readw, &subpage_readl, }; -static CPUWriteMemoryFunc *subpage_write[] = { +static CPUWriteMemoryFunc * const subpage_write[] = { &subpage_writeb, &subpage_writew, &subpage_writel, @@ -2982,8 +2997,8 @@ static int get_free_io_mem_idx(void) value can be used with cpu_register_physical_memory(). (-1) is returned if error. */ static int cpu_register_io_memory_fixed(int io_index, - CPUReadMemoryFunc **mem_read, - CPUWriteMemoryFunc **mem_write, + CPUReadMemoryFunc * const *mem_read, + CPUWriteMemoryFunc * const *mem_write, void *opaque) { int i, subwidth = 0; @@ -3008,8 +3023,8 @@ static int cpu_register_io_memory_fixed(int io_index, return (io_index << IO_MEM_SHIFT) | subwidth; } -int cpu_register_io_memory(CPUReadMemoryFunc **mem_read, - CPUWriteMemoryFunc **mem_write, +int cpu_register_io_memory(CPUReadMemoryFunc * const *mem_read, + CPUWriteMemoryFunc * const *mem_write, void *opaque) { return cpu_register_io_memory_fixed(0, mem_read, mem_write, opaque); @@ -3625,7 +3640,7 @@ void cpu_io_recompile(CPUState *env, void *retaddr) tb = tb_find_pc((unsigned long)retaddr); if (!tb) { - cpu_abort(env, "cpu_io_recompile: could not find TB for pc=%p", + cpu_abort(env, "cpu_io_recompile: could not find TB for pc=%p", retaddr); } n = env->icount_decr.u16.low + tb->icount; @@ -3673,6 +3688,8 @@ void cpu_io_recompile(CPUState *env, void *retaddr) cpu_resume_from_signal(env, NULL); } +#if !defined(CONFIG_USER_ONLY) + void dump_exec_info(FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...)) { @@ -3703,7 +3720,7 @@ void dump_exec_info(FILE *f, cpu_fprintf(f, "Translation buffer state:\n"); cpu_fprintf(f, "gen code size %ld/%ld\n", code_gen_ptr - code_gen_buffer, code_gen_buffer_max_size); - cpu_fprintf(f, "TB count %d/%d\n", + cpu_fprintf(f, "TB count %d/%d\n", nb_tbs, code_gen_max_blocks); cpu_fprintf(f, "TB avg target size %d max=%d bytes\n", nb_tbs ? target_code_size / nb_tbs : 0, @@ -3726,8 +3743,6 @@ void dump_exec_info(FILE *f, tcg_dump_info(f, cpu_fprintf); } -#if !defined(CONFIG_USER_ONLY) - #define MMUSUFFIX _cmmu #define GETPC() NULL #define env cpu_single_env diff --git a/hw/goldfish_audio.c b/hw/goldfish_audio.c index 37e4c09..434522c 100644 --- a/hw/goldfish_audio.c +++ b/hw/goldfish_audio.c @@ -582,7 +582,7 @@ void goldfish_audio_init(uint32_t base, int id, const char* input_source) if (android_hw->hw_audioOutput) { s->voice = AUD_open_out ( &s->card, - s->voice, + NULL, "goldfish_audio", s, goldfish_audio_callback, @@ -7,7 +7,7 @@ hxtoh() case $str in HXCOMM*) ;; - STEXI*|ETEXI*) flag=$(($flag^1)) + STEXI*|ETEXI*|SQMP*|EQMP*) flag=$(($flag^1)) ;; *) test $flag -eq 1 && printf "%s\n" "$str" @@ -19,11 +19,30 @@ hxtoh() hxtotexi() { flag=0 + line=1 while read -r str; do case "$str" in HXCOMM*) ;; - STEXI*|ETEXI*) flag=$(($flag^1)) + STEXI*) + if test $flag -eq 1 ; then + echo "line $line: syntax error: expected ETEXI, found $str" >&2 + exit 1 + fi + flag=1 + ;; + ETEXI*) + if test $flag -ne 1 ; then + echo "line $line: syntax error: expected STEXI, found $str" >&2 + exit 1 + fi + flag=0 + ;; + SQMP*|EQMP*) + if test $flag -eq 1 ; then + echo "line $line: syntax error: expected ETEXI, found $str" >&2 + exit 1 + fi ;; DEFHEADING*) echo "$(expr "$str" : "DEFHEADING(\(.*\))")" @@ -32,12 +51,51 @@ hxtotexi() test $flag -eq 1 && echo "$str" ;; esac + line=$((line+1)) + done +} + +hxtoqmp() +{ + IFS= + flag=0 + line=1 + while read -r str; do + case "$str" in + HXCOMM*) + ;; + SQMP*) + if test $flag -eq 1 ; then + echo "line $line: syntax error: expected EQMP, found $str" >&2 + exit 1 + fi + flag=1 + ;; + EQMP*) + if test $flag -ne 1 ; then + echo "line $line: syntax error: expected SQMP, found $str" >&2 + exit 1 + fi + flag=0 + ;; + STEXI*|ETEXI*) + if test $flag -eq 1 ; then + echo "line $line: syntax error: expected EQMP, found $str" >&2 + exit 1 + fi + ;; + *) + test $flag -eq 1 && echo "$str" + ;; + esac + line=$((line+1)) done } case "$1" in "-h") hxtoh ;; "-t") hxtotexi ;; +"-q") hxtoqmp ;; *) exit 1 ;; esac diff --git a/json-lexer.c b/json-lexer.c index 072ae56..534fcf7 100644 --- a/json-lexer.c +++ b/json-lexer.c @@ -27,8 +27,12 @@ * */ +/* Building with mingw results in an error because ERROR is defined as a + * macro in this environment. Undefined it */ +#undef ERROR + enum json_lexer_state { - JSON_ERROR = 0, + ERROR = 0, IN_DQ_UCODE3, IN_DQ_UCODE2, IN_DQ_UCODE1, @@ -150,7 +154,7 @@ static const uint8_t json_lexer[][256] = { /* Zero */ [IN_ZERO] = { TERMINAL(JSON_INTEGER), - ['0' ... '9'] = JSON_ERROR, + ['0' ... '9'] = ERROR, ['.'] = IN_MANTISSA, }, @@ -302,7 +306,7 @@ static int json_lexer_feed_char(JSONLexer *lexer, char ch) lexer->token = qstring_new(); new_state = IN_START; break; - case JSON_ERROR: + case ERROR: return -EINVAL; default: break; @@ -11,9 +11,6 @@ extern Monitor *cur_mon; extern Monitor *default_mon; /* flags for monitor_init */ -#define MONITOR_IS_DEFAULT 0x01 -#define MONITOR_USE_READLINE 0x02 - #define MONITOR_IS_DEFAULT 0x01 #define MONITOR_USE_READLINE 0x02 #define MONITOR_USE_CONTROL 0x04 @@ -1427,8 +1427,11 @@ alloc_f(int argc, char **argv) cvtstr(offset, s1, sizeof(s1)); - printf("%d/%d sectors allocated at offset %s\n", - sum_alloc, nb_sectors, s1); + if (nb_sectors == 1) + printf("sector allocated at offset %s\n", s1); + else + printf("%d/%d sectors allocated at offset %s\n", + sum_alloc, nb_sectors, s1); return 0; } diff --git a/qemu-options.hx b/qemu-options.hx index aee7b6b..db3dfbb 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1598,7 +1598,7 @@ DEF("nand", HAS_ARG, QEMU_OPTION_nand, \ #endif /* CONFIG_TRACE */ -#if 1 /* ANDROID */ +#ifdef CONFIG_ANDROID DEF("savevm-on-exit", HAS_ARG, QEMU_OPTION_savevm_on_exit, \ "savevm-on-exit [tag|id]\n" \ @@ -1653,12 +1653,6 @@ DEF("gps", HAS_ARG, QEMU_OPTION_gps, \ DEF("audio", HAS_ARG, QEMU_OPTION_audio, \ "-audio <backend> use specific audio backend\n") -DEF("audio-in", HAS_ARG, QEMU_OPTION_audio_in, \ - "-audio-in <backend> use specific audio input backend\n") - -DEF("audio-out", HAS_ARG, QEMU_OPTION_audio_out, \ - "-audio-out <backend> use specific audio output backend\n") - DEF("cpu-delay", HAS_ARG, QEMU_OPTION_cpu_delay, \ "-cpu-delay <cpudelay> throttle CPU emulation\n") @@ -1694,4 +1688,7 @@ DEF("ui-port", HAS_ARG, QEMU_OPTION_ui_port, \ DEF("ui-settings", HAS_ARG, QEMU_OPTION_ui_settings, \ "-ui-settings <string> opaque string containing persitent UI settings\n") -#endif +DEF("audio-test-out", 0, QEMU_OPTION_audio_test_out, \ + "-audio-test-out Test audio output\n") + +#endif /* ANDROID */ diff --git a/qemu-sockets-android.c b/qemu-sockets-android.c index dc64775..b07129b 100644 --- a/qemu-sockets-android.c +++ b/qemu-sockets-android.c @@ -26,6 +26,10 @@ # define AI_ADDRCONFIG 0 #endif +#ifndef INET6_ADDRSTRLEN +# define INET6_ADDRSTRLEN 46 +#endif + static int sockets_debug = 0; static const int on=1, off=0; @@ -52,6 +56,11 @@ static QemuOptsList dummy_opts = { },{ .name = "ipv6", .type = QEMU_OPT_BOOL, +#ifdef CONFIG_ANDROID + },{ + .name = "socket", + .type = QEMU_OPT_NUMBER, +#endif }, { /* end if list */ } }, @@ -79,6 +88,13 @@ int inet_listen_opts(QemuOpts *opts, int port_offset) char uport[33]; int slisten,to,try_next,nn; +#ifdef CONFIG_ANDROID + const char* socket_fd = qemu_opt_get(opts, "socket"); + if (socket_fd) { + return atoi(socket_fd); + } +#endif + if ((qemu_opt_get(opts, "host") == NULL) || (qemu_opt_get(opts, "port") == NULL)) { fprintf(stderr, "%s: host and/or port not specified\n", __FUNCTION__); @@ -177,6 +193,13 @@ int inet_connect_opts(QemuOpts *opts) const char *port; int sock, nn; +#ifdef CONFIG_ANDROID + const char* socket_fd = qemu_opt_get(opts, "socket"); + if (socket_fd) { + return atoi(socket_fd); + } +#endif + addr = qemu_opt_get(opts, "host"); port = qemu_opt_get(opts, "port"); if (addr == NULL || port == NULL) { @@ -233,6 +256,115 @@ EXIT: return sock; } +int inet_dgram_opts(QemuOpts *opts) +{ + SockAddress** peer_list = NULL; + SockAddress** local_list = NULL; + SockAddress* e; + unsigned flags = 0; + const char *addr; + const char *port; + char uaddr[INET6_ADDRSTRLEN+1]; + char uport[33]; + int sock = -1; + int nn; + + /* lookup peer addr */ + addr = qemu_opt_get(opts, "host"); + port = qemu_opt_get(opts, "port"); + if (addr == NULL || strlen(addr) == 0) { + addr = "localhost"; + } + if (port == NULL || strlen(port) == 0) { + fprintf(stderr, "inet_dgram: port not specified\n"); + return -1; + } + + flags = SOCKET_LIST_DGRAM; + if (qemu_opt_get_bool(opts, "ipv4", 0)) { + flags &= SOCKET_LIST_FORCE_IN6; + flags |= SOCKET_LIST_FORCE_INET; + } + if (qemu_opt_get_bool(opts, "ipv6", 0)) { + flags &= SOCKET_LIST_FORCE_INET; + flags |= SOCKET_LIST_FORCE_IN6; + } + + peer_list = sock_address_list_create(addr, port, flags); + if (peer_list == NULL) { + fprintf(stderr,"getaddrinfo(%s,%s): %s\n", + addr, port, errno_str); + return -1; + } + + /* lookup local addr */ + addr = qemu_opt_get(opts, "localaddr"); + port = qemu_opt_get(opts, "localport"); + if (addr == NULL || strlen(addr) == 0) { + addr = NULL; + } + if (!port || strlen(port) == 0) + port = "0"; + + flags = SOCKET_LIST_DGRAM | SOCKET_LIST_PASSIVE; + local_list = sock_address_list_create(addr, port, flags); + if (local_list == NULL) { + fprintf(stderr,"getaddrinfo(%s,%s): %s\n", + addr, port, errno_str); + goto EXIT; + } + + if (sock_address_get_numeric_info(local_list[0], + uaddr, INET6_ADDRSTRLEN, + uport, 32)) { + fprintf(stderr, "%s: getnameinfo: oops\n", __FUNCTION__); + goto EXIT; + } + + for (nn = 0; peer_list[nn] != NULL; nn++) { + SockAddress *local = local_list[0]; + e = peer_list[nn]; + sock = socket_create(sock_address_get_family(e), SOCKET_DGRAM); + if (sock < 0) { + fprintf(stderr,"%s: socket(%s): %s\n", __FUNCTION__, + sock_address_strfamily(e), errno_str); + continue; + } + socket_set_xreuseaddr(sock); + + /* bind socket */ + if (socket_bind(sock, local) < 0) { + fprintf(stderr,"%s: bind(%s,%s,%s): OK\n", __FUNCTION__, + sock_address_strfamily(local), addr, port); + socket_close(sock); + continue; + } + + /* connect to peer */ + if (socket_connect(sock,e) < 0) { + if (sockets_debug) + fprintf(stderr, "%s: connect(%s,%s,%s,%s): %s\n", __FUNCTION__, + sock_address_strfamily(e), + sock_address_to_string(e), addr, port, strerror(errno)); + socket_close(sock); + continue; + } + if (sockets_debug) + fprintf(stderr, "%s: connect(%s,%s,%s,%s): OK\n", __FUNCTION__, + sock_address_strfamily(e), + sock_address_to_string(e), addr, port); + + goto EXIT; + } + sock = -1; +EXIT: + if (local_list) + sock_address_list_free(local_list); + if (peer_list) + sock_address_list_free(peer_list); + return sock; +} + /* compatibility wrapper */ static int inet_parse(QemuOpts *opts, const char *str) { @@ -286,6 +418,25 @@ static int inet_parse(QemuOpts *opts, const char *str) qemu_opt_set(opts, "ipv4", "on"); if (strstr(optstr, ",ipv6")) qemu_opt_set(opts, "ipv6", "on"); +#ifdef CONFIG_ANDROID + h = strstr(optstr, ",socket="); + if (h) { + int socket_fd; + char str_fd[12]; + if (1 != sscanf(h+7,"%d",&socket_fd)) { + fprintf(stderr,"%s: socket fd parse error (%s)\n", + __FUNCTION__, h+7); + return -1; + } + if (socket_fd < 0 || socket_fd >= INT_MAX) { + fprintf(stderr,"%s: socket fd range error (%d)\n", + __FUNCTION__, socket_fd); + return -1; + } + snprintf(str_fd, sizeof str_fd, "%d", socket_fd); + qemu_opt_set(opts, "socket", str_fd); + } +#endif return 0; } diff --git a/qemu-sockets.c b/qemu-sockets.c index c526324..c4c0f65 100644 --- a/qemu-sockets.c +++ b/qemu-sockets.c @@ -52,6 +52,11 @@ static QemuOptsList dummy_opts = { },{ .name = "ipv6", .type = QEMU_OPT_BOOL, +#ifdef CONFIG_ANDROID + },{ + .name = "socket", + .type = QEMU_OPT_NUMBER, +#endif }, { /* end if list */ } }, @@ -130,6 +135,13 @@ int inet_listen_opts(QemuOpts *opts, int port_offset) ai.ai_family = PF_UNSPEC; ai.ai_socktype = SOCK_STREAM; +#ifdef CONFIG_ANDROID + const char* socket_fd = qemu_opt_get(opts, "socket"); + if (socket_fd) { + return atoi(socket_fd); + } +#endif + if ((qemu_opt_get(opts, "host") == NULL) || (qemu_opt_get(opts, "port") == NULL)) { fprintf(stderr, "%s: host and/or port not specified\n", __FUNCTION__); @@ -226,6 +238,13 @@ int inet_connect_opts(QemuOpts *opts) char uport[33]; int sock,rc; +#ifdef CONFIG_ANDROID + const char* socket_fd = qemu_opt_get(opts, "socket"); + if (socket_fd) { + return atoi(socket_fd); + } +#endif + memset(&ai,0, sizeof(ai)); ai.ai_flags = AI_CANONNAME | AI_ADDRCONFIG; ai.ai_family = PF_UNSPEC; @@ -454,6 +473,25 @@ static int inet_parse(QemuOpts *opts, const char *str) qemu_opt_set(opts, "ipv4", "on"); if (strstr(optstr, ",ipv6")) qemu_opt_set(opts, "ipv6", "on"); +#ifdef CONFIG_ANDROID + h = strstr(optstr, ",socket="); + if (h) { + int socket_fd; + char str_fd[12]; + if (1 != sscanf(h+7,"%d",&socket_fd)) { + fprintf(stderr,"%s: socket fd parse error (%s)\n", + __FUNCTION__, h+7); + return -1; + } + if (socket_fd < 0 || socket_fd >= INT_MAX) { + fprintf(stderr,"%s: socket fd range error (%d)\n", + __FUNCTION__, socket_fd); + return -1; + } + snprintf(str_fd, sizeof str_fd, "%d", socket_fd); + qemu_opt_set(opts, "socket", str_fd); + } +#endif return 0; } @@ -750,6 +750,9 @@ sock_address_list_create( const char* hostname, else ai.ai_flags |= AI_CANONNAME; + if (flags & SOCKET_LIST_DGRAM) + ai.ai_socktype = SOCK_DGRAM; + while (1) { struct addrinfo hints = ai; @@ -258,7 +258,8 @@ int sock_address_get_numeric_info( SockAddress* a, enum { SOCKET_LIST_PASSIVE = (1 << 0), SOCKET_LIST_FORCE_INET = (1 << 1), - SOCKET_LIST_FORCE_IN6 = (1 << 2) + SOCKET_LIST_FORCE_IN6 = (1 << 2), + SOCKET_LIST_DGRAM = (1 << 3), }; /* resolve a host and service/port name into a list of SockAddress objects. diff --git a/vl-android.c b/vl-android.c index 1b79404..dd7a255 100644 --- a/vl-android.c +++ b/vl-android.c @@ -191,6 +191,7 @@ #include "android/hw-lcd.h" #include "android/boot-properties.h" #include "android/core-init-utils.h" +#include "android/audio-test.h" #ifdef CONFIG_STANDALONE_CORE /* Verbose value used by the standalone emulator core (without UI) */ @@ -350,12 +351,6 @@ char* android_op_gps = NULL; /* -audio option value. */ char* android_op_audio = NULL; -/* -audio-in option value. */ -char* android_op_audio_in = NULL; - -/* -audio-out option value. */ -char* android_op_audio_out = NULL; - /* -cpu-delay option value. */ char* android_op_cpu_delay = NULL; @@ -2162,110 +2157,6 @@ void qemu_service_io(void) } /***********************************************************/ -/* bottom halves (can be seen as timers which expire ASAP) */ - -struct QEMUBH { - QEMUBHFunc *cb; - void *opaque; - int scheduled; - int idle; - int deleted; - QEMUBH *next; -}; - -static QEMUBH *first_bh = NULL; - -QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque) -{ - QEMUBH *bh; - bh = qemu_mallocz(sizeof(QEMUBH)); - bh->cb = cb; - bh->opaque = opaque; - bh->next = first_bh; - first_bh = bh; - return bh; -} - -int qemu_bh_poll(void) -{ - QEMUBH *bh, **bhp; - int ret; - - ret = 0; - for (bh = first_bh; bh; bh = bh->next) { - if (!bh->deleted && bh->scheduled) { - bh->scheduled = 0; - if (!bh->idle) - ret = 1; - bh->idle = 0; - bh->cb(bh->opaque); - } - } - - /* remove deleted bhs */ - bhp = &first_bh; - while (*bhp) { - bh = *bhp; - if (bh->deleted) { - *bhp = bh->next; - qemu_free(bh); - } else - bhp = &bh->next; - } - - return ret; -} - -void qemu_bh_schedule_idle(QEMUBH *bh) -{ - if (bh->scheduled) - return; - bh->scheduled = 1; - bh->idle = 1; -} - -void qemu_bh_schedule(QEMUBH *bh) -{ - if (bh->scheduled) - return; - bh->scheduled = 1; - bh->idle = 0; - /* stop the currently executing CPU to execute the BH ASAP */ - qemu_notify_event(); -} - -void qemu_bh_cancel(QEMUBH *bh) -{ - bh->scheduled = 0; -} - -void qemu_bh_delete(QEMUBH *bh) -{ - bh->scheduled = 0; - bh->deleted = 1; -} - -void qemu_bh_update_timeout(int *timeout) -{ - QEMUBH *bh; - - for (bh = first_bh; bh; bh = bh->next) { - if (!bh->deleted && bh->scheduled) { - if (bh->idle) { - /* idle bottom halves will be polled at least - * every 10ms */ - *timeout = MIN(10, *timeout); - } else { - /* non-idle bottom halves will be executed - * immediately */ - *timeout = 0; - break; - } - } - } -} - -/***********************************************************/ /* machine registration */ static QEMUMachine *first_machine = NULL; @@ -4601,14 +4492,6 @@ int main(int argc, char **argv, char **envp) android_op_audio = (char*)optarg; break; - case QEMU_OPTION_audio_in: - android_op_audio_in = (char*)optarg; - break; - - case QEMU_OPTION_audio_out: - android_op_audio_out = (char*)optarg; - break; - case QEMU_OPTION_cpu_delay: android_op_cpu_delay = (char*)optarg; break; @@ -4655,6 +4538,10 @@ int main(int argc, char **argv, char **envp) android_op_ui_settings = (char*)optarg; break; + case QEMU_OPTION_audio_test_out: + android_audio_test_start_out(); + break; + #ifdef CONFIG_MEMCHECK case QEMU_OPTION_android_memcheck: android_op_memcheck = (char*)optarg; @@ -4788,48 +4675,10 @@ int main(int argc, char **argv, char **envp) /* Initialize audio. */ if (android_op_audio) { - if (android_op_audio_in || android_op_audio_out) { - PANIC("you can't use -audio with -audio-in or -audio-out"); - } if ( !audio_check_backend_name( 0, android_op_audio ) ) { PANIC("'%s' is not a valid audio output backend. see -help-audio-out", android_op_audio); } - android_op_audio_out = android_op_audio; - android_op_audio_in = android_op_audio; - - if ( !audio_check_backend_name( 1, android_op_audio ) ) { - fprintf(stdout, - "emulator: warning: '%s' is not a valid audio input backend. audio record disabled\n", - android_op_audio); - android_op_audio_in = "none"; - } - } - - if (android_op_audio_in) { - static char env[64]; /* note: putenv needs a static unique string buffer */ - if ( !audio_check_backend_name( 1, android_op_audio_in ) ) { - PANIC("'%s' is not a valid audio input backend. see -help-audio-in", - android_op_audio_in); - } - bufprint( env, env+sizeof(env), "QEMU_AUDIO_IN_DRV=%s", android_op_audio_in ); - putenv( env ); - - if (!android_hw->hw_audioInput) { - fprintf(stdout, "Emulated hardware doesn't have audio input.\n"); - } - } - if (android_op_audio_out) { - static char env[64]; /* note: putenv needs a static unique string buffer */ - if ( !audio_check_backend_name( 0, android_op_audio_out ) ) { - PANIC("'%s' is not a valid audio output backend. see -help-audio-out", - android_op_audio_out); - } - bufprint( env, env+sizeof(env), "QEMU_AUDIO_OUT_DRV=%s", android_op_audio_out ); - putenv( env ); - if (!android_hw->hw_audioOutput) { - fprintf(stdout, "Emulated hardware doesn't have audio output\n"); - } } if (android_op_cpu_delay) { @@ -375,7 +375,7 @@ void hw_error(const char *fmt, ...) va_end(ap); abort(); } - + /***************/ /* ballooning */ @@ -2014,7 +2014,7 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id) if (ram_load_dead(f, opaque) < 0) return -EINVAL; } - + if (flags & RAM_SAVE_FLAG_COMPRESS) { uint8_t ch = qemu_get_byte(f); memset(qemu_get_ram_ptr(addr), ch, TARGET_PAGE_SIZE); @@ -2031,110 +2031,6 @@ void qemu_service_io(void) } /***********************************************************/ -/* bottom halves (can be seen as timers which expire ASAP) */ - -struct QEMUBH { - QEMUBHFunc *cb; - void *opaque; - int scheduled; - int idle; - int deleted; - QEMUBH *next; -}; - -static QEMUBH *first_bh = NULL; - -QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque) -{ - QEMUBH *bh; - bh = qemu_mallocz(sizeof(QEMUBH)); - bh->cb = cb; - bh->opaque = opaque; - bh->next = first_bh; - first_bh = bh; - return bh; -} - -int qemu_bh_poll(void) -{ - QEMUBH *bh, **bhp; - int ret; - - ret = 0; - for (bh = first_bh; bh; bh = bh->next) { - if (!bh->deleted && bh->scheduled) { - bh->scheduled = 0; - if (!bh->idle) - ret = 1; - bh->idle = 0; - bh->cb(bh->opaque); - } - } - - /* remove deleted bhs */ - bhp = &first_bh; - while (*bhp) { - bh = *bhp; - if (bh->deleted) { - *bhp = bh->next; - qemu_free(bh); - } else - bhp = &bh->next; - } - - return ret; -} - -void qemu_bh_schedule_idle(QEMUBH *bh) -{ - if (bh->scheduled) - return; - bh->scheduled = 1; - bh->idle = 1; -} - -void qemu_bh_schedule(QEMUBH *bh) -{ - if (bh->scheduled) - return; - bh->scheduled = 1; - bh->idle = 0; - /* stop the currently executing CPU to execute the BH ASAP */ - qemu_notify_event(); -} - -void qemu_bh_cancel(QEMUBH *bh) -{ - bh->scheduled = 0; -} - -void qemu_bh_delete(QEMUBH *bh) -{ - bh->scheduled = 0; - bh->deleted = 1; -} - -void qemu_bh_update_timeout(int *timeout) -{ - QEMUBH *bh; - - for (bh = first_bh; bh; bh = bh->next) { - if (!bh->deleted && bh->scheduled) { - if (bh->idle) { - /* idle bottom halves will be polled at least - * every 10ms */ - *timeout = MIN(10, *timeout); - } else { - /* non-idle bottom halves will be executed - * immediately */ - *timeout = 0; - break; - } - } - } -} - -/***********************************************************/ /* machine registration */ static QEMUMachine *first_machine = NULL; @@ -3860,7 +3756,7 @@ int main(int argc, char **argv, char **envp) { /* Could easily be extended to 64 devices if needed */ const char *p; - + boot_devices_bitmap = 0; for (p = boot_devices; *p != '\0'; p++) { /* Allowed boot devices are: @@ -4716,7 +4612,7 @@ int main(int argc, char **argv, char **envp) show_vnc_port = 1; #endif } - + switch (display_type) { case DT_NOGRAPHIC: |