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 
46 static int	wve_read_header (SF_PRIVATE *psf) ;
47 static int	wve_write_header (SF_PRIVATE *psf, int calc_length) ;
48 static int	wve_close (SF_PRIVATE *psf) ;
49 
50 /*------------------------------------------------------------------------------
51 ** Public function.
52 */
53 
54 int
wve_open(SF_PRIVATE * psf)55 wve_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 
90 static int
wve_read_header(SF_PRIVATE * psf)91 wve_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 
153 static int
wve_write_header(SF_PRIVATE * psf,int calc_length)154 wve_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 
198 static int
wve_close(SF_PRIVATE * psf)199 wve_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