Mercurial > hg > dmlib
view mod2wav.c @ 510:43ea59887c69
Start work on making C64 formats encoding possible by changing DMDecodeOps
to DMEncDecOps and adding fields and op enums for custom encode functions, renaming,
etc. Split generic op sanity checking into a separate function in
preparation for its use in generic encoding function.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Mon, 19 Nov 2012 15:06:01 +0200 |
parents | 4972ca91d062 |
children |
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 <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; }