diff mbox

[RFC,04/22] Audo replay

Message ID 007801cf951e$80235370$8069fa50$@Dovgaluk@ispras.ru
State New
Headers show

Commit Message

Pavel Dovgalyuk July 1, 2014, 11:20 a.m. UTC
These patches add deterministic replay for audio adapter. Support of audio
record and replay is implemented only for Win32.

Signed-off-by: Pavel Dovgalyuk <pavel.dovgaluk@gmail.com>
---
diff mbox

Patch

diff --git a/audio/audio.c b/audio/audio.c
index 9d018e9..475211c
--- 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,7 @@  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_NONE ?
conf.try_poll_out : 0);
                     audio_reset_timer (s);
                 }
             }
@@ -1763,11 +1764,11 @@  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_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_NONE ? conf.try_poll_in : 0);
     }
     audio_reset_timer (s);
 }
@@ -1810,9 +1811,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/winwaveaudio.c b/audio/winwaveaudio.c
index 8dbd145..8fee218 100644
--- a/audio/winwaveaudio.c
+++ b/audio/winwaveaudio.c
@@ -2,11 +2,14 @@ 
 
 #include "qemu-common.h"
 #include "sysemu/sysemu.h"
+#include "migration/vmstate.h"
 #include "audio.h"
 
 #define AUDIO_CAP "winwave"
 #include "audio_int.h"
 
+#include "replay/replay.h"
+
 #include <windows.h>
 #include <mmsystem.h>
 
@@ -47,6 +50,7 @@  typedef struct {
     int paused;
     int rpos;
     int avail;
+    int non_added;
     CRITICAL_SECTION crit_sect;
 } WaveVoiceIn;
 
@@ -124,6 +128,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 +154,18 @@  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_SAVE) {
+                replay_sound_out_event((WAVEHDR *) dwParam1);
+            }
+            else if (replay_mode == REPLAY_PLAY
+                     && replay_get_play_submode() != REPLAY_PLAY_CHANGED) {
+                /* Do nothing */
+            }
+            else {
+                winwave_callback_out_impl((void*)dwInstance, (WAVEHDR *) dwParam1);
             }
         }
         break;
@@ -163,6 +179,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 +204,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 +252,10 @@  static int winwave_init_out (HWVoiceOut *hw, struct audsettings *as)
         }
     }
 
+    if (replay_mode != REPLAY_NONE) {
+        replay_init_sound_out(wave, wave->hdrs, conf.dac_headers);
+    }
+
     return 0;
 
  err4:
@@ -262,10 +299,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. Without sound */
+        if (replay_mode != REPLAY_PLAY
+            || replay_get_play_submode() == REPLAY_PLAY_CHANGED) {
+            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 +429,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 +457,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_SAVE) {
+                replay_sound_in_event((WAVEHDR *) dwParam1);
+            } else if (replay_mode == REPLAY_PLAY
+                       && replay_get_play_submode() != REPLAY_PLAY_CHANGED) {
+                /* Do nothing */
+            } else {
+                winwave_callback_in_impl((void*)dwInstance, (WAVEHDR *) dwParam1);
             }
         }
         break;
@@ -421,9 +480,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 +495,40 @@  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 in PLAY mode - it will be
+           loaded from log file. */
+        if (replay_mode != REPLAY_PLAY
+            || replay_get_play_submode() != REPLAY_PLAY_CHANGED) {
+            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 +539,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 +565,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 +588,10 @@  static int winwave_init_in (HWVoiceIn *hw, struct audsettings *as)
         }
     }
 
+    if (replay_mode != REPLAY_NONE) {
+        replay_init_sound_in(wave, wave->hdrs, conf.adc_headers);
+    }
+
     wave->paused = 1;
     winwave_add_buffers (wave, hw->samples);
     return 0;
@@ -570,6 +662,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 +675,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;
 }