1 /* $Id: wav_write.c,v 1.4 2006/09/15 13:34:06 toad32767 Exp $ */
2 /**
3 ** 2005, 2006 by Marco Trillo
4 ** This file is part of UModPlayer, and is released by
5 ** its autors to the Public Domain.
6 ** In case it's not legally possible, its autors grant
7 ** anyone the right to use, redistribute and modify
8 ** this software for any purpose, without any conditions,
9 ** unless such conditions are required by law.
10 **
11 ** THIS FILE COMES WITHOUT ANY WARRANTY. THE AUTHORS
12 ** SHALL NOT BE LIABLE FOR ANY DAMAGE RESULTING BY THE
13 ** USE OR MISUSE OF THIS SOFTWARE.
14 **/
15
16 /*
17 * =====================
18 * WAV FILE WRITER
19 * =====================
20 * You can see WAV file documentation at:
21 * http://www.sonicspot.com/guide/wavefiles.html
22 */
23
24 #include <umodplayer.h>
25 #include <coresound.h>
26
27 #define WAV_TYPE_PCM 0x0001
28 struct str_wavefmt {
29 uint16_t wFormatTag;
30 uint16_t nChannels;
31 uint32_t nSamplesPerSec;
32 uint32_t nAvgBytesPerSec;
33 uint16_t nBlockAlign;
34 uint16_t wBitsPerSample;
35 };
36
37 EXPORT uint32_t
WriteWAV(const char * rfile)38 WriteWAV(const char *rfile)
39 {
40 char buffer[20000];
41 int16_t *samples;
42 int fd, bytes_out = 0, bytes_in = 0, upos, epos;
43 uint32_t length = 0;
44 struct str_wavefmt wf;
45 uint16_t qw;
46 uint32_t qp;
47 int order, d, e;
48
49 fd = open(rfile, O_WRONLY | O_TRUNC | O_CREAT, 0644);
50 if (fd == -1)
51 return 0;
52
53 ModPlug_SeekOrder(file.mod, startpos);
54 notice("Exporting audio to WAV ('%s')\n", rfile);
55
56 /* Use 16-bit samples */
57 CoreSound_BitsPerSample = 16;
58 CoreSound_InitOptions();
59
60 /* Fill the WAV format chunk */
61 wf.wFormatTag = bsLE16(WAV_TYPE_PCM);
62 qw = (uint16_t) sets.channels;
63 wf.nChannels = bsLE16(qw);
64 wf.wBitsPerSample = bsLE16(16);
65 /* blockAlign = channels * bytesPerSample = channels * 2 = channels <<
66 * 1 */
67 qw = qw << 1;
68 wf.nBlockAlign = bsLE16(qw);
69 qp = (uint32_t) sets.samplerate;
70 wf.nSamplesPerSec = bsLE32(qp);
71 /* bytesPerSec = samplesPerSec * channels * bytesPerSample =
72 * samplesPerSec * blockAlign */
73 qp = qp * qw;
74 wf.nAvgBytesPerSec = bsLE32(qp);
75
76
77 /* Write WAV header We`ll fill in the blank spaces later. */
78 if (write(fd, "RIFF\0\0\0\0WAVEfmt \20\0\0\0", 20) < 20) {
79 close(fd);
80 return 0;
81 }
82 length += 20;
83
84 /* Write WAV format chunk */
85 if (write(fd, (char *) &wf, 16) < 16) {
86 close(fd);
87 return 0;
88 }
89 length += 16;
90
91 /* Write 'data' chunk header */
92 if (write(fd, "data\0\0\0\0", 8) < 8) {
93 close(fd);
94 return 0;
95 }
96 length += 8;
97
98 /*
99 * Wavedata in 16-bit samples are signed values from -32768 to +32767 (int16_t)
100 */
101 samples = (int16_t *) buffer;
102
103 /*
104 * Read sample data from LibModPlug.
105 * This data will be in system endianness.
106 */
107 while ((bytes_out = ModPlug_Read(file.mod, buffer, 20000)) > 0) {
108 length += bytes_out;
109
110 /* Calculate the number of int16_t values entered in buffer */
111 d = bytes_out >> 1; /* bytes_out / 2 */
112
113 if (sets.endianness == BIG_ENDIAN) {
114 /* WAV files are always Little Endian. So if we are
115 * Big Endian, we need to convert ModPlug`s BigEndian
116 * data into LittleEndian. */
117
118 for (e = 0; e < d; ++e) {
119 samples[e] = swap16(samples[e]);
120 }
121 }
122 /*
123 * Write the data to the file.
124 * If the system cant write the 20000-bytes buffer all in one,
125 * we use many write() calls. */
126 upos = 0;
127 bytes_in = bytes_out;
128 for (;;) {
129 epos = write(fd, buffer + upos, bytes_in);
130 if (epos < 1) {
131 close(fd);
132 return 0;
133 }
134 if (epos == bytes_in)
135 break;
136 upos += epos;
137 bytes_in -= epos;
138 }
139
140 /*
141 * Tell the user how many bytes we wrote.
142 */
143 notice("%lu bytes written \r", (unsigned long) length);
144 fflush(stdout);
145
146 /*
147 * Check if we have to finish reading
148 */
149 if (endpos > 0) {
150 order = ModPlug_GetCurrentOrder(file.mod);
151 if (order >= endpos)
152 break;
153 }
154 }
155
156 if (lseek(fd, 4, SEEK_SET) == -1) {
157 close(fd);
158 return 0;
159 }
160 qp = length - 8; /* Total WAV size except the 8 first bytes */
161 qp = bsLE32(qp);
162
163 if (write(fd, (char *) &qp, 4) < 4) {
164 close(fd);
165 return 0;
166 }
167 if (lseek(fd, 40, SEEK_SET) == -1) {
168 close(fd);
169 return 0;
170 }
171 if (length < 45) {
172 close(fd);
173 return 0;
174 }
175 /*
176 * sizeof samples is equal to total file length minus header length (=44)
177 */
178 qp = length - 44;
179 qp = bsLE32(qp);
180
181 if (write(fd, (char *) &qp, 4) < 4) {
182 close(fd);
183 return 0;
184 }
185 close(fd);
186
187 notice("\ndone!\n");
188
189 return length;
190 }
191