| /* | |
| ** Copyright (C) 2002-2012 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) | |
| { BUF_UNION ubuf ; | |
| TXW_HEADER txwh ; | |
| const char *strptr ; | |
| memset (&txwh, 0, sizeof (txwh)) ; | |
| memset (ubuf.cbuf, 0, sizeof (ubuf.cbuf)) ; | |
| psf_binheader_readf (psf, "pb", 0, ubuf.cbuf, 16) ; | |
| if (memcmp (ubuf.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) | |
| { BUF_UNION ubuf ; | |
| unsigned char *ucptr ; | |
| short sample ; | |
| int k, bufferlen, readcount, count ; | |
| sf_count_t total = 0 ; | |
| bufferlen = sizeof (ubuf.cbuf) / 3 ; | |
| bufferlen -= (bufferlen & 1) ; | |
| while (len > 0) | |
| { readcount = (len >= bufferlen) ? bufferlen : len ; | |
| count = psf_fread (ubuf.cbuf, 3, readcount, psf) ; | |
| ucptr = ubuf.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) | |
| { BUF_UNION ubuf ; | |
| unsigned char *ucptr ; | |
| short sample ; | |
| int k, bufferlen, readcount, count ; | |
| sf_count_t total = 0 ; | |
| bufferlen = sizeof (ubuf.cbuf) / 3 ; | |
| bufferlen -= (bufferlen & 1) ; | |
| while (len > 0) | |
| { readcount = (len >= bufferlen) ? bufferlen : len ; | |
| count = psf_fread (ubuf.cbuf, 3, readcount, psf) ; | |
| ucptr = ubuf.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) | |
| { BUF_UNION ubuf ; | |
| 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 (ubuf.cbuf) / 3 ; | |
| bufferlen -= (bufferlen & 1) ; | |
| while (len > 0) | |
| { readcount = (len >= bufferlen) ? bufferlen : len ; | |
| count = psf_fread (ubuf.cbuf, 3, readcount, psf) ; | |
| ucptr = ubuf.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) | |
| { BUF_UNION ubuf ; | |
| 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 (ubuf.cbuf) / 3 ; | |
| bufferlen -= (bufferlen & 1) ; | |
| while (len > 0) | |
| { readcount = (len >= bufferlen) ? bufferlen : len ; | |
| count = psf_fread (ubuf.cbuf, 3, readcount, psf) ; | |
| ucptr = ubuf.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 |