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