1/* 2** Copyright (C) 2002-2017 Erik de Castro Lopo <erikd@mega-nerd.com> 3** Copyright (C) 2007 Reuben Thomas 4** 5** This program is free software; you can redistribute it and/or modify 6** it under the terms of the GNU Lesser General Public License as published by 7** the Free Software Foundation; either version 2.1 of the License, or 8** (at your option) any later version. 9** 10** This program is distributed in the hope that it will be useful, 11** but WITHOUT ANY WARRANTY; without even the implied warranty of 12** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13** GNU Lesser General Public License for more details. 14** 15** You should have received a copy of the GNU Lesser General Public License 16** along with this program; if not, write to the Free Software 17** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18*/ 19 20#include "sfconfig.h" 21 22#include <stdio.h> 23#include <fcntl.h> 24#include <string.h> 25#include <ctype.h> 26 27#include "sndfile.h" 28#include "sfendian.h" 29#include "common.h" 30 31/*------------------------------------------------------------------------------ 32** Macros to handle big/little endian issues, and other magic numbers. 33*/ 34 35#define ALAW_MARKER MAKE_MARKER ('A', 'L', 'a', 'w') 36#define SOUN_MARKER MAKE_MARKER ('S', 'o', 'u', 'n') 37#define DFIL_MARKER MAKE_MARKER ('d', 'F', 'i', 'l') 38#define ESSN_MARKER MAKE_MARKER ('e', '*', '*', '\0') 39#define PSION_VERSION ((unsigned short) 3856) 40#define PSION_DATAOFFSET 0x20 41 42/*------------------------------------------------------------------------------ 43** Private static functions. 44*/ 45 46static int wve_read_header (SF_PRIVATE *psf) ; 47static int wve_write_header (SF_PRIVATE *psf, int calc_length) ; 48static int wve_close (SF_PRIVATE *psf) ; 49 50/*------------------------------------------------------------------------------ 51** Public function. 52*/ 53 54int 55wve_open (SF_PRIVATE *psf) 56{ int error = 0 ; 57 58 if (psf->is_pipe) 59 return SFE_WVE_NO_PIPE ; 60 61 if (psf->file.mode == SFM_READ || (psf->file.mode == SFM_RDWR && psf->filelength > 0)) 62 { if ((error = wve_read_header (psf))) 63 return error ; 64 } ; 65 66 if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR) 67 { if ((SF_CONTAINER (psf->sf.format)) != SF_FORMAT_WVE) 68 return SFE_BAD_OPEN_FORMAT ; 69 70 psf->endian = SF_ENDIAN_BIG ; 71 72 if ((error = wve_write_header (psf, SF_FALSE))) 73 return error ; 74 75 psf->write_header = wve_write_header ; 76 } ; 77 78 psf->blockwidth = psf->bytewidth * psf->sf.channels ; 79 80 psf->container_close = wve_close ; 81 82 error = alaw_init (psf) ; 83 84 return error ; 85} /* wve_open */ 86 87/*------------------------------------------------------------------------------ 88*/ 89 90static int 91wve_read_header (SF_PRIVATE *psf) 92{ int marker ; 93 unsigned short version, padding, repeats, trash ; 94 unsigned datalength ; 95 96 /* Set position to start of file to begin reading header. */ 97 psf_binheader_readf (psf, "pm", 0, &marker) ; 98 if (marker != ALAW_MARKER) 99 { psf_log_printf (psf, "Could not find '%M'\n", ALAW_MARKER) ; 100 return SFE_WVE_NOT_WVE ; 101 } ; 102 103 psf_binheader_readf (psf, "m", &marker) ; 104 if (marker != SOUN_MARKER) 105 { psf_log_printf (psf, "Could not find '%M'\n", SOUN_MARKER) ; 106 return SFE_WVE_NOT_WVE ; 107 } ; 108 109 psf_binheader_readf (psf, "m", &marker) ; 110 if (marker != DFIL_MARKER) 111 { psf_log_printf (psf, "Could not find '%M'\n", DFIL_MARKER) ; 112 return SFE_WVE_NOT_WVE ; 113 } ; 114 115 psf_binheader_readf (psf, "m", &marker) ; 116 if (marker != ESSN_MARKER) 117 { psf_log_printf (psf, "Could not find '%M'\n", ESSN_MARKER) ; 118 return SFE_WVE_NOT_WVE ; 119 } ; 120 121 psf_binheader_readf (psf, "E2", &version) ; 122 123 psf_log_printf (psf, "Psion Palmtop Alaw (.wve)\n" 124 " Sample Rate : 8000\n" 125 " Channels : 1\n" 126 " Encoding : A-law\n") ; 127 128 if (version != PSION_VERSION) 129 psf_log_printf (psf, "Psion version %d should be %d\n", version, PSION_VERSION) ; 130 131 psf_binheader_readf (psf, "E4", &datalength) ; 132 psf->dataoffset = PSION_DATAOFFSET ; 133 if (datalength != psf->filelength - psf->dataoffset) 134 { psf->datalength = psf->filelength - psf->dataoffset ; 135 psf_log_printf (psf, "Data length %d should be %D\n", datalength, psf->datalength) ; 136 } 137 else 138 psf->datalength = datalength ; 139 140 psf_binheader_readf (psf, "E22222", &padding, &repeats, &trash, &trash, &trash) ; 141 142 psf->sf.format = SF_FORMAT_WVE | SF_FORMAT_ALAW ; 143 psf->sf.samplerate = 8000 ; 144 psf->sf.frames = psf->datalength ; 145 psf->sf.channels = 1 ; 146 147 return SFE_NO_ERROR ; 148} /* wve_read_header */ 149 150/*------------------------------------------------------------------------------ 151*/ 152 153static int 154wve_write_header (SF_PRIVATE *psf, int calc_length) 155{ sf_count_t current ; 156 unsigned datalen ; 157 158 current = psf_ftell (psf) ; 159 160 if (calc_length) 161 { psf->filelength = psf_get_filelen (psf) ; 162 163 psf->datalength = psf->filelength - psf->dataoffset ; 164 if (psf->dataend) 165 psf->datalength -= psf->filelength - psf->dataend ; 166 167 psf->sf.frames = psf->datalength / (psf->bytewidth * psf->sf.channels) ; 168 } ; 169 170 /* Reset the current header length to zero. */ 171 psf->header.ptr [0] = 0 ; 172 psf->header.indx = 0 ; 173 psf_fseek (psf, 0, SEEK_SET) ; 174 175 /* Write header. */ 176 datalen = psf->datalength ; 177 psf_binheader_writef (psf, "Emmmm", BHWm (ALAW_MARKER), BHWm (SOUN_MARKER), BHWm (DFIL_MARKER), BHWm (ESSN_MARKER)) ; 178 psf_binheader_writef (psf, "E2422222", BHW2 (PSION_VERSION), BHW4 (datalen), BHW2 (0), BHW2 (0), BHW2 (0), BHW2 (0), BHW2 (0)) ; 179 psf_fwrite (psf->header.ptr, psf->header.indx, 1, psf) ; 180 181 if (psf->sf.channels != 1) 182 return SFE_CHANNEL_COUNT ; 183 184 if (psf->error) 185 return psf->error ; 186 187 psf->dataoffset = psf->header.indx ; 188 189 if (current > 0) 190 psf_fseek (psf, current, SEEK_SET) ; 191 192 return psf->error ; 193} /* wve_write_header */ 194 195/*------------------------------------------------------------------------------ 196*/ 197 198static int 199wve_close (SF_PRIVATE *psf) 200{ 201 if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR) 202 { /* Now we know for certain the length of the file we can re-write 203 ** the header. 204 */ 205 wve_write_header (psf, SF_TRUE) ; 206 } ; 207 208 return 0 ; 209} /* wve_close */ 210