Mercurial > hg > dmlib
view mod2wav.c @ 46:f939d3896937
Rename another template file.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Mon, 01 Oct 2012 01:11:59 +0300 |
parents | 281b080e8c44 |
children | 033c660c25f5 |
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 <string.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 "dmfile.h" #define JSS_WAVE_FORMAT_PCM (1) #define JSS_WAVE_RIFF_ID "RIFF" #define JSS_WAVE_WAVE_ID "WAVE" #define JSS_WAVE_FMT_ID "fmt " #define JSS_WAVE_DATA_ID "data" typedef struct { Uint8 chunkID[4]; Uint32 chunkSize; } JSSWaveChunk; typedef struct { Uint8 riffID[4]; Uint32 fileSize; Uint8 riffType[4]; JSSWaveChunk chFormat; Uint16 wFormatTag; Uint16 nChannels; Uint32 nSamplesPerSec; Uint32 nAvgBytesPerSec; Uint16 nBlockAlign; Uint16 wBitsPerSample; JSSWaveChunk chData; // Data follows here } JSSWaveFile; char *srcFilename = NULL, *destFilename = 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 (!srcFilename) srcFilename = currArg; else if (!destFilename) destFilename = currArg; else { dmError("Too many filename arguments (only source and dest needed) '%s'\n", currArg); return FALSE; } return TRUE; } BOOL jssWriteChunk(FILE * f, JSSWaveChunk *ch) { if (!dm_fwrite_str(f, ch->chunkID, 4)) return FALSE; return dm_fwrite_le32(f, ch->chunkSize); } void jssMakeChunk(JSSWaveChunk *ch, const char *chunkID, const Uint32 chunkSize) { memcpy(&(ch->chunkID), (const void *) chunkID, 4); ch->chunkSize = chunkSize; } void jssWriteWAVHeader(FILE *outFile, int sampBits, int sampFreq, int sampChn, size_t sampLen) { JSSWaveFile wav; // PCM WAVE chunk jssMakeChunk(&wav.chFormat, JSS_WAVE_FMT_ID, (2 + 2 + 4 + 4 + 2 + 2)); wav.wFormatTag = JSS_WAVE_FORMAT_PCM; wav.nChannels = sampChn; wav.nSamplesPerSec = sampFreq; wav.nAvgBytesPerSec = (sampBits * sampChn * sampFreq) / 8; wav.nBlockAlign = (sampBits * sampChn) / 8; wav.wBitsPerSample = sampBits; // Data chunk jssMakeChunk(&wav.chData, JSS_WAVE_DATA_ID, (sampLen * wav.nBlockAlign)); // RIFF header memcpy(&wav.riffID, (const void *) JSS_WAVE_RIFF_ID, 4); memcpy(&wav.riffType, (const void *) JSS_WAVE_WAVE_ID, 4); wav.fileSize = ((4 + 4 + 4) + wav.chFormat.chunkSize + wav.chData.chunkSize); // Write header dm_fwrite_str(outFile, wav.riffID, sizeof(wav.riffID)); dm_fwrite_le32(outFile, wav.fileSize); dm_fwrite_str(outFile, wav.riffType, sizeof(wav.riffType)); jssWriteChunk(outFile, &wav.chFormat); dm_fwrite_le16(outFile, wav.wFormatTag); dm_fwrite_le16(outFile, wav.nChannels); dm_fwrite_le32(outFile, wav.nSamplesPerSec); dm_fwrite_le32(outFile, wav.nAvgBytesPerSec); dm_fwrite_le16(outFile, wav.nBlockAlign); dm_fwrite_le16(outFile, wav.wBitsPerSample); jssWriteChunk(outFile, &wav.chData); } int main(int argc, char *argv[]) { DMResource *inFile = NULL; FILE *outFile = NULL; JSSModule *m = NULL; JSSMixer *d = NULL; JSSPlayer *p = 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 (!srcFilename || !destFilename) { dmError("Input or output file not specified!\n"); return 1; } // Initialize miniJSS jssInit(); // Open the source file if ((inFile = dmf_create_stdio(srcFilename)) == NULL) { fprintf(stderr, "Error opening input file '%s'. (%s)\n", srcFilename, strerror(errno)); return 1; } // Read module file fprintf(stderr, "Reading file: %s\n", srcFilename); #ifdef JSS_SUP_XM fprintf(stderr, "* Trying XM...\n"); result = jssLoadXM(inFile, &m); #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, &m); dmFree(buf); } #endif dmf_close(inFile); if (result != DMERR_OK) { fprintf(stderr, "Error loading module file, %d: %s\n", result, dmErrorStr(result)); return 3; } // Open mixer d = jvmInit(optOutFormat, optOutChannels, optOutFreq, JMIX_AUTO); if (!d) { fprintf(stderr, "jvmInit() returned NULL\n"); return 4; } sampSize = jvmGetSampleSize(d); mb = dmMalloc(bufLen * sampSize); if (!mb) { fprintf(stderr, "Could not allocate mixing buffer\n"); return 5; } dmMsg(1, "Using fmt=%d, bits=%d, channels=%d, freq=%d [%d / sample]\n", optOutFormat, jvmGetSampleRes(d), optOutChannels, optOutFreq, sampSize); // Initialize player p = jmpInit(d); if (!p) { fprintf(stderr, "jmpInit() returned NULL\n"); return 6; } // Set callback jvmSetCallback(d, jmpExec, p); // Initialize playing jmpSetModule(p, m); if (optStartOrder >= 0) { dmMsg(1, "Starting from song order #%d\n", optStartOrder); } else optStartOrder = 0; jmpPlayOrder(p, optStartOrder); jvmSetGlobalVol(d, 50); if (optMuteOChannels > 0 && optMuteOChannels <= m->nchannels) { int i; for (i = 0; i < m->nchannels; i++) jvmMute(d, i, TRUE); jvmMute(d, optMuteOChannels - 1, FALSE); } // Open output file if ((outFile = fopen(destFilename, "wb")) == NULL) { dmError("Error opening output file '%s'. (%s)\n", srcFilename, strerror(errno)); return 7; } // Write initial header jssWriteWAVHeader(outFile, jvmGetSampleRes(d), 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 (p->isPlaying && dataWritten > 0) { size_t writeLen = bufLen; if (optUsePlayTime && (writeLen + dataTotal) > optPlayTime) writeLen = optPlayTime - dataTotal; if (writeLen > 0) { jvmRenderAudio(d, 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; } jssWriteWAVHeader(outFile, jvmGetSampleRes(d), optOutFreq, optOutChannels, dataTotal); // Done! fclose(outFile); jssFreeModule(m); dmMsg(1, "OK.\n"); return 0; }