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 }