bool AuditLogger::open() { if (-1 != mAuditFd) return true; // @@@ use audit_get_cond() when it's available int acond = au_get_state(); switch (acond) { case AUC_NOAUDIT: return false; case AUC_AUDITING: break; default: logInternalError("error checking auditing status (%d)", acond); UnixError::throwMe(acond); // assume it's a Unix error } if ((mAuditFd = au_open()) < 0) { logInternalError("au_open() failed (%s)", strerror(errno)); UnixError::throwMe(errno); } return true; }
/** * Reads from inputSource. * * @param inputSource The SampleSource to read from. * @param silenceSource The source from where to read silentTailFrames silent tail frames. * @param buffer The SampleBuffer to which the samples will be written. * @param silentTailFrames Number of silent samples that will be provided at the end of inputSource. * @return True if there is more input to read. */ boolByte readInput(SampleSource inputSource, SampleSource silenceSource, SampleBuffer buffer, unsigned long silentTailFrames) { unsigned long framesRead; unsigned long bufferSize = buffer->blocksize; inputSource->readSampleBlock(inputSource, buffer); framesRead = buffer->blocksize; //buffer->blocksize tells how man frames have been read from inputSource, during tail period it will be 0. //We are not done until framesRead < bufferSize and silenceSource->numSamplesProcessed/buffer->numChannels == silentTailFrames if(framesRead == bufferSize) { return true; // more input } else if(framesRead < bufferSize) { unsigned long remainingSilence = silentTailFrames - silenceSource->numSamplesProcessed/buffer->numChannels; // 0 < remainingSilence <= silentTailFrames unsigned long numberOfFrames = remainingSilence < (bufferSize - framesRead) ? remainingSilence : (bufferSize - framesRead); // 0 < numberOfFrames <= bufferSize SampleBuffer silenceBuffer = newSampleBuffer(buffer->numChannels, numberOfFrames); if(!silenceSource->readSampleBlock(silenceSource, silenceBuffer)) { logInternalError("SilentSource does not behave correct."); } buffer->blocksize = framesRead + numberOfFrames; // 0 < buffer->blocksize <= bufferSize. sampleBufferCopyAndMapChannelsWithOffset(buffer, framesRead, silenceBuffer, 0, numberOfFrames); freeSampleBuffer(silenceBuffer); return silenceSource->numSamplesProcessed/buffer->numChannels != silentTailFrames; } else { logInternalError("framesRead > bufferSize"); return false; //stop here. } }
ProgramOption newProgramOptionWithName(const int optionIndex, const char* name, const char* help, boolByte hasShortForm, ProgramOptionType type, ProgramOptionArgumentType argumentType) { ProgramOption option = (ProgramOption)malloc(sizeof(ProgramOptionMembers)); option->index = optionIndex; option->name = newCharStringWithCString(name); option->help = newCharStringWithCString(help); option->hasShortForm = hasShortForm; option->hideInHelp = false; option->type = type; switch(type) { case kProgramOptionTypeEmpty: // Nothing needed here break; case kProgramOptionTypeString: option->_data.string = newCharString(); break; case kProgramOptionTypeNumber: option->_data.number = 0.0f; break; case kProgramOptionTypeList: option->_data.list = newLinkedList(); break; default: logInternalError("ProgramOption with invalid type"); break; } option->argumentType = argumentType; option->enabled = false; return option; }
/** * Writes to outputSource. * * @param outputSource The SampleSource to write to. * @param silenceSource The source from where to write skipHeadFrames frames. * @param buffer The SampleBuffer with the samples to be written. * @param skipHeadFrames Number of frames to ignore before writing to outputSource. */ void writeOutput(SampleSource outputSource, SampleSource silenceSource, SampleBuffer buffer, unsigned long skipHeadFrames) { unsigned long framesSkiped = silenceSource->numSamplesProcessed / buffer->numChannels; unsigned long framesProcessed = framesSkiped + outputSource->numSamplesProcessed / buffer->numChannels; unsigned long nextBlockStart = framesProcessed + buffer->blocksize; if(framesProcessed != getAudioClock()->currentFrame) { logInternalError("framesProcessed (%lu) != getAudioClock()->currentFrame (%lu)", framesProcessed, getAudioClock()->currentFrame); } //Cut the delay at the start if( nextBlockStart <= skipHeadFrames ) { // Cutting away the whole block. nothing is written to the outputSource silenceSource->writeSampleBlock(silenceSource, buffer); } else if(framesProcessed < skipHeadFrames && skipHeadFrames < nextBlockStart) { SampleBuffer sourceBuffer = newSampleBuffer(buffer->numChannels, buffer->blocksize);//blocksize < skipHeadFrames unsigned long skippedFrames = skipHeadFrames - framesProcessed; unsigned long soundFrames = nextBlockStart - skipHeadFrames; // Cutting away start part of the block. sourceBuffer->blocksize = skippedFrames; sampleBufferCopyAndMapChannelsWithOffset(sourceBuffer, 0, buffer, 0, sourceBuffer->blocksize); silenceSource->writeSampleBlock(silenceSource, sourceBuffer); // Writing remaining end part of the block. sourceBuffer->blocksize = soundFrames; sampleBufferCopyAndMapChannelsWithOffset(sourceBuffer, 0, buffer, skippedFrames, sourceBuffer->blocksize); outputSource->writeSampleBlock(outputSource, sourceBuffer); freeSampleBuffer(sourceBuffer); } else { // skipHeadFrames <= framesProcessed // Normal case: Nothing more to cut. The whole block shall be written. outputSource->writeSampleBlock(outputSource, buffer); } }
void AuditLogger::writeToken(token_t *token, const char *name) { const char *tokenName = name ? name : "<unidentified>"; if (NULL == token) { logInternalError("Invalid '%s' token", tokenName); close(); UnixError::throwMe(EPERM); // per audit_submit() } if (au_write(mAuditFd, token) < 0) { logInternalError("Error writing '%s' token (%s)", tokenName, strerror(errno)); close(); UnixError::throwMe(errno); } }
static boolByte _openSampleSourceAiff(void *sampleSourcePtr, const SampleSourceOpenAs openAs) { SampleSource sampleSource = (SampleSource)sampleSourcePtr; #if HAVE_LIBAUDIOFILE SampleSourceAudiofileData extraData = (SampleSourceAudiofileData)(sampleSource->extraData); #else SampleSourcePcmData extraData = (SampleSourcePcmData)(sampleSource->extraData); #endif if(openAs == SAMPLE_SOURCE_OPEN_READ) { #if HAVE_LIBAUDIOFILE extraData->fileHandle = afOpenFile(sampleSource->sourceName->data, "r", NULL); if(extraData->fileHandle != NULL) { setNumChannels(afGetVirtualChannels(extraData->fileHandle, AF_DEFAULT_TRACK)); setSampleRate((float)afGetRate(extraData->fileHandle, AF_DEFAULT_TRACK)); } #else logInternalError("Executable was not built with a library to read AIFF files"); #endif } else if(openAs == SAMPLE_SOURCE_OPEN_WRITE) { #if HAVE_LIBAUDIOFILE AFfilesetup outfileSetup = afNewFileSetup(); afInitFileFormat(outfileSetup, AF_FILE_AIFF); afInitByteOrder(outfileSetup, AF_DEFAULT_TRACK, AF_BYTEORDER_BIGENDIAN); afInitChannels(outfileSetup, AF_DEFAULT_TRACK, getNumChannels()); afInitRate(outfileSetup, AF_DEFAULT_TRACK, getSampleRate()); afInitSampleFormat(outfileSetup, AF_DEFAULT_TRACK, AF_SAMPFMT_TWOSCOMP, DEFAULT_BITRATE); extraData->fileHandle = afOpenFile(sampleSource->sourceName->data, "w", outfileSetup); #else logInternalError("Executable was not built with a library to write AIFF files"); #endif } else { logInternalError("Invalid type for openAs in AIFF file"); return false; } if(extraData->fileHandle == NULL) { logError("AIFF file '%s' could not be opened for '%s'", sampleSource->sourceName->data, openAs == SAMPLE_SOURCE_OPEN_READ ? "reading" : "writing"); return false; } sampleSource->openedAs = openAs; return true; }
boolByte sampleBufferCopyAndMapChannels(SampleBuffer self, const SampleBuffer buffer) { // Definitely not supported, otherwise it would be hard to deal with partial // copies and so forth. if(self->blocksize != buffer->blocksize) { logInternalError("Source and destination buffer are not the same size"); return false; } return sampleBufferCopyAndMapChannelsWithOffset(self, 0, buffer, 0, self->blocksize); }
boolByte sampleBufferCopyAndMapChannelsWithOffset(SampleBuffer destinationBuffer, unsigned long destinationOffset, const SampleBuffer sourceBuffer, unsigned long sourceOffset, unsigned long numberOfFrames) { unsigned int i; // Definitely not supported. if(destinationBuffer->blocksize < destinationOffset + numberOfFrames) { logInternalError("Destination buffer size %d < %d", destinationBuffer->blocksize, destinationOffset + numberOfFrames); return false; } // Definitely not supported. if(sourceBuffer->blocksize < sourceOffset + numberOfFrames) { logInternalError("Source buffer size %d < %d", sourceBuffer->blocksize, sourceOffset + numberOfFrames); return false; } if(sourceBuffer->numChannels != destinationBuffer->numChannels) { logDebug("Mapping channels from %d -> %d", sourceBuffer->numChannels, destinationBuffer->numChannels); } // If the other buffer is bigger (or the same size) as this buffer, then only // copy up to the channel count of this buffer. Any other data will be lost, // sorry about that! if(sourceBuffer->numChannels >= destinationBuffer->numChannels) { for(i = 0; i < destinationBuffer->numChannels; i++) { memcpy(destinationBuffer->samples[i] + destinationOffset, sourceBuffer->samples[i] + sourceOffset, sizeof(Sample) * numberOfFrames); } } // But if this buffer is bigger than the other buffer, then copy all channels // to this one. For example, if this buffer is 4 channels and the other buffer // is 2 channels, then we copy the stereo pair to this channel (L R L R). else { for(i = 0; i < destinationBuffer->numChannels; i++) { if(sourceBuffer->numChannels > 0) { memcpy(destinationBuffer->samples[i] + destinationOffset, sourceBuffer->samples[i % sourceBuffer->numChannels] + sourceOffset, sizeof(Sample) * numberOfFrames); } else { // If the other buffer has zero channels just clear this buffer. memset(destinationBuffer->samples[i] + destinationOffset, 0, sizeof(Sample) * numberOfFrames); } } } return true; }
static const char* _logTimeColor(const LogColorScheme colorScheme) { if(colorScheme == COLOR_SCHEME_DARK) { return ANSI_COLOR_CYAN; } else if(colorScheme == COLOR_SCHEME_LIGHT) { return ANSI_COLOR_GREEN; } else { logInternalError("Invalid color scheme for status char"); return ANSI_COLOR_WHITE; } }
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 const char* _logTimeZebraStripeColor(const long elapsedTime, const LogColorScheme colorScheme, const int zebraSizeInMs) { boolByte zebraState = (boolByte)((elapsedTime / zebraSizeInMs) % 2); if(colorScheme == COLOR_SCHEME_DARK) { return zebraState ? ANSI_COLOR_WHITE : ANSI_COLOR_YELLOW; } else if(colorScheme == COLOR_SCHEME_LIGHT) { return zebraState ? ANSI_COLOR_BLACK : ANSI_COLOR_BLUE; } else { logInternalError("Invalid color scheme for stripe color"); return ANSI_COLOR_WHITE; } }
const char *_getPlatformVstDirName(const PlatformInfo platform) { switch (platform->type) { case PLATFORM_LINUX: return platform->is64BitRuntime ? "Linux-x86_64" : "Linux-i686"; case PLATFORM_MACOSX: return "Mac OS X"; case PLATFORM_WINDOWS: return platform->is64BitRuntime ? "Windows 64-bit" : "Windows 32-bit"; default: logInternalError("No VST resource directory for platform"); return NULL; } }
void AuditLogger::close(bool writeLog/* = true*/) { if (-1 != mAuditFd) { int keep = writeLog == true ? AU_TO_WRITE : AU_TO_NO_WRITE; int error = au_close(mAuditFd, keep, mEvent); mAuditFd = -1; if (writeLog == true && error < 0) { logInternalError("au_close() failed; record not committed"); UnixError::throwMe(error); } } }
static boolByte _fillOptionArgument(ProgramOption self, int* currentArgc, int argc, char** argv) { if(self->argumentType == kProgramOptionArgumentTypeNone) { return true; } else if(self->argumentType == kProgramOptionArgumentTypeOptional) { int potentialNextArgc = *currentArgc + 1; if(potentialNextArgc >= argc) { return true; } else { char* potentialNextArg = argv[potentialNextArgc]; // If the next string in the sequence is NOT an argument, we assume it is the optional argument if(!_isStringShortOption(potentialNextArg) && !_isStringLongOption(potentialNextArg)) { _programOptionSetData(self, potentialNextArg); (*currentArgc)++; return true; } else { // Otherwise, it is another option, but that's ok return true; } } } else if(self->argumentType == kProgramOptionArgumentTypeRequired) { int nextArgc = *currentArgc + 1; if(nextArgc >= argc) { logCritical("Option '%s' requires an argument, but none was given", self->name->data); return false; } else { char* nextArg = argv[nextArgc]; if(_isStringShortOption(nextArg) || _isStringLongOption(nextArg)) { logCritical("Option '%s' requires an argument, but '%s' is not valid", self->name->data, nextArg); return false; } else { _programOptionSetData(self, nextArg); (*currentArgc)++; return true; } } } else { logInternalError("Unknown argument type '%d'", self->argumentType); return false; } }
boolByte fillMidiEventsFromRange(MidiSequence self, const unsigned long startTimestamp, const unsigned long blocksize, LinkedList outMidiEvents) { MidiEvent midiEvent; LinkedListIterator iterator = self->_lastEvent; const unsigned long stopTimestamp = startTimestamp + blocksize; while (true) { if ((iterator == NULL) || (iterator->item == NULL)) { return false; } midiEvent = iterator->item; if (stopTimestamp < midiEvent->timestamp) { // We have not yet reached this event, stop iterating break; } else if (startTimestamp <= midiEvent->timestamp && stopTimestamp > midiEvent->timestamp) { midiEvent->deltaFrames = midiEvent->timestamp - startTimestamp; logDebug("Scheduling MIDI event 0x%x (%x, %x) in %ld frames", midiEvent->status, midiEvent->data1, midiEvent->data2, midiEvent->deltaFrames); linkedListAppend(outMidiEvents, midiEvent); self->_lastEvent = iterator->nextItem; self->numMidiEventsProcessed++; } else if (startTimestamp > midiEvent->timestamp) { logInternalError("Inconsistent MIDI sequence ordering"); } // Last item in the list if (iterator->nextItem == NULL) { if (startTimestamp <= midiEvent->timestamp && stopTimestamp > midiEvent->timestamp) { return false; } break; } iterator = iterator->nextItem; } return true; }
static short _getMonthNumber(const char* abbreviatedMonthName) { if(!strncmp(abbreviatedMonthName, "Jan", 3)) return 1; else if(!strncmp(abbreviatedMonthName, "Feb", 3)) return 2; else if(!strncmp(abbreviatedMonthName, "Mar", 3)) return 3; else if(!strncmp(abbreviatedMonthName, "Apr", 3)) return 4; else if(!strncmp(abbreviatedMonthName, "May", 3)) return 5; else if(!strncmp(abbreviatedMonthName, "Jun", 3)) return 6; else if(!strncmp(abbreviatedMonthName, "Jul", 3)) return 7; else if(!strncmp(abbreviatedMonthName, "Aug", 3)) return 8; else if(!strncmp(abbreviatedMonthName, "Sep", 3)) return 9; else if(!strncmp(abbreviatedMonthName, "Oct", 3)) return 10; else if(!strncmp(abbreviatedMonthName, "Nov", 3)) return 11; else if(!strncmp(abbreviatedMonthName, "Dec", 3)) return 12; else { logInternalError("Invalid build month '%s'", abbreviatedMonthName); return 0; } }
static boolByte _openSampleSourceWave(void *sampleSourcePtr, const SampleSourceOpenAs openAs) { SampleSource sampleSource = (SampleSource)sampleSourcePtr; SampleSourcePcmData extraData = (SampleSourcePcmData)sampleSource->extraData; if (openAs == SAMPLE_SOURCE_OPEN_READ) { extraData->fileHandle = fopen(sampleSource->sourceName->data, "rb"); if (extraData->fileHandle != NULL) { if (_readWaveFileInfo(sampleSource->sourceName->data, extraData)) { setNumChannels(extraData->numChannels); setSampleRate(extraData->sampleRate); } else { fclose(extraData->fileHandle); extraData->fileHandle = NULL; } } } else if (openAs == SAMPLE_SOURCE_OPEN_WRITE) { extraData->fileHandle = fopen(sampleSource->sourceName->data, "wb"); if (extraData->fileHandle != NULL) { extraData->numChannels = (unsigned short)getNumChannels(); extraData->sampleRate = (unsigned int)getSampleRate(); extraData->bitDepth = getBitDepth(); if (!_writeWaveFileInfo(extraData)) { fclose(extraData->fileHandle); extraData->fileHandle = NULL; } } } else { logInternalError("Invalid type for openAs in WAVE file"); return false; } if (extraData->fileHandle == NULL) { logError("WAVE file '%s' could not be opened for %s", sampleSource->sourceName->data, openAs == SAMPLE_SOURCE_OPEN_READ ? "reading" : "writing"); return false; } sampleSource->openedAs = openAs; return true; }
static void _programOptionSetData(ProgramOption self, const char* data) { if(data == NULL) { return; } switch(self->type) { case kProgramOptionTypeString: _programOptionSetCString(self, data); break; case kProgramOptionTypeNumber: // Windows doesn't do strtof :( _programOptionSetNumber(self, (float)strtod(data, NULL)); break; case kProgramOptionTypeList: _programOptionSetListItem(self, (void*)data); break; default: logInternalError("Set ProgramOption with invalid type"); break; } }
static boolByte openSampleSourcePcm(void* sampleSourcePtr, const SampleSourceOpenAs openAs) { SampleSource sampleSource = (SampleSource)sampleSourcePtr; SampleSourcePcmData extraData = (SampleSourcePcmData)(sampleSource->extraData); extraData->dataBufferNumItems = 0; if(openAs == SAMPLE_SOURCE_OPEN_READ) { if(charStringIsEqualToCString(sampleSource->sourceName, "-", false)) { extraData->fileHandle = stdin; charStringCopyCString(sampleSource->sourceName, "stdin"); extraData->isStream = true; } else { extraData->fileHandle = fopen(sampleSource->sourceName->data, "rb"); } } else if(openAs == SAMPLE_SOURCE_OPEN_WRITE) { if(charStringIsEqualToCString(sampleSource->sourceName, "-", false)) { extraData->fileHandle = stdout; charStringCopyCString(sampleSource->sourceName, "stdout"); extraData->isStream = true; } else { extraData->fileHandle = fopen(sampleSource->sourceName->data, "wb"); } } else { logInternalError("Invalid type for openAs in PCM file"); return false; } if(extraData->fileHandle == NULL) { logError("PCM File '%s' could not be opened for %s", sampleSource->sourceName->data, openAs == SAMPLE_SOURCE_OPEN_READ ? "reading" : "writing"); return false; } sampleSource->openedAs = openAs; return true; }
static const char* _logLevelStatusColor(const LogLevel logLevel, const LogColorScheme colorScheme) { if(colorScheme == COLOR_SCHEME_DARK) { switch(logLevel) { case LOG_DEBUG: return ANSI_COLOR_WHITE; case LOG_INFO: return ANSI_COLOR_GREEN; case LOG_WARN: return ANSI_COLOR_MAGENTA; case LOG_ERROR: return ANSI_COLOR_RED; default: return ANSI_COLOR_WHITE; } } else if(colorScheme == COLOR_SCHEME_LIGHT) { switch(logLevel) { case LOG_DEBUG: return ANSI_COLOR_BLACK; case LOG_INFO: return ANSI_COLOR_GREEN; case LOG_WARN: return ANSI_COLOR_MAGENTA; case LOG_ERROR: return ANSI_COLOR_RED; default: return ANSI_COLOR_WHITE; } } else { logInternalError("Invalid color scheme for status char"); return ANSI_COLOR_WHITE; } }
static boolByte _readMidiFileTrack(FILE *midiFile, const int trackNumber, const int timeDivision, const MidiFileTimeDivisionType divisionType, MidiSequence midiSequence) { unsigned int numBytesBuffer; byte *trackData, *currentByte, *endByte; size_t itemsRead, numBytes; unsigned long currentTimeInSampleFrames = 0; unsigned long unpackedVariableLength; MidiEvent midiEvent = NULL; unsigned int i; if (!_readMidiFileChunkHeader(midiFile, "MTrk")) { return false; } itemsRead = fread(&numBytesBuffer, sizeof(unsigned int), 1, midiFile); if (itemsRead < 1) { logError("Short read of MIDI file (at track %d header, num items)", trackNumber); return false; } // Read in the entire track in one pass and parse the events from the buffer data. Much easier // than having to call fread() for each event. numBytes = (size_t)convertBigEndianIntToPlatform(numBytesBuffer); trackData = (byte *)malloc(numBytes); itemsRead = fread(trackData, 1, numBytes, midiFile); if (itemsRead != numBytes) { logError("Short read of MIDI file (at track %d)", trackNumber); free(trackData); return false; } currentByte = trackData; endByte = trackData + numBytes; while (currentByte < endByte) { // Unpack variable length timestamp unpackedVariableLength = *currentByte; if (unpackedVariableLength & 0x80) { unpackedVariableLength &= 0x7f; do { unpackedVariableLength = (unpackedVariableLength << 7) + (*(++currentByte) & 0x7f); } while (*currentByte & 0x80); } currentByte++; freeMidiEvent(midiEvent); midiEvent = newMidiEvent(); switch (*currentByte) { case 0xff: midiEvent->eventType = MIDI_TYPE_META; currentByte++; midiEvent->status = *(currentByte++); numBytes = *(currentByte++); midiEvent->extraData = (byte *)malloc(numBytes); for (i = 0; i < numBytes; i++) { midiEvent->extraData[i] = *(currentByte++); } break; case 0x7f: logUnsupportedFeature("MIDI files containing sysex events"); free(trackData); freeMidiEvent(midiEvent); return false; default: midiEvent->eventType = MIDI_TYPE_REGULAR; midiEvent->status = *currentByte++; midiEvent->data1 = *currentByte++; // All regular MIDI events have 3 bytes except for program change and channel aftertouch if (!((midiEvent->status & 0xf0) == 0xc0 || (midiEvent->status & 0xf0) == 0xd0)) { midiEvent->data2 = *currentByte++; } break; } switch (divisionType) { case TIME_DIVISION_TYPE_TICKS_PER_BEAT: { double ticksPerSecond = (double)timeDivision * getTempo() / 60.0; double sampleFramesPerTick = getSampleRate() / ticksPerSecond; currentTimeInSampleFrames += (long)(unpackedVariableLength * sampleFramesPerTick); } break; case TIME_DIVISION_TYPE_FRAMES_PER_SECOND: // Actually, this should be caught when parsing the file type logUnsupportedFeature("Time division frames/sec"); free(trackData); freeMidiEvent(midiEvent); return false; case TIME_DIVISION_TYPE_INVALID: default: logInternalError("Invalid time division type"); free(trackData); freeMidiEvent(midiEvent); return false; } midiEvent->timestamp = currentTimeInSampleFrames; if (midiEvent->eventType == MIDI_TYPE_META) { switch (midiEvent->status) { case MIDI_META_TYPE_TEXT: case MIDI_META_TYPE_COPYRIGHT: case MIDI_META_TYPE_SEQUENCE_NAME: case MIDI_META_TYPE_INSTRUMENT: case MIDI_META_TYPE_LYRIC: case MIDI_META_TYPE_MARKER: case MIDI_META_TYPE_CUE_POINT: // This event type could theoretically be supported, as long as the // plugin supports it case MIDI_META_TYPE_PROGRAM_NAME: case MIDI_META_TYPE_DEVICE_NAME: case MIDI_META_TYPE_KEY_SIGNATURE: case MIDI_META_TYPE_PROPRIETARY: logDebug("Ignoring MIDI meta event of type 0x%x at %ld", midiEvent->status, midiEvent->timestamp); break; case MIDI_META_TYPE_TEMPO: case MIDI_META_TYPE_TIME_SIGNATURE: case MIDI_META_TYPE_TRACK_END: logDebug("Parsed MIDI meta event of type 0x%02x at %ld", midiEvent->status, midiEvent->timestamp); appendMidiEventToSequence(midiSequence, midiEvent); midiEvent = NULL; break; default: logWarn("Ignoring MIDI meta event of type 0x%x at %ld", midiEvent->status, midiEvent->timestamp); break; } } else { logDebug("MIDI event of type 0x%02x parsed at %ld", midiEvent->status, midiEvent->timestamp); appendMidiEventToSequence(midiSequence, midiEvent); midiEvent = NULL; } } free(trackData); freeMidiEvent(midiEvent); return true; }
static boolByte _openSampleSourceWave(void *sampleSourcePtr, const SampleSourceOpenAs openAs) { SampleSource sampleSource = (SampleSource)sampleSourcePtr; #if HAVE_LIBAUDIOFILE SampleSourceAudiofileData extraData = sampleSource->extraData; #else SampleSourcePcmData extraData = (SampleSourcePcmData)sampleSource->extraData; #endif if(openAs == SAMPLE_SOURCE_OPEN_READ) { #if HAVE_LIBAUDIOFILE extraData->fileHandle = afOpenFile(sampleSource->sourceName->data, "r", NULL); if(extraData->fileHandle != NULL) { setNumChannels(afGetVirtualChannels(extraData->fileHandle, AF_DEFAULT_TRACK)); setSampleRate((float)afGetRate(extraData->fileHandle, AF_DEFAULT_TRACK)); } #else extraData->fileHandle = fopen(sampleSource->sourceName->data, "rb"); if(extraData->fileHandle != NULL) { if(_readWaveFileInfo(sampleSource->sourceName->data, extraData)) { setNumChannels(extraData->numChannels); setSampleRate(extraData->sampleRate); } else { fclose(extraData->fileHandle); extraData->fileHandle = NULL; } } #endif } else if(openAs == SAMPLE_SOURCE_OPEN_WRITE) { #if HAVE_LIBAUDIOFILE AFfilesetup outfileSetup = afNewFileSetup(); afInitFileFormat(outfileSetup, AF_FILE_WAVE); afInitByteOrder(outfileSetup, AF_DEFAULT_TRACK, AF_BYTEORDER_LITTLEENDIAN); afInitChannels(outfileSetup, AF_DEFAULT_TRACK, getNumChannels()); afInitRate(outfileSetup, AF_DEFAULT_TRACK, getSampleRate()); afInitSampleFormat(outfileSetup, AF_DEFAULT_TRACK, AF_SAMPFMT_TWOSCOMP, DEFAULT_BITRATE); extraData->fileHandle = afOpenFile(sampleSource->sourceName->data, "w", outfileSetup); #else extraData->fileHandle = fopen(sampleSource->sourceName->data, "wb"); if(extraData->fileHandle != NULL) { extraData->numChannels = (unsigned short)getNumChannels(); extraData->sampleRate = (unsigned int)getSampleRate(); extraData->bitsPerSample = 16; if(!_writeWaveFileInfo(extraData)) { fclose(extraData->fileHandle); extraData->fileHandle = NULL; } } #endif } else { logInternalError("Invalid type for openAs in WAVE file"); return false; } if(extraData->fileHandle == NULL) { logError("WAVE file '%s' could not be opened for %s", sampleSource->sourceName->data, openAs == SAMPLE_SOURCE_OPEN_READ ? "reading" : "writing"); return false; } sampleSource->openedAs = openAs; return true; }
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; }