src/ : Reimplement handling of broadcast extention chunk in WAV/WAVEX files.

This commit is contained in:
Erik de Castro Lopo 2008-10-26 20:45:37 +11:00
parent 62e45b55ae
commit ba76501702
5 changed files with 249 additions and 26 deletions

View File

@ -18,11 +18,12 @@
*/
#include <stdio.h>
#include <stddef.h>
#include <string.h>
#include "common.h"
static void strncpy_crlf (char *dest, const char *src, size_t n) ;
static void strncpy_crlf (char *dest, const char *src, size_t destmax, size_t srcmax) ;
/*
** Allocate and initialize a broadcast info structure.
@ -43,7 +44,7 @@ broadcast_info_copy (SF_BROADCAST_INFO* dst, const SF_BROADCAST_INFO* src)
{ memcpy (dst, src, sizeof (SF_BROADCAST_INFO)) ;
/* In case the src has the wrong line endings. */
strncpy_crlf (dst->coding_history, src->coding_history, sizeof (dst->coding_history)) ;
strncpy_crlf (dst->coding_history, src->coding_history, sizeof (dst->coding_history), src->coding_history_size) ;
/* Currently writing this version. */
dst->version = 1 ;
@ -127,7 +128,7 @@ broadcast_add_coding_history (SF_BROADCAST_INFO* bext, unsigned int channels, un
/* Note newlines in Coding History should be '\r\n' as per the URL above. */
if (bext->coding_history_size > 0)
strncpy_crlf (history, bext->coding_history, sizeof (history)) ;
strncpy_crlf (history, bext->coding_history, sizeof (history), bext->coding_history_size) ;
else
history [0] = 0 ;
@ -169,15 +170,203 @@ broadcast_add_coding_history (SF_BROADCAST_INFO* bext, unsigned int channels, un
return SF_TRUE ;
} /* broadcast_add_coding_history */
/*==============================================================================
*/
#if 1
static int
gen_coding_history (char * added_history, size_t added_history_max, const SF_INFO * psfinfo)
{ char chnstr [16] ;
int count, width ;
/*
** From : http://www.sr.se/utveckling/tu/bwf/docs/codhist2.htm
**
** Parameter Variable string <allowed option> Unit
** ==========================================================================================
** Coding Algorithm A=<ANALOGUE, PCM, MPEG1L1, MPEG1L2, MPEG1L3,
** MPEG2L1, MPEG2L2, MPEG2L3>
** Sampling frequency F=<11000,22050,24000,32000,44100,48000> [Hz]
** Bit-rate B=<any bit-rate allowed in MPEG 2 (ISO/IEC [kbit/s per channel]
** 13818-3)>
** Word Length W=<8, 12, 14, 16, 18, 20, 22, 24> [bits]
** Mode M=<mono, stereo, dual-mono, joint-stereo>
** Text, free string T=<a free ASCII-text string for in house use.
** This string should contain no commas (ASCII
** 2Chex). Examples of the contents: ID-No; codec
** type; A/D type>
*/
switch (psfinfo->channels)
{ case 0 :
return SF_FALSE ;
case 1 :
strncpy (chnstr, "mono", sizeof (chnstr)) ;
break ;
case 2 :
strncpy (chnstr, "stereo", sizeof (chnstr)) ;
break ;
default :
LSF_SNPRINTF (chnstr, sizeof (chnstr), "%uchn", psfinfo->channels) ;
break ;
} ;
switch (SF_CODEC (psfinfo->format))
{ case SF_FORMAT_PCM_U8 :
case SF_FORMAT_PCM_S8 :
width = 8 ;
break ;
case SF_FORMAT_PCM_16 :
width = 16 ;
break ;
case SF_FORMAT_PCM_24 :
width = 24 ;
break ;
case SF_FORMAT_PCM_32 :
width = 32 ;
break ;
case SF_FORMAT_FLOAT :
width = 24 ; /* Bits in the mantissa + 1 */
break ;
case SF_FORMAT_DOUBLE :
width = 53 ; /* Bits in the mantissa + 1 */
break ;
case SF_FORMAT_ULAW :
case SF_FORMAT_ALAW :
width = 12 ;
break ;
default :
width = 42 ;
break ;
} ;
count = LSF_SNPRINTF (added_history, added_history_max,
"A=PCM,F=%u,W=%hu,M=%s,T=%s-%s\r\n",
psfinfo->samplerate, width, chnstr, PACKAGE, VERSION) ;
if (count >= SIGNED_SIZEOF (added_history))
return 0 ;
return count ;
} /* gen_coding_history */
static inline size_t
bc_min_size (const SF_BROADCAST_INFO* info)
{ if (info == NULL)
return 0 ;
return offsetof (SF_BROADCAST_INFO, coding_history) + info->coding_history_size ;
} /* broadcast_size */
static inline size_t
bc_var_coding_hist_size (const SF_BROADCAST_VAR* var)
{ return var->size - offsetof (SF_BROADCAST_VAR, binfo.coding_history) ;
} /* broadcast_size */
SF_BROADCAST_VAR*
broadcast_var_alloc (size_t datasize)
{ SF_BROADCAST_VAR * data ;
if ((data = calloc (1, datasize)) != NULL)
data->size = datasize ;
return data ;
} /* broadcast_var_alloc */
int
broadcast_var_set (SF_PRIVATE *psf, const SF_BROADCAST_INFO * info, size_t datasize)
{ char added_history [256] ;
int added_history_len, total_history_len ;
if (info == NULL)
return SF_FALSE ;
if (bc_min_size (info) > datasize)
{ psf->error = SFE_BAD_BROADCAST_INFO_SIZE ;
return SF_FALSE ;
} ;
added_history_len = gen_coding_history (added_history, sizeof (added_history), &(psf->sf)) ;
if (psf->broadcast_var != NULL
&& psf->broadcast_var->binfo.coding_history_size + added_history_len < datasize)
{ free (psf->broadcast_var) ;
psf->broadcast_var = NULL ;
} ;
if (psf->broadcast_var == NULL)
{ int size = datasize + added_history_len + 512 ;
psf->broadcast_var = calloc (1, size) ;
psf->broadcast_var->size = size ;
} ;
memcpy (&(psf->broadcast_var->binfo), info, offsetof (SF_BROADCAST_INFO, coding_history)) ;
strncpy_crlf (psf->broadcast_var->binfo.coding_history, info->coding_history, bc_var_coding_hist_size (psf->broadcast_var), info->coding_history_size) ;
total_history_len = strlen (psf->broadcast_var->binfo.coding_history) + added_history_len ;
if (psf->mode == SFM_WRITE)
strncat (psf->broadcast_var->binfo.coding_history, added_history, strlen (added_history)) ;
psf->broadcast_var->binfo.coding_history_size = strlen (psf->broadcast_var->binfo.coding_history) ;
/* Fore coding_history_size to be even. */
psf->broadcast_var->binfo.coding_history_size += (psf->broadcast_var->binfo.coding_history_size & 1) ? 1 : 0 ;
/* Currently writing this version. */
psf->broadcast_var->binfo.version = 1 ;
return SF_TRUE ;
} /* broadcast_var_set */
int
broadcast_var_get (SF_PRIVATE *psf, SF_BROADCAST_INFO * data, size_t datasize)
{ size_t size ;
broadcast_info_to_var (psf) ;
if (psf->broadcast_var == NULL)
return SF_FALSE ;
size = SF_MIN (datasize, bc_min_size (&(psf->broadcast_var->binfo))) ;
memcpy (data, &(psf->broadcast_var->binfo), size) ;
return SF_TRUE ;
} /* broadcast_var_set */
void
broadcast_info_to_var (SF_PRIVATE *psf)
{
if (psf->broadcast_info != NULL)
{ broadcast_var_set (psf, psf->broadcast_info, sizeof (SF_BROADCAST_INFO)) ;
free (psf->broadcast_info) ;
psf->broadcast_info = NULL ;
} ;
} /* broadcast_info_to_var */
#endif
/*------------------------------------------------------------------------------
** Strncpy which converts all line endings to CR/LF.
*/
static void
strncpy_crlf (char *dest, const char *src, size_t max)
{ char * end = dest + max - 1 ;
strncpy_crlf (char *dest, const char *src, size_t destmax, size_t srcmax)
{ char * destend = dest + destmax - 1 ;
const char * srcend = src + srcmax - 1 ;
while (dest < end && src [0])
while (dest < destend && src < srcend)
{ if ((src [0] == '\r' && src [1] == '\n') || (src [0] == '\n' && src [1] == '\r'))
{ *dest++ = '\r' ;
*dest++ = '\n' ;

View File

@ -220,6 +220,12 @@ make_size_t (int x)
** contents.
*/
typedef struct
{ int size ;
SF_BROADCAST_INFO binfo ;
} SF_BROADCAST_VAR ;
typedef struct sf_private_tag
{
/* Canary in a coal mine. */
@ -318,6 +324,7 @@ typedef struct sf_private_tag
/* Broadcast (EBU) Info */
SF_BROADCAST_INFO *broadcast_info ;
SF_BROADCAST_VAR *broadcast_var ;
/* Channel map data (if present) : an array of ints. */
int *channel_map ;
@ -787,6 +794,12 @@ SF_BROADCAST_INFO* broadcast_info_alloc (void) ;
int broadcast_info_copy (SF_BROADCAST_INFO* dst, const SF_BROADCAST_INFO* src) ;
int broadcast_add_coding_history (SF_BROADCAST_INFO* bext, unsigned int channels, unsigned int samplerate, int format) ;
SF_BROADCAST_VAR* broadcast_var_alloc (size_t datasize) ;
int broadcast_var_set (SF_PRIVATE *psf, const SF_BROADCAST_INFO * data, size_t datasize) ;
int broadcast_var_get (SF_PRIVATE *psf, SF_BROADCAST_INFO * data, size_t datasize) ;
void broadcast_info_to_var (SF_PRIVATE *psf) ;
typedef struct
{ int channels ;
int endianness ;

View File

@ -1116,29 +1116,37 @@ sf_command (SNDFILE *sndfile, int command, void *data, int datasize)
if ((psf->mode != SFM_WRITE) && (psf->mode != SFM_RDWR))
return SF_FALSE ;
/* If data has already been written this must fail. */
if (psf->broadcast_info == NULL && psf->have_written)
if (psf->broadcast_var == NULL && psf->have_written)
{ psf->error = SFE_CMD_HAS_DATA ;
return SF_FALSE ;
} ;
#if 0
if (psf->broadcast_info == NULL)
psf->broadcast_info = broadcast_info_alloc () ;
broadcast_info_copy (psf->broadcast_info, data) ;
broadcast_add_coding_history (psf->broadcast_info, psf->sf.channels, psf->sf.samplerate, psf->sf.format) ;
#else
broadcast_var_set (psf, data, datasize) ;
#endif
if (psf->write_header)
psf->write_header (psf, SF_TRUE) ;
return SF_TRUE ;
case SFC_GET_BROADCAST_INFO :
if (datasize != sizeof (SF_BROADCAST_INFO) || data == NULL)
if (data == NULL)
{ psf->error = SFE_BAD_COMMAND_PARAM ;
return SF_FALSE ;
} ;
#if 0
if (psf->broadcast_info == NULL)
return SF_FALSE ;
return broadcast_info_copy (data, psf->broadcast_info) ;
#else
return broadcast_var_get (psf, data, datasize) ;
#endif
case SFC_GET_INSTRUMENT :
if (datasize != sizeof (SF_INSTRUMENT) || data == NULL)
@ -2423,6 +2431,9 @@ psf_close (SF_PRIVATE *psf)
if (psf->broadcast_info)
free (psf->broadcast_info) ;
if (psf->broadcast_var)
free (psf->broadcast_var) ;
if (psf->loop_info)
free (psf->loop_info) ;

View File

@ -427,20 +427,27 @@ typedef struct
/* Struct used to retrieve broadcast (EBU) information from a file.
** Strongly (!) based on EBU "bext" chunk format used in Broadcast WAVE.
*/
typedef struct
{ char description [256] ;
char originator [32] ;
char originator_reference [32] ;
char origination_date [10] ;
char origination_time [8] ;
unsigned int time_reference_low ;
unsigned int time_reference_high ;
short version ;
char umid [64] ;
char reserved [190] ;
unsigned int coding_history_size ;
char coding_history [256] ;
} SF_BROADCAST_INFO ;
#define SF_BROADCAST_INFO_VAR(coding_hist_size) \
struct \
{ char description [256] ; \
char originator [32] ; \
char originator_reference [32] ; \
char origination_date [10] ; \
char origination_time [8] ; \
unsigned int time_reference_low ; \
unsigned int time_reference_high ; \
short version ; \
char umid [64] ; \
char reserved [190] ; \
unsigned int coding_history_size ; \
char coding_history [coding_hist_size] ; \
}
/* SF_BROADCAST_INFO is the above struct with coding_history field of 256 bytes. */
typedef SF_BROADCAST_INFO_VAR (256) SF_BROADCAST_INFO ;
/* Virtual I/O functionality. */
typedef sf_count_t (*sf_vio_get_filelen) (void *user_data) ;
typedef sf_count_t (*sf_vio_seek) (sf_count_t offset, int whence, void *user_data) ;

View File

@ -575,6 +575,7 @@ wav_read_header (SF_PRIVATE *psf, int *blockalign, int *framesperblock)
if ((error = wav_read_bext_chunk (psf, dword)))
return error ;
broadcast_info_to_var (psf) ;
break ;
case PAD_MARKER :
@ -1073,7 +1074,7 @@ wav_write_header (SF_PRIVATE *psf, int calc_length)
psf_binheader_writef (psf, "ft8", (float) psf->peak_info->peaks [k].value, psf->peak_info->peaks [k].position) ;
} ;
if (psf->broadcast_info != NULL)
if (psf->broadcast_var != NULL)
wav_write_bext_chunk (psf) ;
if (psf->instrument != NULL)
@ -1621,12 +1622,12 @@ wav_read_bext_chunk (SF_PRIVATE *psf, unsigned int chunksize)
SF_BROADCAST_INFO* b ;
unsigned int bytes = 0 ;
if ((psf->broadcast_info = calloc (1, sizeof (SF_BROADCAST_INFO))) == NULL)
if ((psf->broadcast_var = broadcast_var_alloc (chunksize + 128)) == NULL)
{ psf->error = SFE_MALLOC_FAILED ;
return psf->error ;
} ;
b = psf->broadcast_info ;
b = & psf->broadcast_var->binfo ;
bytes += psf_binheader_readf (psf, "b", b->description, sizeof (b->description)) ;
bytes += psf_binheader_readf (psf, "b", b->originator, sizeof (b->originator)) ;
@ -1659,9 +1660,11 @@ static int
wav_write_bext_chunk (SF_PRIVATE *psf)
{ SF_BROADCAST_INFO *b ;
if ((b = psf->broadcast_info) == NULL)
if (psf->broadcast_var == NULL)
return -1 ;
b = & psf->broadcast_var->binfo ;
psf_binheader_writef (psf, "m4", bext_MARKER, WAV_BEXT_CHUNK_SIZE + b->coding_history_size) ;
/*