static SampleSourceType _sampleSourceGuess(const CharString sampleSourceName) { File sourceFile = NULL; CharString sourceFileExtension = NULL; SampleSourceType result = SAMPLE_SOURCE_TYPE_PCM; if (sampleSourceName == NULL || charStringIsEmpty(sampleSourceName)) { result = SAMPLE_SOURCE_TYPE_SILENCE; } else { // Look for stdin/stdout if (strlen(sampleSourceName->data) == 1 && sampleSourceName->data[0] == '-') { result = SAMPLE_SOURCE_TYPE_PCM; } else { sourceFile = newFileWithPath(sampleSourceName); sourceFileExtension = fileGetExtension(sourceFile); freeFile(sourceFile); // If there is no file extension, then automatically assume raw PCM data. Deal with it! if (charStringIsEmpty(sourceFileExtension)) { result = SAMPLE_SOURCE_TYPE_PCM; } // Possible file extensions for raw PCM data else if (charStringIsEqualToCString(sourceFileExtension, "pcm", true) || charStringIsEqualToCString(sourceFileExtension, "raw", true) || charStringIsEqualToCString(sourceFileExtension, "dat", true)) { result = SAMPLE_SOURCE_TYPE_PCM; } #if USE_AUDIOFILE else if (charStringIsEqualToCString(sourceFileExtension, "aif", true) || charStringIsEqualToCString(sourceFileExtension, "aiff", true)) { result = SAMPLE_SOURCE_TYPE_AIFF; } #endif #if USE_FLAC else if (charStringIsEqualToCString(sourceFileExtension, "flac", true)) { result = SAMPLE_SOURCE_TYPE_FLAC; } #endif else if (charStringIsEqualToCString(sourceFileExtension, "wav", true) || charStringIsEqualToCString(sourceFileExtension, "wave", true)) { result = SAMPLE_SOURCE_TYPE_WAVE; } else { logCritical("Sample source '%s' does not match any supported type", sampleSourceName->data); result = SAMPLE_SOURCE_TYPE_INVALID; } } } freeCharString(sourceFileExtension); return result; }
static PluginPresetType _pluginPresetGuessType(const CharString presetName) { const char* fileExtension; size_t i; if(presetName == NULL || charStringIsEmpty(presetName)) { return PRESET_TYPE_INVALID; } fileExtension = getFileExtension(presetName->data); if(fileExtension == NULL) { for(i = 0; i < strlen(presetName->data); i++) { if(!charStringIsNumber(presetName, i)) { return PRESET_TYPE_INVALID; } } // If the preset name is all numeric, then it's an internal program number return PRESET_TYPE_INTERNAL_PROGRAM; } else if(!strcasecmp(fileExtension, "fxp")) { return PRESET_TYPE_FXP; } else { logCritical("Preset '%s' does not match any supported type", presetName->data); return PRESET_TYPE_INVALID; } }
boolByte errorReporterCopyPlugins(ErrorReporter self, PluginChain pluginChain) { CharString pluginAbsolutePath = NULL; Plugin currentPlugin = NULL; boolByte failed = false; unsigned int i; PlatformInfo platform = newPlatformInfo(); for (i = 0; i < pluginChain->numPlugins; i++) { currentPlugin = pluginChain->plugins[i]; pluginAbsolutePath = currentPlugin->pluginAbsolutePath; if (charStringIsEmpty(pluginAbsolutePath)) { logInfo( "Plugin '%s' does not have an absolute path and could not be copied", currentPlugin->pluginName->data); } else if (platform->type == PLATFORM_MACOSX) { failed |= !_copyDirectoryToErrorReportDir(self, pluginAbsolutePath); } else { failed |= !errorReportCopyFileToReport(self, pluginAbsolutePath); } } freePlatformInfo(platform); return (boolByte)!failed; }
static int _testBuildAbsolutePathNullFile(void) { CharString d = getCurrentDirectory(); CharString out = newCharString(); buildAbsolutePath(d, NULL, NULL, out); assert(charStringIsEmpty(out)); freeCharString(d); freeCharString(out); return 0; }
static int _testBuildAbsolutePathNullPath(void) { CharString f = newCharString(); CharString out = newCharString(); buildAbsolutePath(NULL, f, NULL, out); assert(charStringIsEmpty(out)); freeCharString(f); freeCharString(out); return 0; }
boolByte programOptionsParseConfigFile(ProgramOptions self, const CharString filename) { boolByte result = false; File configFile = NULL; LinkedList configFileLines = NULL; CharString* argvCharStrings; int argc; char** argv; int i; if(filename == NULL || charStringIsEmpty(filename)) { logCritical("Cannot read options from empty filename"); return false; } configFile = newFileWithPath(filename); if(configFile == NULL || configFile->fileType != kFileTypeFile) { logCritical("Cannot read options from non-existent file '%s'", filename->data); freeFile(configFile); return false; } configFileLines = fileReadLines(configFile); if(configFileLines == NULL) { logInternalError("Could not split config file lines"); return false; } else if(linkedListLength(configFileLines) == 0) { logInfo("Config file '%s' is empty", filename->data); freeLinkedList(configFileLines); freeFile(configFile); return true; } else { // Don't need the file anymore, it can be freed here freeFile(configFile); } argvCharStrings = (CharString*)linkedListToArray(configFileLines); argc = linkedListLength(configFileLines); argv = (char**)malloc(sizeof(char*) * (argc + 1)); // Normally this would be the application name, don't care about it here argv[0] = NULL; for(i = 0; i < argc; i++) { argv[i + 1] = argvCharStrings[i]->data; } argc++; result = programOptionsParseArgs(self, argc, argv); freeLinkedListAndItems(configFileLines, (LinkedListFreeItemFunc)freeCharString); free(argvCharStrings); free(argv); return result; }
static int _testBuildAbsolutePathEmptyPath(void) { CharString d = newCharString(); CharString f = newCharStringWithCString(TEST_FILENAME); CharString out = newCharString(); buildAbsolutePath(d, f, NULL, out); assert(charStringIsEmpty(out)); freeCharString(d); freeCharString(f); freeCharString(out); return 0; }
void buildAbsolutePath(const CharString directory, const CharString file, const char* fileExtension, CharString outString) { const char* extension; CharString absoluteDirectory; if(directory == NULL || charStringIsEmpty(directory)) { logWarn("Attempt to build absolute path with empty directory"); return; } if(file == NULL || charStringIsEmpty(file)) { logWarn("Attempt to build absolute path with empty file"); return; } absoluteDirectory = newCharString(); if(isAbsolutePath(directory)) { charStringCopy(absoluteDirectory, directory); } else { convertRelativePathToAbsolute(directory, absoluteDirectory); } if(fileExtension != NULL) { // Ignore attempts to append the same extension as is already on the file extension = getFileExtension(file->data); if(extension != NULL && !strncasecmp(extension, fileExtension, strlen(extension))) { buildAbsolutePath(absoluteDirectory, file, NULL, outString); } else { snprintf(outString->data, outString->length, "%s%c%s.%s", absoluteDirectory->data, PATH_DELIMITER, file->data, fileExtension); } } else { snprintf(outString->data, outString->length, "%s%c%s", absoluteDirectory->data, PATH_DELIMITER, file->data); } freeCharString(absoluteDirectory); }
void setLoggingColorEnabledWithString(const CharString colorSchemeName) { if (charStringIsEmpty(colorSchemeName)) { setLoggingColorEnabled(false); } else if (charStringIsEqualToCString(colorSchemeName, "none", false)) { setLoggingColorEnabled(false); } else if (charStringIsEqualToCString(colorSchemeName, "auto", false)) { setLoggingColorEnabled((boolByte)isatty(1)); } else if (charStringIsEqualToCString(colorSchemeName, "force", false)) { setLoggingColorEnabled(true); } else { // Use critical log level to avoid colors logCritical("Unknown color scheme '%s'", colorSchemeName->data); } }
SampleSourceType sampleSourceGuess(const CharString sampleSourceTypeString) { if(!charStringIsEmpty(sampleSourceTypeString)) { // Look for stdin/stdout if(strlen(sampleSourceTypeString->data) == 1 && sampleSourceTypeString->data[0] == '-') { return SAMPLE_SOURCE_TYPE_PCM; } else { const char* fileExtension = getFileExtension(sampleSourceTypeString->data); // If there is no file extension, then automatically assume raw PCM data. Deal with it! if(fileExtension == NULL) { return SAMPLE_SOURCE_TYPE_PCM; } // Possible file extensions for raw PCM data else if(!strcasecmp(fileExtension, "pcm") || !strcasecmp(fileExtension, "raw") || !strcasecmp(fileExtension, "dat")) { return SAMPLE_SOURCE_TYPE_PCM; } else if(!strcasecmp(fileExtension, "aif") || !strcasecmp(fileExtension, "aiff")) { return SAMPLE_SOURCE_TYPE_AIFF; } #if HAVE_LIBFLAC else if(!strcasecmp(fileExtension, "flac")) { return SAMPLE_SOURCE_TYPE_FLAC; } #endif #if HAVE_LIBLAME else if(!strcasecmp(fileExtension, "mp3")) { return SAMPLE_SOURCE_TYPE_MP3; } #endif #if HAVE_LIBVORBIS else if(!strcasecmp(fileExtension, "ogg")) { return SAMPLE_SOURCE_TYPE_OGG; } #endif else if(!strcasecmp(fileExtension, "wav") || !strcasecmp(fileExtension, "wave")) { return SAMPLE_SOURCE_TYPE_WAVE; } else { logCritical("Sample source '%s' does not match any supported type", sampleSourceTypeString->data); return SAMPLE_SOURCE_TYPE_INVALID; } } } else { return SAMPLE_SOURCE_TYPE_INVALID; } }
void _programOptionPrintDefaultValue(const ProgramOption self) { CharString stringValue; switch(self->type) { case kProgramOptionTypeString: stringValue = _programOptionGetString(self); if(stringValue != NULL && !charStringIsEmpty(stringValue)) { printf(", default value '%s'", stringValue->data); } break; case kProgramOptionTypeNumber: printf(", default value: %.0f", _programOptionGetNumber(self)); break; default: break; } }
boolByte setTimeSignatureFromString(const CharString signature) { char *slash = NULL; unsigned short numerator = 0; unsigned short denominator = 0; if (!charStringIsEmpty(signature)) { slash = strchr(signature->data, '/'); if (slash != NULL) { *slash = '\0'; numerator = (unsigned short)strtod(signature->data, NULL); denominator = (unsigned short)strtod(slash + 1, NULL); if (numerator > 0 && denominator > 0) { return (boolByte)(setTimeSignatureBeatsPerMeasure(numerator) && setTimeSignatureNoteValue(denominator)); } } } return false; }
static PluginInterfaceType _guessPluginInterfaceType(const CharString pluginName, const CharString pluginSearchRoot) { PluginInterfaceType pluginType = PLUGIN_TYPE_INVALID; if(pluginName == NULL || charStringIsEmpty(pluginName)) { logError("Attempt to guess plugin with empty name"); return pluginType; } logDebug("Trying to find plugin '%s'", pluginName->data); if(pluginVst2xExists(pluginName, pluginSearchRoot)) { logInfo("Plugin '%s' is of type VST2.x", pluginName->data); pluginType = PLUGIN_TYPE_VST_2X; } else if(!strncmp(INTERNAL_PLUGIN_PREFIX, pluginName->data, strlen(INTERNAL_PLUGIN_PREFIX))) { logInfo("Plugin '%s' is an internal plugin", pluginName->data); pluginType = PLUGIN_TYPE_INTERNAL; } else { logError("Plugin '%s' could not be found", pluginName->data); } return pluginType; }
int mrsWatsonMain(ErrorReporter errorReporter, int argc, char** argv) { ReturnCodes result; // Input/Output sources, plugin chain, and other required objects SampleSource inputSource = NULL; SampleSource outputSource = NULL; AudioClock audioClock; PluginChain pluginChain; CharString pluginSearchRoot = newCharString(); boolByte shouldDisplayPluginInfo = false; MidiSequence midiSequence = NULL; MidiSource midiSource = NULL; unsigned long maxTimeInMs = 0; unsigned long maxTimeInFrames = 0; unsigned long tailTimeInMs = 0; unsigned long tailTimeInFrames = 0; unsigned long processingDelayInFrames; ProgramOptions programOptions; ProgramOption option; Plugin headPlugin; SampleBuffer inputSampleBuffer = NULL; SampleBuffer outputSampleBuffer = NULL; TaskTimer initTimer, totalTimer, inputTimer, outputTimer = NULL; LinkedList taskTimerList = NULL; CharString totalTimeString = NULL; boolByte finishedReading = false; SampleSource silentSampleInput; SampleSource silentSampleOutput; unsigned int i; initTimer = newTaskTimerWithCString(PROGRAM_NAME, "Initialization"); totalTimer = newTaskTimerWithCString(PROGRAM_NAME, "Total Time"); taskTimerStart(initTimer); taskTimerStart(totalTimer); initEventLogger(); initAudioSettings(); initAudioClock(); audioClock = getAudioClock(); initPluginChain(); pluginChain = getPluginChain(); programOptions = newMrsWatsonOptions(); inputSource = sampleSourceFactory(NULL); if(!programOptionsParseArgs(programOptions, argc, argv)) { printf("Run with '--help' to see possible options\n"); printf("Or run with '--help full' to see extended help for all options\n"); return RETURN_CODE_INVALID_ARGUMENT; } // These options conflict with standard processing (more or less), so check to see if the user wanted one // of these and then exit right away. if(argc == 1) { printf("%s needs at least a plugin, input source, and output source to run.\n\n", PROGRAM_NAME); printMrsWatsonQuickstart(argv[0]); return RETURN_CODE_NOT_RUN; } else if(programOptions->options[OPTION_HELP]->enabled) { printMrsWatsonQuickstart(argv[0]); if(charStringIsEmpty(programOptionsGetString(programOptions, OPTION_HELP))) { printf("All options, where <argument> is required and [argument] is optional:\n"); programOptionsPrintHelp(programOptions, false, DEFAULT_INDENT_SIZE); } else { if(charStringIsEqualToCString(programOptionsGetString(programOptions, OPTION_HELP), "full", true)) { programOptionsPrintHelp(programOptions, true, DEFAULT_INDENT_SIZE); } // Yeah this is a bit silly, but the performance obviously doesn't matter // here and I don't feel like cluttering up this already huge function // with more variables. else if(programOptionsFind(programOptions, programOptionsGetString(programOptions, OPTION_HELP))) { programOptionPrintHelp(programOptionsFind(programOptions, programOptionsGetString(programOptions, OPTION_HELP)), true, DEFAULT_INDENT_SIZE, 0); } else { printf("Invalid option '%s', try running --help full to see help for all options\n", programOptionsGetString(programOptions, OPTION_HELP)->data); } } return RETURN_CODE_NOT_RUN; } else if(programOptions->options[OPTION_VERSION]->enabled) { printVersion(); return RETURN_CODE_NOT_RUN; } else if(programOptions->options[OPTION_COLOR_TEST]->enabled) { printTestPattern(); return RETURN_CODE_NOT_RUN; } // See if we are to make an error report and make necessary changes to the // options for good diagnostics. Note that error reports cannot be generated // for any of the above options which return with RETURN_CODE_NOT_RUN. else if(programOptions->options[OPTION_ERROR_REPORT]->enabled) { errorReporterInitialize(errorReporter); programOptions->options[OPTION_VERBOSE]->enabled = true; programOptions->options[OPTION_LOG_FILE]->enabled = true; programOptions->options[OPTION_DISPLAY_INFO]->enabled = true; // Shell script with original command line arguments errorReporterCreateLauncher(errorReporter, argc, argv); // Rewrite some paths before any input or output sources have been opened. _remapFileToErrorReport(errorReporter, programOptions->options[OPTION_INPUT_SOURCE], true); _remapFileToErrorReport(errorReporter, programOptions->options[OPTION_OUTPUT_SOURCE], false); _remapFileToErrorReport(errorReporter, programOptions->options[OPTION_MIDI_SOURCE], true); _remapFileToErrorReport(errorReporter, programOptions->options[OPTION_LOG_FILE], false); } // Read in options from a configuration file, if given if(programOptions->options[OPTION_CONFIG_FILE]->enabled) { if(!programOptionsParseConfigFile(programOptions, programOptionsGetString(programOptions, OPTION_CONFIG_FILE))) { return RETURN_CODE_INVALID_ARGUMENT; } } // Parse these options first so that log messages displayed in the below // loop are properly displayed if(programOptions->options[OPTION_VERBOSE]->enabled) { setLogLevel(LOG_DEBUG); } else if(programOptions->options[OPTION_QUIET]->enabled) { setLogLevel(LOG_ERROR); } else if(programOptions->options[OPTION_LOG_LEVEL]->enabled) { setLogLevelFromString(programOptionsGetString(programOptions, OPTION_LOG_LEVEL)); } if(programOptions->options[OPTION_COLOR_LOGGING]->enabled) { // If --color was given but with no string argument, then force color. Otherwise // colors will be provided automatically anyways. if(charStringIsEmpty(programOptionsGetString(programOptions, OPTION_COLOR_LOGGING))) { programOptionsSetCString(programOptions, OPTION_COLOR_LOGGING, "force"); } setLoggingColorEnabledWithString(programOptionsGetString(programOptions, OPTION_COLOR_LOGGING)); } if(programOptions->options[OPTION_LOG_FILE]->enabled) { setLogFile(programOptionsGetString(programOptions, OPTION_LOG_FILE)); } // Parse other options and set up necessary objects for(i = 0; i < programOptions->numOptions; i++) { option = programOptions->options[i]; if(option->enabled) { switch(option->index) { case OPTION_BLOCKSIZE: setBlocksize((const unsigned long)programOptionsGetNumber(programOptions, OPTION_BLOCKSIZE)); break; case OPTION_CHANNELS: setNumChannels((const unsigned long)programOptionsGetNumber(programOptions, OPTION_CHANNELS)); break; case OPTION_DISPLAY_INFO: shouldDisplayPluginInfo = true; break; case OPTION_INPUT_SOURCE: freeSampleSource(inputSource); inputSource = sampleSourceFactory(programOptionsGetString(programOptions, OPTION_INPUT_SOURCE)); break; case OPTION_MAX_TIME: maxTimeInMs = (const unsigned long)programOptionsGetNumber(programOptions, OPTION_MAX_TIME); break; case OPTION_MIDI_SOURCE: midiSource = newMidiSource(guessMidiSourceType(programOptionsGetString( programOptions, OPTION_MIDI_SOURCE)), programOptionsGetString(programOptions, OPTION_MIDI_SOURCE)); break; case OPTION_OUTPUT_SOURCE: outputSource = sampleSourceFactory(programOptionsGetString(programOptions, OPTION_OUTPUT_SOURCE)); break; case OPTION_PLUGIN_ROOT: charStringCopy(pluginSearchRoot, programOptionsGetString(programOptions, OPTION_PLUGIN_ROOT)); break; case OPTION_SAMPLE_RATE: setSampleRate(programOptionsGetNumber(programOptions, OPTION_SAMPLE_RATE)); break; case OPTION_TAIL_TIME: tailTimeInMs = (long)programOptionsGetNumber(programOptions, OPTION_TAIL_TIME); break; case OPTION_TEMPO: setTempo(programOptionsGetNumber(programOptions, OPTION_TEMPO)); break; case OPTION_TIME_SIGNATURE: if(!setTimeSignatureFromString(programOptionsGetString(programOptions, OPTION_TIME_SIGNATURE))) { return RETURN_CODE_INVALID_ARGUMENT; } break; case OPTION_ZEBRA_SIZE: setLoggingZebraSize((int)programOptionsGetNumber(programOptions, OPTION_ZEBRA_SIZE)); break; default: // Ignore -- no special handling needs to be performed here break; } } } if(programOptions->options[OPTION_LIST_PLUGINS]->enabled) { listAvailablePlugins(pluginSearchRoot); return RETURN_CODE_NOT_RUN; } if(programOptions->options[OPTION_LIST_FILE_TYPES]->enabled) { sampleSourcePrintSupportedTypes(); return RETURN_CODE_NOT_RUN; } printWelcomeMessage(argc, argv); if((result = setupInputSource(inputSource)) != RETURN_CODE_SUCCESS) { logError("Input source could not be opened, exiting"); return result; } if((result = buildPluginChain(pluginChain, programOptionsGetString(programOptions, OPTION_PLUGIN), pluginSearchRoot)) != RETURN_CODE_SUCCESS) { logError("Plugin chain could not be constructed, exiting"); return result; } if(midiSource != NULL) { result = setupMidiSource(midiSource, &midiSequence); if(result != RETURN_CODE_SUCCESS) { logError("MIDI source could not be opened, exiting"); return result; } } // Copy plugins before they have been opened if(programOptions->options[OPTION_ERROR_REPORT]->enabled) { if(errorReporterShouldCopyPlugins()) { if(!errorReporterCopyPlugins(errorReporter, pluginChain)) { logWarn("Failed copying plugins to error report directory"); } } } // Initialize the plugin chain after the global sample rate has been set result = pluginChainInitialize(pluginChain); if(result != RETURN_CODE_SUCCESS) { logError("Could not initialize plugin chain"); return result; } // Display info for plugins in the chain before checking for valid input/output sources if(shouldDisplayPluginInfo) { pluginChainInspect(pluginChain); } // Execute any parameter changes if(programOptions->options[OPTION_PARAMETER]->enabled) { if(!pluginChainSetParameters(pluginChain, programOptionsGetList(programOptions, OPTION_PARAMETER))) { return RETURN_CODE_INVALID_ARGUMENT; } } // Setup output source here. Having an invalid output source should not cause the program // to exit if the user only wants to list plugins or query info about a chain. if((result = setupOutputSource(outputSource)) != RETURN_CODE_SUCCESS) { logError("Output source could not be opened, exiting"); return result; } // Verify input/output sources. This must be done after the plugin chain is initialized // otherwise the head plugin type is not known, which influences whether we must abort // processing. if(programOptions->options[OPTION_ERROR_REPORT]->enabled) { if(charStringIsEqualToCString(inputSource->sourceName, "-", false) || charStringIsEqualToCString(outputSource->sourceName, "-", false)) { printf("ERROR: Using stdin/stdout is incompatible with --error-report\n"); return RETURN_CODE_NOT_RUN; } if(midiSource != NULL && charStringIsEqualToCString(midiSource->sourceName, "-", false)) { printf("ERROR: MIDI source from stdin is incompatible with --error-report\n"); return RETURN_CODE_NOT_RUN; } } if(outputSource == NULL) { logInternalError("Default output sample source was null"); return RETURN_CODE_INTERNAL_ERROR; } if(inputSource == NULL || inputSource->sampleSourceType == SAMPLE_SOURCE_TYPE_SILENCE) { // If the first plugin in the chain is an instrument, use the silent source as our input and // make sure that there is a corresponding MIDI file headPlugin = pluginChain->plugins[0]; if(headPlugin->pluginType == PLUGIN_TYPE_INSTRUMENT) { if(midiSource == NULL) { // I guess some instruments (like white noise generators etc.) don't necessarily // need MIDI, actually this is most useful for our internal plugins and generators. // Anyways, this should only be a soft warning for those who know what they're doing. logWarn("Plugin chain contains an instrument, but no MIDI source was supplied"); if(maxTimeInMs == 0) { // However, if --max-time wasn't given, then there is effectively no input source // and thus processing would continue forever. That won't work. logError("No valid input source or maximum time, don't know when to stop processing"); return RETURN_CODE_MISSING_REQUIRED_OPTION; } else { // If maximum time was given and there is no other input source, then use silence inputSource = newSampleSourceSilence(); } } } else { logError("Plugin chain contains only effects, but no input source was supplied"); return RETURN_CODE_MISSING_REQUIRED_OPTION; } } inputSampleBuffer = newSampleBuffer(getNumChannels(), getBlocksize()); inputTimer = newTaskTimerWithCString(PROGRAM_NAME, "Input Source"); outputSampleBuffer = newSampleBuffer(getNumChannels(), getBlocksize()); outputTimer = newTaskTimerWithCString(PROGRAM_NAME, "Output Source"); // Initialization is finished, we should be able to free this memory now freeProgramOptions(programOptions); // If a maximum time was given, figure it out here if(maxTimeInMs > 0) { maxTimeInFrames = (unsigned long)(maxTimeInMs * getSampleRate()) / 1000l; } processingDelayInFrames = pluginChainGetProcessingDelay(pluginChain); // Get largest tail time requested by any plugin in the chain tailTimeInMs += pluginChainGetMaximumTailTimeInMs(pluginChain); tailTimeInFrames = (unsigned long)(tailTimeInMs * getSampleRate()) / 1000l + processingDelayInFrames; pluginChainPrepareForProcessing(pluginChain); // Update sample rate on the event logger setLoggingZebraSize((long)getSampleRate()); logInfo("Starting processing input source"); logDebug("Sample rate: %.0f", getSampleRate()); logDebug("Blocksize: %d", getBlocksize()); logDebug("Channels: %d", getNumChannels()); logDebug("Tempo: %.2f", getTempo()); logDebug("Processing delay frames: %lu", processingDelayInFrames); logDebug("Time signature: %d/%d", getTimeSignatureBeatsPerMeasure(), getTimeSignatureNoteValue()); taskTimerStop(initTimer); silentSampleInput = sampleSourceFactory(NULL); silentSampleOutput = sampleSourceFactory(NULL); // Main processing loop while(!finishedReading) { taskTimerStart(inputTimer); finishedReading = !readInput(inputSource, silentSampleInput, inputSampleBuffer, tailTimeInFrames); // TODO: For streaming MIDI, we would need to read in events from source here if(midiSequence != NULL) { LinkedList midiEventsForBlock = newLinkedList(); // MIDI source overrides the value set to finishedReading by the input source finishedReading = !fillMidiEventsFromRange(midiSequence, audioClock->currentFrame, getBlocksize(), midiEventsForBlock); linkedListForeach(midiEventsForBlock, _processMidiMetaEvent, &finishedReading); pluginChainProcessMidi(pluginChain, midiEventsForBlock); freeLinkedList(midiEventsForBlock); } taskTimerStop(inputTimer); if(maxTimeInFrames > 0 && audioClock->currentFrame >= maxTimeInFrames) { logInfo("Maximum time reached, stopping processing after this block"); finishedReading = true; } pluginChainProcessAudio(pluginChain, inputSampleBuffer, outputSampleBuffer); taskTimerStart(outputTimer); if(finishedReading) { outputSampleBuffer->blocksize = inputSampleBuffer->blocksize;//The input buffer size has been adjusted. logDebug("Using buffer size of %d for final block", outputSampleBuffer->blocksize); } writeOutput(outputSource, silentSampleOutput, outputSampleBuffer, processingDelayInFrames); taskTimerStop(outputTimer); advanceAudioClock(audioClock, outputSampleBuffer->blocksize); } // Close file handles for input/output sources silentSampleInput->closeSampleSource(silentSampleInput); silentSampleOutput->closeSampleSource(silentSampleOutput); inputSource->closeSampleSource(inputSource); outputSource->closeSampleSource(outputSource); // Print out statistics about each plugin's time usage // TODO: On windows, the total processing time is stored in clocks and not milliseconds // These values must be converted using the QueryPerformanceFrequency() function audioClockStop(audioClock); taskTimerStop(totalTimer); if(totalTimer->totalTaskTime > 0) { taskTimerList = newLinkedList(); linkedListAppend(taskTimerList, initTimer); linkedListAppend(taskTimerList, inputTimer); linkedListAppend(taskTimerList, outputTimer); for(i = 0; i < pluginChain->numPlugins; i++) { linkedListAppend(taskTimerList, pluginChain->audioTimers[i]); linkedListAppend(taskTimerList, pluginChain->midiTimers[i]); } totalTimeString = taskTimerHumanReadbleString(totalTimer); logInfo("Total processing time %s, approximate breakdown:", totalTimeString->data); linkedListForeach(taskTimerList, _printTaskTime, totalTimer); } else { // Woo-hoo! logInfo("Total processing time <1ms. Either something went wrong, or your computer is smokin' fast!"); } freeTaskTimer(initTimer); freeTaskTimer(inputTimer); freeTaskTimer(outputTimer); freeTaskTimer(totalTimer); freeLinkedList(taskTimerList); freeCharString(totalTimeString); if(midiSequence != NULL) { logInfo("Read %ld MIDI events from %s", midiSequence->numMidiEventsProcessed, midiSource->sourceName->data); } else { logInfo("Read %ld frames from %s", inputSource->numSamplesProcessed / getNumChannels(), inputSource->sourceName->data); } logInfo("Wrote %ld frames to %s", outputSource->numSamplesProcessed / getNumChannels(), outputSource->sourceName->data); // Shut down and free data (will also close open files, plugins, etc) logInfo("Shutting down"); freeSampleSource(inputSource); freeSampleSource(outputSource); freeSampleBuffer(inputSampleBuffer); freeSampleBuffer(outputSampleBuffer); pluginChainShutdown(pluginChain); freePluginChain(pluginChain); if(midiSource != NULL) { freeMidiSource(midiSource); } if(midiSequence != NULL) { freeMidiSequence(midiSequence); } freeAudioSettings(); logInfo("Goodbye!"); freeEventLogger(); freeAudioClock(getAudioClock()); if(errorReporter->started) { errorReporterClose(errorReporter); } freeErrorReporter(errorReporter); return RETURN_CODE_SUCCESS; }