| /* |
| ** Copyright (C) 2002-2011 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. |
| */ |
| |
| /*=========================================================================== |
| ** Yamaha TX16 Sampler Files. |
| ** |
| ** This header parser was written using information from the SoX source code |
| ** and trial and error experimentation. The code here however is all original. |
| */ |
| |
| #include "sfconfig.h" |
| |
| #include <stdio.h> |
| #include <fcntl.h> |
| #include <string.h> |
| #include <ctype.h> |
| |
| #include "sndfile.h" |
| #include "sfendian.h" |
| #include "common.h" |
| |
| #if (ENABLE_EXPERIMENTAL_CODE == 0) |
| |
| int |
| txw_open (SF_PRIVATE *psf) |
| { if (psf) |
| return SFE_UNIMPLEMENTED ; |
| return 0 ; |
| } /* txw_open */ |
| |
| #else |
| |
| /*------------------------------------------------------------------------------ |
| ** Markers. |
| */ |
| |
| #define TXW_DATA_OFFSET 32 |
| |
| #define TXW_LOOPED 0x49 |
| #define TXW_NO_LOOP 0xC9 |
| |
| /*------------------------------------------------------------------------------ |
| ** Private static functions. |
| */ |
| |
| static int txw_read_header (SF_PRIVATE *psf) ; |
| |
| static sf_count_t txw_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len) ; |
| static sf_count_t txw_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len) ; |
| static sf_count_t txw_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len) ; |
| static sf_count_t txw_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len) ; |
| |
| static sf_count_t txw_seek (SF_PRIVATE *psf, int mode, sf_count_t offset) ; |
| |
| /*------------------------------------------------------------------------------ |
| ** Public functions. |
| */ |
| |
| /* |
| * ftp://ftp.t0.or.at/pub/sound/tx16w/samples.yamaha |
| * ftp://ftp.t0.or.at/pub/sound/tx16w/faq/tx16w.tec |
| * http://www.t0.or.at/~mpakesch/tx16w/ |
| * |
| * from tx16w.c sox 12.15: (7-Oct-98) (Mark Lakata and Leigh Smith) |
| * char filetype[6] "LM8953" |
| * nulls[10], |
| * dummy_aeg[6] |
| * format 0x49 = looped, 0xC9 = non-looped |
| * sample_rate 1 = 33 kHz, 2 = 50 kHz, 3 = 16 kHz |
| * atc_length[3] if sample rate 0, [2]&0xfe = 6: 33kHz, 0x10:50, 0xf6: 16, |
| * depending on [5] but to heck with it |
| * rpt_length[3] (these are for looped samples, attack and loop lengths) |
| * unused[2] |
| */ |
| |
| typedef struct |
| { unsigned char format, srate, sr2, sr3 ; |
| unsigned short srhash ; |
| unsigned int attacklen, repeatlen ; |
| } TXW_HEADER ; |
| |
| #define ERROR_666 666 |
| |
| int |
| txw_open (SF_PRIVATE *psf) |
| { int error ; |
| |
| if (psf->file.mode != SFM_READ) |
| return SFE_UNIMPLEMENTED ; |
| |
| if ((error = txw_read_header (psf))) |
| return error ; |
| |
| if (psf_fseek (psf, psf->dataoffset, SEEK_SET) != psf->dataoffset) |
| return SFE_BAD_SEEK ; |
| |
| psf->read_short = txw_read_s ; |
| psf->read_int = txw_read_i ; |
| psf->read_float = txw_read_f ; |
| psf->read_double = txw_read_d ; |
| |
| psf->seek = txw_seek ; |
| |
| return 0 ; |
| } /* txw_open */ |
| |
| /*------------------------------------------------------------------------------ |
| */ |
| |
| static int |
| txw_read_header (SF_PRIVATE *psf) |
| { TXW_HEADER txwh ; |
| const char *strptr ; |
| |
| memset (&txwh, 0, sizeof (txwh)) ; |
| memset (psf->u.cbuf, 0, sizeof (psf->u.cbuf)) ; |
| psf_binheader_readf (psf, "pb", 0, psf->u.cbuf, 16) ; |
| |
| if (memcmp (psf->u.cbuf, "LM8953\0\0\0\0\0\0\0\0\0\0", 16) != 0) |
| return ERROR_666 ; |
| |
| psf_log_printf (psf, "Read only : Yamaha TX-16 Sampler (.txw)\nLM8953\n") ; |
| |
| /* Jump 6 bytes (dummp_aeg), read format, read sample rate. */ |
| psf_binheader_readf (psf, "j11", 6, &txwh.format, &txwh.srate) ; |
| |
| /* 8 bytes (atc_length[3], rpt_length[3], unused[2]). */ |
| psf_binheader_readf (psf, "e33j", &txwh.attacklen, &txwh.repeatlen, 2) ; |
| txwh.sr2 = (txwh.attacklen >> 16) & 0xFE ; |
| txwh.sr3 = (txwh.repeatlen >> 16) & 0xFE ; |
| txwh.attacklen &= 0x1FFFF ; |
| txwh.repeatlen &= 0x1FFFF ; |
| |
| switch (txwh.format) |
| { case TXW_LOOPED : |
| strptr = "looped" ; |
| break ; |
| |
| case TXW_NO_LOOP : |
| strptr = "non-looped" ; |
| break ; |
| |
| default : |
| psf_log_printf (psf, " Format : 0x%02x => ?????\n", txwh.format) ; |
| return ERROR_666 ; |
| } ; |
| |
| psf_log_printf (psf, " Format : 0x%02X => %s\n", txwh.format, strptr) ; |
| |
| strptr = NULL ; |
| |
| switch (txwh.srate) |
| { case 1 : |
| psf->sf.samplerate = 33333 ; |
| break ; |
| |
| case 2 : |
| psf->sf.samplerate = 50000 ; |
| break ; |
| |
| case 3 : |
| psf->sf.samplerate = 16667 ; |
| break ; |
| |
| default : |
| /* This is ugly and braindead. */ |
| txwh.srhash = ((txwh.sr2 & 0xFE) << 8) | (txwh.sr3 & 0xFE) ; |
| switch (txwh.srhash) |
| { case ((0x6 << 8) | 0x52) : |
| psf->sf.samplerate = 33333 ; |
| break ; |
| |
| case ((0x10 << 8) | 0x52) : |
| psf->sf.samplerate = 50000 ; |
| break ; |
| |
| case ((0xF6 << 8) | 0x52) : |
| psf->sf.samplerate = 166667 ; |
| break ; |
| |
| default : |
| strptr = " Sample Rate : Unknown : forcing to 33333\n" ; |
| psf->sf.samplerate = 33333 ; |
| break ; |
| } ; |
| } ; |
| |
| |
| if (strptr) |
| psf_log_printf (psf, strptr) ; |
| else if (txwh.srhash) |
| psf_log_printf (psf, " Sample Rate : %d (0x%X) => %d\n", txwh.srate, txwh.srhash, psf->sf.samplerate) ; |
| else |
| psf_log_printf (psf, " Sample Rate : %d => %d\n", txwh.srate, psf->sf.samplerate) ; |
| |
| if (txwh.format == TXW_LOOPED) |
| { psf_log_printf (psf, " Attack Len : %d\n", txwh.attacklen) ; |
| psf_log_printf (psf, " Repeat Len : %d\n", txwh.repeatlen) ; |
| } ; |
| |
| psf->dataoffset = TXW_DATA_OFFSET ; |
| psf->datalength = psf->filelength - TXW_DATA_OFFSET ; |
| psf->sf.frames = 2 * psf->datalength / 3 ; |
| |
| |
| if (psf->datalength % 3 == 1) |
| psf_log_printf (psf, "*** File seems to be truncated, %d extra bytes.\n", |
| (int) (psf->datalength % 3)) ; |
| |
| if (txwh.attacklen + txwh.repeatlen > psf->sf.frames) |
| psf_log_printf (psf, "*** File has been truncated.\n") ; |
| |
| psf->sf.format = SF_FORMAT_TXW | SF_FORMAT_PCM_16 ; |
| psf->sf.channels = 1 ; |
| psf->sf.sections = 1 ; |
| psf->sf.seekable = SF_TRUE ; |
| |
| return 0 ; |
| } /* txw_read_header */ |
| |
| static sf_count_t |
| txw_read_s (SF_PRIVATE *psf, short *ptr, sf_count_t len) |
| { unsigned char *ucptr ; |
| short sample ; |
| int k, bufferlen, readcount, count ; |
| sf_count_t total = 0 ; |
| |
| bufferlen = sizeof (psf->u.cbuf) / 3 ; |
| bufferlen -= (bufferlen & 1) ; |
| while (len > 0) |
| { readcount = (len >= bufferlen) ? bufferlen : len ; |
| count = psf_fread (psf->u.cbuf, 3, readcount, psf) ; |
| |
| ucptr = psf->u.ucbuf ; |
| for (k = 0 ; k < readcount ; k += 2) |
| { sample = (ucptr [0] << 8) | (ucptr [1] & 0xF0) ; |
| ptr [total + k] = sample ; |
| sample = (ucptr [2] << 8) | ((ucptr [1] & 0xF) << 4) ; |
| ptr [total + k + 1] = sample ; |
| ucptr += 3 ; |
| } ; |
| |
| total += count ; |
| len -= readcount ; |
| } ; |
| |
| return total ; |
| } /* txw_read_s */ |
| |
| static sf_count_t |
| txw_read_i (SF_PRIVATE *psf, int *ptr, sf_count_t len) |
| { unsigned char *ucptr ; |
| short sample ; |
| int k, bufferlen, readcount, count ; |
| sf_count_t total = 0 ; |
| |
| bufferlen = sizeof (psf->u.cbuf) / 3 ; |
| bufferlen -= (bufferlen & 1) ; |
| while (len > 0) |
| { readcount = (len >= bufferlen) ? bufferlen : len ; |
| count = psf_fread (psf->u.cbuf, 3, readcount, psf) ; |
| |
| ucptr = psf->u.ucbuf ; |
| for (k = 0 ; k < readcount ; k += 2) |
| { sample = (ucptr [0] << 8) | (ucptr [1] & 0xF0) ; |
| ptr [total + k] = sample << 16 ; |
| sample = (ucptr [2] << 8) | ((ucptr [1] & 0xF) << 4) ; |
| ptr [total + k + 1] = sample << 16 ; |
| ucptr += 3 ; |
| } ; |
| |
| total += count ; |
| len -= readcount ; |
| } ; |
| |
| return total ; |
| } /* txw_read_i */ |
| |
| static sf_count_t |
| txw_read_f (SF_PRIVATE *psf, float *ptr, sf_count_t len) |
| { unsigned char *ucptr ; |
| short sample ; |
| int k, bufferlen, readcount, count ; |
| sf_count_t total = 0 ; |
| float normfact ; |
| |
| if (psf->norm_float == SF_TRUE) |
| normfact = 1.0 / 0x8000 ; |
| else |
| normfact = 1.0 / 0x10 ; |
| |
| bufferlen = sizeof (psf->u.cbuf) / 3 ; |
| bufferlen -= (bufferlen & 1) ; |
| while (len > 0) |
| { readcount = (len >= bufferlen) ? bufferlen : len ; |
| count = psf_fread (psf->u.cbuf, 3, readcount, psf) ; |
| |
| ucptr = psf->u.ucbuf ; |
| for (k = 0 ; k < readcount ; k += 2) |
| { sample = (ucptr [0] << 8) | (ucptr [1] & 0xF0) ; |
| ptr [total + k] = normfact * sample ; |
| sample = (ucptr [2] << 8) | ((ucptr [1] & 0xF) << 4) ; |
| ptr [total + k + 1] = normfact * sample ; |
| ucptr += 3 ; |
| } ; |
| |
| total += count ; |
| len -= readcount ; |
| } ; |
| |
| return total ; |
| } /* txw_read_f */ |
| |
| static sf_count_t |
| txw_read_d (SF_PRIVATE *psf, double *ptr, sf_count_t len) |
| { unsigned char *ucptr ; |
| short sample ; |
| int k, bufferlen, readcount, count ; |
| sf_count_t total = 0 ; |
| double normfact ; |
| |
| if (psf->norm_double == SF_TRUE) |
| normfact = 1.0 / 0x8000 ; |
| else |
| normfact = 1.0 / 0x10 ; |
| |
| bufferlen = sizeof (psf->u.cbuf) / 3 ; |
| bufferlen -= (bufferlen & 1) ; |
| while (len > 0) |
| { readcount = (len >= bufferlen) ? bufferlen : len ; |
| count = psf_fread (psf->u.cbuf, 3, readcount, psf) ; |
| |
| ucptr = psf->u.ucbuf ; |
| for (k = 0 ; k < readcount ; k += 2) |
| { sample = (ucptr [0] << 8) | (ucptr [1] & 0xF0) ; |
| ptr [total + k] = normfact * sample ; |
| sample = (ucptr [2] << 8) | ((ucptr [1] & 0xF) << 4) ; |
| ptr [total + k + 1] = normfact * sample ; |
| ucptr += 3 ; |
| } ; |
| |
| total += count ; |
| len -= readcount ; |
| } ; |
| |
| return total ; |
| } /* txw_read_d */ |
| |
| static sf_count_t |
| txw_seek (SF_PRIVATE *psf, int mode, sf_count_t offset) |
| { if (psf && mode) |
| return offset ; |
| |
| return 0 ; |
| } /* txw_seek */ |
| |
| #endif |