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;
+}