From: Toni Wilen Date: Sun, 17 May 2015 11:53:15 +0000 (+0300) Subject: AVIOutput AV sync. X-Git-Tag: 3100~31 X-Git-Url: https://git.unchartedbackwaters.co.uk/w/?a=commitdiff_plain;h=58a37685bb792264bbfb23b7ae440d54ae261188;p=francis%2Fwinuae.git AVIOutput AV sync. --- diff --git a/od-win32/avioutput.cpp b/od-win32/avioutput.cpp index fee05185..f19601e9 100644 --- a/od-win32/avioutput.cpp +++ b/od-win32/avioutput.cpp @@ -67,6 +67,7 @@ int avioutput_width, avioutput_height, avioutput_bits; int avioutput_fps = VBLANK_HZ_PAL; int avioutput_framelimiter = 0, avioutput_nosoundoutput = 0; int avioutput_nosoundsync = 1, avioutput_originalsize = 0; +int avioutput_split_files = 1; TCHAR avioutput_filename_gui[MAX_DPATH]; TCHAR avioutput_filename_auto[MAX_DPATH]; @@ -88,6 +89,7 @@ struct avientry { LPBITMAPINFOHEADER lpbi; uae_u8 *lpAudio; int sndsize; + int expectedsize; }; #define AVIENTRY_MAX 10 @@ -99,7 +101,7 @@ static struct avientry *avientries[AVIENTRY_MAX + 1]; static int FirstAudio; static DWORD dwAudioInputRemaining; static unsigned int StreamSizeAudio; // audio write position -static double StreamSizeAudioExpected; +static double StreamSizeAudioExpected, StreamSizeAudioGot; static PAVISTREAM AVIAudioStream = NULL; // compressed stream pointer static HACMSTREAM has = NULL; // stream handle that can be used to perform conversions static ACMSTREAMHEADER ash; @@ -111,6 +113,9 @@ static FILE *wavfile; static uae_u8 *lpAudioDst, *lpAudioSrc; static DWORD dwAudioOutputBytes, dwAudioInputBytes; +static uae_u8 *avi_sndbuffer, *avi_sndbuffer2; +static int avi_sndbufsize, avi_sndbufsize2; +static int avi_sndbuffered, avi_sndbuffered2; /* video */ @@ -154,12 +159,13 @@ static void freeavientry (struct avientry *ae) xfree (ae); } -static struct avientry *allocavientry_audio (uae_u8 *snd, int size) +static struct avientry *allocavientry_audio (uae_u8 *snd, int size, int expectedsize) { struct avientry *ae = xcalloc (struct avientry, 1); ae->lpAudio = xmalloc (uae_u8, size); memcpy (ae->lpAudio, snd, size); ae->sndsize = size; + ae->expectedsize = expectedsize; return ae; } @@ -226,7 +232,9 @@ static void storesettings (UAEREG *avikey) regsetint (avikey, _T("NoSoundOutput"), avioutput_nosoundoutput); regsetint (avikey, _T("NoSoundSync"), avioutput_nosoundsync); regsetint (avikey, _T("Original"), avioutput_originalsize); - regsetint (avikey, _T("FPS"), avioutput_fps); + regsetint(avikey, _T("FPS"), avioutput_fps); + regsetint(avikey, _T("RecordMode"), avioutput_audio); + regsetstr(avikey, _T("FileName"), avioutput_filename_gui); } static void getsettings (UAEREG *avikey) { @@ -243,6 +251,10 @@ static void getsettings (UAEREG *avikey) avioutput_nosoundoutput = 1; if (regqueryint (avikey, _T("FPS"), &val)) avioutput_fps = val; + if (regqueryint(avikey, _T("RecordMode"), &val)) + avioutput_audio = val; + val = sizeof avioutput_filename_gui / sizeof(TCHAR); + regquerystr(avikey, _T("FileName"), avioutput_filename_gui, &val); } void AVIOutput_GetSettings (void) @@ -680,7 +692,9 @@ int AVIOutput_ChooseVideoCodec (HWND hwnd, TCHAR *s, int len) } } -static void checkAVIsize (int force) +static void AVIOutput_Begin2(bool fullstart); + +static void checkAVIsize(int force) { int tmp_partcnt = partcnt + 1; int tmp_avioutput_video = avioutput_video; @@ -691,16 +705,17 @@ static void checkAVIsize (int force) return; if (total_avi_size == 0) return; + if (!avioutput_split_files) + return; _tcscpy (fn, avioutput_filename_tmp); _stprintf (avioutput_filename_inuse, _T("%s_%d.avi"), fn, tmp_partcnt); write_log (_T("AVI split %d at %d bytes, %d frames\n"), tmp_partcnt, total_avi_size, frame_count); AVIOutput_End (); - first_frame = 0; total_avi_size = 0; avioutput_video = tmp_avioutput_video; avioutput_audio = tmp_avioutput_audio; - AVIOutput_Begin (); + AVIOutput_Begin2(false); _tcscpy (avioutput_filename_tmp, fn); partcnt = tmp_partcnt; } @@ -712,7 +727,7 @@ static void dorestart (void) checkAVIsize (1); } -static void AVIOuput_AVIWriteAudio (uae_u8 *sndbuffer, int sndbufsize) +static void AVIOuput_AVIWriteAudio (uae_u8 *sndbuffer, int sndbufsize, int expectedsize) { struct avientry *ae; @@ -720,19 +735,46 @@ static void AVIOuput_AVIWriteAudio (uae_u8 *sndbuffer, int sndbufsize) AVIOutput_End (); return; } + if (!sndbufsize) + return; checkAVIsize (0); if (avioutput_needs_restart) dorestart (); waitqueuefull (); - ae = allocavientry_audio (sndbuffer, sndbufsize); + ae = allocavientry_audio (sndbuffer, sndbufsize, expectedsize); queueavientry (ae); } + +typedef short HWORD; +typedef unsigned short UHWORD; +typedef int LWORD; +typedef unsigned int ULWORD; +static int resampleFast(double factor, HWORD *in, HWORD *out, int inCount, int outCount, int nChans); + +static uae_u8 *hack_resample(uae_u8 *srcbuffer, int gotSize, int wantedSize) +{ + uae_u8 *outbuf; + int ch = wfxSrc.Format.nChannels; + int bytesperframe = ch * 2; + + int gotSamples = gotSize / bytesperframe; + int wantedSamples = wantedSize / bytesperframe; + + double factor = (double)wantedSamples / gotSamples; + outbuf = xmalloc(uae_u8, wantedSize + bytesperframe); + resampleFast(factor, (HWORD*)srcbuffer, (HWORD*)outbuf, gotSamples, wantedSamples, ch); + return outbuf; +} + static int AVIOutput_AVIWriteAudio_Thread (struct avientry *ae) { DWORD flags; LONG swritten = 0, written = 0; unsigned int err; + uae_u8 *srcbuffer = NULL; + uae_u8 *freebuffer = NULL; + int srcbuffersize = 0; if (!avioutput_audio) return 1; @@ -740,12 +782,23 @@ static int AVIOutput_AVIWriteAudio_Thread (struct avientry *ae) if (!avioutput_init) goto error; + if (ae) { + srcbuffer = ae->lpAudio; + srcbuffersize = ae->sndsize; + // size didn't match, resample it to keep AV sync. + if (ae->expectedsize && ae->expectedsize != ae->sndsize) { + srcbuffer = hack_resample(srcbuffer, ae->sndsize, ae->expectedsize); + srcbuffersize = ae->expectedsize; + freebuffer = srcbuffer; + } + } + if (FirstAudio) { - if ((err = acmStreamSize (has, ae->sndsize, &dwAudioOutputBytes, ACM_STREAMSIZEF_SOURCE) != 0)) { + if ((err = acmStreamSize(has, currprefs.sound_freq, &dwAudioOutputBytes, ACM_STREAMSIZEF_SOURCE) != 0)) { gui_message (_T("acmStreamSize() FAILED (%X)\n"), err); goto error; } - dwAudioInputBytes = ae->sndsize * 2; + dwAudioInputBytes = currprefs.sound_freq; if (!(lpAudioSrc = xcalloc (uae_u8, dwAudioInputBytes))) goto error; if (!(lpAudioDst = xcalloc (uae_u8, dwAudioOutputBytes))) @@ -768,11 +821,16 @@ static int AVIOutput_AVIWriteAudio_Thread (struct avientry *ae) } } - ash.cbSrcLength = ae ? ae->sndsize : 0; + ash.cbSrcLength = 0; + if (srcbuffer) { + if (srcbuffersize + dwAudioInputRemaining > dwAudioInputBytes) { + avi_message(_T("AVIOutput audio buffer overflow!?")); + goto error; + } + ash.cbSrcLength = srcbuffersize; + memcpy(ash.pbSrc + dwAudioInputRemaining, srcbuffer, srcbuffersize); + } ash.cbSrcLength += dwAudioInputRemaining; - if (ae) - memcpy(ash.pbSrc + dwAudioInputRemaining, ae->lpAudio, ash.cbSrcLength); - ash.cbSrcLengthUsed = 0; flags = ACM_STREAMCONVERTF_BLOCKALIGN; @@ -786,6 +844,10 @@ static int AVIOutput_AVIWriteAudio_Thread (struct avientry *ae) goto error; } + dwAudioInputRemaining = ash.cbSrcLength - ash.cbSrcLengthUsed; + if (dwAudioInputRemaining) + memmove(ash.pbSrc, ash.pbSrc + ash.cbSrcLengthUsed, dwAudioInputRemaining); + if (ash.cbDstLengthUsed) { if (FileStream) { zfile_fwrite(lpAudioDst, 1, ash.cbDstLengthUsed, FileStream); @@ -796,25 +858,27 @@ static int AVIOutput_AVIWriteAudio_Thread (struct avientry *ae) } } } + StreamSizeAudio += swritten; total_avi_size += written; - dwAudioInputRemaining = ash.cbSrcLength - ash.cbSrcLengthUsed; - + FirstAudio = 0; + xfree(freebuffer); return 1; error: + xfree(freebuffer); return 0; } static void AVIOutput_AVIWriteAudio_Thread_End(void) { - if (!FirstAudio) { + if (!FirstAudio && !avioutput_failed) { AVIOutput_AVIWriteAudio_Thread(NULL); } - ash.cbSrcLength = dwAudioInputBytes; - acmStreamUnprepareHeader (has, &ash, 0); + ash.cbSrcLength = avioutput_failed ? 0 : dwAudioInputBytes; + acmStreamUnprepareHeader(has, &ash, 0); xfree(lpAudioDst); lpAudioDst = NULL; xfree(lpAudioSrc); @@ -829,24 +893,6 @@ static void AVIOuput_WAVWriteAudio (uae_u8 *sndbuffer, int sndbufsize) fwrite (sndbuffer, 1, sndbufsize, wavfile); } -static int skipsample; - -void AVIOutput_WriteAudio (uae_u8 *sndbuffer, int sndbufsize) -{ - int size = sndbufsize; - - if (!avioutput_audio || !avioutput_enabled) - return; - if (skipsample > 0 && size > wfxSrc.Format.nBlockAlign) { - size -= wfxSrc.Format.nBlockAlign; - skipsample--; - } - if (avioutput_audio == AVIAUDIO_WAV) - AVIOuput_WAVWriteAudio (sndbuffer, size); - else - AVIOuput_AVIWriteAudio (sndbuffer, size); -} - static int getFromDC (struct avientry *avie) { HDC hdc; @@ -1119,7 +1165,16 @@ void AVIOutput_End (void) StreamSizeAudio = frame_count = 0; StreamSizeAudioExpected = 0; + StreamSizeAudioGot = 0; partcnt = 0; + avi_sndbuffered = 0; + avi_sndbuffered2 = 0; + xfree(avi_sndbuffer); + xfree(avi_sndbuffer2); + avi_sndbuffer = NULL; + avi_sndbuffer2 = NULL; + avi_sndbufsize = 0; + avi_sndbufsize2 = 0; if (wavfile) { writewavheader (ftell (wavfile)); @@ -1130,7 +1185,7 @@ void AVIOutput_End (void) static void *AVIOutput_worker (void *arg); -void AVIOutput_Begin (void) +static void AVIOutput_Begin2(bool fullstart) { AVISTREAMINFO avistreaminfo; // Structure containing information about the stream, including the stream type and its sample rate int i, err; @@ -1148,9 +1203,6 @@ void AVIOutput_Begin (void) if (!avioutput_requested) return; - changed_prefs.sound_auto = currprefs.sound_auto = 0; - reset_sound (); - if (avioutput_audio == AVIAUDIO_WAV) { ext1 = _T(".wav"); ext2 = _T(".avi"); } else { @@ -1173,6 +1225,14 @@ void AVIOutput_Begin (void) goto error; } + if (fullstart) { + reset_sound(); + init_hz_normal(); + first_frame = 1; + } else { + first_frame = 0; + } + // delete any existing file before writing AVI SetFileAttributes (avioutput_filename_inuse, FILE_ATTRIBUTE_ARCHIVE); DeleteFile (avioutput_filename_inuse); @@ -1309,6 +1369,11 @@ error: AVIOutput_End (); } +void AVIOutput_Begin(void) +{ + AVIOutput_Begin2(true); +} + void AVIOutput_Release (void) { AVIOutput_End (); @@ -1367,7 +1432,8 @@ static void *AVIOutput_worker (void *arg) ae = getavientry (); LeaveCriticalSection (&AVIOutput_CriticalSection); if (ae == NULL) { - write_log (_T("AVIOutput worker thread: out of entries!?\n")); + if (!avioutput_failed) + write_log (_T("AVIOutput worker thread: out of entries!?\n")); break; } write_comm_pipe_u32 (&queuefull, 0, 1); @@ -1450,57 +1516,250 @@ void AVIOutput_Toggle (int mode, bool immediate) } } +void AVIOutput_WriteAudio(uae_u8 *sndbuffer, int sndbufsize) +{ + if (!avioutput_audio || !avioutput_enabled) + return; + if (avioutput_failed) + return; -#include + if (!avi_sndbuffer) { + avi_sndbufsize = sndbufsize * 2; + avi_sndbuffer = xcalloc(uae_u8, avi_sndbufsize); + avi_sndbuffered = 0; + } + while (avi_sndbuffered + sndbufsize > avi_sndbufsize) { + avi_sndbufsize *= 2; + avi_sndbuffer = xrealloc(uae_u8, avi_sndbuffer, avi_sndbufsize); + } + memcpy(avi_sndbuffer + avi_sndbuffered, sndbuffer, sndbufsize); + avi_sndbuffered += sndbufsize; -#define ADJUST_SIZE 10 -#define EXP 1.1 +} void frame_drawn (void) { -#if 0 - double diff, skipmode; -#endif - int idiff; + if (!avioutput_enabled) + return; + if (avioutput_failed) + return; - if (!avioutput_video || !avioutput_enabled) + if (avioutput_audio == AVIAUDIO_WAV) { + finish_sound_buffer(); + if (!first_frame) { + AVIOuput_WAVWriteAudio(avi_sndbuffer, avi_sndbuffered); + } + first_frame = 0; return; + } + + if (avioutput_audio) { + finish_sound_buffer(); + if (!first_frame) { + int bytesperframe; + bytesperframe = wfxSrc.Format.nChannels * 2; + StreamSizeAudioGot += avi_sndbuffered / bytesperframe; + unsigned int lastexpected = (unsigned int)StreamSizeAudioExpected; + StreamSizeAudioExpected += ((double)currprefs.sound_freq) / avioutput_fps; + if (avioutput_video) { + int idiff = StreamSizeAudioGot - StreamSizeAudioExpected; + if ((timeframes % 5) == 0) + write_log(_T("%.1f %.1f %d\n"), StreamSizeAudioExpected, StreamSizeAudioGot, idiff); + if (idiff) { + StreamSizeAudioGot = StreamSizeAudioExpected; + AVIOuput_AVIWriteAudio(avi_sndbuffer, avi_sndbuffered, (StreamSizeAudioExpected - lastexpected) * bytesperframe); + } else { + AVIOuput_AVIWriteAudio(avi_sndbuffer, avi_sndbuffered, 0); + } + } else { + AVIOuput_AVIWriteAudio(avi_sndbuffer, avi_sndbuffered, 0); + } + } + avi_sndbuffered = 0; + avi_sndbuffered2 = 0; + } if (first_frame) { first_frame = 0; return; } + if (!avioutput_video) + return; + AVIOutput_WriteVideo (); +} - if (!avioutput_audio) - return; +/* Resampler from: + * Digital Audio Resampling Home Page located at + * http ://ccrma.stanford.edu/~jos/resample/. + */ + +#define Nhc 8 +#define Na 7 +#define Np (Nhc+Na) +#define Npc (1<>= scl; + if (v > MAX_HWORD) { + v = MAX_HWORD; + } else if (v < MIN_HWORD) { + v = MIN_HWORD; + } + out = (HWORD)v; + return out; +} - StreamSizeAudioExpected += ((double)currprefs.sound_freq) / avioutput_fps; - idiff = StreamSizeAudio - StreamSizeAudioExpected; - if (idiff > 0) { - skipsample += idiff / 10; - if (skipsample > 4) - skipsample = 4; +static int SrcLinear(HWORD X[], HWORD Y[], double factor, ULWORD *Time, UHWORD Nx) +{ + HWORD iconst; + HWORD *Xp, *Ystart; + LWORD v, x1, x2; + + double dt; /* Step through input signal */ + ULWORD dtb; /* Fixed-point version of Dt */ + ULWORD endTime; /* When Time reaches EndTime, return to user */ + + dt = 1.0 / factor; /* Output sampling period */ + dtb = dt*(1 << Np) + 0.5; /* Fixed-point representation */ + + Ystart = Y; + endTime = *Time + (1 << Np)*(LWORD)Nx; + while (*Time < endTime) { + iconst = (*Time) & Pmask; + Xp = &X[(*Time) >> Np]; /* Ptr to current input sample */ + x1 = *Xp++; + x2 = *Xp; + x1 *= ((1 << Np) - iconst); + x2 *= iconst; + v = x1 + x2; + *Y++ = WordToHword(v, Np); /* Deposit output */ + *Time += dtb; /* Move to next sample by time increment */ } - sound_setadjust (0.0); - -#if 0 - write_log (_T("%d "), idiff); - diff = idiff / 20.0; - skipmode = pow (diff < 0 ? -diff : diff, EXP); - if (idiff < 0) - skipmode = -skipmode; - if (skipmode < -ADJUST_SIZE) - skipmode = -ADJUST_SIZE; - if (skipmode > ADJUST_SIZE) - skipmode = ADJUST_SIZE; - write_log (_T("%d/%.2f\n"), idiff, skipmode); - - sound_setadjust (skipmode); - - if (0 && !(frame_count % avioutput_fps)) - write_log (_T("AVIOutput: diff=%.2f skip=%.2f (%d-%d=%d)\n"), diff, skipmode, - StreamSizeAudio, StreamSizeAudioExpected, idiff); -#endif + return (Y - Ystart); /* Return number of output samples */ +} + +#define IBUFFSIZE 4096 /* Input buffer size */ +#define OBUFFSIZE 8192 /* Output buffer size */ + +static int resampleFast( /* number of output samples returned */ + double factor, /* factor = Sndout/Sndin */ + HWORD *in, /* input and output file descriptors */ + HWORD *out, + int inCount, /* number of input samples to convert */ + int outCount, /* number of output samples to compute */ + int nChans) /* number of sound channels (1 or 2) */ +{ + ULWORD Time, Time2; /* Current time/pos in input sample */ + UHWORD Xp, Ncreep, Xoff, Xread; + HWORD X1[IBUFFSIZE], Y1[OBUFFSIZE]; /* I/O buffers */ + HWORD X2[IBUFFSIZE], Y2[OBUFFSIZE]; /* I/O buffers */ + UHWORD Nout, Nx; + int i, Ycount, last; + int inseekpos = 0; + int outseekpos = 0; + + Xoff = 10; + + Nx = IBUFFSIZE - 2 * Xoff; /* # of samples to process each iteration */ + last = 0; /* Have not read last input sample yet */ + Ycount = 0; /* Current sample and length of output file */ + + Xp = Xoff; /* Current "now"-sample pointer for input */ + Xread = Xoff; /* Position in input array to read into */ + Time = (Xoff << Np); /* Current-time pointer for converter */ + + for (i = 0; i 0 && idx < IBUFFSIZE) { + if (nChans == 2) { + X1[idx] = in[inseekpos * 2 + 0]; + X2[idx] = in[inseekpos * 2 + 1]; + } else { + X1[idx] = in[inseekpos]; + } + inCount--; + idx++; + inseekpos++; + } + if (inCount == 0) + last = idx; + if (last && (last - Xoff> Np) - Xoff; /* Calc time accumulation in Time */ + if (Ncreep) { + Time -= (Ncreep << Np); /* Remove time accumulation */ + Xp += Ncreep; /* and add it to read pointer */ + } + for (i = 0; ioutCount) { + Nout -= (Ycount - outCount); + Ycount = outCount; + } + + if (Nout > OBUFFSIZE) /* Check to see if output buff overflowed */ + return -1; + + if (nChans == 1) { + for (i = 0; i < Nout; i++) { + out[outseekpos] = Y1[i]; + outseekpos++; + } + } else { + for (i = 0; i < Nout; i++) { + out[outseekpos * 2 + 0] = Y1[i]; + out[outseekpos * 2 + 1] = Y2[i]; + outseekpos++; + } + } + + } while (Ycountsndbufsize / 2); + channelswap((uae_s16*)paula_sndbuffer, bufsize / 2); else if (get_audio_nativechannels (currprefs.sound_stereo) == 6) - channelswap6 ((uae_s16*)paula_sndbuffer, sdp->sndbufsize / 2); + channelswap6((uae_s16*)paula_sndbuffer, bufsize / 2); } #ifdef DRIVESOUND - driveclick_mix ((uae_s16*)paula_sndbuffer, sdp->sndbufsize / 2, currprefs.dfxclickchannelmask); + driveclick_mix((uae_s16*)paula_sndbuffer, bufsize / 2, currprefs.dfxclickchannelmask); #endif #ifdef AVIOUTPUT - if (avioutput_enabled && avioutput_audio) - AVIOutput_WriteAudio ((uae_u8*)paula_sndbuffer, sdp->sndbufsize); + if (avioutput_enabled && avioutput_audio) { + AVIOutput_WriteAudio((uae_u8*)paula_sndbuffer, bufsize); + if (avioutput_nosoundsync) + sound_setadjust(0); + } if (avioutput_enabled && (!avioutput_framelimiter || avioutput_nosoundoutput)) return; #endif if (!have_sound) return; + // we got buffer that was not full (recording active). Need special handling. + if (bufsize < sdp->sndbufsize && !extrasndbuf) { + extrasndbufsize = sdp->sndbufsize; + extrasndbuf = xcalloc(uae_u8, sdp->sndbufsize); + extrasndbuffered = 0; + } + if (statuscnt > 0 && tframe != timeframes) { tframe = timeframes; statuscnt--; @@ -2274,7 +2295,21 @@ void finish_sound_buffer (void) } if (gui_data.sndbuf_status == 3) gui_data.sndbuf_status = 0; - send_sound (sdp, paula_sndbuffer); + + if (extrasndbuf) { + int size = extrasndbuffered + bufsize; + int copied = 0; + if (size > extrasndbufsize) { + copied = extrasndbufsize - extrasndbuffered; + memcpy(extrasndbuf + extrasndbuffered, paula_sndbuffer, copied); + send_sound(sdp, (uae_u16*)extrasndbuf); + extrasndbuffered = 0; + } + memcpy(extrasndbuf + extrasndbuffered, (uae_u8*)paula_sndbuffer + copied, bufsize - copied); + extrasndbuffered += bufsize - copied; + } else { + send_sound(sdp, paula_sndbuffer); + } } static BOOL CALLBACK DSEnumProc (LPGUID lpGUID, LPCTSTR lpszDesc, LPCTSTR lpszDrvName, LPVOID lpContext) diff --git a/od-win32/sounddep/sound.h b/od-win32/sounddep/sound.h index 0d91b004..e0f508de 100644 --- a/od-win32/sounddep/sound.h +++ b/od-win32/sounddep/sound.h @@ -108,7 +108,6 @@ STATIC_INLINE void check_sound_buffers (void) #endif if ((uae_u8*)paula_sndbufpt - (uae_u8*)paula_sndbuffer >= paula_sndbufsize) { finish_sound_buffer (); - paula_sndbufpt = paula_sndbuffer; } #if SOUNDSTUFF > 1 while (doublesample-- > 0) {