Mercurial > hg > dmlib
comparison tools/mod2wav.c @ 652:d9888292f971
Rename again.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Tue, 16 Apr 2013 06:04:22 +0300 |
parents | utils/mod2wav.c@e2ac08228a0f |
children | 3d813c81f33c |
comparison
equal
deleted
inserted
replaced
651:e2ac08228a0f | 652:d9888292f971 |
---|---|
1 /* | |
2 * mod2wav - Render XM/JSSMOD module to WAV waveform file | |
3 * Programmed and designed by Matti 'ccr' Hamalainen | |
4 * (C) Copyright 2007 Tecnic Software productions (TNSP) | |
5 * | |
6 * Please read file 'COPYING' for information on license and distribution. | |
7 */ | |
8 #include <stdio.h> | |
9 #include <stdlib.h> | |
10 #include <errno.h> | |
11 #include "jss.h" | |
12 #include "jssmod.h" | |
13 #include "jssmix.h" | |
14 #include "jssplr.h" | |
15 #include "dmlib.h" | |
16 #include "dmargs.h" | |
17 #include "dmwav.h" | |
18 #include "dmmutex.h" | |
19 | |
20 | |
21 char *optInFilename = NULL, *optOutFilename = NULL; | |
22 int optOutFormat = JSS_AUDIO_S16, | |
23 optOutChannels = 2, | |
24 optOutFreq = 44100, | |
25 optMuteOChannels = -1, | |
26 optStartOrder = -1; | |
27 BOOL optUsePlayTime = FALSE; | |
28 size_t optPlayTime; | |
29 | |
30 | |
31 DMOptArg optList[] = | |
32 { | |
33 { 0, '?', "help", "Show this help", OPT_NONE }, | |
34 { 2, 'v', "verbose", "Be more verbose", OPT_NONE }, | |
35 { 3, '1', "16bit", "16-bit output", OPT_NONE }, | |
36 { 4, '8', "8bit", "8-bit output", OPT_NONE }, | |
37 { 5, 'm', "mono", "Mono output", OPT_NONE }, | |
38 { 6, 's', "stereo", "Stereo output", OPT_NONE }, | |
39 { 7, 'f', "freq", "Output frequency", OPT_ARGREQ }, | |
40 { 8, 'M', "mute", "Mute other channels than #", OPT_ARGREQ }, | |
41 { 9, 'o', "order", "Start from order #", OPT_ARGREQ }, | |
42 { 10, 't', "time", "Play for # seconds", OPT_ARGREQ }, | |
43 // {10, 'l', "loop", "Loop for # times", OPT_ARGREQ }, | |
44 }; | |
45 | |
46 const int optListN = sizeof(optList) / sizeof(optList[0]); | |
47 | |
48 | |
49 BOOL argHandleOpt(const int optN, char *optArg, char *currArg) | |
50 { | |
51 (void) optArg; | |
52 | |
53 switch (optN) | |
54 { | |
55 case 0: | |
56 dmPrintBanner(stdout, dmProgName, | |
57 "[options] [sourcefile] [destfile]"); | |
58 | |
59 dmArgsPrintHelp(stdout, optList, optListN); | |
60 exit(0); | |
61 break; | |
62 | |
63 case 2: | |
64 dmVerbosity++; | |
65 break; | |
66 | |
67 case 3: | |
68 optOutFormat = JSS_AUDIO_S16; | |
69 break; | |
70 | |
71 case 4: | |
72 optOutFormat = JSS_AUDIO_U8; | |
73 break; | |
74 | |
75 case 5: | |
76 optOutChannels = JSS_AUDIO_MONO; | |
77 break; | |
78 | |
79 case 6: | |
80 optOutChannels = JSS_AUDIO_STEREO; | |
81 break; | |
82 | |
83 case 7: | |
84 optOutFreq = atoi(optArg); | |
85 break; | |
86 | |
87 case 8: | |
88 optMuteOChannels = atoi(optArg); | |
89 break; | |
90 | |
91 case 9: | |
92 optStartOrder = atoi(optArg); | |
93 break; | |
94 | |
95 case 10: | |
96 optPlayTime = atoi(optArg); | |
97 optUsePlayTime = TRUE; | |
98 break; | |
99 | |
100 default: | |
101 dmError("Unknown argument '%s'.\n", currArg); | |
102 return FALSE; | |
103 } | |
104 | |
105 return TRUE; | |
106 } | |
107 | |
108 | |
109 BOOL argHandleFile(char *currArg) | |
110 { | |
111 if (!optInFilename) | |
112 optInFilename = currArg; | |
113 else | |
114 if (!optOutFilename) | |
115 optOutFilename = currArg; | |
116 else | |
117 { | |
118 dmError("Too many filename arguments (only source and dest needed) '%s'\n", currArg); | |
119 return FALSE; | |
120 } | |
121 | |
122 return TRUE; | |
123 } | |
124 | |
125 | |
126 int main(int argc, char *argv[]) | |
127 { | |
128 DMResource *inFile = NULL; | |
129 FILE *outFile = NULL; | |
130 JSSModule *mod = NULL; | |
131 JSSMixer *dev = NULL; | |
132 JSSPlayer *plr = NULL; | |
133 int result = -1; | |
134 size_t bufLen = 1024*4, dataTotal, dataWritten, sampSize; | |
135 Uint8 *mb = NULL; | |
136 | |
137 dmInitProg("mod2wav", "XM/JSSMOD to WAV renderer", "0.2", NULL, NULL); | |
138 dmVerbosity = 1; | |
139 | |
140 // Parse arguments | |
141 if (!dmArgsProcess(argc, argv, optList, optListN, | |
142 argHandleOpt, argHandleFile, TRUE)) | |
143 exit(1); | |
144 | |
145 // Check arguments | |
146 if (optInFilename == NULL || optOutFilename == NULL) | |
147 { | |
148 dmError("Input or output file not specified. Try --help.\n"); | |
149 return 1; | |
150 } | |
151 | |
152 // Initialize miniJSS | |
153 jssInit(); | |
154 | |
155 // Open the source file | |
156 if ((inFile = dmf_create_stdio(optInFilename, "rb")) == NULL) | |
157 { | |
158 dmError("Error opening input file '%s', %d: %s\n", | |
159 optInFilename, errno, strerror(errno)); | |
160 return 1; | |
161 } | |
162 | |
163 // Read module file | |
164 fprintf(stderr, "Reading file: %s\n", optInFilename); | |
165 #ifdef JSS_SUP_XM | |
166 fprintf(stderr, "* Trying XM...\n"); | |
167 result = jssLoadXM(inFile, &mod); | |
168 #endif | |
169 #ifdef JSS_SUP_JSSMOD | |
170 if (result != 0) | |
171 { | |
172 size_t bufgot, bufsize = dmfsize(inFile); | |
173 Uint8 *buf = dmMalloc(bufsize); | |
174 dmfseek(inFile, 0L, SEEK_SET); | |
175 fprintf(stderr, "* Trying JSSMOD (%d bytes, %p)...\n", bufsize, buf); | |
176 if ((bufgot = dmfread(buf, 1, bufsize, inFile)) != bufsize) | |
177 { | |
178 fprintf(stderr, "Error reading file (not enough data %d), #%d: %s\n", | |
179 bufgot, dmferror(inFile), dmErrorStr(dmferror(inFile))); | |
180 return 2; | |
181 } | |
182 result = jssLoadJSSMOD(buf, bufsize, &mod); | |
183 dmFree(buf); | |
184 } | |
185 #endif | |
186 dmf_close(inFile); | |
187 if (result != DMERR_OK) | |
188 { | |
189 dmError("Error loading module file, %d: %s\n", | |
190 result, dmErrorStr(result)); | |
191 return 3; | |
192 } | |
193 | |
194 // Try to convert it | |
195 if ((result = jssConvertModuleForPlaying(mod)) != DMERR_OK) | |
196 { | |
197 dmError("Could not convert module for playing, %d: %s\n", | |
198 result, dmErrorStr(result)); | |
199 return 3; | |
200 } | |
201 | |
202 // Open mixer | |
203 dev = jvmInit(optOutFormat, optOutChannels, optOutFreq, JMIX_AUTO); | |
204 if (dev == NULL) | |
205 { | |
206 dmError("jvmInit() returned NULL\n"); | |
207 return 4; | |
208 } | |
209 | |
210 sampSize = jvmGetSampleSize(dev); | |
211 if ((mb = dmMalloc(bufLen * sampSize)) == NULL) | |
212 { | |
213 dmError("Could not allocate mixing buffer\n"); | |
214 return 5; | |
215 } | |
216 | |
217 dmMsg(1, "Using fmt=%d, bits=%d, channels=%d, freq=%d [%d / sample]\n", | |
218 optOutFormat, jvmGetSampleRes(dev), optOutChannels, optOutFreq, | |
219 sampSize); | |
220 | |
221 // Initialize player | |
222 if ((plr = jmpInit(dev)) == NULL) | |
223 { | |
224 dmError("jmpInit() returned NULL.\n"); | |
225 return 6; | |
226 } | |
227 | |
228 // Set callback | |
229 jvmSetCallback(dev, jmpExec, plr); | |
230 | |
231 // Initialize playing | |
232 jmpSetModule(plr, mod); | |
233 if (optStartOrder >= 0) | |
234 { | |
235 dmMsg(1, "Starting from song order #%d\n", optStartOrder); | |
236 } else | |
237 optStartOrder = 0; | |
238 | |
239 jmpPlayOrder(plr, optStartOrder); | |
240 jvmSetGlobalVol(dev, 150); | |
241 | |
242 if (optMuteOChannels > 0 && optMuteOChannels <= mod->nchannels) | |
243 { | |
244 int i; | |
245 for (i = 0; i < mod->nchannels; i++) | |
246 jvmMute(dev, i, TRUE); | |
247 jvmMute(dev, optMuteOChannels - 1, FALSE); | |
248 } | |
249 | |
250 // Open output file | |
251 if ((outFile = fopen(optOutFilename, "wb")) == NULL) | |
252 { | |
253 dmError("Error opening output file '%s'. (%s)\n", optInFilename, strerror(errno)); | |
254 return 7; | |
255 } | |
256 | |
257 // Write initial header | |
258 dmWriteWAVHeader(outFile, jvmGetSampleRes(dev), optOutFreq, optOutChannels, 1024); | |
259 | |
260 // Render audio data and output to file | |
261 if (optUsePlayTime) | |
262 dmMsg(1, "Rendering module (%d seconds) ...\n", optPlayTime); | |
263 else | |
264 dmMsg(1, "Rendering module ...\n"); | |
265 | |
266 optPlayTime *= optOutFreq; | |
267 dataTotal = 0; | |
268 dataWritten = 1; | |
269 while (plr->isPlaying && dataWritten > 0) | |
270 { | |
271 size_t writeLen = bufLen; | |
272 if (optUsePlayTime && (writeLen + dataTotal) > optPlayTime) | |
273 writeLen = optPlayTime - dataTotal; | |
274 | |
275 if (writeLen > 0) | |
276 { | |
277 jvmRenderAudio(dev, mb, writeLen); | |
278 #if (SDL_BYTEORDER == SDL_BIG_ENDIAN) | |
279 jssEncodeSample16((Uint16 *)mb, writeLen * optOutChannels, jsampSwapEndianess); | |
280 #endif | |
281 dataWritten = fwrite(mb, sampSize, writeLen, outFile); | |
282 if (dataWritten < writeLen) | |
283 { | |
284 dmError("Error writing data!\n"); | |
285 fclose(outFile); | |
286 return 8; | |
287 } | |
288 dataTotal += dataWritten; | |
289 } | |
290 | |
291 if (optUsePlayTime && dataTotal >= optPlayTime) | |
292 break; | |
293 } | |
294 | |
295 // Write the correct header | |
296 if (fseek(outFile, 0L, SEEK_SET) != 0) | |
297 { | |
298 dmError("Error rewinding to header position!\n"); | |
299 return 9; | |
300 } | |
301 | |
302 dmWriteWAVHeader(outFile, jvmGetSampleRes(dev), optOutFreq, optOutChannels, dataTotal); | |
303 | |
304 // Done! | |
305 fclose(outFile); | |
306 | |
307 jmpClose(plr); | |
308 jvmClose(dev); | |
309 jssFreeModule(mod); | |
310 jssClose(); | |
311 | |
312 dmMsg(1, "OK.\n"); | |
313 return 0; | |
314 } |