/* (c) Copyright 1998-2001 - Tord Jansson ====================================== This file is part of the BladeEnc MP3 Encoder, based on ISO's reference code for MPEG Layer 3 compression. This file doesn't contain any of the ISO reference code and is copyright Tord Jansson (tord.jansson@swipnet.se). BladeEnc is free software; you can redistribute this file and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. ------------ Changes ------------ 2000-11-22 Andre Piotrowski - bug fix: removeJobQueueEntry() -- caught a wrong condition -- harmless, because never called that way - feature: CONCAT != 0 allows to concatenate files encoded with '-noGap' 2000-12-11 ap - include "arglink.h" (now containing the prototype for 'mystrupr()') 2001-01-12 ap - version number 2001-01-19 Tord Jansson - Changed all return values to positive (and different) values. */ #include #include #include #include #include "system.h" #include "common.h" #include "codec.h" #include "samplein.h" #include "arglink.h" #include "bladesys.h" #if SYSTEM == MAC_OS #include #include int main_ (int argc, char* argv[]); void DoEvent (EventRecord *i); extern int gFriendliness; #endif enum { PROG_OFF, PROG_BOTH_PERC_ETA, PROG_FILE_GRAPH, PROG_BATCH_GRAPH, PROG_BOTH_GRAPH, PROG_BOTH_SAMPLES, PROG_BOTH_ETA, PROG_FILE_RLEN, PROG_FILE_NORETURN }; /*____ Structure Definitions __________________________________________________*/ typedef struct JobDef Job; struct JobDef { CodecInitIn sCodec; SI_Stream sInput; Job *psNext; char bDeleteSource; char bNoGap; char bConcat; char outputFilename[MAX_NAMELEN]; char sourceFilename[MAX_NAMELEN]; }; /*____ Function Prototypes ____________________________________________________*/ void quit (int returnValue); void timerCalibrate (void); time_t timerStart (void); float timerStop (time_t startTime); void updateProgressIndicator ( time_t startBatch, double batchLen, double batchDone, time_t startFile, double fileLen, double fileDone, time_t currTime, int progType, Job *psJob ); void setOutputDir (char *pPath); argLink *readGlobalSwitches (argLink *pArgLink); argLink *readLocalSwitches (argLink *pArgLink, Job *psJob); void sortJobs (Job **psJob); int validateJobs (Job *psJob); int printUsage (void); int addCommandlineJob (argLink **pArgLink); int clearJobQueue (void); int removeJobQueueEntry (Job *psJob); /* unused prototypes!? */ int addFileToBatch (char *pFilename, char *pOutputFilename); int readL3EncCommandline (int argc, char *argv[]); /*____ Static Data ____________________________________________________________*/ /* Parameters set through the commandline. */ int wantedBitrate = -1; /* -1 = Unspecified. */ int wantedCRC = FALSE; int wantedPrivate = FALSE; int wantedCopyright = FALSE; int wantedOriginal = TRUE; SI_OutFormat wantedInputType = STEREO; int wantedChannelSwap = FALSE; int wantedDeleteSource = FALSE; int wantedNoGap = FALSE; int wantedSort = FALSE; int wantedConcat = FALSE; #ifdef WAIT_KEY #define DFLT_WANTED_QUIT FALSE #else #define DFLT_WANTED_QUIT FALSE #endif int wantedQuit = DFLT_WANTED_QUIT; int wantedQuiet = FALSE; int wantedSTDOUT = FALSE; int fPreparedSTDIN = FALSE; char prioString[256]; char *pPrioString = NULL; Job *psJobQueue = NULL; char outputDir[MAX_NAMELEN]; FILE *textout; int timerCalibrationValue; /* Only used if PRECISE_TIMER is defined. */ int progType = PROG_BOTH_PERC_ETA; /* Style of the progress bar */ int rawFrequency = 44100; int rawChannels = 2; int rawBits = 16; int rawSigned = TRUE; int rawByteorder = BYTEORDER; /*____ main() _________________________________________________________________*/ #if SYSTEM == MAC_OS int main_ (int argc, char* argv[]) /* Because the real main is somewhere else */ #else int main (int argc, char* argv[]) #endif { int samplesPerFrame = 0; /* Initialized just to prevent compiler warnings */ int nSamples; int nSamples2; short readBuffer[2304]; int x; char input; Job *psTemp; time_t startTimeBatch, startTimeFile, currTime, oldTime = 0; float seconds; double batchSamplesTotal = 0.0, batchSamplesRead = 0.0; double fileSamplesRead; CodecInitOut *pCodecInfo; char *pBuffer = NULL; /* Initialized just to prevent compiler warnings */ uint encodedChunkSize; FILE *fp = 0; /* Initialized just to prevent compiler warnings */ char temp[256]; argLink *pArgLink, *pNextArg; int bContinueNoGap; int bConcatenate; int nNoGapSamples; int noGapOfs; #if SYSTEM == MAC_OS long macTickStart; macTickStart = TickCount (); #endif /* Setting default parameters. Might seem useless since most of them allready are predefined, but the MAC OS port uses main() as a subroutine which can be called multiple times. */ wantedBitrate = -1; /* -1 = Unspecified. */ wantedCRC = FALSE; wantedPrivate = FALSE; wantedCopyright = FALSE; wantedOriginal = TRUE; wantedInputType = STEREO; wantedChannelSwap = FALSE; wantedDeleteSource = FALSE; wantedNoGap = FALSE; wantedConcat = FALSE; wantedSort = FALSE; wantedQuit = DFLT_WANTED_QUIT; wantedQuiet = FALSE; wantedSTDOUT = FALSE; fPreparedSTDIN = FALSE; pPrioString = NULL; psJobQueue = NULL; #if SYSTEM == MAC_OS progType = PROG_FILE_NORETURN; #else progType = PROG_BOTH_PERC_ETA; /* Style of the progress bar */ #endif rawFrequency = 44100; rawChannels = 2; rawBits = 16; rawSigned = TRUE; rawByteorder = BYTEORDER; textout = stdout; outputDir[0] = '\0'; bContinueNoGap = FALSE; bConcatenate = FALSE; nNoGapSamples = 0; noGapOfs = 0; /* Fix ArgLink */ pArgLink = argv2ArgLink (argc-1, argv+1); if (findStrInArgLink (pArgLink, "-NOCFG") == NULL) if (findConfigFile (argv[0], temp) == TRUE) addFileContentToArgLink (&pArgLink, temp); #ifdef WILDCARDS expandWildcards (&pArgLink); #endif pNextArg = readGlobalSwitches (pArgLink); /* Check for STDOUT */ if (findStrInArgLink (pArgLink, "stdout") != NULL) { prepStdout (); textout = stderr; } /* Print Text */ if (!wantedQuiet) { fprintf (textout, "\n"); fprintf (textout, "BladeEnc 0.94.2 (c) Tord Jansson Homepage: http://bladeenc.mp3.no\n"); fprintf (textout, "===============================================================================\n"); fprintf (textout, "BladeEnc is free software, distributed under the Lesser General Public License.\n"); fprintf (textout, "See the file COPYING, BladeEnc's homepage or www.fsf.org for more details.\n"); fprintf (textout, "\n"); } /* Initialise batch */ while (pNextArg != NULL) { x = addCommandlineJob (&pNextArg); if (x == FALSE) { deleteArgLink (pArgLink); quit (4); } } deleteArgLink (pArgLink); /* Validate job settings */ x = validateJobs (psJobQueue); if (x == FALSE) quit (2); /* Possibly sort the jobs alphabetically on input name */ if (wantedSort) sortJobs (&psJobQueue); /* Make sure we don't have certain progress indicators if RAW file in batch */ x = 0; for (psTemp = psJobQueue; psTemp != NULL; psTemp = psTemp->psNext) { if (psTemp->sInput.filetype == RAW) x++; } if (x != 0 && progType != PROG_OFF && progType != PROG_FILE_RLEN) progType = PROG_FILE_RLEN; /* Set priority */ if (setPriority (pPrioString) == FALSE) { fprintf (textout, "Error: '%s' is not a valid priority setting!\n", pPrioString); quit (3); }; /* Procedure if no files found */ if (psJobQueue == NULL) { printUsage (); /* No files on the commandline */ quit (1); } /* Print number of files to encode */ if (!wantedQuiet) { x = 0; for (psTemp = psJobQueue; psTemp != NULL; psTemp = psTemp->psNext) x++; fprintf (textout, "Files to encode: %d\n\n", x); } /* Calculate batchSamplesTotal */ for (psTemp = psJobQueue; psTemp != NULL; psTemp = psTemp->psNext) if (psTemp->sInput.length == 0xFFFFFFFF) { batchSamplesTotal = 0xFFFFFFFF; break; } else batchSamplesTotal += psTemp->sInput.length; /* Encode */ #ifdef PRECISE_TIMER timerCalibrate (); #endif startTimeBatch = timerStart (); while (psJobQueue != NULL) { /* Print information */ if (!wantedQuiet) { fprintf (textout, "Encoding: %s\n", psJobQueue->sourceFilename); fprintf (textout, "Input: %.1f kHz, %d bit, ", psJobQueue->sInput.freq/1000.f, psJobQueue->sInput.channelBits); if (psJobQueue->sInput.nChannels >= 2) fprintf (textout, "stereo.\n"); else fprintf (textout, "mono.\n"); fprintf (textout, "Output: %d kBit, ", psJobQueue->sCodec.bitrate); if (psJobQueue->sCodec.mode == 0) fprintf (textout, "stereo.\n\n"); else fprintf (textout, "mono.\n\n"); if (bConcatenate) fprintf (textout, "Concatenates output to previous mp3-file\n"); else if (bContinueNoGap) fprintf (textout, "Received %d samples from previous file due to -nogap\n", nNoGapSamples); } /* Init a new job */ startTimeFile = timerStart (); fileSamplesRead = 0; if (!bContinueNoGap) { pCodecInfo = codecInit (&psJobQueue->sCodec); samplesPerFrame = pCodecInfo->nSamples; if (psJobQueue->sCodec.mode != 3) samplesPerFrame /= 2; pBuffer = (char *) malloc (pCodecInfo->bufferSize); } if (strcmp (psJobQueue->outputFilename, "STDOUT") == 0) { fp = stdout; } else if (!bConcatenate) { fp = fopen (psJobQueue->outputFilename, "wb"); if (fp == NULL) { /* codecExit (); */ closeInput (&psJobQueue->sInput); fprintf (textout, "ERROR: Couldn't create '%s'!\n", psJobQueue->outputFilename); quit (5); } } /* Encoding loop */ while ((nSamples = readSamples (&psJobQueue->sInput, samplesPerFrame - nNoGapSamples, readBuffer + noGapOfs)) > 0) { nSamples += nNoGapSamples; nNoGapSamples = 0; noGapOfs = 0; if (psJobQueue->bNoGap && nSamples < samplesPerFrame) break; /* Stuff needed for Petteri Kamppuri's Mac OS port */ #if SYSTEM == MAC_OS if (macTickStart + gFriendliness < TickCount ()) { macTickStart = TickCount (); Boolean gotEvent; EventRecord event; gotEvent = GetNextEvent (everyEvent, &event); if (gotEvent) { SIOUXHandleOneEvent (&event); DoEvent (&event); } } #endif /* Encode a chunk and Write to stream */ nSamples2 = nSamples; if (psJobQueue->sCodec.mode != 3) nSamples2 *= 2; encodedChunkSize = codecEncodeChunk (nSamples2, readBuffer, pBuffer); if (fwrite (pBuffer, 1, encodedChunkSize, fp) != encodedChunkSize) { fprintf (textout, "ERROR: Couldn't write '%s'! Disc probably full.\n", psJobQueue->outputFilename); quit (6); } /* Update progress indicator */ batchSamplesRead += nSamples; fileSamplesRead += nSamples; if (!wantedQuiet && progType != PROG_OFF) { currTime = time (NULL); if (currTime != oldTime) { oldTime = currTime; updateProgressIndicator (startTimeBatch, batchSamplesTotal, batchSamplesRead, startTimeFile, psJobQueue->sInput.length, fileSamplesRead, currTime, progType, psJobQueue); } } /* Handling pause through ESC (Windows & OS/2 only) */ if (be_kbhit() != 0) { input = be_getch (); if (input == 27) { fprintf (textout, "\r \r"); fprintf (textout, "Quit, are you sure? (y/n)"); fflush (textout); input = be_getch(); if (input == 'y' || input == 'Y') { encodedChunkSize = codecExit (pBuffer); if (encodedChunkSize != 0) if (fwrite (pBuffer, encodedChunkSize, 1, fp) != 1) { fprintf (textout, "ERROR: Couldn't write '%s'! Disc probably full.\n", psJobQueue->outputFilename); quit (6); } free (pBuffer); closeInput (&psJobQueue->sInput); if (fp != stdout) fclose (fp); return 0; } else fprintf (textout, "\r \r"); } } } /* File done */ batchSamplesRead += nSamples; fileSamplesRead += nSamples; if (!wantedQuiet) updateProgressIndicator (startTimeBatch, batchSamplesTotal, batchSamplesRead, startTimeFile, psJobQueue->sInput.length, fileSamplesRead, time (NULL), progType, psJobQueue); if (psJobQueue->bNoGap && psJobQueue->psNext != NULL) { encodedChunkSize = codecFlush (pBuffer); bContinueNoGap = TRUE; nNoGapSamples = nSamples; noGapOfs = nSamples; if (psJobQueue->sInput.outputType == STEREO || psJobQueue->sInput.outputType == INVERSE_STEREO) noGapOfs *= 2; if (psJobQueue->bConcat) bConcatenate = TRUE; } else { encodedChunkSize = codecExit (pBuffer); bContinueNoGap = FALSE; bConcatenate = FALSE; } if (encodedChunkSize != 0) if (fwrite (pBuffer, encodedChunkSize, 1, fp) != 1) { fprintf (textout, "ERROR: Couldn't write '%s'! Disc probably full.\n", psJobQueue->outputFilename); quit (6); } if (!bConcatenate && fp != stdout) fclose (fp); if (!bContinueNoGap || psJobQueue->psNext == NULL) free (pBuffer); if (psJobQueue->bDeleteSource == TRUE) remove (psJobQueue->sourceFilename); seconds = timerStop (startTimeFile); x = (int) seconds; if (!wantedQuiet) { fprintf (textout, "\r \r"); fprintf (textout, "Completed. Encoding time: %02d:%02d:%02d (%.2fX)\n\n", x/3600, (x/60)%60, x%60, ((float)fileSamplesRead) / (psJobQueue->sInput.freq*seconds)); } removeJobQueueEntry (psJobQueue); } /* Batch done */ if (!wantedQuiet) { seconds = timerStop (startTimeBatch); fprintf (textout, "All operations completed. Total encoding time: %02d:%02d:%02d\n", (int) seconds/3600, (int)(seconds/60)%60, (int) seconds%60); #ifdef WAIT_KEY if (!wantedQuit) { fprintf (textout, "Press ENTER to exit..."); fflush( textout ); be_getch (); fprintf (textout, "\n"); } #endif } return 0; } /*____ quit() _________________________________________________________________*/ void quit (int returnValue) { #ifdef WAIT_KEY if (!wantedQuit) { fprintf (textout, "Press ENTER to exit..."); fflush( textout ); be_getch (); fprintf (textout, "\n"); } #endif #if SYSTEM == MAC_OS throw returnValue; #else exit (returnValue); #endif } /*____ timerCalibrate() _______________________________________________________*/ void timerCalibrate (void) { time_t x, y; int i; x = y = time (NULL); while (y == x) y = time (NULL); for (i = 0; y == time (NULL); i++) {} /* avoid compiler warning */ printf ("Timer Calibration Value: %d\n", timerCalibrationValue = i); } /*____ timerStart() ___________________________________________________________*/ time_t timerStart (void) { time_t x, y; x = y = time (NULL); #ifdef PRECISE_TIMER while (y == x) y = time (NULL); #endif return y; } /*____ timerStop() ____________________________________________________________*/ float timerStop (time_t startTime) { float seconds; time_t stopTime; stopTime = time (NULL); #ifdef PRECISE_TIMER { int i = 0; while (stopTime == time (NULL)) i++; } #endif seconds = (float) (stopTime - startTime); #ifdef PRECISE_TIMER seconds += (float)(timerCalibrationValue - i) / timerCalibrationValue; printf ("Exact seconds: %.5f\n", seconds); #endif return seconds; } /*____ updateProgressIndicator() ______________________________________________*/ void updateProgressIndicator ( time_t startBatch, double batchLen, double batchDone, time_t startFile, double fileLen, double fileDone, time_t currTime, int progType, Job *psJob ) { /* time_t currTime; */ float percentageFile, percentageBatch; int fileEta, batchEta; char temp[82]; int x, y, i; static char wheel[4] = { '|' , '/' , '-' , '\\' }; static int wheelindex = 0; static int prevFileDone = -1; /* currTime = time (NULL); */ switch (progType) { case PROG_BOTH_PERC_ETA: percentageFile = (float) (fileDone / fileLen * 100); if (percentageFile >= 100.f) percentageFile = (float) 99.9; fileEta = (int) ((float)(currTime - startFile) / fileDone * (fileLen - fileDone)); percentageBatch = (float)(batchDone / batchLen * 100); batchEta = (int) ((float)(currTime - startBatch) / batchDone * (batchLen - batchDone)); fprintf (textout, "Status: %4.1f%% done, ETA %02d:%02d:%02d BATCH: %4.1f%% done, ETA %02d:%02d:%02d\r", percentageFile, fileEta/3600, (fileEta/60)%60, fileEta%60, percentageBatch, batchEta/3600, (batchEta/60)%60, batchEta%60); fflush (textout); break; case PROG_FILE_GRAPH: strcpy (temp, "File progress: [..................................................]\r"); x = (int) (fileDone * 50 / fileLen); memset (temp + 16, '*', x); if (x < 50) { temp[16+x] = wheel[wheelindex]; wheelindex = (wheelindex + 1) % 4; } fprintf (textout, temp); fflush (textout); break; case PROG_BATCH_GRAPH: strcpy (temp, "Batch progress: [..................................................]\r"); x = (int) (batchDone * 50 / batchLen); memset (temp + 16, '*', x); if (x < 50) { temp[16+x] = wheel[wheelindex]; wheelindex = (wheelindex + 1) % 4; } fprintf (textout, temp); fflush (textout); break; case PROG_BOTH_GRAPH: strcpy (temp, "File: [.........................] Batch: [.........................]\r"); x = (int) (fileDone * 25 / fileLen); memset (temp + 7, '*', x); y = (int) (batchDone * 25 / batchLen); memset (temp + 44, '*', y); if (y < 25) { temp[7+x] = temp[44+y] = wheel[wheelindex]; wheelindex = (wheelindex + 1) % 4; } fprintf (textout, temp); fflush (textout); break; case PROG_BOTH_SAMPLES: fprintf (textout, "Samples encoded:%10d / %d BATCH:%10d / %d\r", (int) fileDone, (int) fileLen, (int) batchDone, (int) batchLen); break; case PROG_BOTH_ETA: fileEta = (int) (((float)(currTime - startFile)) / fileDone * (fileLen - fileDone)); batchEta = (int) ((float)(currTime - startBatch) / batchDone * (batchLen - batchDone)); fprintf (textout, " >>> %02d:%02d:%02d <<< >>> %02d:%02d:%02d <<<\r", fileEta/3600, (fileEta/60)%60, fileEta%60, batchEta/3600, (batchEta/60)%60, batchEta%60); fflush (textout); break; case PROG_FILE_RLEN: x = (int) (fileDone / psJob->sCodec.frequency); fprintf (textout, "Encoded runlength (current file): %02d:%02d:%02d\r", x/3600, (x/60)%60, x%60); fflush (textout); break; case PROG_FILE_NORETURN: x = (int) (fileDone * 70 / fileLen); if (prevFileDone > x || prevFileDone == -1) { prevFileDone = 0; fprintf (textout, " |----------------------------------------------------------------------|\n |"); fflush (textout); } for (i = prevFileDone; i < x; i++) { fprintf (textout, "#"); fflush (textout); } if (x == 70) fprintf (textout, "|\n\n"); prevFileDone = x; break; } } /*____ setOutputDir() _______________________________________________________*/ void setOutputDir (char *pPath) { int i; strcpy (outputDir, pPath); i = strlen (outputDir) -1; if (outputDir[i] != DIRECTORY_SEPARATOR) { outputDir[i+1] = DIRECTORY_SEPARATOR; outputDir[i+2] = 0; } } /*____ readGlobalSwitches() ___________________________________________________*/ #define option_is(s) (!strcmp (arg+1, s)) #define option_starts_with(s) (strstr (arg+1, s) == arg+1) #define option_cont_with(s,n) (strstr (arg+n, s) == arg+n) argLink *readGlobalSwitches (argLink *pArgLink) { char arg[256]; int x, y; for ( ; pArgLink != NULL; pArgLink = pArgLink->psNext) { strcpy (arg, pArgLink->pString); mystrupr (arg); if (arg[0] != '-') return pArgLink; if (option_is("SORT")) wantedSort = TRUE; else if (option_is("MONO") || option_is("DM")) wantedInputType = DOWNMIX_MONO; else if (option_is("LEFTMONO") || option_is("LM")) wantedInputType = LEFT_CHANNEL_MONO; else if (option_is("RIGHTMONO") || option_is("RM")) wantedInputType = RIGHT_CHANNEL_MONO; else if (option_is("SWAP")) wantedInputType = INVERSE_STEREO; else if (option_is("CRC")) wantedCRC = TRUE; else if (option_is("PRIVATE") || option_is("P")) wantedPrivate = TRUE; else if (option_is("COPYRIGHT") || option_is("C")) wantedCopyright = TRUE; else if (option_is("ORIGINAL")) wantedOriginal = TRUE; else if (option_is("COPY")) wantedOriginal = FALSE; else if (option_is("DELETE") || option_is("DEL")) wantedDeleteSource = TRUE; else if (option_is("NOGAP")) wantedNoGap = TRUE; else if (option_is("CONCAT")) { wantedConcat = TRUE; wantedNoGap = TRUE; } else if (option_is("QUIT") || option_is("Q")) wantedQuit = TRUE; else if (option_is("QUIET")) wantedQuiet = TRUE; else if (option_is("NOCFG")) ; /* simply do nothing... */ else if (option_starts_with ("PROGRESS=")) progType = atoi (arg+10); else if (option_starts_with ("OUTDIR=")) setOutputDir (pArgLink->pString + 8); else if (option_starts_with ("REFRESH=")) { /* Legacy switch, not supported anymore... */ } #ifdef PRIO else if (option_starts_with ("PRIO=")) { strcpy (prioString, arg+6); pPrioString = prioString; } #endif else if (option_is("RAWMONO")) rawChannels = 1; else if (option_is("RAWSTEREO")) rawChannels = 2; else if (option_is("RAWSIGNED")) rawSigned = TRUE; else if (option_is("RAWUNSIGNED")) rawSigned = FALSE; else if (option_starts_with ("RAWBITS=")) { rawBits = atoi (arg+9); if (rawBits != 8 && rawBits != 16) rawBits = 16; } else if (option_starts_with ("RAWFREQ=")) { rawFrequency = atoi (arg+9); } else if (option_starts_with ("RAWBYTEORDER=")) { if (option_cont_with ("LITTLE", 14)) rawByteorder = LITTLE_ENDIAN; if (option_cont_with ("BIG", 14)) rawByteorder = BIG_ENDIAN; if (option_cont_with ("DEFAULT", 14)) rawByteorder = BYTEORDER; } else if (option_starts_with ("RAWCHANNELS=")) { rawChannels = atoi (arg+13); if (rawChannels != 1 && rawChannels != 2) rawChannels = 2; } else if (option_is("BR")) { pArgLink = pArgLink->psNext; if (pArgLink == NULL) return NULL; wantedBitrate = atoi (pArgLink->pString); if (wantedBitrate > 1000) wantedBitrate /= 1000; } else { y = 0; for (x = 1; arg[x] >= '0' && arg[x] <= '9'; x++) y = y * 10 + (arg[x] - '0'); if (arg[x] == 0) { wantedBitrate = y; if (wantedBitrate > 1000) wantedBitrate /= 1000; } else return pArgLink; } } return pArgLink; } /*____ readLocalSwitches() ___________________________________________________*/ argLink *readLocalSwitches (argLink *pArgLink, Job *psJob) { char arg[256]; int x, y; for ( ; pArgLink != NULL; pArgLink = pArgLink->psNext) { strcpy (arg, pArgLink->pString); mystrupr (arg); if (arg[0] != '-') return pArgLink; if (option_is("MONO") || option_is("DM")) { psJob->sInput.outputType = DOWNMIX_MONO; psJob->sCodec.mode = 3; } else if (option_is("LEFTMONO") || option_is("LM")) { if (psJob->sInput.nChannels >= 2) { psJob->sInput.outputType = LEFT_CHANNEL_MONO; psJob->sCodec.mode = 3; } } else if (option_is("RIGHTMONO") || option_is("RM")) { if (psJob->sInput.nChannels >= 2) { psJob->sInput.outputType = RIGHT_CHANNEL_MONO; psJob->sCodec.mode = 3; } } else if (option_is("SWAP")) { if (psJob->sInput.nChannels >= 2) { psJob->sInput.outputType = INVERSE_STEREO; psJob->sCodec.mode = 0; } } else if (option_is("CRC")) psJob->sCodec.fCRC = TRUE; else if (option_is("PRIVATE") || option_is("P")) psJob->sCodec.fPrivate = TRUE; else if (option_is("COPYRIGHT") || option_is("C")) psJob->sCodec.fCopyright = TRUE; else if (option_is("ORIGINAL")) psJob->sCodec.fOriginal = TRUE; else if (option_is("COPY")) psJob->sCodec.fOriginal = FALSE; else if (option_is("DELETE") || option_is("DEL")) psJob->bDeleteSource = TRUE; else if (option_is("NOGAP")) psJob->bNoGap = TRUE; else if (option_is("CONCAT")) { psJob->bConcat = TRUE; psJob->bNoGap = TRUE; } else if (option_is("RAWMONO")) { if (psJob->sInput.filetype == RAW) psJob->sInput.nChannels = 1; } else if (option_is("RAWSTEREO")) { if (psJob->sInput.filetype == RAW) psJob->sInput.nChannels = 2; } else if (option_is("RAWSIGNED")) { if (psJob->sInput.filetype == RAW) psJob->sInput.fSign = TRUE; } else if (option_is("RAWUNSIGNED")) { if (psJob->sInput.filetype == RAW) psJob->sInput.fSign = FALSE; } else if (option_starts_with ("RAWBITS=")) { if (psJob->sInput.filetype == RAW) { psJob->sInput.channelBits = atoi (arg+9); if (psJob->sInput.channelBits != 8 && psJob->sInput.channelBits != 16) psJob->sInput.channelBits = 16; psJob->sInput.sampleBits = psJob->sInput.channelBits * psJob->sInput.nChannels; } } else if (option_starts_with ("RAWFREQ=")) { if (psJob->sInput.filetype == RAW) psJob->sInput.freq = atoi (arg+9); } else if (option_starts_with ("RAWBYTEORDER=")) { if (psJob->sInput.filetype == RAW) { if (option_cont_with ("LITTLE", 14)) psJob->sInput.byteorder = LITTLE_ENDIAN; if (option_cont_with ("BIG", 14)) psJob->sInput.byteorder = BIG_ENDIAN; if (option_cont_with ("DEFAULT", 14)) psJob->sInput.byteorder = BYTEORDER; } } else if (option_starts_with ("RAWCHANNELS=")) { if (psJob->sInput.filetype == RAW) { psJob->sInput.nChannels = atoi (arg+13)-1; if (psJob->sInput.nChannels != 1 && psJob->sInput.nChannels != 2) psJob->sInput.nChannels = 2; } } else if (option_is("BR")) { pArgLink = pArgLink->psNext; if (pArgLink == NULL) return pArgLink; psJob->sCodec.bitrate = atoi (pArgLink->pString); if (psJob->sCodec.bitrate > 1000) { psJob->sCodec.bitrate /= 1000; wantedQuit = TRUE; } } else if (option_is("HQ")) { wantedQuit = TRUE; /* Dummy for l3enc support */ } else { y = 0; for (x = 1 ; arg[x] >= '0' && arg[x] <= '9' ; x++) y = y * 10 + (arg[x] - '0'); if (arg[x] == 0) { psJob->sCodec.bitrate = y; if (psJob->sCodec.bitrate > 1000) psJob->sCodec.bitrate /= 1000; } else return pArgLink; } } return pArgLink; } /*____ sortJobs() ___________________________________________________________*/ void sortJobs (Job **ppsJob) { Job *psJob = *ppsJob; Job *psJobSorted = NULL; Job **ppsLink; Job *psTemp; while (psJob != NULL) { for (ppsLink = &psJobSorted; *ppsLink != NULL; ppsLink = &(*ppsLink)->psNext) if (strcmp ((*ppsLink)->sourceFilename, psJob->sourceFilename) > 0) break; psTemp = psJob; psJob = psJob->psNext; psTemp->psNext = *ppsLink; *ppsLink = psTemp; } *ppsJob = psJobSorted; } /*____ validateJobs() _______________________________________________________*/ int validateJobs (Job * psJob) { #if 0 static int aValidBitrates[14] = { 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320 }; #else int *aValidBitrates = &bitratex[1][1]; #endif int i; int fOk = TRUE; int badBitrate = -1; while (psJob != NULL && fOk) { fOk = FALSE; for (i = 0; i < 14; i++) if (psJob->sCodec.bitrate == aValidBitrates[i]) fOk = TRUE; if (fOk == FALSE && badBitrate == -1) badBitrate = psJob->sCodec.bitrate; psJob = psJob->psNext; } if (fOk) return TRUE; fprintf (textout, "ERROR: %d is not a valid bitrate!\n\n", badBitrate); fprintf (textout, "Valid bitrates are:\n\n"); for (i = 0; i < 13; i++) fprintf (textout, "%d, ", aValidBitrates[i]); fprintf (textout, "and %d kBit.\n", aValidBitrates[i]); return FALSE; } /*____ printUsage() ___________________________________________________________*/ int printUsage (void) { fprintf (textout, "Usage: bladeenc [global switches] input1 [output1 [switches]] input2 ...\n"); fprintf (textout, "\n"); fprintf (textout, "General switches:\n"); fprintf (textout, " -[kbit], -br [kbit] Set MP3 bitrate. Default is 128 (64 for mono output).\n"); fprintf (textout, " -crc Include checksum data in MP3 file.\n"); fprintf (textout, " -delete, -del Delete sample after successful encoding.\n"); fprintf (textout, " -private, -p Set the private-flag in the output file.\n"); fprintf (textout, " -copyright, -c Set the copyright-flag in the output file.\n"); fprintf (textout, " -copy Clears the original-flag in the output file.\n"); fprintf (textout, " -mono, -dm Produce mono MP3 files by combining stereo channels.\n"); fprintf (textout, " -leftmono, -lm Produce mono MP3 files from left stereo channel only.\n"); fprintf (textout, " -rightmono, -rm Produce mono MP3 files from right stereo channel only.\n"); fprintf (textout, " -swap Swap left and right stereo channels.\n"); fprintf (textout, " -rawfreq=[freq] Specify frequency for RAW samples. Default is 44100.\n"); fprintf (textout, " -rawbits=[bits] Specify bits per channel for RAW samples. Default is 16.\n"); fprintf (textout, " -rawmono Specifies that RAW samples are in mono, not stereo.\n"); fprintf (textout, " -rawstereo Specifies that RAW samples are in stereo (default).\n"); fprintf (textout, " -rawsigned Specifies that RAW samples are signed (default).\n"); fprintf (textout, " -rawunsigned Specifies that RAW samples are unsigned.\n"); #ifdef PAUSE_25_LINES fprintf (textout, "Press ENTER to continue..."); fflush (textout); be_getch(); fprintf (textout, "\n"); #endif fprintf (textout, " -rawbyteorder=[order]Specifies byteorder for RAW samples, LITTLE or BIG.\n"); fprintf (textout, " -rawchannels=[1/2] Specifies number of channels for RAW samples. Does\n"); fprintf (textout, " the same as -rawmono and -rawstereo respectively.\n"); fprintf (textout, " -nogap Make sure there's no gap between this and next track.\n"); fprintf (textout, " -concat Concatenate this and next track (also enables -nogap).\n"); fprintf (textout, "\n"); fprintf (textout, "Global only switches:\n"); fprintf (textout, " -quit, -q Quit without waiting for keypress when finished.\n"); fprintf (textout, " -outdir=[dir] Save MP3 files in specified directory.\n"); fprintf (textout, " -quiet Disable screen output.\n"); fprintf (textout, " -nocfg Don't take settings from the config-file.\n"); #ifdef PRIO fprintf (textout, " -prio=[prio] Sets the task priority for BladeEnc. Valid settings are\n"); fprintf (textout, " HIGHEST, HIGHER, NORMAL, LOWER, LOWEST(default) and IDLE\n"); #endif fprintf (textout, " -progress=[0-8] Which progress indicator to use. 0=Off, 1=Default.\n"); fprintf (textout, " -sort Sort jobs on input name. Useful for -nogap on '*'.\n"); fprintf (textout, "\n"); fprintf (textout, "Input/output files can be replaced with STDIN and STDOUT respectively.\n"); #ifdef DRAG_DROP fprintf (textout, "To make a normal 128kBit MP3, just drag-n-drop your WAV onto the BladeEnc icon.\n"); #endif fprintf (textout, "\n"); return TRUE; } /*____ addCommandlineJob() ____________________________________________________*/ int addCommandlineJob (argLink **ppArgLink) { char temp[256]; Job *psOp, *psTemp; int x; argLink *pArgLink; pArgLink = *ppArgLink; psOp = (Job *) malloc (sizeof(Job)); psOp->psNext = NULL; /* Open Input File */ strcpy (temp, pArgLink->pString); mystrupr (temp); if (strcmp (temp, "STDIN") == 0) { if (!fPreparedSTDIN) { prepStdin(); fPreparedSTDIN = TRUE; } strcpy (psOp->sourceFilename, "Standard input stream"); x = openInput (&psOp->sInput, NULL); } else { strcpy (psOp->sourceFilename, pArgLink->pString); x = openInput (&psOp->sInput, pArgLink->pString); } if (x != TRUE) { switch (psOp->sInput.errcode) { case -1: fprintf (textout, "ERROR: '%s' is not a WAV or AIFF file!\n", psOp->sourceFilename); break; case -2: fprintf (textout, "ERROR: Couldn't open '%s'!\n", psOp->sourceFilename); break; case -3: fprintf (textout, "ERROR: Unexpected end of file '%s'!\n", psOp->sourceFilename); break; case -5: fprintf (textout, "ERROR: Necessary chunk missing in '%s'!\n", psOp->sourceFilename); break; case -6: fprintf (textout, "ERROR: Sample '%s' is of an unknown subtype!\n", psOp->sourceFilename); break; default: fprintf (textout, "ERROR: Unknown error while opening '%s'!\n", psOp->sourceFilename); } free (psOp); return FALSE; } /* If RAW, set default values */ if (psOp->sInput.filetype == RAW) { psOp->sInput.freq = rawFrequency; psOp->sInput.outputFreq = rawFrequency; psOp->sInput.nChannels = rawChannels; psOp->sInput.channelBits = rawBits; psOp->sInput.sampleBits = rawBits * rawChannels; psOp->sInput.fSign = rawSigned; psOp->sInput.byteorder = rawByteorder; } /* */ if (psOp->sInput.nChannels == 1 && (wantedInputType == STEREO || wantedInputType == INVERSE_STEREO)) psOp->sInput.outputType = DOWNMIX_MONO; else psOp->sInput.outputType = wantedInputType; /* Set sCodec.mode (MONO or STEREO) */ if (psOp->sInput.outputType == DOWNMIX_MONO || psOp->sInput.outputType == LEFT_CHANNEL_MONO || psOp->sInput.outputType == RIGHT_CHANNEL_MONO) psOp->sCodec.mode = 3; /* Force to mono... */ else { psOp->sCodec.mode = 0; } /* Set frequency */ if (psOp->sInput.freq != 44100 && psOp->sInput.freq != 48000 && psOp->sInput.freq != 32000) { fprintf (textout, "ERROR: Sample '%s' is not in 32, 44.1 or 48 kHz!\n", psOp->sourceFilename); closeInput (&(psOp->sInput)); free (psOp); return FALSE; } psOp->sCodec.frequency = psOp->sInput.freq; /* Set other parameters */ psOp->sCodec.bitrate = wantedBitrate; psOp->sCodec.fPrivate = wantedPrivate; psOp->sCodec.fCRC = wantedCRC; psOp->sCodec.fCopyright = wantedCopyright; psOp->sCodec.fOriginal = wantedOriginal; psOp->bDeleteSource = wantedDeleteSource; psOp->bNoGap = wantedNoGap; psOp->bConcat = wantedConcat; /* Set unsupported parameters */ psOp->sCodec.emphasis = 0; pArgLink = pArgLink->psNext; /* Check for output specification and set output name */ psOp->outputFilename[0] = 0; if (pArgLink != NULL) { strcpy (temp, pArgLink->pString); mystrupr (temp); if (!strcmp (temp, "STDOUT")) { wantedSTDOUT = TRUE; strcpy (psOp->outputFilename, "STDOUT"); pArgLink = pArgLink->psNext; } else if (strlen (temp) >= 4 && !strcmp (temp+strlen(temp)-4, ".MP3")) { strcpy (psOp->outputFilename, pArgLink->pString); pArgLink = pArgLink->psNext; } } /* Generate output name if not allready set */ if (psOp->outputFilename[0] == 0) { if (outputDir[0] != 0) { strcpy (psOp->outputFilename, outputDir); strcpy (temp, psOp->sourceFilename); x = strlen (temp); while (temp[x] != '.' && x >=0 && temp[x] != DIRECTORY_SEPARATOR) x--; if (temp[x] == DIRECTORY_SEPARATOR) x = strlen(temp); if (x >= 0) strcpy (temp + x, ".mp3"); else { x = strlen (temp); strcat (temp, ".mp3"); } while (x >= 0 && temp[x] != '\\' && temp[x] != '/' && temp[x] != ':') x--; x++; strcat (psOp->outputFilename, temp + x); } else { strcpy (temp, psOp->sourceFilename); x = strlen (temp); while (temp[x] != '.' && x >=0 && temp[x] != DIRECTORY_SEPARATOR) x--; if (temp[x] == DIRECTORY_SEPARATOR) x = strlen(temp); if (x >= 0) strcpy (temp + x, ".mp3"); else strcat (temp, ".mp3"); strcpy (psOp->outputFilename, temp); } } /* Read local switches */ pArgLink = readLocalSwitches (pArgLink, psOp); /* Set default bitrate if not allready defined */ if (psOp->sCodec.bitrate == -1) { if (psOp->sCodec.mode == 3) psOp->sCodec.bitrate = 64; else psOp->sCodec.bitrate = 128; } /* Put this Job in the batch */ if (psJobQueue == NULL) psJobQueue = psOp; else { psTemp = psJobQueue; while (psTemp->psNext != NULL) psTemp = psTemp->psNext; psTemp->psNext = psOp; } * ppArgLink = pArgLink; return TRUE; } /*____ clearJobQueue() ________________________________________________________*/ int clearJobQueue (void) { while (psJobQueue != NULL) removeJobQueueEntry (psJobQueue); return TRUE; } /*____ removeQueueEntry() _____________________________________________________*/ int removeJobQueueEntry (Job *psJob) { Job *psPrev; /* Unlink specified entry */ if (psJob == psJobQueue) psJobQueue = psJob->psNext; else { psPrev = psJobQueue; while (psPrev->psNext != psJob && psPrev->psNext != NULL) psPrev = psPrev->psNext; if (psPrev->psNext == NULL) return FALSE; psPrev->psNext = psJob->psNext; } /* Close open file, free the entry and return. */ closeInput (&psJob->sInput); free (psJob); return TRUE; }