AudioChannelLayout * CreateDefaultAudioChannelLayout(UInt32 channelsPerFrame) { assert(0 < channelsPerFrame); AudioChannelLayout *channelLayout = nullptr; switch(channelsPerFrame) { case 1: channelLayout = CreateChannelLayoutWithTag(kAudioChannelLayoutTag_Mono); break; case 2: channelLayout = CreateChannelLayoutWithTag(kAudioChannelLayoutTag_Stereo); break; case 4: channelLayout = CreateChannelLayoutWithTag(kAudioChannelLayoutTag_Quadraphonic); break; case 6: channelLayout = CreateChannelLayoutWithTag(kAudioChannelLayoutTag_MPEG_5_1_A); break; case 8: channelLayout = CreateChannelLayoutWithTag(kAudioChannelLayoutTag_MPEG_7_1_A); break; default: channelLayout = CreateChannelLayoutWithTag(kAudioChannelLayoutTag_Unknown | channelsPerFrame); break; } return channelLayout; }
bool OggSpeexDecoder::Open(CFErrorRef *error) { if(IsOpen()) { LOGGER_WARNING("org.sbooth.AudioEngine.AudioDecoder.OggSpeex", "Open() called on an AudioDecoder that is already open"); return true; } // Ensure the input source is open if(!mInputSource->IsOpen() && !mInputSource->Open(error)) return false; // Initialize Ogg data struct ogg_sync_init(&mOggSyncState); // Get the ogg buffer for writing char *data = ogg_sync_buffer(&mOggSyncState, READ_SIZE_BYTES); // Read bitstream from input file ssize_t bytesRead = GetInputSource()->Read(data, READ_SIZE_BYTES); if(-1 == bytesRead) { if(error) { CFMutableDictionaryRef errorDictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFStringRef displayName = CreateDisplayNameForURL(mInputSource->GetURL()); CFStringRef errorString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFCopyLocalizedString(CFSTR("The file “%@” could not be read."), ""), displayName); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedDescriptionKey, errorString); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedFailureReasonKey, CFCopyLocalizedString(CFSTR("Read error"), "")); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedRecoverySuggestionKey, CFCopyLocalizedString(CFSTR("Unable to read from the input file."), "")); CFRelease(errorString), errorString = NULL; CFRelease(displayName), displayName = NULL; *error = CFErrorCreate(kCFAllocatorDefault, AudioDecoderErrorDomain, AudioDecoderInputOutputError, errorDictionary); CFRelease(errorDictionary), errorDictionary = NULL; } ogg_sync_destroy(&mOggSyncState); return false; } // Tell the sync layer how many bytes were written to its internal buffer int result = ogg_sync_wrote(&mOggSyncState, bytesRead); if(-1 == result) { if(error) { CFMutableDictionaryRef errorDictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFStringRef displayName = CreateDisplayNameForURL(mInputSource->GetURL()); CFStringRef errorString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFCopyLocalizedString(CFSTR("The file “%@” does not appear to be an Ogg file."), ""), displayName); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedDescriptionKey, errorString); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedFailureReasonKey, CFCopyLocalizedString(CFSTR("Not an Ogg file"), "")); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedRecoverySuggestionKey, CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), "")); CFRelease(errorString), errorString = NULL; CFRelease(displayName), displayName = NULL; *error = CFErrorCreate(kCFAllocatorDefault, AudioDecoderErrorDomain, AudioDecoderFileFormatNotRecognizedError, errorDictionary); CFRelease(errorDictionary), errorDictionary = NULL; } ogg_sync_destroy(&mOggSyncState); return false; } // Turn the data we wrote into an ogg page result = ogg_sync_pageout(&mOggSyncState, &mOggPage); if(1 != result) { if(error) { CFMutableDictionaryRef errorDictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFStringRef displayName = CreateDisplayNameForURL(mInputSource->GetURL()); CFStringRef errorString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFCopyLocalizedString(CFSTR("The file “%@” does not appear to be an Ogg file."), ""), displayName); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedDescriptionKey, errorString); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedFailureReasonKey, CFCopyLocalizedString(CFSTR("Not an Ogg file"), "")); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedRecoverySuggestionKey, CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), "")); CFRelease(errorString), errorString = NULL; CFRelease(displayName), displayName = NULL; *error = CFErrorCreate(kCFAllocatorDefault, AudioDecoderErrorDomain, AudioDecoderFileFormatNotRecognizedError, errorDictionary); CFRelease(errorDictionary), errorDictionary = NULL; } ogg_sync_destroy(&mOggSyncState); return false; } // Initialize the stream and grab the serial number ogg_stream_init(&mOggStreamState, ogg_page_serialno(&mOggPage)); // Get the first Ogg page result = ogg_stream_pagein(&mOggStreamState, &mOggPage); if(0 != result) { if(error) { CFMutableDictionaryRef errorDictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFStringRef displayName = CreateDisplayNameForURL(mInputSource->GetURL()); CFStringRef errorString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFCopyLocalizedString(CFSTR("The file “%@” does not appear to be an Ogg file."), ""), displayName); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedDescriptionKey, errorString); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedFailureReasonKey, CFCopyLocalizedString(CFSTR("Not an Ogg file"), "")); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedRecoverySuggestionKey, CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), "")); CFRelease(errorString), errorString = NULL; CFRelease(displayName), displayName = NULL; *error = CFErrorCreate(kCFAllocatorDefault, AudioDecoderErrorDomain, AudioDecoderFileFormatNotRecognizedError, errorDictionary); CFRelease(errorDictionary), errorDictionary = NULL; } ogg_sync_destroy(&mOggSyncState); return false; } // Get the first packet (should be the header) from the page ogg_packet op; result = ogg_stream_packetout(&mOggStreamState, &op); if(1 != result) { if(error) { CFMutableDictionaryRef errorDictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFStringRef displayName = CreateDisplayNameForURL(mInputSource->GetURL()); CFStringRef errorString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFCopyLocalizedString(CFSTR("The file “%@” does not appear to be an Ogg file."), ""), displayName); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedDescriptionKey, errorString); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedFailureReasonKey, CFCopyLocalizedString(CFSTR("Not an Ogg file"), "")); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedRecoverySuggestionKey, CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), "")); CFRelease(errorString), errorString = NULL; CFRelease(displayName), displayName = NULL; *error = CFErrorCreate(kCFAllocatorDefault, AudioDecoderErrorDomain, AudioDecoderFileFormatNotRecognizedError, errorDictionary); CFRelease(errorDictionary), errorDictionary = NULL; } ogg_sync_destroy(&mOggSyncState); return false; } if(op.bytes >= 5 && !memcmp(op.packet, "Speex", 5)) mSpeexSerialNumber = mOggStreamState.serialno; ++mOggPacketCount; // Convert the packet to the Speex header SpeexHeader *header = speex_packet_to_header((char *)op.packet, static_cast<int>(op.bytes)); if(NULL == header) { if(error) { CFMutableDictionaryRef errorDictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFStringRef displayName = CreateDisplayNameForURL(mInputSource->GetURL()); CFStringRef errorString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFCopyLocalizedString(CFSTR("The file “%@” does not appear to be an Ogg Speex file."), ""), displayName); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedDescriptionKey, errorString); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedFailureReasonKey, CFCopyLocalizedString(CFSTR("Not an Ogg Speex file"), "")); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedRecoverySuggestionKey, CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), "")); CFRelease(errorString), errorString = NULL; CFRelease(displayName), displayName = NULL; *error = CFErrorCreate(kCFAllocatorDefault, AudioDecoderErrorDomain, AudioDecoderFileFormatNotRecognizedError, errorDictionary); CFRelease(errorDictionary), errorDictionary = NULL; } ogg_sync_destroy(&mOggSyncState); return false; } else if(SPEEX_NB_MODES <= header->mode) { if(error) { CFMutableDictionaryRef errorDictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFStringRef displayName = CreateDisplayNameForURL(mInputSource->GetURL()); CFStringRef errorString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFCopyLocalizedString(CFSTR("The Speex mode in the file “%@” is not supported."), ""), displayName); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedDescriptionKey, errorString); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedFailureReasonKey, CFCopyLocalizedString(CFSTR("Unsupported Ogg Speex file mode"), "")); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedRecoverySuggestionKey, CFCopyLocalizedString(CFSTR("This file may have been encoded with a newer version of Speex."), "")); CFRelease(errorString), errorString = NULL; CFRelease(displayName), displayName = NULL; *error = CFErrorCreate(kCFAllocatorDefault, AudioDecoderErrorDomain, AudioDecoderFileFormatNotSupportedError, errorDictionary); CFRelease(errorDictionary), errorDictionary = NULL; } speex_header_free(header), header = NULL; ogg_sync_destroy(&mOggSyncState); return false; } const SpeexMode *mode = speex_lib_get_mode(header->mode); if(mode->bitstream_version != header->mode_bitstream_version) { if(error) { CFMutableDictionaryRef errorDictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFStringRef displayName = CreateDisplayNameForURL(mInputSource->GetURL()); CFStringRef errorString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFCopyLocalizedString(CFSTR("The Speex version in the file “%@” is not supported."), ""), displayName); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedDescriptionKey, errorString); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedFailureReasonKey, CFCopyLocalizedString(CFSTR("Unsupported Ogg Speex file version"), "")); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedRecoverySuggestionKey, CFCopyLocalizedString(CFSTR("This file was encoded with a different version of Speex."), "")); CFRelease(errorString), errorString = NULL; CFRelease(displayName), displayName = NULL; *error = CFErrorCreate(kCFAllocatorDefault, AudioDecoderErrorDomain, AudioDecoderFileFormatNotSupportedError, errorDictionary); CFRelease(errorDictionary), errorDictionary = NULL; } speex_header_free(header), header = NULL; ogg_sync_destroy(&mOggSyncState); return false; } // Initialize the decoder mSpeexDecoder = speex_decoder_init(mode); if(NULL== mSpeexDecoder) { if(error) { CFMutableDictionaryRef errorDictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedDescriptionKey, CFCopyLocalizedString(CFSTR("Unable to initialize the Speex decoder."), "")); // CFDictionarySetValue(errorDictionary, // kCFErrorLocalizedFailureReasonKey, // CFCopyLocalizedString(CFSTR("Unsupported Ogg Speex file version"), "")); // CFDictionarySetValue(errorDictionary, // kCFErrorLocalizedRecoverySuggestionKey, // CFCopyLocalizedString(CFSTR("This file was encoded with a different version of Speex."), "")); *error = CFErrorCreate(kCFAllocatorDefault, AudioDecoderErrorDomain, AudioDecoderInputOutputError, errorDictionary); CFRelease(errorDictionary), errorDictionary = NULL; } speex_header_free(header), header = NULL; ogg_sync_destroy(&mOggSyncState); return false; } speex_decoder_ctl(mSpeexDecoder, SPEEX_SET_SAMPLING_RATE, &header->rate); mSpeexFramesPerOggPacket = (0 == header->frames_per_packet ? 1 : header->frames_per_packet); mExtraSpeexHeaderCount = header->extra_headers; // Initialize the speex bit-packing data structure speex_bits_init(&mSpeexBits); // Initialize the stereo mode mSpeexStereoState = speex_stereo_state_init(); if(2 == header->nb_channels) { SpeexCallback callback; callback.callback_id = SPEEX_INBAND_STEREO; callback.func = speex_std_stereo_request_handler; callback.data = mSpeexStereoState; speex_decoder_ctl(mSpeexDecoder, SPEEX_SET_HANDLER, &callback); } // Canonical Core Audio format mFormat.mFormatID = kAudioFormatLinearPCM; mFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved; mFormat.mBitsPerChannel = 8 * sizeof(float); mFormat.mSampleRate = header->rate; mFormat.mChannelsPerFrame = header->nb_channels; mFormat.mBytesPerPacket = (mFormat.mBitsPerChannel / 8); mFormat.mFramesPerPacket = 1; mFormat.mBytesPerFrame = mFormat.mBytesPerPacket * mFormat.mFramesPerPacket; mFormat.mReserved = 0; // Set up the source format mSourceFormat.mFormatID = 'SPEE'; mSourceFormat.mSampleRate = header->rate; mSourceFormat.mChannelsPerFrame = header->nb_channels; switch(header->nb_channels) { case 1: mChannelLayout = CreateChannelLayoutWithTag(kAudioChannelLayoutTag_Mono); break; case 2: mChannelLayout = CreateChannelLayoutWithTag(kAudioChannelLayoutTag_Stereo); break; } speex_header_free(header), header = NULL; // Allocate the buffer list spx_int32_t speexFrameSize = 0; speex_decoder_ctl(mSpeexDecoder, SPEEX_GET_FRAME_SIZE, &speexFrameSize); mBufferList = AllocateABL(mFormat, speexFrameSize); if(NULL == mBufferList) { if(error) *error = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainPOSIX, ENOMEM, NULL); speex_header_free(header), header = NULL; speex_stereo_state_destroy(mSpeexStereoState), mSpeexStereoState = NULL; speex_decoder_destroy(mSpeexDecoder), mSpeexDecoder = NULL; speex_bits_destroy(&mSpeexBits); ogg_sync_destroy(&mOggSyncState); return false; } for(UInt32 i = 0; i < mBufferList->mNumberBuffers; ++i) mBufferList->mBuffers[i].mDataByteSize = 0; mIsOpen = true; return true; }
bool MonkeysAudioDecoder::Open(CFErrorRef *error) { if(IsOpen()) { LOGGER_WARNING("org.sbooth.AudioEngine.AudioDecoder.MonkeysAudio", "Open() called on an AudioDecoder that is already open"); return true; } // Ensure the input source is open if(!mInputSource->IsOpen() && !mInputSource->Open(error)) return false; mIOInterface = new APEIOInterface(GetInputSource()); int errorCode; mDecompressor = CreateIAPEDecompressEx(mIOInterface, &errorCode); if(nullptr == mDecompressor) { if(error) { CFStringRef description = CFCopyLocalizedString(CFSTR("The file “%@” is not a valid Monkey's Audio file."), ""); CFStringRef failureReason = CFCopyLocalizedString(CFSTR("Not a Monkey's Audio file"), ""); CFStringRef recoverySuggestion = CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), ""); *error = CreateErrorForURL(AudioDecoderErrorDomain, AudioDecoderInputOutputError, description, mInputSource->GetURL(), failureReason, recoverySuggestion); CFRelease(description), description = nullptr; CFRelease(failureReason), failureReason = nullptr; CFRelease(recoverySuggestion), recoverySuggestion = nullptr; } return false; } // The file format mFormat.mFormatID = kAudioFormatLinearPCM; mFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked; mFormat.mBitsPerChannel = static_cast<UInt32>(mDecompressor->GetInfo(APE_INFO_BITS_PER_SAMPLE)); mFormat.mSampleRate = mDecompressor->GetInfo(APE_INFO_SAMPLE_RATE); mFormat.mChannelsPerFrame = static_cast<UInt32>(mDecompressor->GetInfo(APE_INFO_CHANNELS)); mFormat.mBytesPerPacket = (mFormat.mBitsPerChannel / 8) * mFormat.mChannelsPerFrame; mFormat.mFramesPerPacket = 1; mFormat.mBytesPerFrame = mFormat.mBytesPerPacket * mFormat.mFramesPerPacket; mFormat.mReserved = 0; // Set up the source format mSourceFormat.mFormatID = 'APE '; mSourceFormat.mSampleRate = mFormat.mSampleRate; mSourceFormat.mChannelsPerFrame = mFormat.mChannelsPerFrame; switch(mFormat.mChannelsPerFrame) { case 1: mChannelLayout = CreateChannelLayoutWithTag(kAudioChannelLayoutTag_Mono); break; case 2: mChannelLayout = CreateChannelLayoutWithTag(kAudioChannelLayoutTag_Stereo); break; case 4: mChannelLayout = CreateChannelLayoutWithTag(kAudioChannelLayoutTag_Quadraphonic); break; } mIsOpen = true; return true; }
bool TrueAudioDecoder::Open(CFErrorRef *error) { if(IsOpen()) { LOGGER_WARNING("org.sbooth.AudioEngine.AudioDecoder.TrueAudio", "Open() called on an AudioDecoder that is already open"); return true; } // Ensure the input source is open if(!mInputSource->IsOpen() && !mInputSource->Open(error)) return false; mCallbacks = new TTA_io_callback_wrapper; mCallbacks->iocb.read = read_callback; mCallbacks->iocb.write = nullptr; mCallbacks->iocb.seek = seek_callback; mCallbacks->decoder = this; TTA_info streamInfo; try { mDecoder = new tta::tta_decoder((TTA_io_callback *)mCallbacks); mDecoder->init_get_info(&streamInfo, 0); } catch(tta::tta_exception e) { LOGGER_WARNING("org.sbooth.AudioEngine.AudioDecoder.TrueAudio", "Error creating True Audio decoder: " << e.code()); if(mDecoder) delete mDecoder, mDecoder = nullptr; } if(nullptr == mDecoder) { if(error) { CFStringRef description = CFCopyLocalizedString(CFSTR("The file “%@” is not a valid True Audio file."), ""); CFStringRef failureReason = CFCopyLocalizedString(CFSTR("Not a True Audio file"), ""); CFStringRef recoverySuggestion = CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), ""); *error = CreateErrorForURL(AudioDecoderErrorDomain, AudioDecoderInputOutputError, description, mInputSource->GetURL(), failureReason, recoverySuggestion); CFRelease(description), description = nullptr; CFRelease(failureReason), failureReason = nullptr; CFRelease(recoverySuggestion), recoverySuggestion = nullptr; } return false; } mFormat.mFormatID = kAudioFormatLinearPCM; mFormat.mFormatFlags = kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsSignedInteger; mFormat.mSampleRate = streamInfo.sps; mFormat.mChannelsPerFrame = streamInfo.nch; mFormat.mBitsPerChannel = streamInfo.bps; mFormat.mBytesPerPacket = ((streamInfo.bps + 7) / 8) * mFormat.mChannelsPerFrame; mFormat.mFramesPerPacket = 1; mFormat.mBytesPerFrame = mFormat.mBytesPerPacket * mFormat.mFramesPerPacket; mFormat.mReserved = 0; // Support 4 to 32 bits per sample (True Audio may support more or less, but the documentation didn't say) switch(mFormat.mBitsPerChannel) { case 8: case 16: case 24: case 32: mFormat.mFormatFlags |= kAudioFormatFlagIsPacked; break; case 4 ... 7: case 9 ... 15: case 17 ... 23: case 25 ... 31: // Align high because Apple's AudioConverter doesn't handle low alignment mFormat.mFormatFlags |= kAudioFormatFlagIsAlignedHigh; break; default: { LOGGER_ERR("org.sbooth.AudioEngine.AudioDecoder.TrueAudio", "Unsupported bit depth: " << mFormat.mBitsPerChannel) if(error) { CFStringRef description = CFCopyLocalizedString(CFSTR("The file “%@” is not a supported True Audio file."), ""); CFStringRef failureReason = CFCopyLocalizedString(CFSTR("Bit depth not supported"), ""); CFStringRef recoverySuggestion = CFCopyLocalizedString(CFSTR("The file's bit depth is not supported."), ""); *error = CreateErrorForURL(AudioDecoderErrorDomain, AudioDecoderFileFormatNotSupportedError, description, mInputSource->GetURL(), failureReason, recoverySuggestion); CFRelease(description), description = nullptr; CFRelease(failureReason), failureReason = nullptr; CFRelease(recoverySuggestion), recoverySuggestion = nullptr; } delete mDecoder, mDecoder = nullptr; delete mCallbacks, mCallbacks = nullptr; return false; } } // Set up the source format mSourceFormat.mFormatID = 'TTA '; mSourceFormat.mSampleRate = streamInfo.sps; mSourceFormat.mChannelsPerFrame = streamInfo.nch; mSourceFormat.mBitsPerChannel = streamInfo.bps; // Setup the channel layout mChannelLayout = CreateChannelLayoutWithTag(streamInfo.nch); mTotalFrames = streamInfo.samples; mIsOpen = true; return true; }
bool MODDecoder::Open(CFErrorRef *error) { if(IsOpen()) { log4cxx::LoggerPtr logger = log4cxx::Logger::getLogger("org.sbooth.AudioEngine.AudioDecoder.MOD"); LOG4CXX_WARN(logger, "Open() called on an AudioDecoder that is already open"); return true; } // Ensure the input source is open if(!mInputSource->IsOpen() && !mInputSource->Open(error)) return false; dfs.open = NULL; dfs.skip = skip_callback; dfs.getc = getc_callback; dfs.getnc = getnc_callback; dfs.close = close_callback; df = dumbfile_open_ex(this, &dfs); if(NULL == df) { return false; } CFStringRef fileSystemPath = CFURLCopyFileSystemPath(GetURL(), kCFURLPOSIXPathStyle); CFStringRef extension = NULL; CFRange range; if(CFStringFindWithOptionsAndLocale(fileSystemPath, CFSTR("."), CFRangeMake(0, CFStringGetLength(fileSystemPath)), kCFCompareBackwards, CFLocaleGetSystem(), &range)) { extension = CFStringCreateWithSubstring(kCFAllocatorDefault, fileSystemPath, CFRangeMake(range.location + 1, CFStringGetLength(fileSystemPath) - range.location - 1)); } CFRelease(fileSystemPath), fileSystemPath = NULL; if(NULL == extension) { return false; } // Attempt to create the appropriate decoder based on the file's extension if(kCFCompareEqualTo == CFStringCompare(extension, CFSTR("it"), kCFCompareCaseInsensitive)) duh = dumb_read_it(df); else if(kCFCompareEqualTo == CFStringCompare(extension, CFSTR("xm"), kCFCompareCaseInsensitive)) duh = dumb_read_xm(df); else if(kCFCompareEqualTo == CFStringCompare(extension, CFSTR("s3m"), kCFCompareCaseInsensitive)) duh = dumb_read_s3m(df); else if(kCFCompareEqualTo == CFStringCompare(extension, CFSTR("mod"), kCFCompareCaseInsensitive)) duh = dumb_read_mod(df); CFRelease(extension), extension = NULL; if(NULL == duh) { if(error) { CFMutableDictionaryRef errorDictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 32, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFStringRef displayName = CreateDisplayNameForURL(mInputSource->GetURL()); CFStringRef errorString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFCopyLocalizedString(CFSTR("The file “%@” is not a valid MOD file."), ""), displayName); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedDescriptionKey, errorString); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedFailureReasonKey, CFCopyLocalizedString(CFSTR("Not a MOD file"), "")); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedRecoverySuggestionKey, CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), "")); CFRelease(errorString), errorString = NULL; CFRelease(displayName), displayName = NULL; *error = CFErrorCreate(kCFAllocatorDefault, AudioDecoderErrorDomain, AudioDecoderInputOutputError, errorDictionary); CFRelease(errorDictionary), errorDictionary = NULL; } if(df) dumbfile_close(df), df = NULL; return false; } mTotalFrames = duh_get_length(duh); dsr = duh_start_sigrenderer(duh, 0, 2, 0); if(NULL == dsr) { if(error) { CFMutableDictionaryRef errorDictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 32, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFStringRef displayName = CreateDisplayNameForURL(mInputSource->GetURL()); CFStringRef errorString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFCopyLocalizedString(CFSTR("The file “%@” is not a valid MOD file."), ""), displayName); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedDescriptionKey, errorString); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedFailureReasonKey, CFCopyLocalizedString(CFSTR("Not a MOD file"), "")); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedRecoverySuggestionKey, CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), "")); CFRelease(errorString), errorString = NULL; CFRelease(displayName), displayName = NULL; *error = CFErrorCreate(kCFAllocatorDefault, AudioDecoderErrorDomain, AudioDecoderInputOutputError, errorDictionary); CFRelease(errorDictionary), errorDictionary = NULL; } if(df) dumbfile_close(df), df = NULL; if(duh) unload_duh(duh), duh = NULL; return false; } // Generate interleaved 2 channel 44.1 16-bit output mFormat.mFormatID = kAudioFormatLinearPCM; mFormat.mFormatFlags = kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; mFormat.mSampleRate = DUMB_SAMPLE_RATE; mFormat.mChannelsPerFrame = DUMB_CHANNELS; mFormat.mBitsPerChannel = DUMB_BIT_DEPTH; mFormat.mBytesPerPacket = (mFormat.mBitsPerChannel / 8) * mFormat.mChannelsPerFrame; mFormat.mFramesPerPacket = 1; mFormat.mBytesPerFrame = mFormat.mBytesPerPacket * mFormat.mFramesPerPacket; mFormat.mReserved = 0; // Set up the source format mSourceFormat.mFormatID = 'MOD '; mSourceFormat.mSampleRate = DUMB_SAMPLE_RATE; mSourceFormat.mChannelsPerFrame = DUMB_CHANNELS; // Setup the channel layout mChannelLayout = CreateChannelLayoutWithTag(kAudioChannelLayoutTag_Stereo); mIsOpen = true; return true; }
bool WavPackDecoder::Open(CFErrorRef *error) { if(IsOpen()) { LOGGER_WARNING("org.sbooth.AudioEngine.AudioDecoder.WavPack", "Open() called on an AudioDecoder that is already open"); return true; } // Ensure the input source is open if(!mInputSource->IsOpen() && !mInputSource->Open(error)) return false; mStreamReader.read_bytes = read_bytes_callback; mStreamReader.get_pos = get_pos_callback; mStreamReader.set_pos_abs = set_pos_abs_callback; mStreamReader.set_pos_rel = set_pos_rel_callback; mStreamReader.push_back_byte = push_back_byte_callback; mStreamReader.get_length = get_length_callback; mStreamReader.can_seek = can_seek_callback; char errorBuf [80]; // Setup converter mWPC = WavpackOpenFileInputEx(&mStreamReader, this, nullptr, errorBuf, OPEN_WVC | OPEN_NORMALIZE, 0); if(nullptr == mWPC) { if(error) { CFStringRef description = CFCopyLocalizedString(CFSTR("The file “%@” is not a valid WavPack file."), ""); CFStringRef failureReason = CFCopyLocalizedString(CFSTR("Not a WavPack file"), ""); CFStringRef recoverySuggestion = CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), ""); *error = CreateErrorForURL(AudioDecoderErrorDomain, AudioDecoderInputOutputError, description, mInputSource->GetURL(), failureReason, recoverySuggestion); CFRelease(description), description = nullptr; CFRelease(failureReason), failureReason = nullptr; CFRelease(recoverySuggestion), recoverySuggestion = nullptr; } return false; } // Floating-point and lossy files will be handed off in the canonical Core Audio format int mode = WavpackGetMode(mWPC); if(MODE_FLOAT & mode || !(MODE_LOSSLESS & mode)) { // Canonical Core Audio format mFormat.mFormatID = kAudioFormatLinearPCM; mFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved; mFormat.mSampleRate = WavpackGetSampleRate(mWPC); mFormat.mChannelsPerFrame = WavpackGetNumChannels(mWPC); mFormat.mBitsPerChannel = 8 * sizeof(float); mFormat.mBytesPerPacket = (mFormat.mBitsPerChannel / 8); mFormat.mFramesPerPacket = 1; mFormat.mBytesPerFrame = mFormat.mBytesPerPacket * mFormat.mFramesPerPacket; mFormat.mReserved = 0; } else { mFormat.mFormatID = kAudioFormatLinearPCM; mFormat.mFormatFlags = kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsNonInterleaved; // Don't set kAudioFormatFlagIsAlignedHigh for 32-bit integer files mFormat.mFormatFlags |= (32 == WavpackGetBitsPerSample(mWPC) ? kAudioFormatFlagIsPacked : kAudioFormatFlagIsAlignedHigh); mFormat.mSampleRate = WavpackGetSampleRate(mWPC); mFormat.mChannelsPerFrame = WavpackGetNumChannels(mWPC); mFormat.mBitsPerChannel = WavpackGetBitsPerSample(mWPC); mFormat.mBytesPerPacket = sizeof(int32_t); mFormat.mFramesPerPacket = 1; mFormat.mBytesPerFrame = mFormat.mBytesPerPacket * mFormat.mFramesPerPacket; mFormat.mReserved = 0; } mTotalFrames = WavpackGetNumSamples(mWPC); // Set up the source format mSourceFormat.mFormatID = 'WVPK'; mSourceFormat.mSampleRate = WavpackGetSampleRate(mWPC); mSourceFormat.mChannelsPerFrame = WavpackGetNumChannels(mWPC); mSourceFormat.mBitsPerChannel = WavpackGetBitsPerSample(mWPC); // Setup the channel layout switch(mFormat.mChannelsPerFrame) { case 1: mChannelLayout = CreateChannelLayoutWithTag(kAudioChannelLayoutTag_Mono); break; case 2: mChannelLayout = CreateChannelLayoutWithTag(kAudioChannelLayoutTag_Stereo); break; case 4: mChannelLayout = CreateChannelLayoutWithTag(kAudioChannelLayoutTag_Quadraphonic); break; } mBuffer = static_cast<int32_t *>(calloc(BUFFER_SIZE_FRAMES * mFormat.mChannelsPerFrame, sizeof(int32_t))); if(nullptr == mBuffer) { if(error) *error = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainPOSIX, ENOMEM, nullptr); return false; } mIsOpen = true; return true; }
bool WavPackDecoder::Open(CFErrorRef *error) { if(IsOpen()) { log4cxx::LoggerPtr logger = log4cxx::Logger::getLogger("org.sbooth.AudioEngine.AudioDecoder.WavPack"); LOG4CXX_WARN(logger, "Open() called on an AudioDecoder that is already open"); return true; } // Ensure the input source is open if(!mInputSource->IsOpen() && !mInputSource->Open(error)) return false; mStreamReader.read_bytes = read_bytes_callback; mStreamReader.get_pos = get_pos_callback; mStreamReader.set_pos_abs = set_pos_abs_callback; mStreamReader.set_pos_rel = set_pos_rel_callback; mStreamReader.push_back_byte = push_back_byte_callback; mStreamReader.get_length = get_length_callback; mStreamReader.can_seek = can_seek_callback; char errorBuf [80]; // Setup converter mWPC = WavpackOpenFileInputEx(&mStreamReader, this, NULL, errorBuf, OPEN_WVC | OPEN_NORMALIZE, 0); if(NULL == mWPC) { if(error) { CFMutableDictionaryRef errorDictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 32, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFStringRef displayName = CreateDisplayNameForURL(mInputSource->GetURL()); CFStringRef errorString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFCopyLocalizedString(CFSTR("The file “%@” is not a valid WavPack file."), ""), displayName); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedDescriptionKey, errorString); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedFailureReasonKey, CFCopyLocalizedString(CFSTR("Not a WavPack file"), "")); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedRecoverySuggestionKey, CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), "")); CFRelease(errorString), errorString = NULL; CFRelease(displayName), displayName = NULL; *error = CFErrorCreate(kCFAllocatorDefault, AudioDecoderErrorDomain, AudioDecoderInputOutputError, errorDictionary); CFRelease(errorDictionary), errorDictionary = NULL; } return false; } // Floating-point and lossy files will be handed off in the canonical Core Audio format int mode = WavpackGetMode(mWPC); if(MODE_FLOAT & mode || !(MODE_LOSSLESS & mode)) { // Canonical Core Audio format mFormat.mFormatID = kAudioFormatLinearPCM; mFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved; mFormat.mSampleRate = WavpackGetSampleRate(mWPC); mFormat.mChannelsPerFrame = WavpackGetNumChannels(mWPC); mFormat.mBitsPerChannel = 8 * sizeof(float); mFormat.mBytesPerPacket = (mFormat.mBitsPerChannel / 8); mFormat.mFramesPerPacket = 1; mFormat.mBytesPerFrame = mFormat.mBytesPerPacket * mFormat.mFramesPerPacket; mFormat.mReserved = 0; } else { mFormat.mFormatID = kAudioFormatLinearPCM; mFormat.mFormatFlags = kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsAlignedHigh | kAudioFormatFlagIsNonInterleaved; mFormat.mSampleRate = WavpackGetSampleRate(mWPC); mFormat.mChannelsPerFrame = WavpackGetNumChannels(mWPC); mFormat.mBitsPerChannel = WavpackGetBitsPerSample(mWPC); mFormat.mBytesPerPacket = sizeof(int32_t); mFormat.mFramesPerPacket = 1; mFormat.mBytesPerFrame = mFormat.mBytesPerPacket * mFormat.mFramesPerPacket; mFormat.mReserved = 0; } mTotalFrames = WavpackGetNumSamples(mWPC); // Set up the source format mSourceFormat.mFormatID = 'WVPK'; mSourceFormat.mSampleRate = WavpackGetSampleRate(mWPC); mSourceFormat.mChannelsPerFrame = WavpackGetNumChannels(mWPC); mSourceFormat.mBitsPerChannel = WavpackGetBitsPerSample(mWPC); // Setup the channel layout switch(mFormat.mChannelsPerFrame) { case 1: mChannelLayout = CreateChannelLayoutWithTag(kAudioChannelLayoutTag_Mono); break; case 2: mChannelLayout = CreateChannelLayoutWithTag(kAudioChannelLayoutTag_Stereo); break; case 4: mChannelLayout = CreateChannelLayoutWithTag(kAudioChannelLayoutTag_Quadraphonic); break; } mBuffer = static_cast<int32_t *>(calloc(BUFFER_SIZE_FRAMES * mFormat.mChannelsPerFrame, sizeof(int32_t))); if(NULL == mBuffer) { if(error) *error = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainPOSIX, ENOMEM, NULL); return false; } mIsOpen = true; return true; }