mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-04-19 19:35:26 +00:00
added libraries: opus flac libsndfile updated: libvorbis libogg openal - Everything works as expected for now. Bare in mind libsndfile needed the check for whether or not it could find the xiph libraries removed in order for this to work.
400 lines
10 KiB
C
400 lines
10 KiB
C
/*
|
|
** Copyright (C) 2002-2014 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.
|
|
*/
|
|
|
|
/*
|
|
** This is the OKI / Dialogic ADPCM encoder/decoder. It converts from
|
|
** 12 bit linear sample data to a 4 bit ADPCM.
|
|
*/
|
|
|
|
/*
|
|
* Note: some early Dialogic hardware does not always reset the ADPCM encoder
|
|
* at the start of each vox file. This can result in clipping and/or DC offset
|
|
* problems when it comes to decoding the audio. Whilst little can be done
|
|
* about the clipping, a DC offset can be removed by passing the decoded audio
|
|
* through a high-pass filter at e.g. 10Hz.
|
|
*/
|
|
|
|
#include "sfconfig.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
|
|
#include "sndfile.h"
|
|
#include "sfendian.h"
|
|
#include "common.h"
|
|
#include "ima_oki_adpcm.h"
|
|
|
|
|
|
static sf_count_t vox_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ;
|
|
static sf_count_t vox_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ;
|
|
static sf_count_t vox_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ;
|
|
static sf_count_t vox_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ;
|
|
|
|
static sf_count_t vox_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t len) ;
|
|
static sf_count_t vox_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t len) ;
|
|
static sf_count_t vox_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len) ;
|
|
static sf_count_t vox_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len) ;
|
|
|
|
static int vox_read_block (SF_PRIVATE *psf, IMA_OKI_ADPCM *pvox, short *ptr, int len) ;
|
|
|
|
/*------------------------------------------------------------------------------
|
|
*/
|
|
|
|
static int
|
|
codec_close (SF_PRIVATE * psf)
|
|
{
|
|
IMA_OKI_ADPCM * p = (IMA_OKI_ADPCM *) psf->codec_data ;
|
|
|
|
if (p->errors)
|
|
psf_log_printf (psf, "*** Warning : ADPCM state errors: %d\n", p->errors) ;
|
|
return p->errors ;
|
|
} /* code_close */
|
|
|
|
int
|
|
vox_adpcm_init (SF_PRIVATE *psf)
|
|
{ IMA_OKI_ADPCM *pvox = NULL ;
|
|
|
|
if (psf->file.mode == SFM_RDWR)
|
|
return SFE_BAD_MODE_RW ;
|
|
|
|
if (psf->file.mode == SFM_WRITE && psf->sf.channels != 1)
|
|
return SFE_CHANNEL_COUNT ;
|
|
|
|
if ((pvox = malloc (sizeof (IMA_OKI_ADPCM))) == NULL)
|
|
return SFE_MALLOC_FAILED ;
|
|
|
|
psf->codec_data = (void*) pvox ;
|
|
memset (pvox, 0, sizeof (IMA_OKI_ADPCM)) ;
|
|
|
|
if (psf->file.mode == SFM_WRITE)
|
|
{ psf->write_short = vox_write_s ;
|
|
psf->write_int = vox_write_i ;
|
|
psf->write_float = vox_write_f ;
|
|
psf->write_double = vox_write_d ;
|
|
}
|
|
else
|
|
{ psf_log_printf (psf, "Header-less OKI Dialogic ADPCM encoded file.\n") ;
|
|
psf_log_printf (psf, "Setting up for 8kHz, mono, Vox ADPCM.\n") ;
|
|
|
|
psf->read_short = vox_read_s ;
|
|
psf->read_int = vox_read_i ;
|
|
psf->read_float = vox_read_f ;
|
|
psf->read_double = vox_read_d ;
|
|
} ;
|
|
|
|
/* Standard sample rate chennels etc. */
|
|
if (psf->sf.samplerate < 1)
|
|
psf->sf.samplerate = 8000 ;
|
|
psf->sf.channels = 1 ;
|
|
|
|
psf->sf.frames = psf->filelength * 2 ;
|
|
|
|
psf->sf.seekable = SF_FALSE ;
|
|
psf->codec_close = codec_close ;
|
|
|
|
/* Seek back to start of data. */
|
|
if (psf_fseek (psf, 0 , SEEK_SET) == -1)
|
|
return SFE_BAD_SEEK ;
|
|
|
|
ima_oki_adpcm_init (pvox, IMA_OKI_ADPCM_TYPE_OKI) ;
|
|
|
|
return 0 ;
|
|
} /* vox_adpcm_init */
|
|
|
|
/*==============================================================================
|
|
*/
|
|
|
|
static int
|
|
vox_read_block (SF_PRIVATE *psf, IMA_OKI_ADPCM *pvox, short *ptr, int len)
|
|
{ int indx = 0, k ;
|
|
|
|
while (indx < len)
|
|
{ pvox->code_count = (len - indx > IMA_OKI_ADPCM_PCM_LEN) ? IMA_OKI_ADPCM_CODE_LEN : (len - indx + 1) / 2 ;
|
|
|
|
if ((k = (int) psf_fread (pvox->codes, 1, pvox->code_count, psf)) != pvox->code_count)
|
|
{ if (psf_ftell (psf) != psf->filelength)
|
|
psf_log_printf (psf, "*** Warning : short read (%d != %d).\n", k, pvox->code_count) ;
|
|
if (k == 0)
|
|
break ;
|
|
} ;
|
|
|
|
pvox->code_count = k ;
|
|
|
|
ima_oki_adpcm_decode_block (pvox) ;
|
|
|
|
memcpy (&(ptr [indx]), pvox->pcm, pvox->pcm_count * sizeof (short)) ;
|
|
indx += pvox->pcm_count ;
|
|
} ;
|
|
|
|
return indx ;
|
|
} /* vox_read_block */
|
|
|
|
|
|
static sf_count_t
|
|
vox_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len)
|
|
{ IMA_OKI_ADPCM *pvox ;
|
|
int readcount, count ;
|
|
sf_count_t total = 0 ;
|
|
|
|
if (! psf->codec_data)
|
|
return 0 ;
|
|
pvox = (IMA_OKI_ADPCM*) psf->codec_data ;
|
|
|
|
while (len > 0)
|
|
{ readcount = (len > 0x10000000) ? 0x10000000 : (int) len ;
|
|
|
|
count = vox_read_block (psf, pvox, ptr, readcount) ;
|
|
|
|
total += count ;
|
|
len -= count ;
|
|
if (count != readcount)
|
|
break ;
|
|
} ;
|
|
|
|
return total ;
|
|
} /* vox_read_s */
|
|
|
|
static sf_count_t
|
|
vox_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len)
|
|
{ IMA_OKI_ADPCM *pvox ;
|
|
BUF_UNION ubuf ;
|
|
short *sptr ;
|
|
int k, bufferlen, readcount, count ;
|
|
sf_count_t total = 0 ;
|
|
|
|
if (! psf->codec_data)
|
|
return 0 ;
|
|
pvox = (IMA_OKI_ADPCM*) psf->codec_data ;
|
|
|
|
sptr = ubuf.sbuf ;
|
|
bufferlen = ARRAY_LEN (ubuf.sbuf) ;
|
|
while (len > 0)
|
|
{ readcount = (len >= bufferlen) ? bufferlen : (int) len ;
|
|
count = vox_read_block (psf, pvox, sptr, readcount) ;
|
|
for (k = 0 ; k < readcount ; k++)
|
|
ptr [total + k] = arith_shift_left (sptr [k], 16) ;
|
|
total += count ;
|
|
len -= readcount ;
|
|
if (count != readcount)
|
|
break ;
|
|
} ;
|
|
|
|
return total ;
|
|
} /* vox_read_i */
|
|
|
|
static sf_count_t
|
|
vox_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len)
|
|
{ IMA_OKI_ADPCM *pvox ;
|
|
BUF_UNION ubuf ;
|
|
short *sptr ;
|
|
int k, bufferlen, readcount, count ;
|
|
sf_count_t total = 0 ;
|
|
float normfact ;
|
|
|
|
if (! psf->codec_data)
|
|
return 0 ;
|
|
pvox = (IMA_OKI_ADPCM*) psf->codec_data ;
|
|
|
|
normfact = (psf->norm_float == SF_TRUE) ? 1.0 / ((float) 0x8000) : 1.0 ;
|
|
|
|
sptr = ubuf.sbuf ;
|
|
bufferlen = ARRAY_LEN (ubuf.sbuf) ;
|
|
while (len > 0)
|
|
{ readcount = (len >= bufferlen) ? bufferlen : (int) len ;
|
|
count = vox_read_block (psf, pvox, sptr, readcount) ;
|
|
for (k = 0 ; k < readcount ; k++)
|
|
ptr [total + k] = normfact * (float) (sptr [k]) ;
|
|
total += count ;
|
|
len -= readcount ;
|
|
if (count != readcount)
|
|
break ;
|
|
} ;
|
|
|
|
return total ;
|
|
} /* vox_read_f */
|
|
|
|
static sf_count_t
|
|
vox_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len)
|
|
{ IMA_OKI_ADPCM *pvox ;
|
|
BUF_UNION ubuf ;
|
|
short *sptr ;
|
|
int k, bufferlen, readcount, count ;
|
|
sf_count_t total = 0 ;
|
|
double normfact ;
|
|
|
|
if (! psf->codec_data)
|
|
return 0 ;
|
|
pvox = (IMA_OKI_ADPCM*) psf->codec_data ;
|
|
|
|
normfact = (psf->norm_double == SF_TRUE) ? 1.0 / ((double) 0x8000) : 1.0 ;
|
|
|
|
sptr = ubuf.sbuf ;
|
|
bufferlen = ARRAY_LEN (ubuf.sbuf) ;
|
|
while (len > 0)
|
|
{ readcount = (len >= bufferlen) ? bufferlen : (int) len ;
|
|
count = vox_read_block (psf, pvox, sptr, readcount) ;
|
|
for (k = 0 ; k < readcount ; k++)
|
|
ptr [total + k] = normfact * (double) (sptr [k]) ;
|
|
total += count ;
|
|
len -= readcount ;
|
|
if (count != readcount)
|
|
break ;
|
|
} ;
|
|
|
|
return total ;
|
|
} /* vox_read_d */
|
|
|
|
/*------------------------------------------------------------------------------
|
|
*/
|
|
|
|
static int
|
|
vox_write_block (SF_PRIVATE *psf, IMA_OKI_ADPCM *pvox, const short *ptr, int len)
|
|
{ int indx = 0, k ;
|
|
|
|
while (indx < len)
|
|
{ pvox->pcm_count = (len - indx > IMA_OKI_ADPCM_PCM_LEN) ? IMA_OKI_ADPCM_PCM_LEN : len - indx ;
|
|
|
|
memcpy (pvox->pcm, &(ptr [indx]), pvox->pcm_count * sizeof (short)) ;
|
|
|
|
ima_oki_adpcm_encode_block (pvox) ;
|
|
|
|
if ((k = (int) psf_fwrite (pvox->codes, 1, pvox->code_count, psf)) != pvox->code_count)
|
|
psf_log_printf (psf, "*** Warning : short write (%d != %d).\n", k, pvox->code_count) ;
|
|
|
|
indx += pvox->pcm_count ;
|
|
} ;
|
|
|
|
return indx ;
|
|
} /* vox_write_block */
|
|
|
|
static sf_count_t
|
|
vox_write_s (SF_PRIVATE *psf, const short *ptr, sf_count_t len)
|
|
{ IMA_OKI_ADPCM *pvox ;
|
|
int writecount, count ;
|
|
sf_count_t total = 0 ;
|
|
|
|
if (! psf->codec_data)
|
|
return 0 ;
|
|
pvox = (IMA_OKI_ADPCM*) psf->codec_data ;
|
|
|
|
while (len)
|
|
{ writecount = (len > 0x10000000) ? 0x10000000 : (int) len ;
|
|
|
|
count = vox_write_block (psf, pvox, ptr, writecount) ;
|
|
|
|
total += count ;
|
|
len -= count ;
|
|
if (count != writecount)
|
|
break ;
|
|
} ;
|
|
|
|
return total ;
|
|
} /* vox_write_s */
|
|
|
|
static sf_count_t
|
|
vox_write_i (SF_PRIVATE *psf, const int *ptr, sf_count_t len)
|
|
{ IMA_OKI_ADPCM *pvox ;
|
|
BUF_UNION ubuf ;
|
|
short *sptr ;
|
|
int k, bufferlen, writecount, count ;
|
|
sf_count_t total = 0 ;
|
|
|
|
if (! psf->codec_data)
|
|
return 0 ;
|
|
pvox = (IMA_OKI_ADPCM*) psf->codec_data ;
|
|
|
|
sptr = ubuf.sbuf ;
|
|
bufferlen = ARRAY_LEN (ubuf.sbuf) ;
|
|
while (len > 0)
|
|
{ writecount = (len >= bufferlen) ? bufferlen : (int) len ;
|
|
for (k = 0 ; k < writecount ; k++)
|
|
sptr [k] = ptr [total + k] >> 16 ;
|
|
count = vox_write_block (psf, pvox, sptr, writecount) ;
|
|
total += count ;
|
|
len -= writecount ;
|
|
if (count != writecount)
|
|
break ;
|
|
} ;
|
|
|
|
return total ;
|
|
} /* vox_write_i */
|
|
|
|
static sf_count_t
|
|
vox_write_f (SF_PRIVATE *psf, const float *ptr, sf_count_t len)
|
|
{ IMA_OKI_ADPCM *pvox ;
|
|
BUF_UNION ubuf ;
|
|
short *sptr ;
|
|
int k, bufferlen, writecount, count ;
|
|
sf_count_t total = 0 ;
|
|
float normfact ;
|
|
|
|
if (! psf->codec_data)
|
|
return 0 ;
|
|
pvox = (IMA_OKI_ADPCM*) psf->codec_data ;
|
|
|
|
normfact = (psf->norm_float == SF_TRUE) ? (1.0 * 0x7FFF) : 1.0 ;
|
|
|
|
sptr = ubuf.sbuf ;
|
|
bufferlen = ARRAY_LEN (ubuf.sbuf) ;
|
|
while (len > 0)
|
|
{ writecount = (len >= bufferlen) ? bufferlen : (int) len ;
|
|
for (k = 0 ; k < writecount ; k++)
|
|
sptr [k] = psf_lrintf (normfact * ptr [total + k]) ;
|
|
count = vox_write_block (psf, pvox, sptr, writecount) ;
|
|
total += count ;
|
|
len -= writecount ;
|
|
if (count != writecount)
|
|
break ;
|
|
} ;
|
|
|
|
return total ;
|
|
} /* vox_write_f */
|
|
|
|
static sf_count_t
|
|
vox_write_d (SF_PRIVATE *psf, const double *ptr, sf_count_t len)
|
|
{ IMA_OKI_ADPCM *pvox ;
|
|
BUF_UNION ubuf ;
|
|
short *sptr ;
|
|
int k, bufferlen, writecount, count ;
|
|
sf_count_t total = 0 ;
|
|
double normfact ;
|
|
|
|
if (! psf->codec_data)
|
|
return 0 ;
|
|
pvox = (IMA_OKI_ADPCM*) psf->codec_data ;
|
|
|
|
normfact = (psf->norm_double == SF_TRUE) ? (1.0 * 0x7FFF) : 1.0 ;
|
|
|
|
sptr = ubuf.sbuf ;
|
|
bufferlen = ARRAY_LEN (ubuf.sbuf) ;
|
|
while (len > 0)
|
|
{ writecount = (len >= bufferlen) ? bufferlen : (int) len ;
|
|
for (k = 0 ; k < writecount ; k++)
|
|
sptr [k] = psf_lrint (normfact * ptr [total + k]) ;
|
|
count = vox_write_block (psf, pvox, sptr, writecount) ;
|
|
total += count ;
|
|
len -= writecount ;
|
|
if (count != writecount)
|
|
break ;
|
|
} ;
|
|
|
|
return total ;
|
|
} /* vox_write_d */
|
|
|