xref: /third_party/libsnd/src/wve.c (revision b815c7f3)
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