Mercurial > hg > dmlib
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/mod2wav.c Tue Apr 16 06:04:22 2013 +0300 @@ -0,0 +1,314 @@ +/* + * mod2wav - Render XM/JSSMOD module to WAV waveform file + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2007 Tecnic Software productions (TNSP) + * + * Please read file 'COPYING' for information on license and distribution. + */ +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include "jss.h" +#include "jssmod.h" +#include "jssmix.h" +#include "jssplr.h" +#include "dmlib.h" +#include "dmargs.h" +#include "dmwav.h" +#include "dmmutex.h" + + +char *optInFilename = NULL, *optOutFilename = NULL; +int optOutFormat = JSS_AUDIO_S16, + optOutChannels = 2, + optOutFreq = 44100, + optMuteOChannels = -1, + optStartOrder = -1; +BOOL optUsePlayTime = FALSE; +size_t optPlayTime; + + +DMOptArg optList[] = +{ + { 0, '?', "help", "Show this help", OPT_NONE }, + { 2, 'v', "verbose", "Be more verbose", OPT_NONE }, + { 3, '1', "16bit", "16-bit output", OPT_NONE }, + { 4, '8', "8bit", "8-bit output", OPT_NONE }, + { 5, 'm', "mono", "Mono output", OPT_NONE }, + { 6, 's', "stereo", "Stereo output", OPT_NONE }, + { 7, 'f', "freq", "Output frequency", OPT_ARGREQ }, + { 8, 'M', "mute", "Mute other channels than #", OPT_ARGREQ }, + { 9, 'o', "order", "Start from order #", OPT_ARGREQ }, + { 10, 't', "time", "Play for # seconds", OPT_ARGREQ }, +// {10, 'l', "loop", "Loop for # times", OPT_ARGREQ }, +}; + +const int optListN = sizeof(optList) / sizeof(optList[0]); + + +BOOL argHandleOpt(const int optN, char *optArg, char *currArg) +{ + (void) optArg; + + switch (optN) + { + case 0: + dmPrintBanner(stdout, dmProgName, + "[options] [sourcefile] [destfile]"); + + dmArgsPrintHelp(stdout, optList, optListN); + exit(0); + break; + + case 2: + dmVerbosity++; + break; + + case 3: + optOutFormat = JSS_AUDIO_S16; + break; + + case 4: + optOutFormat = JSS_AUDIO_U8; + break; + + case 5: + optOutChannels = JSS_AUDIO_MONO; + break; + + case 6: + optOutChannels = JSS_AUDIO_STEREO; + break; + + case 7: + optOutFreq = atoi(optArg); + break; + + case 8: + optMuteOChannels = atoi(optArg); + break; + + case 9: + optStartOrder = atoi(optArg); + break; + + case 10: + optPlayTime = atoi(optArg); + optUsePlayTime = TRUE; + break; + + default: + dmError("Unknown argument '%s'.\n", currArg); + return FALSE; + } + + return TRUE; +} + + +BOOL argHandleFile(char *currArg) +{ + if (!optInFilename) + optInFilename = currArg; + else + if (!optOutFilename) + optOutFilename = currArg; + else + { + dmError("Too many filename arguments (only source and dest needed) '%s'\n", currArg); + return FALSE; + } + + return TRUE; +} + + +int main(int argc, char *argv[]) +{ + DMResource *inFile = NULL; + FILE *outFile = NULL; + JSSModule *mod = NULL; + JSSMixer *dev = NULL; + JSSPlayer *plr = NULL; + int result = -1; + size_t bufLen = 1024*4, dataTotal, dataWritten, sampSize; + Uint8 *mb = NULL; + + dmInitProg("mod2wav", "XM/JSSMOD to WAV renderer", "0.2", NULL, NULL); + dmVerbosity = 1; + + // Parse arguments + if (!dmArgsProcess(argc, argv, optList, optListN, + argHandleOpt, argHandleFile, TRUE)) + exit(1); + + // Check arguments + if (optInFilename == NULL || optOutFilename == NULL) + { + dmError("Input or output file not specified. Try --help.\n"); + return 1; + } + + // Initialize miniJSS + jssInit(); + + // Open the source file + if ((inFile = dmf_create_stdio(optInFilename, "rb")) == NULL) + { + dmError("Error opening input file '%s', %d: %s\n", + optInFilename, errno, strerror(errno)); + return 1; + } + + // Read module file + fprintf(stderr, "Reading file: %s\n", optInFilename); +#ifdef JSS_SUP_XM + fprintf(stderr, "* Trying XM...\n"); + result = jssLoadXM(inFile, &mod); +#endif +#ifdef JSS_SUP_JSSMOD + if (result != 0) + { + size_t bufgot, bufsize = dmfsize(inFile); + Uint8 *buf = dmMalloc(bufsize); + dmfseek(inFile, 0L, SEEK_SET); + fprintf(stderr, "* Trying JSSMOD (%d bytes, %p)...\n", bufsize, buf); + if ((bufgot = dmfread(buf, 1, bufsize, inFile)) != bufsize) + { + fprintf(stderr, "Error reading file (not enough data %d), #%d: %s\n", + bufgot, dmferror(inFile), dmErrorStr(dmferror(inFile))); + return 2; + } + result = jssLoadJSSMOD(buf, bufsize, &mod); + dmFree(buf); + } +#endif + dmf_close(inFile); + if (result != DMERR_OK) + { + dmError("Error loading module file, %d: %s\n", + result, dmErrorStr(result)); + return 3; + } + + // Try to convert it + if ((result = jssConvertModuleForPlaying(mod)) != DMERR_OK) + { + dmError("Could not convert module for playing, %d: %s\n", + result, dmErrorStr(result)); + return 3; + } + + // Open mixer + dev = jvmInit(optOutFormat, optOutChannels, optOutFreq, JMIX_AUTO); + if (dev == NULL) + { + dmError("jvmInit() returned NULL\n"); + return 4; + } + + sampSize = jvmGetSampleSize(dev); + if ((mb = dmMalloc(bufLen * sampSize)) == NULL) + { + dmError("Could not allocate mixing buffer\n"); + return 5; + } + + dmMsg(1, "Using fmt=%d, bits=%d, channels=%d, freq=%d [%d / sample]\n", + optOutFormat, jvmGetSampleRes(dev), optOutChannels, optOutFreq, + sampSize); + + // Initialize player + if ((plr = jmpInit(dev)) == NULL) + { + dmError("jmpInit() returned NULL.\n"); + return 6; + } + + // Set callback + jvmSetCallback(dev, jmpExec, plr); + + // Initialize playing + jmpSetModule(plr, mod); + if (optStartOrder >= 0) + { + dmMsg(1, "Starting from song order #%d\n", optStartOrder); + } else + optStartOrder = 0; + + jmpPlayOrder(plr, optStartOrder); + jvmSetGlobalVol(dev, 150); + + if (optMuteOChannels > 0 && optMuteOChannels <= mod->nchannels) + { + int i; + for (i = 0; i < mod->nchannels; i++) + jvmMute(dev, i, TRUE); + jvmMute(dev, optMuteOChannels - 1, FALSE); + } + + // Open output file + if ((outFile = fopen(optOutFilename, "wb")) == NULL) + { + dmError("Error opening output file '%s'. (%s)\n", optInFilename, strerror(errno)); + return 7; + } + + // Write initial header + dmWriteWAVHeader(outFile, jvmGetSampleRes(dev), optOutFreq, optOutChannels, 1024); + + // Render audio data and output to file + if (optUsePlayTime) + dmMsg(1, "Rendering module (%d seconds) ...\n", optPlayTime); + else + dmMsg(1, "Rendering module ...\n"); + + optPlayTime *= optOutFreq; + dataTotal = 0; + dataWritten = 1; + while (plr->isPlaying && dataWritten > 0) + { + size_t writeLen = bufLen; + if (optUsePlayTime && (writeLen + dataTotal) > optPlayTime) + writeLen = optPlayTime - dataTotal; + + if (writeLen > 0) + { + jvmRenderAudio(dev, mb, writeLen); +#if (SDL_BYTEORDER == SDL_BIG_ENDIAN) + jssEncodeSample16((Uint16 *)mb, writeLen * optOutChannels, jsampSwapEndianess); +#endif + dataWritten = fwrite(mb, sampSize, writeLen, outFile); + if (dataWritten < writeLen) + { + dmError("Error writing data!\n"); + fclose(outFile); + return 8; + } + dataTotal += dataWritten; + } + + if (optUsePlayTime && dataTotal >= optPlayTime) + break; + } + + // Write the correct header + if (fseek(outFile, 0L, SEEK_SET) != 0) + { + dmError("Error rewinding to header position!\n"); + return 9; + } + + dmWriteWAVHeader(outFile, jvmGetSampleRes(dev), optOutFreq, optOutChannels, dataTotal); + + // Done! + fclose(outFile); + + jmpClose(plr); + jvmClose(dev); + jssFreeModule(mod); + jssClose(); + + dmMsg(1, "OK.\n"); + return 0; +}