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