SampleSource newSampleSourcePcm(const CharString sampleSourceName) { SampleSource sampleSource = (SampleSource)malloc(sizeof(SampleSourceMembers)); SampleSourcePcmData extraData = (SampleSourcePcmData)malloc(sizeof(SampleSourcePcmDataMembers)); sampleSource->sampleSourceType = SAMPLE_SOURCE_TYPE_PCM; sampleSource->openedAs = SAMPLE_SOURCE_OPEN_NOT_OPENED; sampleSource->sourceName = newCharString(); charStringCopy(sampleSource->sourceName, sampleSourceName); sampleSource->numSamplesProcessed = 0; sampleSource->openSampleSource = openSampleSourcePcm; sampleSource->readSampleBlock = readBlockFromPcmFile; sampleSource->writeSampleBlock = writeBlockToPcmFile; sampleSource->closeSampleSource = _closeSampleSourcePcm; sampleSource->freeSampleSourceData = freeSampleSourceDataPcm; extraData->isStream = false; extraData->isLittleEndian = true; extraData->fileHandle = NULL; extraData->dataBufferNumItems = 0; extraData->interlacedPcmDataBuffer = NULL; extraData->numChannels = (unsigned short)getNumChannels(); extraData->sampleRate = (unsigned int)getSampleRate(); extraData->bitsPerSample = 16; sampleSource->extraData = extraData; return sampleSource; }
SampleSource _newSampleSourceWave(const CharString sampleSourceName) { SampleSource sampleSource = (SampleSource)malloc(sizeof(SampleSourceMembers)); SampleSourcePcmData extraData = (SampleSourcePcmData)malloc(sizeof(SampleSourcePcmDataMembers)); sampleSource->sampleSourceType = SAMPLE_SOURCE_TYPE_WAVE; sampleSource->openedAs = SAMPLE_SOURCE_OPEN_NOT_OPENED; sampleSource->sourceName = newCharString(); charStringCopy(sampleSource->sourceName, sampleSourceName); sampleSource->numSamplesProcessed = 0; sampleSource->openSampleSource = _openSampleSourceWave; sampleSource->readSampleBlock = _readBlockFromWaveFile; sampleSource->writeSampleBlock = _writeBlockToWaveFile; sampleSource->closeSampleSource = _closeSampleSourceWave; sampleSource->freeSampleSourceData = freeSampleSourceDataPcm; extraData->isStream = false; extraData->isLittleEndian = true; extraData->fileHandle = NULL; // Assume default values for these items. However, if an incoming SampleBuffer // has different values for the channel count or blocksize, then we will reassign // based on those values. extraData->dataBufferNumItems = getNumChannels() * getBlocksize(); extraData->pcmSampleBuffer = newPcmSampleBuffer(getNumChannels(), getBlocksize(), getBitDepth()); extraData->numChannels = (unsigned short)getNumChannels(); extraData->sampleRate = (unsigned int)getSampleRate(); extraData->bitDepth = kBitDepthDefault; sampleSource->extraData = extraData; return sampleSource; }
boolByte errorReportCopyFileToReport(ErrorReporter self, CharString path) { boolByte success = false; CharString destination = newCharString(); charStringCopy(destination, path); errorReporterRemapPath(self, destination); success = copyFileToDirectory(path, self->reportDirPath); freeCharString(destination); return success; }
void errorReporterRemapPath(ErrorReporter self, CharString path) { CharString basename = newCharString(); CharString outString = newCharStringWithCapacity(path->capacity); charStringCopyCString(basename, getFileBasename(path->data)); buildAbsolutePath(self->reportDirPath, basename, NULL, outString); charStringCopy(path, outString); freeCharString(basename); freeCharString(outString); }
void getFileDirname(const CharString filename, CharString outString) { const char *lastDelimiter; if(filename == NULL) { return; } lastDelimiter = strrchr(filename->data, PATH_DELIMITER); if(lastDelimiter == NULL) { charStringCopy(outString, filename); } else { strncpy(outString->data, filename->data, lastDelimiter - filename->data); } }
void errorReporterRemapPath(ErrorReporter self, CharString path) { File pathAsFile = newFileWithPath(path); CharString basename = fileGetBasename(pathAsFile); File parent = newFileWithPath(self->reportDirPath); File remappedPath = newFileWithParent(parent, basename); charStringCopy(path, remappedPath->absolutePath); freeCharString(basename); freeFile(parent); freeFile(pathAsFile); freeFile(remappedPath); }
SampleSource newSampleSourceWave(const CharString sampleSourceName) { SampleSource sampleSource = (SampleSource)malloc(sizeof(SampleSourceMembers)); #if HAVE_LIBAUDIOFILE SampleSourceAudiofileData extraData = (SampleSourceAudiofileData)malloc(sizeof(SampleSourceAudiofileDataMembers)); #else SampleSourcePcmData extraData = (SampleSourcePcmData)malloc(sizeof(SampleSourcePcmDataMembers)); #endif sampleSource->sampleSourceType = SAMPLE_SOURCE_TYPE_WAVE; sampleSource->openedAs = SAMPLE_SOURCE_OPEN_NOT_OPENED; sampleSource->sourceName = newCharString(); charStringCopy(sampleSource->sourceName, sampleSourceName); sampleSource->numSamplesProcessed = 0; sampleSource->openSampleSource = _openSampleSourceWave; #if HAVE_LIBAUDIOFILE sampleSource->readSampleBlock = readBlockFromAudiofile; sampleSource->writeSampleBlock = writeBlockToAudiofile; sampleSource->freeSampleSourceData = freeSampleSourceDataAudiofile; sampleSource->closeSampleSource = closeSampleSourceAudiofile; #else sampleSource->readSampleBlock = _readBlockFromWaveFile; sampleSource->writeSampleBlock = _writeBlockToWaveFile; sampleSource->closeSampleSource = closeSampleSourceWave; sampleSource->freeSampleSourceData = freeSampleSourceDataPcm; #endif #if HAVE_LIBAUDIOFILE extraData->fileHandle = NULL; extraData->interlacedBuffer = NULL; extraData->pcmBuffer = NULL; #else extraData->isStream = false; extraData->isLittleEndian = true; extraData->fileHandle = NULL; extraData->dataBufferNumItems = 0; extraData->interlacedPcmDataBuffer = NULL; extraData->numChannels = (unsigned short)getNumChannels(); extraData->sampleRate = (unsigned int)getSampleRate(); extraData->bitsPerSample = 16; #endif sampleSource->extraData = extraData; return sampleSource; }
Plugin newPluginPassthru(const CharString pluginName) { Plugin plugin = _newPlugin(PLUGIN_TYPE_INTERNAL, PLUGIN_TYPE_EFFECT); charStringCopy(plugin->pluginName, pluginName); charStringCopyCString(plugin->pluginLocation, "Internal"); plugin->openPlugin = _pluginPassthruOpen; plugin->displayInfo = _pluginPassthruDisplayInfo; plugin->getSetting = _pluginPassthruGetSetting; plugin->prepareForProcessing = _pluginPassthruEmpty; plugin->processAudio = _pluginPassthruProcessAudio; plugin->processMidiEvents = _pluginPassthruProcessMidiEvents; plugin->setParameter = _pluginPassthruSetParameter; plugin->closePlugin = _pluginPassthruEmpty; plugin->freePluginData = _pluginPassthruEmpty; plugin->extraData = NULL; return plugin; }
MidiSource newMidiSourceFile(const CharString midiSourceName) { MidiSource midiSource = (MidiSource)malloc(sizeof(MidiSourceMembers)); MidiSourceFileData extraData = (MidiSourceFileData)malloc(sizeof(MidiSourceFileDataMembers)); midiSource->midiSourceType = MIDI_SOURCE_TYPE_FILE; midiSource->sourceName = newCharString(); charStringCopy(midiSource->sourceName, midiSourceName); midiSource->openMidiSource = _openMidiSourceFile; midiSource->readMidiEvents = _readMidiEventsFile; midiSource->freeMidiSourceData = _freeMidiEventsFile; extraData->divisionType = TIME_DIVISION_TYPE_INVALID; extraData->fileHandle = NULL; midiSource->extraData = extraData; return midiSource; }
Plugin newPluginSilence(const CharString pluginName) { Plugin plugin = _newPlugin(PLUGIN_TYPE_INTERNAL, PLUGIN_TYPE_INSTRUMENT); charStringCopy(plugin->pluginName, pluginName); charStringCopyCString(plugin->pluginLocation, "Internal"); plugin->openPlugin = _pluginSilenceOpen; plugin->displayInfo = _pluginSilenceDisplayInfo; plugin->getSetting = _pluginSilenceGetSetting; plugin->prepareForProcessing = _pluginSilenceEmpty; plugin->showEditor = _pluginSilenceEmpty; plugin->processAudio = _pluginSilenceProcessAudio; plugin->processMidiEvents = _pluginSilenceProcessMidiEvents; plugin->setParameter = _pluginSilenceSetParameter; plugin->closePlugin = _pluginSilenceEmpty; plugin->freePluginData = _pluginSilenceEmpty; plugin->extraData = NULL; return plugin; }
boolByte errorReportCopyFileToReport(ErrorReporter self, CharString path) { boolByte success; // Copy the destination path so that the original is not modified CharString destination = newCharString(); charStringCopy(destination, path); errorReporterRemapPath(self, destination); File reportDirPath = newFileWithPath(self->reportDirPath); File distinationPath = newFileWithPath(path); File result = fileCopyTo(distinationPath, reportDirPath); success = fileExists(result); freeCharString(destination); freeFile(reportDirPath); freeFile(distinationPath); freeFile(result); return success; }
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); }
Plugin newPluginGain(const CharString pluginName) { Plugin plugin = _newPlugin(PLUGIN_TYPE_INTERNAL, PLUGIN_TYPE_EFFECT); PluginGainSettings settings = (PluginGainSettings)malloc(sizeof(PluginGainSettingsMembers)); charStringCopy(plugin->pluginName, pluginName); charStringCopyCString(plugin->pluginLocation, "Internal"); plugin->openPlugin = _pluginGainOpen; plugin->displayInfo = _pluginGainDisplayInfo; plugin->getSetting = _pluginGainGetSetting; plugin->prepareForProcessing = _pluginGainEmpty; plugin->showEditor = _pluginGainEmpty; plugin->processAudio = _pluginGainProcessAudio; plugin->processMidiEvents = _pluginGainProcessMidiEvents; plugin->setParameter = _pluginGainSetParameter; plugin->closePlugin = _pluginGainEmpty; plugin->freePluginData = _pluginGainEmpty; settings->gain = 1.0f; plugin->extraData = settings; return plugin; }
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; }
static void _programOptionSetString(ProgramOption self, const CharString value) { if(self->type == kProgramOptionTypeString) { charStringCopy(self->_data.string, value); } }
void errorReporterInitialize(ErrorReporter self) { CharString infoText = newCharStringWithCString(kErrorReportInfoText); CharString wrappedInfoText; time_t now; size_t length; size_t i; printf("=== Starting error report ===\n"); wrappedInfoText = charStringWrap(infoText, 0); // The second newline here is intentional printf("%s\n", wrappedInfoText->data); time(&now); self->started = true; snprintf(self->reportName->data, self->reportName->capacity, "MrsWatson Report %s", ctime(&now)); // Trim the final newline character from this string if it exists length = strlen(self->reportName->data); if (self->reportName->data[length - 1] == '\n') { self->reportName->data[length - 1] = '\0'; length--; } for (i = 0; i < length; i++) { if (!(charStringIsLetter(self->reportName, i) || charStringIsNumber(self->reportName, i))) { self->reportName->data[i] = '-'; } } #if UNIX snprintf(self->desktopPath->data, self->desktopPath->capacity, "%s/Desktop", getenv("HOME")); #elif WINDOWS SHGetFolderPathA(NULL, CSIDL_DESKTOPDIRECTORY, NULL, 0, self->desktopPath->data); #endif // Try to place the report on the user's desktop. However, if we cannot find // the desktop (which may occur with localized Linux installations, for instance), // then just dump it in the current directory instead. File desktopPath = newFileWithPath(self->desktopPath); File reportPath; if (!fileExists(desktopPath)) { logWarn("Could not find desktop location, placing error report in current directory instead"); CharString currentDirString = fileGetCurrentDirectory(); File currentDir = newFileWithPath(currentDirString); reportPath = newFileWithParent(currentDir, self->reportName); freeFile(currentDir); freeCharString(currentDirString); } else { reportPath = newFileWithParent(desktopPath, self->reportName); freeFile(desktopPath); } if (fileExists(reportPath)) { logCritical("The path '%s' already contains a previous error report. Please remove the report data and try again."); } else { fileCreate(reportPath, kFileTypeDirectory); } // Now we should have a real error report path charStringCopy(self->reportDirPath, reportPath->absolutePath); freeFile(reportPath); freeCharString(wrappedInfoText); freeCharString(infoText); }