mirror of
https://github.com/holub/mame
synced 2025-05-22 21:58:57 +03:00
Added new tool ldresample to assist in resynchronizing audio tracks in
a CHD with frames. Currently still WIP but useful if you know what you're doing. [Aaron Giles]
This commit is contained in:
parent
a1c2887d78
commit
819c231992
1
.gitattributes
vendored
1
.gitattributes
vendored
@ -3473,6 +3473,7 @@ src/tools/chdcd.c svneol=native#text/plain
|
|||||||
src/tools/chdcd.h svneol=native#text/plain
|
src/tools/chdcd.h svneol=native#text/plain
|
||||||
src/tools/chdman.c svneol=native#text/plain
|
src/tools/chdman.c svneol=native#text/plain
|
||||||
src/tools/jedutil.c svneol=native#text/plain
|
src/tools/jedutil.c svneol=native#text/plain
|
||||||
|
src/tools/ldresample.c svneol=native#text/plain
|
||||||
src/tools/ldverify.c svneol=native#text/plain
|
src/tools/ldverify.c svneol=native#text/plain
|
||||||
src/tools/regrep.c svneol=native#text/plain
|
src/tools/regrep.c svneol=native#text/plain
|
||||||
src/tools/romcmp.c svneol=native#text/plain
|
src/tools/romcmp.c svneol=native#text/plain
|
||||||
|
663
src/tools/ldresample.c
Normal file
663
src/tools/ldresample.c
Normal file
@ -0,0 +1,663 @@
|
|||||||
|
/***************************************************************************
|
||||||
|
|
||||||
|
ldresample.c
|
||||||
|
|
||||||
|
Laserdisc audio synchronizer and resampler.
|
||||||
|
|
||||||
|
Copyright Nicola Salmoria and the MAME Team.
|
||||||
|
Visit http://mamedev.org for licensing and usage restrictions.
|
||||||
|
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include "bitmap.h"
|
||||||
|
#include "chd.h"
|
||||||
|
#include "vbiparse.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
CONSTANTS
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
/* size of window where we scan ahead to find maximum; this should be large enough to
|
||||||
|
catch peaks of even slow waves */
|
||||||
|
#define MAXIMUM_WINDOW_SIZE 40
|
||||||
|
|
||||||
|
/* number of standard deviations away from silence that we consider a real signal */
|
||||||
|
#define SIGNAL_DEVIATIONS 100
|
||||||
|
|
||||||
|
/* number of standard deviations away from silence that we consider the start of a signal */
|
||||||
|
#define SIGNAL_START_DEVIATIONS 5
|
||||||
|
|
||||||
|
/* number of consecutive entries of signal before we consider that we found it */
|
||||||
|
#define MINIMUM_SIGNAL_COUNT 20
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
TYPE DEFINITIONS
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
typedef struct _movie_info movie_info;
|
||||||
|
struct _movie_info
|
||||||
|
{
|
||||||
|
double framerate;
|
||||||
|
int iframerate;
|
||||||
|
int numfields;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
int samplerate;
|
||||||
|
int channels;
|
||||||
|
int interlaced;
|
||||||
|
bitmap_t * bitmap;
|
||||||
|
INT16 * lsound;
|
||||||
|
INT16 * rsound;
|
||||||
|
UINT32 samples;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
GLOBAL VARIABLES
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
INLINE FUNCTIONS
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
/*-------------------------------------------------
|
||||||
|
field_to_sample_number - given a field number
|
||||||
|
compute the absolute sample number for the
|
||||||
|
first sample of that field
|
||||||
|
-------------------------------------------------*/
|
||||||
|
|
||||||
|
INLINE UINT32 field_to_sample_number(const movie_info *info, UINT32 field)
|
||||||
|
{
|
||||||
|
return ((UINT64)info->samplerate * (UINT64)field * (UINT64)1000000 + info->iframerate - 1) / (UINT64)info->iframerate;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*-------------------------------------------------
|
||||||
|
sample_number_to_field - given a sample number
|
||||||
|
compute the field where it is located and
|
||||||
|
the offset within the field
|
||||||
|
-------------------------------------------------*/
|
||||||
|
|
||||||
|
INLINE UINT32 sample_number_to_field(const movie_info *info, UINT32 samplenum, UINT32 *offset)
|
||||||
|
{
|
||||||
|
UINT32 guess = ((UINT64)samplenum * (UINT64)info->iframerate + ((UINT64)info->samplerate * (UINT64)1000000 - 1)) / ((UINT64)info->samplerate * (UINT64)1000000);
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
UINT32 fieldstart = field_to_sample_number(info, guess);
|
||||||
|
UINT32 fieldend = field_to_sample_number(info, guess + 1);
|
||||||
|
if (samplenum >= fieldstart && samplenum < fieldend)
|
||||||
|
{
|
||||||
|
*offset = samplenum - fieldstart;
|
||||||
|
return guess;
|
||||||
|
}
|
||||||
|
else if (samplenum < fieldstart)
|
||||||
|
guess--;
|
||||||
|
else
|
||||||
|
guess++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
CHD HANDLING
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
/*-------------------------------------------------
|
||||||
|
chd_allocate_buffers - allocate buffers for
|
||||||
|
CHD I/O
|
||||||
|
-------------------------------------------------*/
|
||||||
|
|
||||||
|
static int chd_allocate_buffers(movie_info *info)
|
||||||
|
{
|
||||||
|
/* allocate a bitmap */
|
||||||
|
info->bitmap = bitmap_alloc(info->width, info->height, BITMAP_FORMAT_YUY16);
|
||||||
|
if (info->bitmap == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Out of memory creating %dx%d bitmap\n", info->width, info->height);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* allocate sound buffers */
|
||||||
|
info->lsound = malloc(info->samplerate * sizeof(*info->lsound));
|
||||||
|
info->rsound = malloc(info->samplerate * sizeof(*info->rsound));
|
||||||
|
if (info->lsound == NULL || info->rsound == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Out of memory allocating sound buffers of %d bytes\n", (INT32)(info->samplerate * sizeof(*info->rsound)));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*-------------------------------------------------
|
||||||
|
chd_free_buffers - release buffers for
|
||||||
|
CHD I/O
|
||||||
|
-------------------------------------------------*/
|
||||||
|
|
||||||
|
static void chd_free_buffers(movie_info *info)
|
||||||
|
{
|
||||||
|
if (info->bitmap != NULL)
|
||||||
|
free(info->bitmap);
|
||||||
|
if (info->lsound != NULL)
|
||||||
|
free(info->lsound);
|
||||||
|
if (info->rsound != NULL)
|
||||||
|
free(info->rsound);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*-------------------------------------------------
|
||||||
|
open_chd - open a CHD file and return
|
||||||
|
information about it
|
||||||
|
-------------------------------------------------*/
|
||||||
|
|
||||||
|
static chd_file *open_chd(const char *filename, movie_info *info)
|
||||||
|
{
|
||||||
|
int fps, fpsfrac, width, height, interlaced, channels, rate;
|
||||||
|
char metadata[256];
|
||||||
|
chd_error chderr;
|
||||||
|
chd_file *chd;
|
||||||
|
|
||||||
|
/* open the file */
|
||||||
|
chderr = chd_open(filename, CHD_OPEN_READ, NULL, &chd);
|
||||||
|
if (chderr != CHDERR_NONE)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Error opening CHD file: %s\n", chd_error_string(chderr));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get the metadata */
|
||||||
|
chderr = chd_get_metadata(chd, AV_METADATA_TAG, 0, metadata, sizeof(metadata), NULL, NULL);
|
||||||
|
if (chderr != CHDERR_NONE)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Error getting A/V metadata: %s\n", chd_error_string(chderr));
|
||||||
|
chd_close(chd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* extract the info */
|
||||||
|
if (sscanf(metadata, AV_METADATA_FORMAT, &fps, &fpsfrac, &width, &height, &interlaced, &channels, &rate) != 7)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Improperly formatted metadata\n");
|
||||||
|
chd_close(chd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* extract movie info */
|
||||||
|
info->iframerate = fps * 1000000 + fpsfrac;
|
||||||
|
info->framerate = info->iframerate / 1000000.0;
|
||||||
|
info->numfields = chd_get_header(chd)->totalhunks;
|
||||||
|
info->width = width;
|
||||||
|
info->height = height;
|
||||||
|
info->interlaced = interlaced;
|
||||||
|
info->samplerate = rate;
|
||||||
|
info->channels = channels;
|
||||||
|
|
||||||
|
/* allocate buffers */
|
||||||
|
if (!chd_allocate_buffers(info))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return chd;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*-------------------------------------------------
|
||||||
|
create_chd - create a new CHD file
|
||||||
|
-------------------------------------------------*/
|
||||||
|
|
||||||
|
static chd_file *create_chd(const char *filename, chd_file *source, const movie_info *info)
|
||||||
|
{
|
||||||
|
const chd_header *srcheader = chd_get_header(source);
|
||||||
|
chd_error chderr;
|
||||||
|
chd_file *chd;
|
||||||
|
|
||||||
|
/* create the file */
|
||||||
|
chderr = chd_create(filename, srcheader->logicalbytes, srcheader->hunkbytes, CHDCOMPRESSION_AV, NULL);
|
||||||
|
if (chderr != CHDERR_NONE)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Error creating new CHD file: %s\n", chd_error_string(chderr));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* open the file */
|
||||||
|
chderr = chd_open(filename, CHD_OPEN_READWRITE, NULL, &chd);
|
||||||
|
if (chderr != CHDERR_NONE)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Error opening new CHD file: %s\n", chd_error_string(chderr));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* clone the metadata */
|
||||||
|
chderr = chd_clone_metadata(source, chd);
|
||||||
|
if (chderr != CHDERR_NONE)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Error cloning metadata: %s\n", chd_error_string(chderr));
|
||||||
|
chd_close(chd);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* begin compressing */
|
||||||
|
chderr = chd_compress_begin(chd);
|
||||||
|
if (chderr != CHDERR_NONE)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Error beginning compression: %s\n", chd_error_string(chderr));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return chd;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*-------------------------------------------------
|
||||||
|
read_chd - read a field from a CHD file
|
||||||
|
-------------------------------------------------*/
|
||||||
|
|
||||||
|
static int read_chd(chd_file *file, UINT32 field, movie_info *info, UINT32 soundoffs)
|
||||||
|
{
|
||||||
|
av_codec_decompress_config avconfig = { 0 };
|
||||||
|
chd_error chderr;
|
||||||
|
|
||||||
|
/* configure the codec */
|
||||||
|
avconfig.video = info->bitmap;
|
||||||
|
avconfig.maxsamples = 48000;
|
||||||
|
avconfig.actsamples = &info->samples;
|
||||||
|
avconfig.audio[0] = info->lsound + soundoffs;
|
||||||
|
avconfig.audio[1] = info->rsound + soundoffs;
|
||||||
|
|
||||||
|
/* configure the decompressor for this field */
|
||||||
|
chd_codec_config(file, AV_CODEC_DECOMPRESS_CONFIG, &avconfig);
|
||||||
|
|
||||||
|
/* read the field */
|
||||||
|
chderr = chd_read(file, field, NULL);
|
||||||
|
if (chderr != CHDERR_NONE)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*-------------------------------------------------
|
||||||
|
write_chd - write a field to a CHD file
|
||||||
|
-------------------------------------------------*/
|
||||||
|
|
||||||
|
static int write_chd(chd_file *file, UINT32 field, movie_info *info)
|
||||||
|
{
|
||||||
|
av_codec_compress_config avconfig = { 0 };
|
||||||
|
chd_error chderr;
|
||||||
|
|
||||||
|
/* configure the codec */
|
||||||
|
avconfig.video = info->bitmap;
|
||||||
|
avconfig.channels = 2;
|
||||||
|
avconfig.samples = info->samples;
|
||||||
|
avconfig.audio[0] = info->lsound;
|
||||||
|
avconfig.audio[1] = info->rsound;
|
||||||
|
|
||||||
|
/* configure the decompressor for this field */
|
||||||
|
chd_codec_config(file, AV_CODEC_COMPRESS_CONFIG, &avconfig);
|
||||||
|
|
||||||
|
/* read the field */
|
||||||
|
chderr = chd_compress_hunk(file, NULL, NULL);
|
||||||
|
if (chderr != CHDERR_NONE)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*-------------------------------------------------
|
||||||
|
create_close_chd - close a CHD file
|
||||||
|
-------------------------------------------------*/
|
||||||
|
|
||||||
|
static void create_close_chd(chd_file *file)
|
||||||
|
{
|
||||||
|
chd_error err;
|
||||||
|
|
||||||
|
err = chd_compress_finish(file);
|
||||||
|
if (err != CHDERR_NONE)
|
||||||
|
fprintf(stderr, "Error finishing compression: %s\n", chd_error_string(err));
|
||||||
|
|
||||||
|
chd_close(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*-------------------------------------------------
|
||||||
|
close_chd - close a CHD file
|
||||||
|
-------------------------------------------------*/
|
||||||
|
|
||||||
|
static void close_chd(chd_file *file, movie_info *info)
|
||||||
|
{
|
||||||
|
if (info != NULL)
|
||||||
|
chd_free_buffers(info);
|
||||||
|
chd_close(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
CORE IMPLEMENTATION
|
||||||
|
***************************************************************************/
|
||||||
|
|
||||||
|
/*-------------------------------------------------
|
||||||
|
find_edge_near_field - given a field number,
|
||||||
|
load +/- 1/2 second on either side and find
|
||||||
|
an audio edge
|
||||||
|
-------------------------------------------------*/
|
||||||
|
|
||||||
|
static int find_edge_near_field(chd_file *srcfile, UINT32 fieldnum, movie_info *info, int report_best_field, INT32 *delta)
|
||||||
|
{
|
||||||
|
int fields_to_read = info->iframerate / 1000000;
|
||||||
|
UINT32 firstlavg = 0, firstravg = 0;
|
||||||
|
UINT32 firstldev = 0, firstrdev = 0;
|
||||||
|
UINT32 lcount = 0, rcount = 0;
|
||||||
|
UINT32 targetsoundstart = 0;
|
||||||
|
UINT32 firstfieldend = 0;
|
||||||
|
INT32 firstfield, curfield;
|
||||||
|
UINT32 fieldstart[100];
|
||||||
|
UINT32 soundend = 0;
|
||||||
|
UINT32 sampnum;
|
||||||
|
|
||||||
|
/* clear the sound buffers */
|
||||||
|
memset(info->lsound, 0, info->samplerate * sizeof(*info->lsound));
|
||||||
|
memset(info->rsound, 0, info->samplerate * sizeof(*info->rsound));
|
||||||
|
|
||||||
|
/* read 1 second around the target area */
|
||||||
|
firstfield = fieldnum - (fields_to_read / 2);
|
||||||
|
for (curfield = 0; curfield < fields_to_read; curfield++)
|
||||||
|
{
|
||||||
|
/* remember the start of each field */
|
||||||
|
fieldstart[curfield] = soundend;
|
||||||
|
|
||||||
|
/* remember the sound offset where the initial fieldnum is */
|
||||||
|
if (firstfield + curfield == fieldnum)
|
||||||
|
targetsoundstart = soundend;
|
||||||
|
|
||||||
|
/* read the frame and samples */
|
||||||
|
if (firstfield + curfield >= 0)
|
||||||
|
{
|
||||||
|
read_chd(srcfile, firstfield + curfield, info, soundend);
|
||||||
|
soundend += info->samples;
|
||||||
|
|
||||||
|
/* also remember the offset at the end of the first field */
|
||||||
|
if (firstfieldend == 0)
|
||||||
|
firstfieldend = soundend;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* compute absolute deltas across the samples */
|
||||||
|
for (sampnum = 0; sampnum < soundend; sampnum++)
|
||||||
|
{
|
||||||
|
info->lsound[sampnum] = labs(info->lsound[sampnum + 1] - info->lsound[sampnum]);
|
||||||
|
info->rsound[sampnum] = labs(info->rsound[sampnum + 1] - info->rsound[sampnum]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* for each sample in the collection, find the highest deltas over the
|
||||||
|
next few samples, and take the nth highest value (to remove outliers) */
|
||||||
|
for (sampnum = 0; sampnum < soundend - MAXIMUM_WINDOW_SIZE; sampnum++)
|
||||||
|
{
|
||||||
|
UINT32 lmax = 0, rmax = 0;
|
||||||
|
UINT32 scannum;
|
||||||
|
|
||||||
|
/* scan forward over the maximum window */
|
||||||
|
for (scannum = 0; scannum < MAXIMUM_WINDOW_SIZE; scannum++)
|
||||||
|
{
|
||||||
|
if (info->lsound[sampnum + scannum] > lmax)
|
||||||
|
lmax = info->lsound[sampnum + scannum];
|
||||||
|
if (info->rsound[sampnum + scannum] > rmax)
|
||||||
|
rmax = info->rsound[sampnum + scannum];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* replace this sample with the maximum value found */
|
||||||
|
info->lsound[sampnum] = lmax;
|
||||||
|
info->rsound[sampnum] = rmax;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* now compute the average over the first field, which is assumed to be silence */
|
||||||
|
for (sampnum = 0; sampnum < firstfieldend; sampnum++)
|
||||||
|
{
|
||||||
|
firstlavg += info->lsound[sampnum];
|
||||||
|
firstravg += info->rsound[sampnum];
|
||||||
|
}
|
||||||
|
firstlavg /= firstfieldend;
|
||||||
|
firstravg /= firstfieldend;
|
||||||
|
|
||||||
|
/* then compute the standard deviation over the first field */
|
||||||
|
for (sampnum = 0; sampnum < firstfieldend; sampnum++)
|
||||||
|
{
|
||||||
|
firstldev += (info->lsound[sampnum] - firstlavg) * (info->lsound[sampnum] - firstlavg);
|
||||||
|
firstrdev += (info->rsound[sampnum] - firstravg) * (info->rsound[sampnum] - firstravg);
|
||||||
|
}
|
||||||
|
firstldev = sqrt((double)firstldev / firstfieldend);
|
||||||
|
firstrdev = sqrt((double)firstrdev / firstfieldend);
|
||||||
|
|
||||||
|
/* scan forward through the samples, counting consecutive samples more than
|
||||||
|
SIGNAL_DEVIATIONS standard deviations away from silence */
|
||||||
|
for (sampnum = 0; sampnum < soundend; sampnum++)
|
||||||
|
{
|
||||||
|
/* left speaker */
|
||||||
|
if (info->lsound[sampnum] > firstlavg + SIGNAL_DEVIATIONS * firstldev)
|
||||||
|
lcount++;
|
||||||
|
else
|
||||||
|
lcount = 0;
|
||||||
|
|
||||||
|
/* right speaker */
|
||||||
|
if (info->rsound[sampnum] > firstravg + SIGNAL_DEVIATIONS * firstrdev)
|
||||||
|
rcount++;
|
||||||
|
else
|
||||||
|
rcount = 0;
|
||||||
|
|
||||||
|
/* stop if we find enough */
|
||||||
|
if (lcount > MINIMUM_SIGNAL_COUNT || rcount > MINIMUM_SIGNAL_COUNT)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if we didn't find any, return failure */
|
||||||
|
if (sampnum >= soundend)
|
||||||
|
{
|
||||||
|
if (!report_best_field)
|
||||||
|
printf("Field %5d: Unable to find edge\n", fieldnum);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* scan backwards to find the start of the signal */
|
||||||
|
for ( ; sampnum > 0; sampnum--)
|
||||||
|
if (info->lsound[sampnum - 1] < firstlavg + SIGNAL_START_DEVIATIONS * firstldev ||
|
||||||
|
info->rsound[sampnum - 1] < firstravg + SIGNAL_START_DEVIATIONS * firstrdev)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* if we're to report the best field, figure out which field we are in */
|
||||||
|
if (report_best_field)
|
||||||
|
{
|
||||||
|
for (curfield = 0; curfield < fields_to_read - 1; curfield++)
|
||||||
|
if (sampnum < fieldstart[curfield + 1])
|
||||||
|
break;
|
||||||
|
printf("Field %5d: Edge found at offset %d (frame %.1f)\n", firstfield + curfield, sampnum - fieldstart[curfield], (double)(firstfield + curfield) * 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* otherwise, compute the delta from the provided field number */
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("Field %5d: Edge at offset %d from expected (found at %d, expected %d)\n", fieldnum, sampnum - targetsoundstart, sampnum, targetsoundstart);
|
||||||
|
*delta = sampnum - targetsoundstart;
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*-------------------------------------------------
|
||||||
|
usage - display program usage
|
||||||
|
-------------------------------------------------*/
|
||||||
|
|
||||||
|
static int usage(void)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Usage: \n");
|
||||||
|
fprintf(stderr, " ldresample source.chd\n");
|
||||||
|
fprintf(stderr, " ldresample source.chd output.chd offset [slope]\n");
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
fprintf(stderr, "Where offset and slope make a linear equation f(x) which\n");
|
||||||
|
fprintf(stderr, "describes the sample offset from the source as a function\n");
|
||||||
|
fprintf(stderr, "of field number.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*-------------------------------------------------
|
||||||
|
main - main entry point
|
||||||
|
-------------------------------------------------*/
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
movie_info info = { 0 };
|
||||||
|
const char *srcfilename;
|
||||||
|
const char *dstfilename;
|
||||||
|
double offset, slope;
|
||||||
|
chd_file *srcfile;
|
||||||
|
chd_file *dstfile;
|
||||||
|
|
||||||
|
/* verify arguments */
|
||||||
|
if (argc < 2)
|
||||||
|
return usage();
|
||||||
|
srcfilename = argv[1];
|
||||||
|
dstfilename = (argc < 3) ? NULL : argv[2];
|
||||||
|
offset = (argc < 4) ? 0.0 : atof(argv[3]);
|
||||||
|
slope = (argc < 5) ? 1.0 : atof(argv[4]);
|
||||||
|
|
||||||
|
/* print basic information */
|
||||||
|
printf("Input file: %s\n", srcfilename);
|
||||||
|
if (dstfilename != NULL)
|
||||||
|
{
|
||||||
|
printf("Output file: %s\n", dstfilename);
|
||||||
|
printf("Offset: %f\n", offset);
|
||||||
|
printf("Slope: %f\n", slope);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* open the source file */
|
||||||
|
srcfile = open_chd(srcfilename, &info);
|
||||||
|
if (srcfile == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Unable to open file '%s'\n", srcfilename);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* output some basics */
|
||||||
|
printf("Video dimensions: %dx%d\n", info.width, info.height);
|
||||||
|
printf("Video frame rate: %.2fHz\n", info.framerate);
|
||||||
|
printf("Sample rate: %dHz\n", info.samplerate);
|
||||||
|
printf("Total fields: %d\n", info.numfields);
|
||||||
|
|
||||||
|
/* if we don't have a destination file, scan for edges */
|
||||||
|
if (dstfilename == NULL)
|
||||||
|
{
|
||||||
|
UINT32 fieldnum;
|
||||||
|
INT32 delta;
|
||||||
|
|
||||||
|
for (fieldnum = 60; fieldnum < info.numfields - 60; fieldnum += 30)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Field %5d\r", fieldnum);
|
||||||
|
find_edge_near_field(srcfile, fieldnum, &info, TRUE, &delta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* otherwise, resample the source to the destination */
|
||||||
|
else
|
||||||
|
{
|
||||||
|
INT64 ioffset = (INT64)(offset * 65536.0 * 256.0);
|
||||||
|
INT64 islope = (INT64)(slope * 65536.0 * 256.0);
|
||||||
|
UINT32 fieldnum;
|
||||||
|
|
||||||
|
/* open the destination file */
|
||||||
|
dstfile = create_chd(dstfilename, srcfile, &info);
|
||||||
|
if (dstfile == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Unable to create file '%s'\n", dstfilename);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* loop over all the fields in the source file */
|
||||||
|
for (fieldnum = 0; fieldnum < info.numfields; fieldnum++)
|
||||||
|
{
|
||||||
|
UINT32 srcbegin = field_to_sample_number(&info, fieldnum);
|
||||||
|
UINT32 srcend = field_to_sample_number(&info, fieldnum + 1);
|
||||||
|
INT64 dstbegin = ((INT64)srcbegin << 24) + ioffset + islope * fieldnum;
|
||||||
|
INT64 dstend = ((INT64)srcend << 24) + ioffset + islope * (fieldnum + 1);
|
||||||
|
UINT32 dstbeginoffset, dstendoffset, dstoffset;
|
||||||
|
INT32 dstbeginfield, dstendfield, dstfield;
|
||||||
|
INT64 dstpos, dststep;
|
||||||
|
UINT32 srcoffset;
|
||||||
|
|
||||||
|
/* update progress (this ain't fast!) */
|
||||||
|
if (fieldnum % 10 == 0)
|
||||||
|
fprintf(stderr, "Field %d\r", fieldnum);
|
||||||
|
|
||||||
|
/* determine the first and last fields needed to cover this range of samples */
|
||||||
|
if (dstbegin >= 0)
|
||||||
|
dstbeginfield = sample_number_to_field(&info, dstbegin >> 24, &dstbeginoffset);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dstbeginfield = -1 - sample_number_to_field(&info, -dstbegin >> 24, &dstbeginoffset);
|
||||||
|
dstbeginoffset = (field_to_sample_number(&info, -dstbeginfield) - field_to_sample_number(&info, -dstbeginfield - 1)) - dstbeginoffset;
|
||||||
|
}
|
||||||
|
if (dstend >= 0)
|
||||||
|
dstendfield = sample_number_to_field(&info, dstend >> 24, &dstendoffset);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dstendfield = -1 - -sample_number_to_field(&info, -dstend >> 24, &dstendoffset);
|
||||||
|
dstendoffset = (field_to_sample_number(&info, -dstendfield) - field_to_sample_number(&info, -dstendfield - 1)) - dstendoffset;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
printf("%5d: start=%10d (%5d.%03d) end=%10d (%5d.%03d)\n",
|
||||||
|
fieldnum,
|
||||||
|
(INT32)(dstbegin >> 24), dstbeginfield, dstbeginoffset,
|
||||||
|
(INT32)(dstend >> 24), dstendfield, dstendoffset);
|
||||||
|
*/
|
||||||
|
/* read all samples required into the end of the sound buffers */
|
||||||
|
dstoffset = srcend - srcbegin;
|
||||||
|
for (dstfield = dstbeginfield; dstfield <= dstendfield; dstfield++)
|
||||||
|
{
|
||||||
|
if (dstfield >= 0)
|
||||||
|
read_chd(srcfile, dstfield, &info, dstoffset);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
info.samples = field_to_sample_number(&info, -dstfield) - field_to_sample_number(&info, -dstfield - 1);
|
||||||
|
memset(&info.lsound[dstoffset], 0, info.samples * sizeof(info.lsound[0]));
|
||||||
|
memset(&info.rsound[dstoffset], 0, info.samples * sizeof(info.rsound[0]));
|
||||||
|
}
|
||||||
|
dstoffset += info.samples;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* resample the destination samples to the source */
|
||||||
|
dstoffset = srcend - srcbegin;
|
||||||
|
dstpos = dstbegin;
|
||||||
|
dststep = (dstend - dstbegin) / (INT64)(srcend - srcbegin);
|
||||||
|
for (srcoffset = 0; srcoffset < srcend - srcbegin; srcoffset++)
|
||||||
|
{
|
||||||
|
info.lsound[srcoffset] = info.lsound[dstoffset + dstbeginoffset + (dstpos >> 24) - (dstbegin >> 24)];
|
||||||
|
info.rsound[srcoffset] = info.rsound[dstoffset + dstbeginoffset + (dstpos >> 24) - (dstbegin >> 24)];
|
||||||
|
dstpos += dststep;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* read the original frame, pointing the sound buffer past where we've calculated */
|
||||||
|
read_chd(srcfile, fieldnum, &info, srcend - srcbegin);
|
||||||
|
|
||||||
|
/* write it to the destination */
|
||||||
|
write_chd(dstfile, fieldnum, &info);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* close the destination file */
|
||||||
|
create_close_chd(dstfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* close the source file */
|
||||||
|
close_chd(srcfile, &info);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -650,7 +650,7 @@ static void verify_audio_final(void)
|
|||||||
static int usage(void)
|
static int usage(void)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Usage: \n");
|
fprintf(stderr, "Usage: \n");
|
||||||
fprintf(stderr, " ldverify [avifile.avi|chdfile.chd]\n");
|
fprintf(stderr, " ldverify <avifile.avi|chdfile.chd>\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ TOOLS += \
|
|||||||
romcmp$(EXE) \
|
romcmp$(EXE) \
|
||||||
chdman$(EXE) \
|
chdman$(EXE) \
|
||||||
jedutil$(EXE) \
|
jedutil$(EXE) \
|
||||||
|
ldresample$(EXE) \
|
||||||
ldverify$(EXE) \
|
ldverify$(EXE) \
|
||||||
regrep$(EXE) \
|
regrep$(EXE) \
|
||||||
srcclean$(EXE) \
|
srcclean$(EXE) \
|
||||||
@ -73,6 +74,19 @@ jedutil$(EXE): $(JEDUTILOBJS) $(LIBUTIL) $(LIBOCORE) $(ZLIB) $(EXPAT)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#-------------------------------------------------
|
||||||
|
# ldresample
|
||||||
|
#-------------------------------------------------
|
||||||
|
|
||||||
|
LDRESAMPLEOBJS = \
|
||||||
|
$(TOOLSOBJ)/ldresample.o \
|
||||||
|
|
||||||
|
ldresample$(EXE): $(LDRESAMPLEOBJS) $(LIBUTIL) $(LIBOCORE) $(ZLIB) $(EXPAT)
|
||||||
|
@echo Linking $@...
|
||||||
|
$(LD) $(LDFLAGS) $^ $(LIBS) -o $@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#-------------------------------------------------
|
#-------------------------------------------------
|
||||||
# ldverify
|
# ldverify
|
||||||
#-------------------------------------------------
|
#-------------------------------------------------
|
||||||
|
Loading…
Reference in New Issue
Block a user