Mercurial > hg > dmlib
view tools/mod2wav.c @ 1723:13fe4010623f
Allow writing of xx1 and xx2 formats, as useless that is.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Wed, 06 Jun 2018 15:23:31 +0300 |
parents | 93d1050eac99 |
children | c3e88d9343ca |
line wrap: on
line source
/* * 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 "dmtool.h" #include <stdio.h> #include <stdlib.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; static const 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, 0); 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: dmErrorMsg("Unknown argument '%s'.\n", currArg); return FALSE; } return TRUE; } BOOL argHandleFile(char *currArg) { if (!optInFilename) optInFilename = currArg; else if (!optOutFilename) optOutFilename = currArg; else { dmErrorMsg("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, OPTH_BAILOUT)) exit(1); // Check arguments if (optInFilename == NULL || optOutFilename == NULL) { dmErrorMsg("Input or output file not specified. Try --help.\n"); return 1; } // Initialize miniJSS jssInit(); // Open the source file if ((result = dmf_open_stdio(optInFilename, "rb", &inFile)) != DMERR_OK) { dmErrorMsg("Error opening input file '%s', %d: %s\n", optInFilename, result, dmErrorStr(result)); return 1; } // Read module file dmMsg(1, "Reading file: %s\n", optInFilename); #ifdef JSS_SUP_XM result = jssLoadXM(inFile, &mod, TRUE); #endif #ifdef JSS_SUP_JSSMOD dmfreset(inFile); if (result != DMERR_OK) { dmMsg(1, "* Trying JSSMOD ...\n"); result = jssLoadJSSMOD(inFile, &mod, TRUE); dmfreset(inFile); if (result == DMERR_OK) result = jssLoadJSSMOD(inFile, &mod, FALSE); } else { dmMsg(2, "* Trying XM...\n"); result = jssLoadXM(inFile, &mod, FALSE); } #endif dmf_close(inFile); // Check for errors, we still might have some data tho if (result != DMERR_OK) { dmErrorMsg("Error loading module file, %d: %s\n", result, dmErrorStr(result)); } // Check if we have anything if (mod == NULL) return 3; // Try to convert it if ((result = jssConvertModuleForPlaying(mod)) != DMERR_OK) { dmErrorMsg("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) { dmErrorMsg("jvmInit() returned NULL\n"); return 4; } sampSize = jvmGetSampleSize(dev); if ((mb = dmMalloc(bufLen * sampSize)) == NULL) { dmErrorMsg("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) { dmErrorMsg("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) { int err = dmGetErrno(); dmErrorMsg("Error opening output file '%s' #%d: %s.\n", optInFilename, err, dmErrorStr(err)); 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) { dmErrorMsg("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) { dmErrorMsg("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; }