805 lines
24 KiB
C
805 lines
24 KiB
C
/*
|
|
** Copyright (C) 2005-2013 Erik de Castro Lopo <erikd@mega-nerd.com>
|
|
**
|
|
** This program is free software; you can redistribute it and/or modify
|
|
** it under the terms of the GNU Lesser General Public License as published by
|
|
** the Free Software Foundation; either version 2.1 of the License, or
|
|
** (at your option) any later version.
|
|
**
|
|
** This program is distributed in the hope that it will be useful,
|
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
** GNU Lesser General Public License for more details.
|
|
**
|
|
** You should have received a copy of the GNU Lesser General Public License
|
|
** along with this program; if not, write to the Free Software
|
|
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#include "sfconfig.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <math.h>
|
|
#ifdef HAVE_INTTYPES_H
|
|
#include <inttypes.h>
|
|
#endif
|
|
|
|
#include "sndfile.h"
|
|
#include "sfendian.h"
|
|
#include "common.h"
|
|
#include "chanmap.h"
|
|
|
|
/*------------------------------------------------------------------------------
|
|
** Macros to handle big/little endian issues.
|
|
*/
|
|
|
|
#define aac_MARKER MAKE_MARKER ('a', 'a', 'c', ' ')
|
|
#define alac_MARKER MAKE_MARKER ('a', 'l', 'a', 'c')
|
|
#define alaw_MARKER MAKE_MARKER ('a', 'l', 'a', 'w')
|
|
#define caff_MARKER MAKE_MARKER ('c', 'a', 'f', 'f')
|
|
#define chan_MARKER MAKE_MARKER ('c', 'h', 'a', 'n')
|
|
#define data_MARKER MAKE_MARKER ('d', 'a', 't', 'a')
|
|
#define desc_MARKER MAKE_MARKER ('d', 'e', 's', 'c')
|
|
#define edct_MARKER MAKE_MARKER ('e', 'd', 'c', 't')
|
|
#define free_MARKER MAKE_MARKER ('f', 'r', 'e', 'e')
|
|
#define ima4_MARKER MAKE_MARKER ('i', 'm', 'a', '4')
|
|
#define info_MARKER MAKE_MARKER ('i', 'n', 'f', 'o')
|
|
#define inst_MARKER MAKE_MARKER ('i', 'n', 's', 't')
|
|
#define kuki_MARKER MAKE_MARKER ('k', 'u', 'k', 'i')
|
|
#define lpcm_MARKER MAKE_MARKER ('l', 'p', 'c', 'm')
|
|
#define mark_MARKER MAKE_MARKER ('m', 'a', 'r', 'k')
|
|
#define midi_MARKER MAKE_MARKER ('m', 'i', 'd', 'i')
|
|
#define mp1_MARKER MAKE_MARKER ('.', 'm', 'p', '1')
|
|
#define mp2_MARKER MAKE_MARKER ('.', 'm', 'p', '2')
|
|
#define mp3_MARKER MAKE_MARKER ('.', 'm', 'p', '3')
|
|
#define ovvw_MARKER MAKE_MARKER ('o', 'v', 'v', 'w')
|
|
#define pakt_MARKER MAKE_MARKER ('p', 'a', 'k', 't')
|
|
#define peak_MARKER MAKE_MARKER ('p', 'e', 'a', 'k')
|
|
#define regn_MARKER MAKE_MARKER ('r', 'e', 'g', 'n')
|
|
#define strg_MARKER MAKE_MARKER ('s', 't', 'r', 'g')
|
|
#define umid_MARKER MAKE_MARKER ('u', 'm', 'i', 'd')
|
|
#define uuid_MARKER MAKE_MARKER ('u', 'u', 'i', 'd')
|
|
#define ulaw_MARKER MAKE_MARKER ('u', 'l', 'a', 'w')
|
|
#define MAC3_MARKER MAKE_MARKER ('M', 'A', 'C', '3')
|
|
#define MAC6_MARKER MAKE_MARKER ('M', 'A', 'C', '6')
|
|
|
|
#define CAF_PEAK_CHUNK_SIZE(ch) ((int) (sizeof (int) + ch * (sizeof (float) + 8)))
|
|
|
|
#define SFE_CAF_NOT_CAF 666
|
|
#define SFE_CAF_NO_DESC 667
|
|
#define SFE_CAF_BAD_PEAK 668
|
|
|
|
/*------------------------------------------------------------------------------
|
|
** Typedefs.
|
|
*/
|
|
|
|
typedef struct
|
|
{ uint8_t srate [8] ;
|
|
uint32_t fmt_id ;
|
|
uint32_t fmt_flags ;
|
|
uint32_t pkt_bytes ;
|
|
uint32_t frames_per_packet ;
|
|
uint32_t channels_per_frame ;
|
|
uint32_t bits_per_chan ;
|
|
} DESC_CHUNK ;
|
|
|
|
typedef struct
|
|
{ int chanmap_tag ;
|
|
|
|
ALAC_DECODER_INFO alac ;
|
|
} CAF_PRIVATE ;
|
|
|
|
/*------------------------------------------------------------------------------
|
|
** Private static functions.
|
|
*/
|
|
|
|
static int caf_close (SF_PRIVATE *psf) ;
|
|
static int caf_read_header (SF_PRIVATE *psf) ;
|
|
static int caf_write_header (SF_PRIVATE *psf, int calc_length) ;
|
|
static int caf_command (SF_PRIVATE *psf, int command, void *data, int datasize) ;
|
|
static int caf_read_chanmap (SF_PRIVATE * psf, sf_count_t chunk_size) ;
|
|
|
|
static int caf_set_chunk (SF_PRIVATE *psf, const SF_CHUNK_INFO * chunk_info) ;
|
|
static SF_CHUNK_ITERATOR * caf_next_chunk_iterator (SF_PRIVATE *psf, SF_CHUNK_ITERATOR * iterator) ;
|
|
static int caf_get_chunk_size (SF_PRIVATE *psf, const SF_CHUNK_ITERATOR * iterator, SF_CHUNK_INFO * chunk_info) ;
|
|
static int caf_get_chunk_data (SF_PRIVATE *psf, const SF_CHUNK_ITERATOR * iterator, SF_CHUNK_INFO * chunk_info) ;
|
|
|
|
/*------------------------------------------------------------------------------
|
|
** Public function.
|
|
*/
|
|
|
|
int
|
|
caf_open (SF_PRIVATE *psf)
|
|
{ CAF_PRIVATE * pcaf ;
|
|
int subformat, format, error = 0 ;
|
|
|
|
if ((psf->container_data = calloc (1, sizeof (CAF_PRIVATE))) == NULL)
|
|
return SFE_MALLOC_FAILED ;
|
|
|
|
pcaf = psf->container_data ;
|
|
|
|
if (psf->file.mode == SFM_READ || (psf->file.mode == SFM_RDWR && psf->filelength > 0))
|
|
{ if ((error = caf_read_header (psf)))
|
|
return error ;
|
|
|
|
psf->next_chunk_iterator = caf_next_chunk_iterator ;
|
|
psf->get_chunk_size = caf_get_chunk_size ;
|
|
psf->get_chunk_data = caf_get_chunk_data ;
|
|
} ;
|
|
|
|
subformat = SF_CODEC (psf->sf.format) ;
|
|
|
|
if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
|
|
{ if (psf->is_pipe)
|
|
return SFE_NO_PIPE_WRITE ;
|
|
|
|
format = SF_CONTAINER (psf->sf.format) ;
|
|
if (format != SF_FORMAT_CAF)
|
|
return SFE_BAD_OPEN_FORMAT ;
|
|
|
|
psf->blockwidth = psf->bytewidth * psf->sf.channels ;
|
|
|
|
if (psf->file.mode != SFM_RDWR || psf->filelength < 44)
|
|
{ psf->filelength = 0 ;
|
|
psf->datalength = 0 ;
|
|
psf->dataoffset = 0 ;
|
|
psf->sf.frames = 0 ;
|
|
} ;
|
|
|
|
psf->strings.flags = SF_STR_ALLOW_START ;
|
|
|
|
/*
|
|
** By default, add the peak chunk to floating point files. Default behaviour
|
|
** can be switched off using sf_command (SFC_SET_PEAK_CHUNK, SF_FALSE).
|
|
*/
|
|
if (psf->file.mode == SFM_WRITE && (subformat == SF_FORMAT_FLOAT || subformat == SF_FORMAT_DOUBLE))
|
|
{ if ((psf->peak_info = peak_info_calloc (psf->sf.channels)) == NULL)
|
|
return SFE_MALLOC_FAILED ;
|
|
psf->peak_info->peak_loc = SF_PEAK_START ;
|
|
} ;
|
|
|
|
if ((error = caf_write_header (psf, SF_FALSE)) != 0)
|
|
return error ;
|
|
|
|
psf->write_header = caf_write_header ;
|
|
psf->set_chunk = caf_set_chunk ;
|
|
} ;
|
|
|
|
psf->container_close = caf_close ;
|
|
psf->command = caf_command ;
|
|
|
|
switch (subformat)
|
|
{ case SF_FORMAT_PCM_S8 :
|
|
case SF_FORMAT_PCM_16 :
|
|
case SF_FORMAT_PCM_24 :
|
|
case SF_FORMAT_PCM_32 :
|
|
error = pcm_init (psf) ;
|
|
break ;
|
|
|
|
case SF_FORMAT_ULAW :
|
|
error = ulaw_init (psf) ;
|
|
break ;
|
|
|
|
case SF_FORMAT_ALAW :
|
|
error = alaw_init (psf) ;
|
|
break ;
|
|
|
|
/* Lite remove start */
|
|
case SF_FORMAT_FLOAT :
|
|
error = float32_init (psf) ;
|
|
break ;
|
|
|
|
case SF_FORMAT_DOUBLE :
|
|
error = double64_init (psf) ;
|
|
break ;
|
|
|
|
case SF_FORMAT_ALAC_16 :
|
|
case SF_FORMAT_ALAC_20 :
|
|
case SF_FORMAT_ALAC_24 :
|
|
case SF_FORMAT_ALAC_32 :
|
|
if (psf->file.mode == SFM_READ)
|
|
/* Only pass the ALAC_DECODER_INFO in read mode. */
|
|
error = alac_init (psf, &pcaf->alac) ;
|
|
else
|
|
error = alac_init (psf, NULL) ;
|
|
break ;
|
|
|
|
/* Lite remove end */
|
|
|
|
default :
|
|
return SFE_UNSUPPORTED_ENCODING ;
|
|
} ;
|
|
|
|
return error ;
|
|
} /* caf_open */
|
|
|
|
static int
|
|
caf_close (SF_PRIVATE *psf)
|
|
{
|
|
if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
|
|
caf_write_header (psf, SF_TRUE) ;
|
|
|
|
return 0 ;
|
|
} /* caf_close */
|
|
|
|
static int
|
|
caf_command (SF_PRIVATE * psf, int command, void * UNUSED (data), int UNUSED (datasize))
|
|
{ CAF_PRIVATE *pcaf ;
|
|
|
|
if ((pcaf = psf->container_data) == NULL)
|
|
return SFE_INTERNAL ;
|
|
|
|
switch (command)
|
|
{ case SFC_SET_CHANNEL_MAP_INFO :
|
|
pcaf->chanmap_tag = aiff_caf_find_channel_layout_tag (psf->channel_map, psf->sf.channels) ;
|
|
return (pcaf->chanmap_tag != 0) ;
|
|
|
|
default :
|
|
break ;
|
|
} ;
|
|
|
|
return 0 ;
|
|
} /* caf_command */
|
|
|
|
/*------------------------------------------------------------------------------
|
|
*/
|
|
|
|
static int
|
|
decode_desc_chunk (SF_PRIVATE *psf, const DESC_CHUNK *desc)
|
|
{ int format = SF_FORMAT_CAF ;
|
|
|
|
psf->sf.channels = desc->channels_per_frame ;
|
|
|
|
if (desc->fmt_id == alac_MARKER)
|
|
{ CAF_PRIVATE *pcaf ;
|
|
|
|
if ((pcaf = psf->container_data) != NULL)
|
|
{ switch (desc->fmt_flags)
|
|
{ case 1 :
|
|
pcaf->alac.bits_per_sample = 16 ;
|
|
format |= SF_FORMAT_ALAC_16 ;
|
|
break ;
|
|
case 2 :
|
|
pcaf->alac.bits_per_sample = 20 ;
|
|
format |= SF_FORMAT_ALAC_20 ;
|
|
break ;
|
|
case 3 :
|
|
pcaf->alac.bits_per_sample = 24 ;
|
|
format |= SF_FORMAT_ALAC_24 ;
|
|
break ;
|
|
case 4 :
|
|
pcaf->alac.bits_per_sample = 32 ;
|
|
format |= SF_FORMAT_ALAC_32 ;
|
|
break ;
|
|
default :
|
|
psf_log_printf (psf, "Bad ALAC format flag value of %d\n", desc->fmt_flags) ;
|
|
} ;
|
|
|
|
pcaf->alac.frames_per_packet = desc->frames_per_packet ;
|
|
} ;
|
|
|
|
return format ;
|
|
} ;
|
|
|
|
format |= psf->endian == SF_ENDIAN_LITTLE ? SF_ENDIAN_LITTLE : 0 ;
|
|
|
|
if (desc->fmt_id == lpcm_MARKER && desc->fmt_flags & 1)
|
|
{ /* Floating point data. */
|
|
if (desc->bits_per_chan == 32 && desc->pkt_bytes == 4 * desc->channels_per_frame)
|
|
{ psf->bytewidth = 4 ;
|
|
return format | SF_FORMAT_FLOAT ;
|
|
} ;
|
|
if (desc->bits_per_chan == 64 && desc->pkt_bytes == 8 * desc->channels_per_frame)
|
|
{ psf->bytewidth = 8 ;
|
|
return format | SF_FORMAT_DOUBLE ;
|
|
} ;
|
|
} ;
|
|
|
|
if (desc->fmt_id == lpcm_MARKER && (desc->fmt_flags & 1) == 0)
|
|
{ /* Integer data. */
|
|
if (desc->bits_per_chan == 32 && desc->pkt_bytes == 4 * desc->channels_per_frame)
|
|
{ psf->bytewidth = 4 ;
|
|
return format | SF_FORMAT_PCM_32 ;
|
|
} ;
|
|
if (desc->bits_per_chan == 24 && desc->pkt_bytes == 3 * desc->channels_per_frame)
|
|
{ psf->bytewidth = 3 ;
|
|
return format | SF_FORMAT_PCM_24 ;
|
|
} ;
|
|
if (desc->bits_per_chan == 16 && desc->pkt_bytes == 2 * desc->channels_per_frame)
|
|
{ psf->bytewidth = 2 ;
|
|
return format | SF_FORMAT_PCM_16 ;
|
|
} ;
|
|
if (desc->bits_per_chan == 8 && desc->pkt_bytes == 1 * desc->channels_per_frame)
|
|
{ psf->bytewidth = 1 ;
|
|
return format | SF_FORMAT_PCM_S8 ;
|
|
} ;
|
|
} ;
|
|
|
|
if (desc->fmt_id == alaw_MARKER && desc->bits_per_chan == 8)
|
|
{ psf->bytewidth = 1 ;
|
|
return format | SF_FORMAT_ALAW ;
|
|
} ;
|
|
|
|
if (desc->fmt_id == ulaw_MARKER && desc->bits_per_chan == 8)
|
|
{ psf->bytewidth = 1 ;
|
|
return format | SF_FORMAT_ULAW ;
|
|
} ;
|
|
|
|
psf_log_printf (psf, "**** Unknown format identifier.\n") ;
|
|
|
|
return 0 ;
|
|
} /* decode_desc_chunk */
|
|
|
|
static int
|
|
caf_read_header (SF_PRIVATE *psf)
|
|
{ CAF_PRIVATE *pcaf ;
|
|
BUF_UNION ubuf ;
|
|
DESC_CHUNK desc ;
|
|
sf_count_t chunk_size ;
|
|
double srate ;
|
|
short version, flags ;
|
|
int marker, k, have_data = 0, error ;
|
|
|
|
if ((pcaf = psf->container_data) == NULL)
|
|
return SFE_INTERNAL ;
|
|
|
|
memset (&desc, 0, sizeof (desc)) ;
|
|
|
|
/* Set position to start of file to begin reading header. */
|
|
psf_binheader_readf (psf, "pmE2E2", 0, &marker, &version, &flags) ;
|
|
psf_log_printf (psf, "%M\n Version : %d\n Flags : %x\n", marker, version, flags) ;
|
|
if (marker != caff_MARKER)
|
|
return SFE_CAF_NOT_CAF ;
|
|
|
|
psf_binheader_readf (psf, "mE8b", &marker, &chunk_size, ubuf.ucbuf, 8) ;
|
|
srate = double64_be_read (ubuf.ucbuf) ;
|
|
snprintf (ubuf.cbuf, sizeof (ubuf.cbuf), "%5.3f", srate) ;
|
|
psf_log_printf (psf, "%M : %D\n Sample rate : %s\n", marker, chunk_size, ubuf.cbuf) ;
|
|
if (marker != desc_MARKER)
|
|
return SFE_CAF_NO_DESC ;
|
|
|
|
if (chunk_size < SIGNED_SIZEOF (DESC_CHUNK))
|
|
{ psf_log_printf (psf, "**** Chunk size too small. Should be > 32 bytes.\n") ;
|
|
return SFE_MALFORMED_FILE ;
|
|
} ;
|
|
|
|
psf->sf.samplerate = lrint (srate) ;
|
|
|
|
psf_binheader_readf (psf, "mE44444", &desc.fmt_id, &desc.fmt_flags, &desc.pkt_bytes, &desc.frames_per_packet,
|
|
&desc.channels_per_frame, &desc.bits_per_chan) ;
|
|
psf_log_printf (psf, " Format id : %M\n Format flags : %x\n Bytes / packet : %u\n"
|
|
" Frames / packet : %u\n Channels / frame : %u\n Bits / channel : %u\n",
|
|
desc.fmt_id, desc.fmt_flags, desc.pkt_bytes, desc.frames_per_packet, desc.channels_per_frame, desc.bits_per_chan) ;
|
|
|
|
if (desc.channels_per_frame > SF_MAX_CHANNELS)
|
|
{ psf_log_printf (psf, "**** Bad channels per frame value %u.\n", desc.channels_per_frame) ;
|
|
return SFE_MALFORMED_FILE ;
|
|
} ;
|
|
|
|
if (chunk_size > SIGNED_SIZEOF (DESC_CHUNK))
|
|
psf_binheader_readf (psf, "j", (int) (chunk_size - sizeof (DESC_CHUNK))) ;
|
|
|
|
psf->sf.channels = desc.channels_per_frame ;
|
|
|
|
while (psf_ftell (psf) < psf->filelength)
|
|
{ marker = 0 ;
|
|
chunk_size = 0 ;
|
|
|
|
psf_binheader_readf (psf, "mE8", &marker, &chunk_size) ;
|
|
if (marker == 0)
|
|
{ psf_log_printf (psf, "Have 0 marker.\n") ;
|
|
break ;
|
|
} ;
|
|
|
|
psf_store_read_chunk_u32 (&psf->rchunks, marker, psf_ftell (psf), chunk_size) ;
|
|
|
|
switch (marker)
|
|
{ case peak_MARKER :
|
|
psf_log_printf (psf, "%M : %D\n", marker, chunk_size) ;
|
|
if (chunk_size != CAF_PEAK_CHUNK_SIZE (psf->sf.channels))
|
|
{ psf_binheader_readf (psf, "j", make_size_t (chunk_size)) ;
|
|
psf_log_printf (psf, "*** File PEAK chunk %D should be %d.\n", chunk_size, CAF_PEAK_CHUNK_SIZE (psf->sf.channels)) ;
|
|
return SFE_CAF_BAD_PEAK ;
|
|
} ;
|
|
|
|
if ((psf->peak_info = peak_info_calloc (psf->sf.channels)) == NULL)
|
|
return SFE_MALLOC_FAILED ;
|
|
|
|
/* read in rest of PEAK chunk. */
|
|
psf_binheader_readf (psf, "E4", & (psf->peak_info->edit_number)) ;
|
|
psf_log_printf (psf, " edit count : %d\n", psf->peak_info->edit_number) ;
|
|
|
|
psf_log_printf (psf, " Ch Position Value\n") ;
|
|
for (k = 0 ; k < psf->sf.channels ; k++)
|
|
{ sf_count_t position ;
|
|
float value ;
|
|
|
|
psf_binheader_readf (psf, "Ef8", &value, &position) ;
|
|
psf->peak_info->peaks [k].value = value ;
|
|
psf->peak_info->peaks [k].position = position ;
|
|
|
|
snprintf (ubuf.cbuf, sizeof (ubuf.cbuf), " %2d %-12" PRId64 " %g\n", k, position, value) ;
|
|
psf_log_printf (psf, ubuf.cbuf) ;
|
|
} ;
|
|
|
|
psf->peak_info->peak_loc = SF_PEAK_START ;
|
|
break ;
|
|
|
|
case chan_MARKER :
|
|
if (chunk_size < 12)
|
|
{ psf_log_printf (psf, "%M : %D (should be >= 12)\n", marker, chunk_size) ;
|
|
psf_binheader_readf (psf, "j", make_size_t (chunk_size)) ;
|
|
break ;
|
|
}
|
|
|
|
psf_log_printf (psf, "%M : %D\n", marker, chunk_size) ;
|
|
|
|
if ((error = caf_read_chanmap (psf, chunk_size)))
|
|
return error ;
|
|
break ;
|
|
|
|
case free_MARKER :
|
|
psf_log_printf (psf, "%M : %D\n", marker, chunk_size) ;
|
|
psf_binheader_readf (psf, "j", make_size_t (chunk_size)) ;
|
|
break ;
|
|
|
|
case data_MARKER :
|
|
psf_binheader_readf (psf, "E4", &k) ;
|
|
psf_log_printf (psf, " edit : %u\n", k) ;
|
|
|
|
if (chunk_size == -1)
|
|
{ psf_log_printf (psf, "%M : -1\n") ;
|
|
chunk_size = psf->filelength - psf->headindex ;
|
|
}
|
|
else if (psf->filelength > 0 && psf->filelength < psf->headindex + chunk_size - 16)
|
|
psf_log_printf (psf, "%M : %D (should be %D)\n", marker, chunk_size, psf->filelength - psf->headindex - 8) ;
|
|
else
|
|
psf_log_printf (psf, "%M : %D\n", marker, chunk_size) ;
|
|
|
|
|
|
psf->dataoffset = psf->headindex ;
|
|
|
|
/* Subtract the 4 bytes of the 'edit' field above. */
|
|
psf->datalength = chunk_size - 4 ;
|
|
|
|
psf_binheader_readf (psf, "j", psf->datalength) ;
|
|
have_data = 1 ;
|
|
break ;
|
|
|
|
case kuki_MARKER :
|
|
psf_log_printf (psf, "%M : %D\n", marker, chunk_size) ;
|
|
pcaf->alac.kuki_offset = psf_ftell (psf) - 12 ;
|
|
psf_binheader_readf (psf, "j", make_size_t (chunk_size)) ;
|
|
break ;
|
|
|
|
case pakt_MARKER :
|
|
psf_log_printf (psf, "%M : %D\n", marker, chunk_size) ;
|
|
|
|
psf_binheader_readf (psf, "E8844", &pcaf->alac.packets, &pcaf->alac.valid_frames,
|
|
&pcaf->alac.priming_frames, &pcaf->alac.remainder_frames) ;
|
|
|
|
psf_log_printf (psf,
|
|
" Packets : %D\n"
|
|
" Valid frames : %D\n"
|
|
" Priming frames : %d\n"
|
|
" Remainder frames : %d\n",
|
|
pcaf->alac.packets, pcaf->alac.valid_frames, pcaf->alac.priming_frames,
|
|
pcaf->alac.remainder_frames
|
|
) ;
|
|
|
|
if (pcaf->alac.packets == 0 && pcaf->alac.valid_frames == 0
|
|
&& pcaf->alac.priming_frames == 0 && pcaf->alac.remainder_frames == 0)
|
|
psf_log_printf (psf, "*** 'pakt' chunk header is all zero.\n") ;
|
|
|
|
pcaf->alac.pakt_offset = psf_ftell (psf) - 12 ;
|
|
psf_binheader_readf (psf, "j", make_size_t (chunk_size) - 24) ;
|
|
break ;
|
|
|
|
default :
|
|
psf_log_printf (psf, "%M : %D (skipped)\n", marker, chunk_size) ;
|
|
psf_binheader_readf (psf, "j", make_size_t (chunk_size)) ;
|
|
break ;
|
|
} ;
|
|
|
|
if (! psf->sf.seekable && have_data)
|
|
break ;
|
|
|
|
if (psf_ftell (psf) >= psf->filelength - SIGNED_SIZEOF (chunk_size))
|
|
{ psf_log_printf (psf, "End\n") ;
|
|
break ;
|
|
} ;
|
|
} ;
|
|
|
|
if (have_data == 0)
|
|
{ psf_log_printf (psf, "**** Error, could not find 'data' chunk.\n") ;
|
|
return SFE_MALFORMED_FILE ;
|
|
} ;
|
|
|
|
psf->endian = (desc.fmt_flags & 2) ? SF_ENDIAN_LITTLE : SF_ENDIAN_BIG ;
|
|
|
|
psf_fseek (psf, psf->dataoffset, SEEK_SET) ;
|
|
|
|
if ((psf->sf.format = decode_desc_chunk (psf, &desc)) == 0)
|
|
return SFE_UNSUPPORTED_ENCODING ;
|
|
|
|
if (psf->bytewidth > 0)
|
|
psf->sf.frames = psf->datalength / psf->bytewidth ;
|
|
|
|
return 0 ;
|
|
} /* caf_read_header */
|
|
|
|
/*------------------------------------------------------------------------------
|
|
*/
|
|
|
|
static int
|
|
caf_write_header (SF_PRIVATE *psf, int calc_length)
|
|
{ BUF_UNION ubuf ;
|
|
CAF_PRIVATE *pcaf ;
|
|
DESC_CHUNK desc ;
|
|
sf_count_t current, free_len ;
|
|
uint32_t uk ;
|
|
int subformat, append_free_block = SF_TRUE ;
|
|
|
|
if ((pcaf = psf->container_data) == NULL)
|
|
return SFE_INTERNAL ;
|
|
|
|
memset (&desc, 0, sizeof (desc)) ;
|
|
|
|
current = psf_ftell (psf) ;
|
|
|
|
if (calc_length)
|
|
{ psf->filelength = psf_get_filelen (psf) ;
|
|
|
|
psf->datalength = psf->filelength - psf->dataoffset ;
|
|
|
|
if (psf->dataend)
|
|
psf->datalength -= psf->filelength - psf->dataend ;
|
|
|
|
if (psf->bytewidth > 0)
|
|
psf->sf.frames = psf->datalength / (psf->bytewidth * psf->sf.channels) ;
|
|
} ;
|
|
|
|
/* Reset the current header length to zero. */
|
|
psf->header [0] = 0 ;
|
|
psf->headindex = 0 ;
|
|
psf_fseek (psf, 0, SEEK_SET) ;
|
|
|
|
/* 'caff' marker, version and flags. */
|
|
psf_binheader_writef (psf, "Em22", caff_MARKER, 1, 0) ;
|
|
|
|
/* 'desc' marker and chunk size. */
|
|
psf_binheader_writef (psf, "Em8", desc_MARKER, (sf_count_t) (sizeof (DESC_CHUNK))) ;
|
|
|
|
double64_be_write (1.0 * psf->sf.samplerate, ubuf.ucbuf) ;
|
|
psf_binheader_writef (psf, "b", ubuf.ucbuf, make_size_t (8)) ;
|
|
|
|
subformat = SF_CODEC (psf->sf.format) ;
|
|
|
|
psf->endian = SF_ENDIAN (psf->sf.format) ;
|
|
|
|
if (CPU_IS_BIG_ENDIAN && (psf->endian == 0 || psf->endian == SF_ENDIAN_CPU))
|
|
psf->endian = SF_ENDIAN_BIG ;
|
|
else if (CPU_IS_LITTLE_ENDIAN && (psf->endian == SF_ENDIAN_LITTLE || psf->endian == SF_ENDIAN_CPU))
|
|
psf->endian = SF_ENDIAN_LITTLE ;
|
|
|
|
if (psf->endian == SF_ENDIAN_LITTLE)
|
|
desc.fmt_flags = 2 ;
|
|
else
|
|
psf->endian = SF_ENDIAN_BIG ;
|
|
|
|
/* initial section (same for all, it appears) */
|
|
switch (subformat)
|
|
{ case SF_FORMAT_PCM_S8 :
|
|
desc.fmt_id = lpcm_MARKER ;
|
|
psf->bytewidth = 1 ;
|
|
desc.pkt_bytes = psf->bytewidth * psf->sf.channels ;
|
|
desc.frames_per_packet = 1 ;
|
|
desc.channels_per_frame = psf->sf.channels ;
|
|
desc.bits_per_chan = 8 ;
|
|
break ;
|
|
|
|
case SF_FORMAT_PCM_16 :
|
|
desc.fmt_id = lpcm_MARKER ;
|
|
psf->bytewidth = 2 ;
|
|
desc.pkt_bytes = psf->bytewidth * psf->sf.channels ;
|
|
desc.frames_per_packet = 1 ;
|
|
desc.channels_per_frame = psf->sf.channels ;
|
|
desc.bits_per_chan = 16 ;
|
|
break ;
|
|
|
|
case SF_FORMAT_PCM_24 :
|
|
psf->bytewidth = 3 ;
|
|
desc.pkt_bytes = psf->bytewidth * psf->sf.channels ;
|
|
desc.frames_per_packet = 1 ;
|
|
desc.channels_per_frame = psf->sf.channels ;
|
|
desc.bits_per_chan = 24 ;
|
|
desc.fmt_id = lpcm_MARKER ;
|
|
break ;
|
|
|
|
case SF_FORMAT_PCM_32 :
|
|
desc.fmt_id = lpcm_MARKER ;
|
|
psf->bytewidth = 4 ;
|
|
desc.pkt_bytes = psf->bytewidth * psf->sf.channels ;
|
|
desc.frames_per_packet = 1 ;
|
|
desc.channels_per_frame = psf->sf.channels ;
|
|
desc.bits_per_chan = 32 ;
|
|
break ;
|
|
|
|
case SF_FORMAT_FLOAT :
|
|
desc.fmt_id = lpcm_MARKER ;
|
|
desc.fmt_flags |= 1 ;
|
|
psf->bytewidth = 4 ;
|
|
desc.pkt_bytes = psf->bytewidth * psf->sf.channels ;
|
|
desc.frames_per_packet = 1 ;
|
|
desc.channels_per_frame = psf->sf.channels ;
|
|
desc.bits_per_chan = 32 ;
|
|
break ;
|
|
|
|
case SF_FORMAT_DOUBLE :
|
|
desc.fmt_id = lpcm_MARKER ;
|
|
desc.fmt_flags |= 1 ;
|
|
psf->bytewidth = 8 ;
|
|
desc.pkt_bytes = psf->bytewidth * psf->sf.channels ;
|
|
desc.frames_per_packet = 1 ;
|
|
desc.channels_per_frame = psf->sf.channels ;
|
|
desc.bits_per_chan = 64 ;
|
|
break ;
|
|
|
|
case SF_FORMAT_ALAW :
|
|
desc.fmt_id = alaw_MARKER ;
|
|
psf->bytewidth = 1 ;
|
|
desc.pkt_bytes = psf->bytewidth * psf->sf.channels ;
|
|
desc.frames_per_packet = 1 ;
|
|
desc.channels_per_frame = psf->sf.channels ;
|
|
desc.bits_per_chan = 8 ;
|
|
break ;
|
|
|
|
case SF_FORMAT_ULAW :
|
|
desc.fmt_id = ulaw_MARKER ;
|
|
psf->bytewidth = 1 ;
|
|
desc.pkt_bytes = psf->bytewidth * psf->sf.channels ;
|
|
desc.frames_per_packet = 1 ;
|
|
desc.channels_per_frame = psf->sf.channels ;
|
|
desc.bits_per_chan = 8 ;
|
|
break ;
|
|
|
|
case SF_FORMAT_ALAC_16 :
|
|
case SF_FORMAT_ALAC_20 :
|
|
case SF_FORMAT_ALAC_24 :
|
|
case SF_FORMAT_ALAC_32 :
|
|
desc.fmt_id = alac_MARKER ;
|
|
desc.pkt_bytes = psf->bytewidth * psf->sf.channels ;
|
|
desc.channels_per_frame = psf->sf.channels ;
|
|
alac_get_desc_chunk_items (subformat, &desc.fmt_flags, &desc.frames_per_packet) ;
|
|
append_free_block = SF_FALSE ;
|
|
break ;
|
|
|
|
default :
|
|
return SFE_UNIMPLEMENTED ;
|
|
} ;
|
|
|
|
psf_binheader_writef (psf, "mE44444", desc.fmt_id, desc.fmt_flags, desc.pkt_bytes, desc.frames_per_packet, desc.channels_per_frame, desc.bits_per_chan) ;
|
|
|
|
#if 0
|
|
if (psf->strings.flags & SF_STR_LOCATE_START)
|
|
caf_write_strings (psf, SF_STR_LOCATE_START) ;
|
|
#endif
|
|
|
|
if (psf->peak_info != NULL)
|
|
{ int k ;
|
|
psf_binheader_writef (psf, "Em84", peak_MARKER, (sf_count_t) CAF_PEAK_CHUNK_SIZE (psf->sf.channels), psf->peak_info->edit_number) ;
|
|
for (k = 0 ; k < psf->sf.channels ; k++)
|
|
psf_binheader_writef (psf, "Ef8", (float) psf->peak_info->peaks [k].value, psf->peak_info->peaks [k].position) ;
|
|
} ;
|
|
|
|
if (psf->channel_map && pcaf->chanmap_tag)
|
|
psf_binheader_writef (psf, "Em8444", chan_MARKER, (sf_count_t) 12, pcaf->chanmap_tag, 0, 0) ;
|
|
|
|
/* Write custom headers. */
|
|
for (uk = 0 ; uk < psf->wchunks.used ; uk++)
|
|
psf_binheader_writef (psf, "m44b", (int) psf->wchunks.chunks [uk].mark32, 0, psf->wchunks.chunks [uk].len, psf->wchunks.chunks [uk].data, make_size_t (psf->wchunks.chunks [uk].len)) ;
|
|
|
|
if (append_free_block)
|
|
{ /* Add free chunk so that the actual audio data starts at a multiple 0x1000. */
|
|
free_len = 0x1000 - psf->headindex - 16 - 12 ;
|
|
while (free_len < 0)
|
|
free_len += 0x1000 ;
|
|
psf_binheader_writef (psf, "Em8z", free_MARKER, free_len, (int) free_len) ;
|
|
} ;
|
|
|
|
psf_binheader_writef (psf, "Em84", data_MARKER, psf->datalength + 4, 0) ;
|
|
|
|
psf_fwrite (psf->header, psf->headindex, 1, psf) ;
|
|
if (psf->error)
|
|
return psf->error ;
|
|
|
|
psf->dataoffset = psf->headindex ;
|
|
if (current < psf->dataoffset)
|
|
psf_fseek (psf, psf->dataoffset, SEEK_SET) ;
|
|
else if (current > 0)
|
|
psf_fseek (psf, current, SEEK_SET) ;
|
|
|
|
return psf->error ;
|
|
} /* caf_write_header */
|
|
|
|
static int
|
|
caf_read_chanmap (SF_PRIVATE * psf, sf_count_t chunk_size)
|
|
{ const AIFF_CAF_CHANNEL_MAP * map_info ;
|
|
unsigned channel_bitmap, channel_decriptions, bytesread ;
|
|
int layout_tag ;
|
|
|
|
bytesread = psf_binheader_readf (psf, "E444", &layout_tag, &channel_bitmap, &channel_decriptions) ;
|
|
|
|
map_info = aiff_caf_of_channel_layout_tag (layout_tag) ;
|
|
|
|
psf_log_printf (psf, " Tag : %x\n", layout_tag) ;
|
|
if (map_info)
|
|
psf_log_printf (psf, " Layout : %s\n", map_info->name) ;
|
|
|
|
if (bytesread < chunk_size)
|
|
psf_binheader_readf (psf, "j", chunk_size - bytesread) ;
|
|
|
|
if (map_info->channel_map != NULL)
|
|
{ size_t chanmap_size = psf->sf.channels * sizeof (psf->channel_map [0]) ;
|
|
|
|
free (psf->channel_map) ;
|
|
|
|
if ((psf->channel_map = malloc (chanmap_size)) == NULL)
|
|
return SFE_MALLOC_FAILED ;
|
|
|
|
memcpy (psf->channel_map, map_info->channel_map, chanmap_size) ;
|
|
} ;
|
|
|
|
return 0 ;
|
|
} /* caf_read_chanmap */
|
|
|
|
/*==============================================================================
|
|
*/
|
|
|
|
static int
|
|
caf_set_chunk (SF_PRIVATE *psf, const SF_CHUNK_INFO * chunk_info)
|
|
{ return psf_save_write_chunk (&psf->wchunks, chunk_info) ;
|
|
} /* caf_set_chunk */
|
|
|
|
static SF_CHUNK_ITERATOR *
|
|
caf_next_chunk_iterator (SF_PRIVATE *psf, SF_CHUNK_ITERATOR * iterator)
|
|
{ return psf_next_chunk_iterator (&psf->rchunks, iterator) ;
|
|
} /* caf_next_chunk_iterator */
|
|
|
|
static int
|
|
caf_get_chunk_size (SF_PRIVATE *psf, const SF_CHUNK_ITERATOR * iterator, SF_CHUNK_INFO * chunk_info)
|
|
{ int indx ;
|
|
|
|
if ((indx = psf_find_read_chunk_iterator (&psf->rchunks, iterator)) < 0)
|
|
return SFE_UNKNOWN_CHUNK ;
|
|
|
|
chunk_info->datalen = psf->rchunks.chunks [indx].len ;
|
|
|
|
return SFE_NO_ERROR ;
|
|
} /* caf_get_chunk_size */
|
|
|
|
static int
|
|
caf_get_chunk_data (SF_PRIVATE *psf, const SF_CHUNK_ITERATOR * iterator, SF_CHUNK_INFO * chunk_info)
|
|
{ int indx ;
|
|
sf_count_t pos ;
|
|
|
|
if ((indx = psf_find_read_chunk_iterator (&psf->rchunks, iterator)) < 0)
|
|
return SFE_UNKNOWN_CHUNK ;
|
|
|
|
if (chunk_info->data == NULL)
|
|
return SFE_BAD_CHUNK_DATA_PTR ;
|
|
|
|
chunk_info->id_size = psf->rchunks.chunks [indx].id_size ;
|
|
memcpy (chunk_info->id, psf->rchunks.chunks [indx].id, sizeof (chunk_info->id) / sizeof (*chunk_info->id)) ;
|
|
|
|
pos = psf_ftell (psf) ;
|
|
psf_fseek (psf, psf->rchunks.chunks [indx].offset, SEEK_SET) ;
|
|
psf_fread (chunk_info->data, SF_MIN (chunk_info->datalen, psf->rchunks.chunks [indx].len), 1, psf) ;
|
|
psf_fseek (psf, pos, SEEK_SET) ;
|
|
|
|
return SFE_NO_ERROR ;
|
|
} /* caf_get_chunk_data */
|