From patchwork Thu Jul 31 12:57:31 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pavel Dovgalyuk X-Patchwork-Id: 375287 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id BFF0A1400AF for ; Thu, 31 Jul 2014 23:15:32 +1000 (EST) Received: from localhost ([::1]:56546 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1XCqCg-0006sl-Jd for incoming@patchwork.ozlabs.org; Thu, 31 Jul 2014 09:15:30 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:49382) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1XCpvL-0001wO-G9 for qemu-devel@nongnu.org; Thu, 31 Jul 2014 08:57:41 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1XCpvF-0006eb-An for qemu-devel@nongnu.org; Thu, 31 Jul 2014 08:57:35 -0400 Received: from mail.ispras.ru ([83.149.199.45]:54899) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1XCpvE-0006eJ-Qc for qemu-devel@nongnu.org; Thu, 31 Jul 2014 08:57:29 -0400 Received: from PASHA-ISP.novsu.ac.ru (unknown [80.250.189.177]) by mail.ispras.ru (Postfix) with ESMTPSA id DF296540151; Thu, 31 Jul 2014 16:57:27 +0400 (MSK) To: qemu-devel@nongnu.org From: Pavel Dovgalyuk Date: Thu, 31 Jul 2014 16:57:31 +0400 Message-ID: <20140731125731.1600.17685.stgit@PASHA-ISP.novsu.ac.ru> In-Reply-To: <20140731125321.1600.46604.stgit@PASHA-ISP.novsu.ac.ru> References: <20140731125321.1600.46604.stgit@PASHA-ISP.novsu.ac.ru> User-Agent: StGit/0.16 MIME-Version: 1.0 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 83.149.199.45 Cc: peter.maydell@linaro.org, peter.crosthwaite@xilinx.com, mark.burton@greensocs.com, real@ispras.ru, batuzovk@ispras.ru, maria.klimushenkova@ispras.ru, pavel.dovgaluk@ispras.ru, pbonzini@redhat.com, afaerber@suse.de, fred.konrad@greensocs.com Subject: [Qemu-devel] [RFC PATCH v3 43/49] replay: audio data record/replay X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org This patch adds deterministic replay for audio adapter. Replay module saves data from the microphone and "end-of-playback" events. Support of audio record and replay is implemented only for Win32 hosts. Signed-off-by: Pavel Dovgalyuk --- audio/audio.c | 14 ++- audio/audio_win_int.h | 3 + audio/winwaveaudio.c | 167 ++++++++++++++++++++++++++-------- replay/Makefile.objs | 1 replay/replay-audio.c | 228 ++++++++++++++++++++++++++++++++++++++++++++++ replay/replay-internal.c | 4 + replay/replay-internal.h | 14 +++ replay/replay.h | 21 ++++ 8 files changed, 409 insertions(+), 43 deletions(-) create mode 100755 replay/replay-audio.c diff --git a/audio/audio.c b/audio/audio.c index 9d018e9..72d7a8a 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -26,6 +26,7 @@ #include "monitor/monitor.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" +#include "replay/replay.h" #define AUDIO_CAP "audio" #include "audio_int.h" @@ -1201,7 +1202,9 @@ void AUD_set_active_out (SWVoiceOut *sw, int on) if (!hw->enabled) { hw->enabled = 1; if (s->vm_running) { - hw->pcm_ops->ctl_out (hw, VOICE_ENABLE, conf.try_poll_out); + hw->pcm_ops->ctl_out(hw, VOICE_ENABLE, + replay_mode == REPLAY_MODE_NONE + ? conf.try_poll_out : 0); audio_reset_timer (s); } } @@ -1763,11 +1766,13 @@ static void audio_vm_change_state_handler (void *opaque, int running, s->vm_running = running; while ((hwo = audio_pcm_hw_find_any_enabled_out (hwo))) { - hwo->pcm_ops->ctl_out (hwo, op, conf.try_poll_out); + hwo->pcm_ops->ctl_out(hwo, op, replay_mode == REPLAY_MODE_NONE + ? conf.try_poll_out : 0); } while ((hwi = audio_pcm_hw_find_any_enabled_in (hwi))) { - hwi->pcm_ops->ctl_in (hwi, op, conf.try_poll_in); + hwi->pcm_ops->ctl_in(hwi, op, replay_mode == REPLAY_MODE_NONE + ? conf.try_poll_in : 0); } audio_reset_timer (s); } @@ -1810,9 +1815,10 @@ static void audio_atexit (void) static const VMStateDescription vmstate_audio = { .name = "audio", - .version_id = 1, + .version_id = 2, .minimum_version_id = 1, .fields = (VMStateField[]) { + VMSTATE_TIMER_V(ts, AudioState, 2), VMSTATE_END_OF_LIST() } }; diff --git a/audio/audio_win_int.h b/audio/audio_win_int.h index fa5b3cb..0525ae6 100644 --- a/audio/audio_win_int.h +++ b/audio/audio_win_int.h @@ -7,4 +7,7 @@ int waveformat_from_audio_settings (WAVEFORMATEX *wfx, int waveformat_to_audio_settings (WAVEFORMATEX *wfx, struct audsettings *as); +void winwave_callback_out_impl(void *dwInstance, WAVEHDR *h); +void winwave_callback_in_impl(void *dwInstance, WAVEHDR *h); + #endif /* AUDIO_WIN_INT_H */ diff --git a/audio/winwaveaudio.c b/audio/winwaveaudio.c index 8dbd145..40b9a89 100644 --- a/audio/winwaveaudio.c +++ b/audio/winwaveaudio.c @@ -2,7 +2,9 @@ #include "qemu-common.h" #include "sysemu/sysemu.h" +#include "migration/vmstate.h" #include "audio.h" +#include "replay/replay.h" #define AUDIO_CAP "winwave" #include "audio_int.h" @@ -47,6 +49,7 @@ typedef struct { int paused; int rpos; int avail; + int non_added; CRITICAL_SECTION crit_sect; } WaveVoiceIn; @@ -124,6 +127,24 @@ static void winwave_anal_close_out (WaveVoiceOut *wave) wave->hwo = NULL; } +void winwave_callback_out_impl(void *dwInstance, WAVEHDR *h) +{ + WaveVoiceOut *wave = (WaveVoiceOut *)dwInstance; + if (!h->dwUser) { + h->dwUser = 1; + EnterCriticalSection(&wave->crit_sect); + { + wave->avail += conf.dac_samples; + } + LeaveCriticalSection(&wave->crit_sect); + if (wave->hw.poll_mode) { + if (!SetEvent(wave->event)) { + dolog("DAC SetEvent failed %lx\n", GetLastError()); + } + } + } +} + static void CALLBACK winwave_callback_out ( HWAVEOUT hwo, UINT msg, @@ -132,24 +153,16 @@ static void CALLBACK winwave_callback_out ( DWORD_PTR dwParam2 ) { - WaveVoiceOut *wave = (WaveVoiceOut *) dwInstance; - switch (msg) { case WOM_DONE: { - WAVEHDR *h = (WAVEHDR *) dwParam1; - if (!h->dwUser) { - h->dwUser = 1; - EnterCriticalSection (&wave->crit_sect); - { - wave->avail += conf.dac_samples; - } - LeaveCriticalSection (&wave->crit_sect); - if (wave->hw.poll_mode) { - if (!SetEvent (wave->event)) { - dolog ("DAC SetEvent failed %lx\n", GetLastError ()); - } - } + if (replay_mode == REPLAY_MODE_RECORD) { + replay_sound_out_event((WAVEHDR *)dwParam1); + } else if (replay_mode == REPLAY_MODE_PLAY) { + /* Do nothing */ + } else { + winwave_callback_out_impl((void *)dwInstance, + (WAVEHDR *)dwParam1); } } break; @@ -163,6 +176,21 @@ static void CALLBACK winwave_callback_out ( } } +static const VMStateDescription vmstate_audio_out = { + .name = "audio_out", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_INT32(enabled, HWVoiceOut), + VMSTATE_INT32(poll_mode, HWVoiceOut), + VMSTATE_INT32(pending_disable, HWVoiceOut), + VMSTATE_INT32(rpos, HWVoiceOut), + VMSTATE_UINT64(ts_helper, HWVoiceOut), + VMSTATE_END_OF_LIST() + } +}; + static int winwave_init_out (HWVoiceOut *hw, struct audsettings *as) { int i; @@ -173,6 +201,8 @@ static int winwave_init_out (HWVoiceOut *hw, struct audsettings *as) wave = (WaveVoiceOut *) hw; + vmstate_register(NULL, 0, &vmstate_audio_out, hw); + InitializeCriticalSection (&wave->crit_sect); err = waveformat_from_audio_settings (&wfx, as); @@ -219,6 +249,10 @@ static int winwave_init_out (HWVoiceOut *hw, struct audsettings *as) } } + if (replay_mode != REPLAY_MODE_NONE) { + replay_init_sound_out(wave, wave->hdrs, conf.dac_headers); + } + return 0; err4: @@ -262,10 +296,20 @@ static int winwave_run_out (HWVoiceOut *hw, int live) WAVEHDR *h = &wave->hdrs[wave->curhdr]; h->dwUser = 0; - mr = waveOutWrite (wave->hwo, h, sizeof (*h)); - if (mr != MMSYSERR_NOERROR) { - winwave_logerr (mr, "waveOutWrite(%d)", wave->curhdr); - break; + /* Only events will be simulated in REPLAY_PLAY mode, + no sounds will be emitted */ + if (replay_mode != REPLAY_MODE_PLAY) { + mr = waveOutWrite(wave->hwo, h, sizeof(*h)); + if (mr != MMSYSERR_NOERROR) { + winwave_logerr(mr, "waveOutWrite(%d)", wave->curhdr); + break; + } + } else { + /* This header will be passed to callback function, + when event will be read in the log */ + if (replay_sound_out_event(h)) { + break; + } } wave->pending -= conf.dac_samples; @@ -382,6 +426,26 @@ static void winwave_anal_close_in (WaveVoiceIn *wave) wave->hwi = NULL; } +void winwave_callback_in_impl(void *dwInstance, WAVEHDR *h) +{ + WaveVoiceIn *wave = (WaveVoiceIn *)dwInstance; + + if (!h->dwUser) { + h->dwUser = 1; + EnterCriticalSection(&wave->crit_sect); + { + wave->avail += conf.adc_samples; + } + LeaveCriticalSection(&wave->crit_sect); + + if (wave->hw.poll_mode) { + if (!SetEvent(wave->event)) { + dolog("ADC SetEvent failed %lx\n", GetLastError()); + } + } + } +} + static void CALLBACK winwave_callback_in ( HWAVEIN *hwi, UINT msg, @@ -390,24 +454,16 @@ static void CALLBACK winwave_callback_in ( DWORD_PTR dwParam2 ) { - WaveVoiceIn *wave = (WaveVoiceIn *) dwInstance; - switch (msg) { case WIM_DATA: { - WAVEHDR *h = (WAVEHDR *) dwParam1; - if (!h->dwUser) { - h->dwUser = 1; - EnterCriticalSection (&wave->crit_sect); - { - wave->avail += conf.adc_samples; - } - LeaveCriticalSection (&wave->crit_sect); - if (wave->hw.poll_mode) { - if (!SetEvent (wave->event)) { - dolog ("ADC SetEvent failed %lx\n", GetLastError ()); - } - } + if (replay_mode == REPLAY_MODE_RECORD) { + replay_sound_in_event((WAVEHDR *)dwParam1); + } else if (replay_mode == REPLAY_MODE_PLAY) { + /* Do nothing */ + } else { + winwave_callback_in_impl((void *)dwInstance, + (WAVEHDR *)dwParam1); } } break; @@ -421,9 +477,10 @@ static void CALLBACK winwave_callback_in ( } } -static void winwave_add_buffers (WaveVoiceIn *wave, int samples) +static int winwave_add_buffers(WaveVoiceIn *wave, int samples) { int doreset; + int added = 0; doreset = wave->hw.poll_mode && (samples >= conf.adc_samples); if (doreset && !ResetEvent (wave->event)) { @@ -435,15 +492,39 @@ static void winwave_add_buffers (WaveVoiceIn *wave, int samples) WAVEHDR *h = &wave->hdrs[wave->curhdr]; h->dwUser = 0; - mr = waveInAddBuffer (wave->hwi, h, sizeof (*h)); - if (mr != MMSYSERR_NOERROR) { - winwave_logerr (mr, "waveInAddBuffer(%d)", wave->curhdr); + /* Add buffer to replay queue in replay mode - it will be + loaded from log file. */ + if (replay_mode != REPLAY_MODE_PLAY) { + mr = waveInAddBuffer(wave->hwi, h, sizeof(*h)); + if (mr != MMSYSERR_NOERROR) { + winwave_logerr(mr, "waveInAddBuffer(%d)", wave->curhdr); + } + } else { + replay_sound_in_event(h); } wave->curhdr = (wave->curhdr + 1) % conf.adc_headers; samples -= conf.adc_samples; + added += conf.adc_samples; } + + return added; } +static const VMStateDescription vmstate_audio_in = { + .name = "audio_in", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_INT32(enabled, HWVoiceIn), + VMSTATE_INT32(poll_mode, HWVoiceIn), + VMSTATE_INT32(wpos, HWVoiceIn), + VMSTATE_INT32(total_samples_captured, HWVoiceIn), + VMSTATE_UINT64(ts_helper, HWVoiceIn), + VMSTATE_END_OF_LIST() + } +}; + static int winwave_init_in (HWVoiceIn *hw, struct audsettings *as) { int i; @@ -454,6 +535,8 @@ static int winwave_init_in (HWVoiceIn *hw, struct audsettings *as) wave = (WaveVoiceIn *) hw; + vmstate_register(NULL, 0, &vmstate_audio_in, hw); + InitializeCriticalSection (&wave->crit_sect); err = waveformat_from_audio_settings (&wfx, as); @@ -478,6 +561,7 @@ static int winwave_init_in (HWVoiceIn *hw, struct audsettings *as) audio_pcm_init_info (&hw->info, as); hw->samples = conf.adc_samples * conf.adc_headers; wave->avail = 0; + wave->non_added = 0; wave->pcm_buf = audio_calloc (AUDIO_FUNC, conf.adc_samples, conf.adc_headers << hw->info.shift); @@ -500,6 +584,10 @@ static int winwave_init_in (HWVoiceIn *hw, struct audsettings *as) } } + if (replay_mode != REPLAY_MODE_NONE) { + replay_init_sound_in(wave, wave->hdrs, conf.adc_headers); + } + wave->paused = 1; winwave_add_buffers (wave, hw->samples); return 0; @@ -570,6 +658,7 @@ static int winwave_run_in (HWVoiceIn *hw) LeaveCriticalSection (&wave->crit_sect); ret = decr; + wave->non_added += ret; while (decr) { int left = hw->samples - hw->wpos; int conv = audio_MIN (left, decr); @@ -582,7 +671,7 @@ static int winwave_run_in (HWVoiceIn *hw) decr -= conv; } - winwave_add_buffers (wave, ret); + wave->non_added -= winwave_add_buffers(wave, wave->non_added); return ret; } diff --git a/replay/Makefile.objs b/replay/Makefile.objs index ad262d0..9fea604 100755 --- a/replay/Makefile.objs +++ b/replay/Makefile.objs @@ -5,3 +5,4 @@ obj-y += replay-time.o obj-y += replay-icount.o obj-y += replay-input.o obj-y += replay-net.o +obj-y += replay-audio.o diff --git a/replay/replay-audio.c b/replay/replay-audio.c new file mode 100755 index 0000000..b6bf1a1 --- /dev/null +++ b/replay/replay-audio.c @@ -0,0 +1,228 @@ +/* + * replay-audio.c + * + * Copyright (c) 2010-2014 Institute for System Programming + * of the Russian Academy of Sciences. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu-common.h" +#include "exec/cpu-common.h" +#include "replay.h" +#include "replay-internal.h" +#ifdef _WIN32 +struct audsettings; +#include "audio/audio_win_int.h" +#endif + +/* Sound card state */ +typedef struct { + void *instance; + const int event_id; +#ifdef _WIN32 + WAVEHDR *queue; +#endif + /*! Maximum size of the queue */ + int size; + /*! Current size of the queue */ + sig_atomic_t cur_size; + unsigned int head, tail; +} SoundQueue; + + +static SoundQueue sound_in = { + .event_id = EVENT_SOUND_IN + }, + sound_out = { + .event_id = EVENT_SOUND_OUT, + }; + +#ifdef _WIN32 +/*! Spinlock for sound events processing. */ +static spinlock_t sound_lock = SPIN_LOCK_UNLOCKED; +#endif + +/***************************************************************************** + * Sound queue functions * + *****************************************************************************/ + +/* callback functions */ +#ifdef _WIN32 + +void replay_init_sound_in(void *instance, WAVEHDR *hdrs, int sz) +{ + sound_in.instance = instance; + sound_in.queue = hdrs; + sound_in.size = sz; + sound_in.head = 0; + sound_in.tail = 0; + sound_in.cur_size = 0; +} + +void replay_init_sound_out(void *instance, WAVEHDR *hdrs, int sz) +{ + sound_out.instance = instance; + sound_out.queue = hdrs; + sound_out.size = sz; + sound_out.head = 0; + sound_out.tail = 0; + sound_out.cur_size = 0; +} + +static int sound_queue_add(SoundQueue *q, WAVEHDR *hdr) +{ + if (q->queue + q->tail != hdr) { + /* state was loaded and we need to reset the queue */ + if (q->cur_size == 0) { + q->head = q->tail = hdr - q->queue; + } else { + fprintf(stderr, "Replay: Sound queue error\n"); + exit(1); + } + } + + if (q->cur_size == q->size) { + if (replay_mode == REPLAY_MODE_PLAY) { + return 1; + } + + fprintf(stderr, "Replay: Sound queue overflow\n"); + exit(1); + } + + q->tail = (q->tail + 1) % q->size; + ++q->cur_size; + + return 0; +} + +void replay_save_sound_out(void) +{ + spin_lock(&sound_lock); + while (sound_out.cur_size != 0) { + /* put the message ID */ + replay_put_event(sound_out.event_id); + /* save the buffer size */ + replay_put_dword(sound_out.queue[sound_out.head].dwBytesRecorded); + /* perform winwave-specific actions */ + winwave_callback_out_impl(sound_out.instance, + &sound_out.queue[sound_out.head]); + /* goto the next buffer */ + sound_out.head = (sound_out.head + 1) % sound_out.size; + --sound_out.cur_size; + } + spin_unlock(&sound_lock); +} + +void replay_save_sound_in(void) +{ + spin_lock(&sound_lock); + while (sound_in.cur_size != 0) { + /* put the message ID */ + replay_put_event(sound_in.event_id); + /* save the buffer */ + replay_put_array((const uint8_t *)sound_in.queue[sound_in.head].lpData, + sound_in.queue[sound_in.head].dwBytesRecorded); + /* perform winwave-specific actions */ + winwave_callback_in_impl(sound_in.instance, + &sound_in.queue[sound_in.head]); + /* goto the next buffer */ + sound_in.head = (sound_in.head + 1) % sound_in.size; + --sound_in.cur_size; + } + spin_unlock(&sound_lock); +} + +void replay_read_sound_out(void) +{ + if (sound_out.cur_size == 0) { + fprintf(stderr, "Replay: Sound queue underflow\n"); + exit(1); + } + + /* get the buffer size */ + sound_out.queue[sound_out.head].dwBytesRecorded = replay_get_dword(); + + replay_check_error(); + replay_has_unread_data = 0; + + /* perform winwave-specific actions */ + winwave_callback_out_impl(sound_out.instance, + &sound_out.queue[sound_out.head]); + sound_out.head = (sound_out.head + 1) % sound_out.size; + --sound_out.cur_size; +} + +void replay_read_sound_in(void) +{ + if (sound_in.cur_size == 0) { + fprintf(stderr, "Replay: Sound queue underflow\n"); + exit(1); + } + + /* get the buffer size */ + size_t size; + replay_get_array((uint8_t *)sound_in.queue[sound_in.head].lpData, &size); + sound_in.queue[sound_in.head].dwBytesRecorded = (unsigned int)size; + + replay_check_error(); + replay_has_unread_data = 0; + + /* perform winwave-specific actions */ + winwave_callback_in_impl(sound_in.instance, &sound_in.queue[sound_in.head]); + sound_in.head = (sound_in.head + 1) % sound_in.size; + --sound_in.cur_size; +} + +void replay_sound_in_event(WAVEHDR *hdr) +{ + spin_lock(&sound_lock); + if (sound_queue_add(&sound_in, hdr)) { + fprintf(stderr, "Replay: Input sound buffer overflow\n"); + exit(1); + } + spin_unlock(&sound_lock); +} + +int replay_sound_out_event(WAVEHDR *hdr) +{ + spin_lock(&sound_lock); + int result = sound_queue_add(&sound_out, hdr); + spin_unlock(&sound_lock); + + return result; +} +#endif + +bool replay_has_sound_events(void) +{ + return sound_in.cur_size || sound_out.cur_size; +} + +void replay_sound_flush_queue(void) +{ +#ifdef _WIN32 + spin_lock(&sound_lock); + while (sound_out.cur_size != 0) { + /* perform winwave-specific actions */ + winwave_callback_out_impl(sound_out.instance, + &sound_out.queue[sound_out.head]); + /* goto the next buffer */ + sound_out.head = (sound_out.head + 1) % sound_out.size; + --sound_out.cur_size; + } + while (sound_in.cur_size != 0) { + /* perform winwave-specific actions */ + winwave_callback_in_impl(sound_in.instance, + &sound_in.queue[sound_in.head]); + /* goto the next buffer */ + sound_in.head = (sound_in.head + 1) % sound_in.size; + --sound_in.cur_size; + } + spin_unlock(&sound_lock); +#endif +} + diff --git a/replay/replay-internal.c b/replay/replay-internal.c index 0c303d6..b35ec57 100755 --- a/replay/replay-internal.c +++ b/replay/replay-internal.c @@ -151,5 +151,9 @@ void replay_save_instructions(void) replay_state.current_step += first_cpu->instructions_count; first_cpu->instructions_count = 0; } +#ifdef _WIN32 + replay_save_sound_in(); + replay_save_sound_out(); +#endif } } diff --git a/replay/replay-internal.h b/replay/replay-internal.h index eb469f0..d2b1936 100755 --- a/replay/replay-internal.h +++ b/replay/replay-internal.h @@ -21,6 +21,10 @@ #define EVENT_TIME_T 1 /* for tm event */ #define EVENT_TM 2 +/* for outgoing sound event */ +#define EVENT_SOUND_OUT 7 +/* for incoming sound event */ +#define EVENT_SOUND_IN 8 /* for software interrupt */ #define EVENT_INTERRUPT 15 /* for shutdown request */ @@ -169,4 +173,14 @@ void *replay_net_read_packet(void); to the net queue. */ void replay_net_send_packet(void *opaque); +/* Sound events */ + +/*! Returns true, when there are any pending sound events. */ +bool replay_has_sound_events(void); +void replay_save_sound_out(void); +void replay_save_sound_in(void); +void replay_read_sound_out(void); +void replay_read_sound_in(void); +void replay_sound_flush_queue(void); + #endif diff --git a/replay/replay.h b/replay/replay.h index 419dec9..1d32a2a 100755 --- a/replay/replay.h +++ b/replay/replay.h @@ -15,6 +15,10 @@ #include #include #include +#ifdef _WIN32 +#include +#include +#endif #include "qapi-types.h" struct QemuOpts; @@ -128,4 +132,21 @@ void replay_add_network_client(struct NetClientState *nc); void replay_save_net_packet(struct NetClientState *nc, const uint8_t *buf, size_t size); +/* Audio */ + +#ifdef _WIN32 +/*! Microphone event. */ +void replay_sound_in_event(WAVEHDR *hdr); +/*! Adds header to the queue. + In record mode this header is queued for saving into log. + In replay mode this header is queued for reading from log. + Returns 1 in replay mode when queue is full. + Otherwise returns 0. */ +int replay_sound_out_event(WAVEHDR *hdr); +/*! Initializes queue for sound input. */ +void replay_init_sound_in(void *instance, WAVEHDR *hdrs, int sz); +/*! Initializes queue for sound output. */ +void replay_init_sound_out(void *instance, WAVEHDR *hdrs, int sz); +#endif + #endif