xref: /third_party/libsnd/src/svx.c (revision b815c7f3)
1/*
2** Copyright (C) 1999-2017 Erik de Castro Lopo <erikd@mega-nerd.com>
3**
4** This program is free software; you can redistribute it and/or modify
5** it under the terms of the GNU Lesser General Public License as published by
6** the Free Software Foundation; either version 2.1 of the License, or
7** (at your option) any later version.
8**
9** This program is distributed in the hope that it will be useful,
10** but WITHOUT ANY WARRANTY; without even the implied warranty of
11** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12** GNU Lesser General Public License for more details.
13**
14** You should have received a copy of the GNU Lesser General Public License
15** along with this program; if not, write to the Free Software
16** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17*/
18
19#include "sfconfig.h"
20
21#include <stdio.h>
22#include <string.h>
23#include <ctype.h>
24#include <stdarg.h>
25
26#include "sndfile.h"
27#include "sfendian.h"
28#include "common.h"
29
30
31/*------------------------------------------------------------------------------
32 * Macros to handle big/little endian issues.
33*/
34
35#define FORM_MARKER	(MAKE_MARKER ('F', 'O', 'R', 'M'))
36#define SVX8_MARKER	(MAKE_MARKER ('8', 'S', 'V', 'X'))
37#define SV16_MARKER	(MAKE_MARKER ('1', '6', 'S', 'V'))
38#define VHDR_MARKER	(MAKE_MARKER ('V', 'H', 'D', 'R'))
39#define BODY_MARKER	(MAKE_MARKER ('B', 'O', 'D', 'Y'))
40
41#define ATAK_MARKER	(MAKE_MARKER ('A', 'T', 'A', 'K'))
42#define RLSE_MARKER	(MAKE_MARKER ('R', 'L', 'S', 'E'))
43
44#define c_MARKER	(MAKE_MARKER ('(', 'c', ')', ' '))
45#define NAME_MARKER	(MAKE_MARKER ('N', 'A', 'M', 'E'))
46#define AUTH_MARKER	(MAKE_MARKER ('A', 'U', 'T', 'H'))
47#define ANNO_MARKER	(MAKE_MARKER ('A', 'N', 'N', 'O'))
48#define CHAN_MARKER	(MAKE_MARKER ('C', 'H', 'A', 'N'))
49
50/*------------------------------------------------------------------------------
51 * Typedefs for file chunks.
52*/
53
54typedef struct
55{	unsigned int	oneShotHiSamples, repeatHiSamples, samplesPerHiCycle ;
56	unsigned short	samplesPerSec ;
57	unsigned char	octave, compression ;
58	unsigned int	volume ;
59} VHDR_CHUNK ;
60
61enum {
62	HAVE_FORM	= 0x01,
63
64	HAVE_SVX	= 0x02,
65	HAVE_VHDR	= 0x04,
66	HAVE_BODY	= 0x08
67} ;
68
69/*------------------------------------------------------------------------------
70 * Private static functions.
71*/
72
73static int	svx_close	(SF_PRIVATE *psf) ;
74static int	svx_write_header (SF_PRIVATE *psf, int calc_length) ;
75static int 	svx_read_header	(SF_PRIVATE *psf) ;
76
77/*------------------------------------------------------------------------------
78** Public function.
79*/
80
81int
82svx_open	(SF_PRIVATE *psf)
83{	int error ;
84
85	if (psf->file.mode == SFM_READ || (psf->file.mode == SFM_RDWR && psf->filelength > 0))
86	{	if ((error = svx_read_header (psf)))
87			return error ;
88
89		psf->endian = SF_ENDIAN_BIG ;			/* All SVX files are big endian. */
90
91		psf->blockwidth = psf->sf.channels * psf->bytewidth ;
92		if (psf->blockwidth)
93			psf->sf.frames = psf->datalength / psf->blockwidth ;
94
95		psf_fseek (psf, psf->dataoffset, SEEK_SET) ;
96		} ;
97
98	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
99	{	if (psf->is_pipe)
100			return SFE_NO_PIPE_WRITE ;
101
102		if ((SF_CONTAINER (psf->sf.format)) != SF_FORMAT_SVX)
103			return	SFE_BAD_OPEN_FORMAT ;
104
105		psf->endian = SF_ENDIAN (psf->sf.format) ;
106
107		if (psf->endian == SF_ENDIAN_LITTLE || (CPU_IS_LITTLE_ENDIAN && psf->endian == SF_ENDIAN_CPU))
108			return SFE_BAD_ENDIAN ;
109
110		psf->endian = SF_ENDIAN_BIG ;			/* All SVX files are big endian. */
111
112		error = svx_write_header (psf, SF_FALSE) ;
113		if (error)
114			return error ;
115
116		psf->write_header = svx_write_header ;
117		} ;
118
119	psf->container_close = svx_close ;
120
121	if ((error = pcm_init (psf)))
122		return error ;
123
124	return 0 ;
125} /* svx_open */
126
127/*------------------------------------------------------------------------------
128*/
129
130static int
131svx_read_header	(SF_PRIVATE *psf)
132{	VHDR_CHUNK		vhdr ;
133	uint32_t		chunk_size, marker ;
134	int				filetype = 0, parsestage = 0, done = 0 ;
135	int 			bytecount = 0, channels ;
136
137	if (psf->filelength > 0xFFFFFFFFLL)
138		psf_log_printf (psf, "Warning : filelength > 0xffffffff. This is bad!!!!\n") ;
139
140	memset (&vhdr, 0, sizeof (vhdr)) ;
141	psf_binheader_readf (psf, "p", 0) ;
142
143	/* Set default number of channels. Modify later if necessary */
144	psf->sf.channels = 1 ;
145
146	psf->sf.format = SF_FORMAT_SVX ;
147
148	while (! done)
149	{	psf_binheader_readf (psf, "Em4", &marker, &chunk_size) ;
150
151		switch (marker)
152		{	case FORM_MARKER :
153					if (parsestage)
154						return SFE_SVX_NO_FORM ;
155
156					if (chunk_size != psf->filelength - 2 * sizeof (chunk_size))
157						psf_log_printf (psf, "FORM : %u (should be %u)\n", chunk_size, (uint32_t) psf->filelength - 2 * sizeof (chunk_size)) ;
158					else
159						psf_log_printf (psf, "FORM : %u\n", chunk_size) ;
160					parsestage |= HAVE_FORM ;
161
162					psf_binheader_readf (psf, "m", &marker) ;
163
164					filetype = marker ;
165					psf_log_printf (psf, " %M\n", marker) ;
166					parsestage |= HAVE_SVX ;
167					break ;
168
169			case VHDR_MARKER :
170					if (! (parsestage & (HAVE_FORM | HAVE_SVX)))
171						return SFE_SVX_NO_FORM ;
172
173					psf_log_printf (psf, " VHDR : %d\n", chunk_size) ;
174
175					psf_binheader_readf (psf, "E4442114", &(vhdr.oneShotHiSamples), &(vhdr.repeatHiSamples),
176						&(vhdr.samplesPerHiCycle), &(vhdr.samplesPerSec), &(vhdr.octave), &(vhdr.compression),
177						&(vhdr.volume)) ;
178
179					psf_log_printf (psf, "  OneShotHiSamples  : %d\n", vhdr.oneShotHiSamples) ;
180					psf_log_printf (psf, "  RepeatHiSamples   : %d\n", vhdr.repeatHiSamples) ;
181					psf_log_printf (psf, "  samplesPerHiCycle : %d\n", vhdr.samplesPerHiCycle) ;
182					psf_log_printf (psf, "  Sample Rate       : %d\n", vhdr.samplesPerSec) ;
183					psf_log_printf (psf, "  Octave            : %d\n", vhdr.octave) ;
184
185					psf_log_printf (psf, "  Compression       : %d => ", vhdr.compression) ;
186
187					switch (vhdr.compression)
188					{	case 0 : psf_log_printf (psf, "None.\n") ;
189								break ;
190						case 1 : psf_log_printf (psf, "Fibonacci delta\n") ;
191								break ;
192						case 2 : psf_log_printf (psf, "Exponential delta\n") ;
193								break ;
194						} ;
195
196					psf_log_printf (psf, "  Volume            : %d\n", vhdr.volume) ;
197
198					psf->sf.samplerate 	= vhdr.samplesPerSec ;
199
200					if (filetype == SVX8_MARKER)
201					{	psf->sf.format |= SF_FORMAT_PCM_S8 ;
202						psf->bytewidth = 1 ;
203						}
204					else if (filetype == SV16_MARKER)
205					{	psf->sf.format |= SF_FORMAT_PCM_16 ;
206						psf->bytewidth = 2 ;
207						} ;
208
209					parsestage |= HAVE_VHDR ;
210					break ;
211
212			case BODY_MARKER :
213					if (! (parsestage & HAVE_VHDR))
214						return SFE_SVX_NO_BODY ;
215
216					psf->datalength = chunk_size ;
217
218					psf->dataoffset = psf_ftell (psf) ;
219					if (psf->dataoffset < 0)
220						return SFE_SVX_NO_BODY ;
221
222					if (psf->datalength > psf->filelength - psf->dataoffset)
223					{	psf_log_printf (psf, " BODY : %D (should be %D)\n", psf->datalength, psf->filelength - psf->dataoffset) ;
224						psf->datalength = psf->filelength - psf->dataoffset ;
225						}
226					else
227						psf_log_printf (psf, " BODY : %D\n", psf->datalength) ;
228
229					parsestage |= HAVE_BODY ;
230
231					if (! psf->sf.seekable)
232						break ;
233
234					psf_fseek (psf, psf->datalength, SEEK_CUR) ;
235					break ;
236
237			case NAME_MARKER :
238					if (! (parsestage & HAVE_SVX))
239						return SFE_SVX_NO_FORM ;
240
241					psf_log_printf (psf, " %M : %u\n", marker, chunk_size) ;
242
243					if (strlen (psf->file.name) != chunk_size)
244					{	if (chunk_size > sizeof (psf->file.name) - 1)
245							return SFE_SVX_BAD_NAME_LENGTH ;
246
247						psf_binheader_readf (psf, "b", psf->file.name, chunk_size) ;
248						psf->file.name [chunk_size] = 0 ;
249						}
250					else
251						psf_binheader_readf (psf, "j", chunk_size) ;
252					break ;
253
254			case ANNO_MARKER :
255					if (! (parsestage & HAVE_SVX))
256						return SFE_SVX_NO_FORM ;
257
258					psf_log_printf (psf, " %M : %u\n", marker, chunk_size) ;
259
260					psf_binheader_readf (psf, "j", chunk_size) ;
261					break ;
262
263			case CHAN_MARKER :
264					if (! (parsestage & HAVE_SVX))
265						return SFE_SVX_NO_FORM ;
266
267					psf_log_printf (psf, " %M : %u\n", marker, chunk_size) ;
268
269					bytecount += psf_binheader_readf (psf, "E4", &channels) ;
270
271					if (channels == 2 || channels == 4)
272						psf_log_printf (psf, "  Channels : %d => mono\n", channels) ;
273					else if (channels == 6)
274					{	psf->sf.channels = 2 ;
275						psf_log_printf (psf, "  Channels : %d => stereo\n", channels) ;
276						}
277					else
278						psf_log_printf (psf, "  Channels : %d *** assuming mono\n", channels) ;
279
280					psf_binheader_readf (psf, "j", chunk_size - bytecount) ;
281					break ;
282
283
284			case AUTH_MARKER :
285			case c_MARKER :
286					if (! (parsestage & HAVE_SVX))
287						return SFE_SVX_NO_FORM ;
288
289					psf_log_printf (psf, " %M : %u\n", marker, chunk_size) ;
290
291					psf_binheader_readf (psf, "j", chunk_size) ;
292					break ;
293
294			default :
295					if (chunk_size >= 0xffff0000)
296					{	done = SF_TRUE ;
297						psf_log_printf (psf, "*** Unknown chunk marker (%X) at position %D with length %u. Exiting parser.\n", marker, psf_ftell (psf) - 8, chunk_size) ;
298						break ;
299						} ;
300
301					if (psf_isprint ((marker >> 24) & 0xFF) && psf_isprint ((marker >> 16) & 0xFF)
302						&& psf_isprint ((marker >> 8) & 0xFF) && psf_isprint (marker & 0xFF))
303					{	psf_log_printf (psf, "%M : %u (unknown marker)\n", marker, chunk_size) ;
304						psf_binheader_readf (psf, "j", chunk_size) ;
305						break ;
306						} ;
307					if ((chunk_size = psf_ftell (psf)) & 0x03)
308					{	psf_log_printf (psf, "  Unknown chunk marker at position %d. Resynching.\n", chunk_size - 4) ;
309
310						chunk_size = chunk_size & 3 ;
311						psf_binheader_readf (psf, "j", 4 - chunk_size) ;
312						break ;
313						} ;
314					psf_log_printf (psf, "*** Unknown chunk marker (%X) at position %D. Exiting parser.\n", marker, psf_ftell (psf) - 8) ;
315					done = SF_TRUE ;
316			} ;	/* switch (marker) */
317
318		if (! psf->sf.seekable && (parsestage & HAVE_BODY))
319			break ;
320
321		if (psf_ftell (psf) >= psf->filelength - SIGNED_SIZEOF (chunk_size))
322			break ;
323		} ; /* while (1) */
324
325	if (vhdr.compression)
326		return SFE_SVX_BAD_COMP ;
327
328	if (psf->dataoffset <= 0)
329		return SFE_SVX_NO_DATA ;
330
331	return 0 ;
332} /* svx_read_header */
333
334static int
335svx_close (SF_PRIVATE *psf)
336{
337	if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
338		svx_write_header (psf, SF_TRUE) ;
339
340	return 0 ;
341} /* svx_close */
342
343static int
344svx_write_header (SF_PRIVATE *psf, int calc_length)
345{	static	char 	annotation	[] = "libsndfile by Erik de Castro Lopo\0\0\0" ;
346	sf_count_t	current ;
347
348	current = psf_ftell (psf) ;
349
350	if (calc_length)
351	{	psf->filelength = psf_get_filelen (psf) ;
352
353		psf->datalength = psf->filelength - psf->dataoffset ;
354
355		if (psf->dataend)
356			psf->datalength -= psf->filelength - psf->dataend ;
357
358		psf->sf.frames = psf->datalength / (psf->bytewidth * psf->sf.channels) ;
359		} ;
360
361	psf->header.ptr [0] = 0 ;
362	psf->header.indx = 0 ;
363	psf_fseek (psf, 0, SEEK_SET) ;
364
365	/* FORM marker and FORM size. */
366	psf_binheader_writef (psf, "Etm8", BHWm (FORM_MARKER), BHW8 ((psf->filelength < 8) ?
367			psf->filelength * 0 : psf->filelength - 8)) ;
368
369	psf_binheader_writef (psf, "m", BHWm ((psf->bytewidth == 1) ? SVX8_MARKER : SV16_MARKER)) ;
370
371	/* VHDR chunk. */
372	psf_binheader_writef (psf, "Em4", BHWm (VHDR_MARKER), BHW4 (sizeof (VHDR_CHUNK))) ;
373	/* VHDR : oneShotHiSamples, repeatHiSamples, samplesPerHiCycle */
374	psf_binheader_writef (psf, "E444", BHW4 (psf->sf.frames), BHW4 (0), BHW4 (0)) ;
375	/* VHDR : samplesPerSec, octave, compression */
376	psf_binheader_writef (psf, "E211", BHW2 (psf->sf.samplerate), BHW1 (1), BHW1 (0)) ;
377	/* VHDR : volume */
378	psf_binheader_writef (psf, "E4", BHW4 ((psf->bytewidth == 1) ? 0xFF : 0xFFFF)) ;
379
380	if (psf->sf.channels == 2)
381		psf_binheader_writef (psf, "Em44", BHWm (CHAN_MARKER), BHW4 (4), BHW4 (6)) ;
382
383	/* Filename and annotation strings. */
384	psf_binheader_writef (psf, "Emsms", BHWm (NAME_MARKER), BHWs (psf->file.name), BHWm (ANNO_MARKER), BHWs (annotation)) ;
385
386	/* BODY marker and size. */
387	psf_binheader_writef (psf, "Etm8", BHWm (BODY_MARKER), BHW8 ((psf->datalength < 0) ?
388			psf->datalength * 0 : psf->datalength)) ;
389
390	psf_fwrite (psf->header.ptr, psf->header.indx, 1, psf) ;
391
392	if (psf->error)
393		return psf->error ;
394
395	psf->dataoffset = psf->header.indx ;
396
397	if (current > 0)
398		psf_fseek (psf, current, SEEK_SET) ;
399
400	return psf->error ;
401} /* svx_write_header */
402
403