replay: add record/replay for audio passthrough
This patch adds recording and replaying audio data. Is saves synchronization information for audio out and inputs from the microphone. v2: removed unneeded whitespace change Signed-off-by: Pavel Dovgalyuk <pavel.dovgaluk@ispras.ru> Message-id: 20170202055054.4848.94901.stgit@PASHA-ISP.lan02.inno [ kraxel: add qemu/error-report.h include to fix osx build failure ] Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
This commit is contained in:
		
				
					committed by
					
						 Gerd Hoffmann
						Gerd Hoffmann
					
				
			
			
				
	
			
			
			
						parent
						
							e7c83a885f
						
					
				
				
					commit
					3d4d16f4dc
				
			| @@ -28,6 +28,7 @@ | ||||
| #include "qemu/timer.h" | ||||
| #include "sysemu/sysemu.h" | ||||
| #include "qemu/cutils.h" | ||||
| #include "sysemu/replay.h" | ||||
|  | ||||
| #define AUDIO_CAP "audio" | ||||
| #include "audio_int.h" | ||||
| @@ -1387,6 +1388,7 @@ static void audio_run_out (AudioState *s) | ||||
|  | ||||
|         prev_rpos = hw->rpos; | ||||
|         played = hw->pcm_ops->run_out (hw, live); | ||||
|         replay_audio_out(&played); | ||||
|         if (audio_bug (AUDIO_FUNC, hw->rpos >= hw->samples)) { | ||||
|             dolog ("hw->rpos=%d hw->samples=%d played=%d\n", | ||||
|                    hw->rpos, hw->samples, played); | ||||
| @@ -1450,9 +1452,12 @@ static void audio_run_in (AudioState *s) | ||||
|  | ||||
|     while ((hw = audio_pcm_hw_find_any_enabled_in (hw))) { | ||||
|         SWVoiceIn *sw; | ||||
|         int captured, min; | ||||
|         int captured = 0, min; | ||||
|  | ||||
|         captured = hw->pcm_ops->run_in (hw); | ||||
|         if (replay_mode != REPLAY_MODE_PLAY) { | ||||
|             captured = hw->pcm_ops->run_in(hw); | ||||
|         } | ||||
|         replay_audio_in(&captured, hw->conv_buf, &hw->wpos, hw->samples); | ||||
|  | ||||
|         min = audio_pcm_hw_find_min_in (hw); | ||||
|         hw->total_samples_captured += captured - min; | ||||
|   | ||||
| @@ -166,4 +166,9 @@ int wav_start_capture (CaptureState *s, const char *path, int freq, | ||||
| bool audio_is_cleaning_up(void); | ||||
| void audio_cleanup(void); | ||||
|  | ||||
| void audio_sample_to_uint64(void *samples, int pos, | ||||
|                             uint64_t *left, uint64_t *right); | ||||
| void audio_sample_from_uint64(void *samples, int pos, | ||||
|                             uint64_t left, uint64_t right); | ||||
|  | ||||
| #endif /* QEMU_AUDIO_H */ | ||||
|   | ||||
| @@ -25,6 +25,7 @@ | ||||
| #include "qemu/osdep.h" | ||||
| #include "qemu-common.h" | ||||
| #include "qemu/bswap.h" | ||||
| #include "qemu/error-report.h" | ||||
| #include "audio.h" | ||||
|  | ||||
| #define AUDIO_CAP "mixeng" | ||||
| @@ -267,6 +268,37 @@ f_sample *mixeng_clip[2][2][2][3] = { | ||||
|     } | ||||
| }; | ||||
|  | ||||
|  | ||||
| void audio_sample_to_uint64(void *samples, int pos, | ||||
|                             uint64_t *left, uint64_t *right) | ||||
| { | ||||
|     struct st_sample *sample = samples; | ||||
|     sample += pos; | ||||
| #ifdef FLOAT_MIXENG | ||||
|     error_report( | ||||
|         "Coreaudio and floating point samples are not supported by replay yet"); | ||||
|     abort(); | ||||
| #else | ||||
|     *left = sample->l; | ||||
|     *right = sample->r; | ||||
| #endif | ||||
| } | ||||
|  | ||||
| void audio_sample_from_uint64(void *samples, int pos, | ||||
|                             uint64_t left, uint64_t right) | ||||
| { | ||||
|     struct st_sample *sample = samples; | ||||
|     sample += pos; | ||||
| #ifdef FLOAT_MIXENG | ||||
|     error_report( | ||||
|         "Coreaudio and floating point samples are not supported by replay yet"); | ||||
|     abort(); | ||||
| #else | ||||
|     sample->l = left; | ||||
|     sample->r = right; | ||||
| #endif | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * August 21, 1998 | ||||
|  * Copyright 1998 Fabrice Bellard. | ||||
|   | ||||
| @@ -225,3 +225,10 @@ recording the virtual machine this filter puts all packets coming from | ||||
| the outer world into the log. In replay mode packets from the log are | ||||
| injected into the network device. All interactions with network backend | ||||
| in replay mode are disabled. | ||||
|  | ||||
| Audio devices | ||||
| ------------- | ||||
|  | ||||
| Audio data is recorded and replay automatically. The command line for recording | ||||
| and replaying must contain identical specifications of audio hardware, e.g.: | ||||
|  -soundhw ac97 | ||||
|   | ||||
| @@ -152,6 +152,13 @@ void replay_unregister_net(ReplayNetState *rns); | ||||
| void replay_net_packet_event(ReplayNetState *rns, unsigned flags, | ||||
|                              const struct iovec *iov, int iovcnt); | ||||
|  | ||||
| /* Audio */ | ||||
|  | ||||
| /*! Saves/restores number of played samples of audio out operation. */ | ||||
| void replay_audio_out(int *played); | ||||
| /*! Saves/restores recorded samples of audio in operation. */ | ||||
| void replay_audio_in(int *recorded, void *samples, int *wpos, int size); | ||||
|  | ||||
| /* VM state operations */ | ||||
|  | ||||
| /*! Called at the start of execution. | ||||
|   | ||||
| @@ -6,3 +6,4 @@ common-obj-y += replay-input.o | ||||
| common-obj-y += replay-char.o | ||||
| common-obj-y += replay-snapshot.o | ||||
| common-obj-y += replay-net.o | ||||
| common-obj-y += replay-audio.o | ||||
							
								
								
									
										79
									
								
								replay/replay-audio.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								replay/replay-audio.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,79 @@ | ||||
| /* | ||||
|  * replay-audio.c | ||||
|  * | ||||
|  * Copyright (c) 2010-2017 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/osdep.h" | ||||
| #include "qemu/error-report.h" | ||||
| #include "sysemu/replay.h" | ||||
| #include "replay-internal.h" | ||||
| #include "sysemu/sysemu.h" | ||||
| #include "audio/audio.h" | ||||
|  | ||||
| void replay_audio_out(int *played) | ||||
| { | ||||
|     if (replay_mode == REPLAY_MODE_RECORD) { | ||||
|         replay_save_instructions(); | ||||
|         replay_mutex_lock(); | ||||
|         replay_put_event(EVENT_AUDIO_OUT); | ||||
|         replay_put_dword(*played); | ||||
|         replay_mutex_unlock(); | ||||
|     } else if (replay_mode == REPLAY_MODE_PLAY) { | ||||
|         replay_account_executed_instructions(); | ||||
|         replay_mutex_lock(); | ||||
|         if (replay_next_event_is(EVENT_AUDIO_OUT)) { | ||||
|             *played = replay_get_dword(); | ||||
|             replay_finish_event(); | ||||
|             replay_mutex_unlock(); | ||||
|         } else { | ||||
|             replay_mutex_unlock(); | ||||
|             error_report("Missing audio out event in the replay log"); | ||||
|             abort(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| void replay_audio_in(int *recorded, void *samples, int *wpos, int size) | ||||
| { | ||||
|     int pos; | ||||
|     uint64_t left, right; | ||||
|     if (replay_mode == REPLAY_MODE_RECORD) { | ||||
|         replay_save_instructions(); | ||||
|         replay_mutex_lock(); | ||||
|         replay_put_event(EVENT_AUDIO_IN); | ||||
|         replay_put_dword(*recorded); | ||||
|         replay_put_dword(*wpos); | ||||
|         for (pos = (*wpos - *recorded + size) % size ; pos != *wpos | ||||
|              ; pos = (pos + 1) % size) { | ||||
|             audio_sample_to_uint64(samples, pos, &left, &right); | ||||
|             replay_put_qword(left); | ||||
|             replay_put_qword(right); | ||||
|         } | ||||
|         replay_mutex_unlock(); | ||||
|     } else if (replay_mode == REPLAY_MODE_PLAY) { | ||||
|         replay_account_executed_instructions(); | ||||
|         replay_mutex_lock(); | ||||
|         if (replay_next_event_is(EVENT_AUDIO_IN)) { | ||||
|             *recorded = replay_get_dword(); | ||||
|             *wpos = replay_get_dword(); | ||||
|             for (pos = (*wpos - *recorded + size) % size ; pos != *wpos | ||||
|                  ; pos = (pos + 1) % size) { | ||||
|                 left = replay_get_qword(); | ||||
|                 right = replay_get_qword(); | ||||
|                 audio_sample_from_uint64(samples, pos, left, right); | ||||
|             } | ||||
|             replay_finish_event(); | ||||
|             replay_mutex_unlock(); | ||||
|         } else { | ||||
|             replay_mutex_unlock(); | ||||
|             error_report("Missing audio in event in the replay log"); | ||||
|             abort(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -29,6 +29,10 @@ enum ReplayEvents { | ||||
|     /* for character device read all event */ | ||||
|     EVENT_CHAR_READ_ALL, | ||||
|     EVENT_CHAR_READ_ALL_ERROR, | ||||
|     /* for audio out event */ | ||||
|     EVENT_AUDIO_OUT, | ||||
|     /* for audio in event */ | ||||
|     EVENT_AUDIO_IN, | ||||
|     /* for clock read/writes */ | ||||
|     /* some of greater codes are reserved for clocks */ | ||||
|     EVENT_CLOCK, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user