1 /* fmfconv_wav.c: wav output routine included into fmfconv.c
2    Copyright (c) 2004-2015 Gergely Szasz
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2 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 General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License along
15    with this program; if not, write to the Free Software Foundation, Inc.,
16    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 
18    Author contact information:
19 
20    E-mail: szaszg@hu.inter.net
21 
22 */
23 #include <config.h>
24 
25 #include <stdio.h>
26 #include <stdlib.h>
27 
28 #include "libspectrum.h"
29 #include "fmfconv.h"
30 
31 /*
32 Byte order: Little-endian
33 
34 Offset   Length   Contents
35   0      4 bytes  "RIFF"
36   4      4 bytes  <File length - 8>
37   8      4 bytes  "WAVE"
38 
39  12      4 bytes  "fmt "
40  16      4 bytes  <Length of the fmt data> // (=16)
41  20      2 bytes  <WAVE File Encoding Tag>
42  22      2 bytes  <Channels>     // Channels: 1 = mono, 2 = stereo
43  24      4 bytes  <Sample rate>  // Samples per second: e.g., 44100
44  28      4 bytes  <bytes/second> // sample rate * block align
45  32      2 bytes  <block align>  // channels * bits/sample / 8
46  34      2 bytes  <bits/sample>  // 8 or 16
47 .................................................... PCM
48  36      4 bytes  "data"
49  40      4 bytes  <Sample data size(n)>
50  44     (n)bytes  <Sample data>
51 ..................................................... Law!!!
52  36      2 bytes  <cbLength>    // 0
53 
54  38      4 bytes  "fact"
55  42      4 bytes  <Chunk size>  // 4
56  46      4 bytes  <Number of samples >
57 
58  50      4 bytes  "data"
59  54      4 bytes  <Sample data size(n)>
60  58     (n)bytes  <Sample data>
61 ......................................................
62 
63 */
64 #define WAV_POS_FILELEN 4L
65 #define WAV_POS_PCMLEN 40L
66 #define WAV_POS_LAWLEN 54L
67 #define WAV_POS_SAMPLES 46L
68 
69 static int
snd_write_wavheader(void)70 snd_write_wavheader( void )
71 {
72   libspectrum_byte buff[4];
73   int tmp;
74 
75   fwrite( "RIFF\377\377\377\377WAVEfmt ", 16, 1, snd );
76 
77   if( snd_enc == PCM )
78     fwrite( "\020\000\000\000\001\000", 6, 1, snd );	/* PCM */
79   else if( snd_enc == ALW )
80     fwrite( "\022\000\000\000\006\000", 6, 1, snd );	/* A-Law */
81   else
82     fwrite( "\022\000\000\000\007\000", 6, 1, snd );	/* u-Law */
83 
84   if( snd_chn == 2 )
85     fwrite( "\002\000", 2, 1, snd );			/* Stereo */
86   else
87     fwrite( "\001\000", 2, 1, snd );			/* Mono */
88 
89   buff[0] = out_rte & 0xff; buff[1] = out_rte >> 8; buff[2] = buff[3] = 0;
90   fwrite( buff, 4, 1, snd );			/* sampling rate in Hz */
91 
92   tmp = out_rte * snd_fsz;
93   buff[0] = tmp & 0xff; buff[1] = ( tmp >> 8 )  & 0xff ; buff[2] = tmp >> 16; buff[3] = 0;
94   fwrite( buff, 4, 1, snd );			/* avarege byte rate */
95 
96   buff[0] = snd_fsz & 0xff; buff[1] = snd_fsz >> 8; buff[2] = snd_fsz / snd_chn * 8; buff[3] = 0;
97   fwrite( buff, 4, 1, snd );			/* frame size + bits/sample */
98 
99   if( snd_enc != PCM )
100     fwrite( "\000\000fact\004\000\000\000\377\377\377\377", 14, 1, snd );/*  */
101 
102   fwrite( "data\377\377\377\377", 8, 1, snd );/* data Chunk header */
103   snd_header_ok = 1;
104   printi( 1, "snd_write_wavheader(): %dHz %c encoded %s\n", out_rte, snd_enc,
105 		 snd_chn == 2 ? "stereo" : "mono" );
106   return 0;
107 }
108 
109 int
snd_write_wav(void)110 snd_write_wav( void )
111 {
112   int err;
113 
114   if( !snd_header_ok && ( err = snd_write_wavheader() ) ) return err;
115   if( snd_enc == PCM && !snd_little_endian ) {	/* we have to swap all sample */
116     pcm_swap_endian();
117   }
118 
119   if( fwrite( sound8, snd_len, 1, snd ) != 1 ) return ERR_WRITE_SND;
120   printi( 2, "snd_write_wav(): %d samples (%d bytes) sound\n", snd_len/snd_fsz, snd_len );
121 
122   return 0;
123 }
124 
125 void
snd_finalize_wav(void)126 snd_finalize_wav( void )
127 {
128   libspectrum_byte buff[4];
129   long pos = ftell( snd );
130   long dsz = pos;
131 
132   if( fseek( snd, WAV_POS_FILELEN, SEEK_SET ) == -1 ) {
133     printw( "snd_finalize_wav(): cannot finalize sound output file (not seekable).\n" );
134   } else {
135     if( pos & 1 ) {
136       buff[0] = 0;
137       fwrite( buff, 1, 1, snd );		/* padding byte */
138       pos++;
139     }
140     pos -= 8;
141     buff[0] = pos & 0xff; buff[1] = ( pos >> 8 ) & 0xff;
142     buff[2] = ( pos >> 16 ) & 0xff; buff[3] = pos >> 24;
143     fwrite( buff, 4, 1, snd );			/* set file length - 8 */
144 
145     if( snd_enc == PCM ) {
146       dsz -= 44;
147       fseek( snd, WAV_POS_PCMLEN, SEEK_SET );
148     } else {
149       dsz -= 58;
150       fseek( snd, WAV_POS_LAWLEN, SEEK_SET );
151     }
152     buff[0] = dsz & 0xff; buff[1] = ( dsz >> 8 ) & 0xff;
153     buff[2] = ( dsz >> 16 ) & 0xff; buff[3] = dsz >> 24;
154     fwrite( buff, 4, 1, snd );			/* set data size */
155     if( snd_enc != PCM ) {
156       fseek( snd, WAV_POS_SAMPLES, SEEK_SET );
157       fwrite( buff, 4, 1, snd );			/* set sample num (1 byte/sample) */
158     }
159 
160     printi( 1, "snd_finalize_wav(): RIFF size: %ldbyte DATA size: %ldbyte\n", pos, dsz );
161   }
162 }
163