From e0e6518825c2546b2b002378e66d7646b6a69183 Mon Sep 17 00:00:00 2001 From: ragnaringi Date: Fri, 24 Aug 2018 18:11:26 +0000 Subject: [PATCH 01/18] Temp --- ESMusicExtractor.xcodeproj/project.pbxproj | 14 +- src/ESExtractor.mm | 10 +- src/internal/audioloader_ios.cpp | 33 +- src/internal/audioloader_ios.h | 14 - src/internal/audioloader_ios_native.cpp | 788 +++++++++++++++++++++ src/internal/audioloader_ios_native.h | 241 +++++++ 6 files changed, 1051 insertions(+), 49 deletions(-) create mode 100644 src/internal/audioloader_ios_native.cpp create mode 100755 src/internal/audioloader_ios_native.h diff --git a/ESMusicExtractor.xcodeproj/project.pbxproj b/ESMusicExtractor.xcodeproj/project.pbxproj index 63762fb..18eee95 100644 --- a/ESMusicExtractor.xcodeproj/project.pbxproj +++ b/ESMusicExtractor.xcodeproj/project.pbxproj @@ -493,6 +493,7 @@ C0245CD4212DBC6C00DBFEA3 /* extractor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C01377DB211A06090046020A /* extractor.cpp */; }; C0245CD5212DBC7200DBFEA3 /* keyextractor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C01377DF211A06090046020A /* keyextractor.cpp */; }; C0245CD6212DBC7500DBFEA3 /* levelextractor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C01377E1211A06090046020A /* levelextractor.cpp */; }; + C037276621304F4D00E9C15C /* audioloader_ios_native.h in Headers */ = {isa = PBXBuildFile; fileRef = C037276421304F4C00E9C15C /* audioloader_ios_native.h */; }; C088C619211B54C3009ED9DB /* taglib_config.h in Headers */ = {isa = PBXBuildFile; fileRef = C088C618211B54C3009ED9DB /* taglib_config.h */; }; C088CB10211B561C009ED9DB /* checked.h in Headers */ = {isa = PBXBuildFile; fileRef = C088C961211B561C009ED9DB /* checked.h */; }; C088CB11211B561C009ED9DB /* core.h in Headers */ = {isa = PBXBuildFile; fileRef = C088C962211B561C009ED9DB /* core.h */; }; @@ -1344,6 +1345,8 @@ C0137A36211A06090046020A /* tnt_version.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tnt_version.h; sourceTree = ""; }; C0138400211A06BD0046020A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; C0138401211A06BD0046020A /* ESMusicExtractor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ESMusicExtractor.h; sourceTree = ""; }; + C037276321304F4C00E9C15C /* audioloader_ios_native.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = audioloader_ios_native.cpp; sourceTree = ""; }; + C037276421304F4C00E9C15C /* audioloader_ios_native.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = audioloader_ios_native.h; sourceTree = ""; }; C088C618211B54C3009ED9DB /* taglib_config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = taglib_config.h; sourceTree = ""; }; C088C961211B561C009ED9DB /* checked.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = checked.h; sourceTree = ""; }; C088C962211B561C009ED9DB /* core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = core.h; sourceTree = ""; }; @@ -2912,12 +2915,14 @@ C0B559FD211B27D0007817F6 /* internal */ = { isa = PBXGroup; children = ( + C0B55A02211B27D0007817F6 /* music_extractor */, C088C618211B54C3009ED9DB /* taglib_config.h */, C0B559FE211B27D0007817F6 /* audioloader_ios.cpp */, C0B559FF211B27D0007817F6 /* audioloader_ios.h */, + C037276321304F4C00E9C15C /* audioloader_ios_native.cpp */, + C037276421304F4C00E9C15C /* audioloader_ios_native.h */, C0B55A00211B27D0007817F6 /* essentia_algorithms_reg.cpp */, C0B55A01211B27D0007817F6 /* ffmpegapi.h */, - C0B55A02211B27D0007817F6 /* music_extractor */, C0B55A05211B27D0007817F6 /* streaming_extractor_music.h */, C0B55A06211B27D0007817F6 /* version.h */, ); @@ -3266,6 +3271,7 @@ C0137FD0211A060B0046020A /* constantq.h in Headers */, C0137FF6211A060B0046020A /* multiplexer.h in Headers */, C0137F56211A060B0046020A /* noveltycurve.h in Headers */, + C037276621304F4D00E9C15C /* audioloader_ios_native.h in Headers */, C01380FC211A060C0046020A /* asciidag.h in Headers */, C01380EB211A060C0046020A /* sinkproxy.h in Headers */, C0137FCA211A060B0046020A /* bpf.h in Headers */, @@ -3938,6 +3944,11 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + USE_ACCELERATE, + ); HEADER_SEARCH_PATHS = ( "$(SRCROOT)/libs/samplerate/libsamplerate/src", "$(SRCROOT)/libs/ffmpeg-ios-static-libs/include", @@ -3980,6 +3991,7 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREPROCESSOR_DEFINITIONS = USE_ACCELERATE; HEADER_SEARCH_PATHS = ( "$(SRCROOT)/libs/samplerate/libsamplerate/src", "$(SRCROOT)/libs/ffmpeg-ios-static-libs/include", diff --git a/src/ESExtractor.mm b/src/ESExtractor.mm index c2e6a17..49eddec 100644 --- a/src/ESExtractor.mm +++ b/src/ESExtractor.mm @@ -13,10 +13,12 @@ @implementation ESExtractor - (NSDictionary *)analyseTrack:(NSURL *)url { essentia_main(url.path.UTF8String, [self tempFile].path.UTF8String, ""); - return ({ - NSData* data = [NSData dataWithContentsOfURL:[self tempFile]]; - [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]; - }); + NSData* data = [NSData dataWithContentsOfURL:[self tempFile]]; + @try { + return [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil]; + } @catch (NSException *exception) { + return nil; + } } - (NSURL *)tempFile { diff --git a/src/internal/audioloader_ios.cpp b/src/internal/audioloader_ios.cpp index 11fa414..f6dec76 100644 --- a/src/internal/audioloader_ios.cpp +++ b/src/internal/audioloader_ios.cpp @@ -38,7 +38,6 @@ namespace essentia { const char* AudioLoader::category = essentia::standard::AudioLoader::category; const char* AudioLoader::description = essentia::standard::AudioLoader::description; - AudioLoader::~AudioLoader() { closeAudioFile(); @@ -129,11 +128,7 @@ namespace essentia { E_DEBUG(EAlgorithm, "AudioLoader: using sample format conversion from libavresample"); -#ifndef TARGET_IOS - _convertCtxAv = avresample_alloc(); -#else _convertCtxAv = swr_alloc(); -#endif av_opt_set_int(_convertCtxAv, "in_channel_layout", layout, 0); av_opt_set_int(_convertCtxAv, "out_channel_layout", layout, 0); @@ -142,17 +137,11 @@ namespace essentia { av_opt_set_int(_convertCtxAv, "in_sample_fmt", _audioCtx->sample_fmt, 0); av_opt_set_int(_convertCtxAv, "out_sample_fmt", AV_SAMPLE_FMT_FLT, 0); -#ifndef TARGET_IOS - if (avresample_open(_convertCtxAv) < 0) { - throw EssentiaException("AudioLoader: Could not initialize avresample context"); - } -#else swr_init(_convertCtxAv); if (swr_is_initialized(_convertCtxAv) == 0) { throw EssentiaException("AudioLoader: Could not initialize swresample context"); } -#endif av_init_packet(&_packet); @@ -170,17 +159,10 @@ namespace essentia { return; } -#ifndef TARGET_IOS - if (_convertCtxAv) { - avresample_close(_convertCtxAv); - avresample_free(&_convertCtxAv); - } -#else if (_convertCtxAv) { swr_close(_convertCtxAv); swr_free(&_convertCtxAv); } -#endif // Close the codec if (_audioCtx) avcodec_close(_audioCtx); @@ -314,23 +296,14 @@ namespace essentia { memcpy(output, _decodedFrame->data[0], inputPlaneSize); } else { -#ifndef TARGET_IOS - int samplesWrittern = avresample_convert(_convertCtxAv, - (uint8_t**) &output, - outputPlaneSize, - outputBufferSamples, - (uint8_t**)_decodedFrame->data, - inputPlaneSize, - inputSamples); -#else - int samplesWrittern = swr_convert(_convertCtxAv, + + int samplesWritten = swr_convert(_convertCtxAv, (uint8_t**)&output, outputBufferSamples, (const uint8_t**)_decodedFrame->data, inputSamples); -#endif - if (samplesWrittern < inputSamples) { + if (samplesWritten < inputSamples) { // TODO: there may be data remaining in the internal FIFO buffer // to get this data: call avresample_convert() with NULL input // Test if this happens in practice diff --git a/src/internal/audioloader_ios.h b/src/internal/audioloader_ios.h index 9b83882..d11c5f5 100755 --- a/src/internal/audioloader_ios.h +++ b/src/internal/audioloader_ios.h @@ -20,14 +20,6 @@ #ifndef ESSENTIA_STREAMING_AUDIOLOADER_H #define ESSENTIA_STREAMING_AUDIOLOADER_H -#if __APPLE__ -#import "TargetConditionals.h" -#endif - -#if (TARGET_OS_IPHONE_SIMULATOR) || (TARGET_OS_IPHONE) || (TARGET_IPHONE) -#define TARGET_IOS -#endif - #include "streamingalgorithm.h" #include "network.h" #include "poolstorage.h" @@ -70,18 +62,13 @@ class AudioLoader : public Algorithm { uint8_t _checksum[16]; bool _computeMD5; -#ifndef TARGET_IOS - struct AVAudioResampleContext* _convertCtxAv; -#else struct SwrContext* _convertCtxAv; -#endif int _streamIdx; // index of the audio stream among all the streams contained in the file std::vector _streams; int _selectedStream; bool _configured; - void openAudioFile(const std::string& filename); void closeAudioFile(); @@ -95,7 +82,6 @@ class AudioLoader : public Algorithm { void flushPacket(); void copyFFmpegOutput(); - public: AudioLoader() : Algorithm(), _buffer(0), _demuxCtx(0), _audioCtx(0), _audioCodec(0), _decodedFrame(0), diff --git a/src/internal/audioloader_ios_native.cpp b/src/internal/audioloader_ios_native.cpp new file mode 100644 index 0000000..4d81eca --- /dev/null +++ b/src/internal/audioloader_ios_native.cpp @@ -0,0 +1,788 @@ +// +// audioloader_ios.cpp +// ESMusicExtractor +// +// Created by Ragnar Hrafnkelsson on 07/08/2018. +// Copyright © 2018 Reactify. All rights reserved. +// + +/* + * Copyright (C) 2006-2016 Music Technology Group - Universitat Pompeu Fabra + * + * This file is part of Essentia + * + * Essentia is free software: you can redistribute it and/or modify it under + * the terms of the GNU Affero General Public License as published by the Free + * Software Foundation (FSF), either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the Affero GNU General Public License + * version 3 along with this program. If not, see http://www.gnu.org/licenses/ + */ + +#include "audioloader_ios.h" +#include "algorithmfactory.h" +#include // setw() + +using namespace std; + +namespace essentia { + namespace streaming { + + const char* AudioLoader::name = essentia::standard::AudioLoader::name; + const char* AudioLoader::category = essentia::standard::AudioLoader::category; + const char* AudioLoader::description = essentia::standard::AudioLoader::description; + + + AudioLoader::~AudioLoader() { + closeAudioFile(); +#if defined(USE_ACCELERATE) +// free(_buffer); +#else + av_freep(&_buffer); + av_freep(&_md5Encoded); + av_freep(&_decodedFrame); +#endif + } + + void AudioLoader::configure() { + // set ffmpeg to be silent by default, so we don't have these annoying + // "invalid new backstep" messages anymore, when everything is actually fine +#if defined(USE_ACCELERATE) +#else + av_log_set_level(AV_LOG_QUIET); +#endif + //av_log_set_level(AV_LOG_VERBOSE); + _computeMD5 = parameter("computeMD5").toBool(); + _selectedStream = parameter("audioStream").toInt(); + reset(); + } + + + void AudioLoader::openAudioFile(const string& filename) { + E_DEBUG(EAlgorithm, "AudioLoader: opening file: " << filename); +#if defined(USE_ACCELERATE) + auto path = + CFStringCreateWithCString(kCFAllocatorDefault, filename.c_str(),kCFStringEncodingUTF8); + auto url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path, kCFURLPOSIXPathStyle, false); + OSStatus result = ExtAudioFileOpenURL(url, &_file); + if ( result != noErr ) { + throw EssentiaException("AudioLoader: Could not open file \"", filename, "\", error = ", result); + } +#else + // Open file + int errnum; + if ((errnum = avformat_open_input(&_demuxCtx, filename.c_str(), NULL, NULL)) != 0) { + char errorstr[128]; + string error = "Unknown error"; + if (av_strerror(errnum, errorstr, 128) == 0) error = errorstr; + throw EssentiaException("AudioLoader: Could not open file \"", filename, "\", error = ", error); + } + + // Retrieve stream information + if ((errnum = avformat_find_stream_info(_demuxCtx, NULL)) < 0) { + char errorstr[128]; + string error = "Unknown error"; + if (av_strerror(errnum, errorstr, 128) == 0) error = errorstr; + avformat_close_input(&_demuxCtx); + _demuxCtx = 0; + throw EssentiaException("AudioLoader: Could not find stream information, error = ", error); + } + + // Dump information about file onto standard error + //dump_format(_demuxCtx, 0, filename.c_str(), 0); + + // Check that we have only 1 audio stream in the file + _streams.clear(); + for (int i=0; i<(int)_demuxCtx->nb_streams; i++) { + if (_demuxCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) { + _streams.push_back(i); + } + } + int nAudioStreams = _streams.size(); + + if (nAudioStreams == 0) { + avformat_close_input(&_demuxCtx); + _demuxCtx = 0; + throw EssentiaException("AudioLoader ERROR: found 0 streams in the file, expecting one or more audio streams"); + } + + if (_selectedStream >= nAudioStreams) { + avformat_close_input(&_demuxCtx); + _demuxCtx = 0; + throw EssentiaException("AudioLoader ERROR: 'audioStream' parameter set to ", _selectedStream ,". It should be smaller than the audio streams count, ", nAudioStreams); + } + + _streamIdx = _streams[_selectedStream]; + + // Load corresponding audio codec + _audioCtx = _demuxCtx->streams[_streamIdx]->codec; + _audioCodec = avcodec_find_decoder(_audioCtx->codec_id); + + if (!_audioCodec) { + throw EssentiaException("AudioLoader: Unsupported codec!"); + } + + if (avcodec_open2(_audioCtx, _audioCodec, NULL) < 0) { + throw EssentiaException("AudioLoader: Unable to instantiate codec..."); + } + + // Configure format convertion (no samplerate conversion yet) + int64_t layout = av_get_default_channel_layout(_audioCtx->channels); + + /* + const char* fmt = 0; + get_format_from_sample_fmt(&fmt, _audioCtx->sample_fmt); + E_DEBUG(EAlgorithm, "AudioLoader: converting from " << (fmt ? fmt : "unknown") << " to FLT"); + */ + + E_DEBUG(EAlgorithm, "AudioLoader: using sample format conversion from libavresample"); + +#ifndef TARGET_IOS + _convertCtxAv = avresample_alloc(); +#else + _convertCtxAv = swr_alloc(); +#endif + + av_opt_set_int(_convertCtxAv, "in_channel_layout", layout, 0); + av_opt_set_int(_convertCtxAv, "out_channel_layout", layout, 0); + av_opt_set_int(_convertCtxAv, "in_sample_rate", _audioCtx->sample_rate, 0); + av_opt_set_int(_convertCtxAv, "out_sample_rate", _audioCtx->sample_rate, 0); + av_opt_set_int(_convertCtxAv, "in_sample_fmt", _audioCtx->sample_fmt, 0); + av_opt_set_int(_convertCtxAv, "out_sample_fmt", AV_SAMPLE_FMT_FLT, 0); + +#ifndef TARGET_IOS + if (avresample_open(_convertCtxAv) < 0) { + throw EssentiaException("AudioLoader: Could not initialize avresample context"); + } +#else + #if defined(USE_ACCELERATE) +#warning TODO + #else + swr_init(_convertCtxAv); + + if (swr_is_initialized(_convertCtxAv) == 0) { + throw EssentiaException("AudioLoader: Could not initialize swresample context"); + } + #endif +#endif + av_init_packet(&_packet); + + _decodedFrame = av_frame_alloc(); + if (!_decodedFrame) { + throw EssentiaException("AudioLoader: Could not allocate audio frame"); + } + + av_md5_init(_md5Encoded); // TODO +#endif +#warning TODO + } + + + void AudioLoader::closeAudioFile() { +#if defined(USE_ACCELERATE) +#else + if (!_demuxCtx) { + return; + } +#endif + +#ifndef TARGET_IOS + if (_convertCtxAv) { + avresample_close(_convertCtxAv); + avresample_free(&_convertCtxAv); + } +#else + #if defined(USE_ACCELERATE) + #else + if (_convertCtxAv) { + swr_close(_convertCtxAv); + swr_free(&_convertCtxAv); + } + #endif +#endif + +#if defined(USE_ACCELERATE) + if ( _file != NULL ) { + ExtAudioFileDispose( _file ); + _file = NULL; + } +#else + // Close the codec + if (_audioCtx) avcodec_close(_audioCtx); + // Close the audio file + if (_demuxCtx) avformat_close_input(&_demuxCtx); + + // free AVPacket + // TODO: use a variable for whether _packet is initialized or not + av_free_packet(&_packet); + _demuxCtx = 0; + _audioCtx = 0; + + _streams.clear(); +#endif + } + + + void AudioLoader::pushChannelsSampleRateInfo(int nChannels, Real sampleRate) { + if (nChannels > 2) { + throw EssentiaException("AudioLoader: could not load audio. Audio file has more than 2 channels."); + } + if (sampleRate <= 0) { + throw EssentiaException("AudioLoader: could not load audio. Audio sampling rate must be greater than 0."); + } + + _nChannels = nChannels; + + _channels.push(nChannels); + _sampleRate.push(sampleRate); + } + + + void AudioLoader::pushCodecInfo(std::string codec, int bit_rate) { + _codec.push(codec); + _bit_rate.push(bit_rate); + } + + + string uint8_t_to_hex(uint8_t* input, int size) { + ostringstream result; + for(int i=0; imNumberBuffers = numberOfBuffers; + for ( int i=0; i 0 ) { + bufferList->mBuffers[i].mData = calloc(bytesPerBuffer, 1); + if ( !bufferList->mBuffers[i].mData ) { + for ( int j=0; jmBuffers[j].mData); + free(bufferList); + throw EssentiaException("AudioLoader: Error allocating data for AudioBufferList"); + } + } else { + bufferList->mBuffers[i].mData = NULL; + } + bufferList->mBuffers[i].mDataByteSize = bytesPerBuffer; + bufferList->mBuffers[i].mNumberChannels = channelsPerBuffer; + } + + // Set destination format + if ( ExtAudioFileSetProperty(_file, kExtAudioFileProperty_ClientDataFormat, sizeof(audioFormat), &audioFormat) != noErr ) { + throw EssentiaException("AudioLoader: Error setting client data format"); + } + + // Read audio + // TODO: Read in chunks instead of whole file at once + UInt32 readFrames = frameCount; + if ( ExtAudioFileRead(_file, &readFrames, bufferList) != noErr ) { + throw EssentiaException("AudioLoader: Error reading file" ); + } + // assert( readFrames == frameCount && "Read frames don't match file size" ); + bool eof = readFrames < frameCount; + if ( eof ) { // EOF + shouldStop(true); + } + + int nsamples = readFrames; + + // acquire necessary data + bool ok = _audio.acquire(nsamples); + if (!ok) { + throw EssentiaException("AudioLoader: could not acquire output for audio"); + } + + vector& audio = *((vector*)_audio.getTokens()); + + if (_nChannels == 1) { + for (int i=0; imBuffers[0].mData)[i]; + //audio[i].left() = scale(_buffer[i]); + } + } + else { // _nChannels == 2 + // The output format is always AV_SAMPLE_FMT_FLT, which is interleaved + for (int i=0; imBuffers[0].mData)[i]; + audio[i].right() = ((float *)bufferList->mBuffers[1].mData)[i]; + //audio[i].left() = scale(_buffer[2*i]); + //audio[i].right() = scale(_buffer[2*i+1]); + } + /* + // planar + for (int i=0; imNumberBuffers; i++ ) { + if ( bufferList->mBuffers[i].mData ) free(bufferList->mBuffers[i].mData); + } + free(bufferList); + + if ( eof ) { + closeAudioFile(); + } +#else + // read frames until we get a good one + do { + int result = av_read_frame(_demuxCtx, &_packet); + //E_DEBUG(EAlgorithm, "AudioLoader: called av_read_frame(), got result = " << result); + if (result != 0) { + // 0 = OK, < 0 = error or EOF + if (result != AVERROR_EOF) { + char errstring[1204]; + av_strerror(result, errstring, sizeof(errstring)); + ostringstream msg; + msg << "AudioLoader: Error reading frame: " << errstring; + E_WARNING(msg.str()); + } + // TODO: should try reading again on EAGAIN error? + // https://github.com/FFmpeg/FFmpeg/blob/master/ffmpeg.c + shouldStop(true); + flushPacket(); + closeAudioFile(); + if (_computeMD5) { + av_md5_final(_md5Encoded, _checksum); + _md5.push(uint8_t_to_hex(_checksum, 16)); + } + else { + string md5 = ""; + _md5.push(md5); + } + return FINISHED; + } + } while (_packet.stream_index != _streamIdx); + + // compute md5 first + if (_computeMD5) { + av_md5_update(_md5Encoded, _packet.data, _packet.size); + } + + // decode frames in packet + while(_packet.size > 0) { + if (!decodePacket()) break; + copyFFmpegOutput(); + } + // neds to be freed !! + av_free_packet(&_packet); + +#endif + return OK; + } + + +#if defined(USE_ACCELERATE) +#warning TODO +#else + int AudioLoader::decode_audio_frame(AVCodecContext* audioCtx, + float* output, + int* outputSize, + AVPacket* packet) { + + // _dataSize input = number of bytes available for write in buff + // output = number of bytes actually written (actual: FLT data) + //E_DEBUG(EAlgorithm, "decode_audio_frame, available bytes in buffer = " << _dataSize); + int gotFrame = 0; + av_frame_unref(_decodedFrame); //avcodec_get_frame_defaults(_decodedFrame); + + int len = avcodec_decode_audio4(audioCtx, _decodedFrame, &gotFrame, packet); + + if (len < 0) return len; // error handling should be done outside + + if (gotFrame) { + int inputSamples = _decodedFrame->nb_samples; + int inputPlaneSize = av_samples_get_buffer_size(NULL, _nChannels, inputSamples, + audioCtx->sample_fmt, 1); + int outputPlaneSize = av_samples_get_buffer_size(NULL, _nChannels, inputSamples, + AV_SAMPLE_FMT_FLT, 1); + // the size of the output buffer in samples + int outputBufferSamples = *outputSize / + (av_get_bytes_per_sample(AV_SAMPLE_FMT_FLT) * _nChannels); + + if (outputBufferSamples < inputSamples) { + // this should never happen, throw exception here + throw EssentiaException("AudioLoader: Insufficient buffer size for format conversion"); + } + + if (audioCtx->sample_fmt == AV_SAMPLE_FMT_FLT) { + // TODO: no need in this check? Not many of common formats support FLT + // no conversion needed, direct copy from our frame to output buffer + memcpy(output, _decodedFrame->data[0], inputPlaneSize); + } + else { +#ifndef TARGET_IOS + int samplesWrittern = avresample_convert(_convertCtxAv, + (uint8_t**) &output, + outputPlaneSize, + outputBufferSamples, + (uint8_t**)_decodedFrame->data, + inputPlaneSize, + inputSamples); +#else + int samplesWrittern = swr_convert(_convertCtxAv, + (uint8_t**)&output, + outputBufferSamples, + (const uint8_t**)_decodedFrame->data, + inputSamples); +#endif + if (samplesWrittern < inputSamples) { + // TODO: there may be data remaining in the internal FIFO buffer + // to get this data: call avresample_convert() with NULL input + // Test if this happens in practice + ostringstream msg; + msg << "AudioLoader: Incomplete format conversion (some samples missing)" + << " from " << av_get_sample_fmt_name(_audioCtx->sample_fmt) + << " to " << av_get_sample_fmt_name(AV_SAMPLE_FMT_FLT); + throw EssentiaException(msg); + } + } + *outputSize = outputPlaneSize; + } + else { + E_DEBUG(EAlgorithm, "AudioLoader: tried to decode packet but didn't get any frame..."); + *outputSize = 0; + } + + return len; + } +#endif + + + void AudioLoader::flushPacket() { +#if defined(USE_ACCELERATE) +#warning TODO +#else + AVPacket empty; + av_init_packet(&empty); + do { + _dataSize = FFMPEG_BUFFER_SIZE; + empty.data = NULL; + empty.size = 0; + + int len = decode_audio_frame(_audioCtx, _buffer, &_dataSize, &empty); + if (len < 0) { + char errstring[1204]; + av_strerror(len, errstring, sizeof(errstring)); + ostringstream msg; + msg << "AudioLoader: decoding error while flushing a packet:" << errstring; + E_WARNING(msg.str()); + } + copyFFmpegOutput(); + + } while (_dataSize > 0); +#endif + } + + + /** + * Gets the AVPacket stored in _packet, and decodes all the samples it can from it, + * putting them in _buffer, the total number of bytes written begin stored in _dataSize. + */ + int AudioLoader::decodePacket() { + /* + E_DEBUG(EAlgorithm, "-----------------------------------------------------"); + E_DEBUG(EAlgorithm, "decoding packet of " << _packet.size << " bytes"); + E_DEBUG(EAlgorithm, "pts: " << _packet.pts << " - dts: " << _packet.dts); //" - pos: " << pkt->pos); + E_DEBUG(EAlgorithm, "flags: " << _packet.flags); + E_DEBUG(EAlgorithm, "duration: " << _packet.duration); + */ + int len = 0; +#if defined(USE_ACCELERATE) +#warning TODO +#else + + // buff is an offset in our output buffer, it points to where we should start + // writing the next decoded samples + float* buff = _buffer; + + // _dataSize gets the size of the buffer, in bytes + _dataSize = FFMPEG_BUFFER_SIZE; + + // Note: md5 should be computed before decoding frame, as the decoding may + // change the content of a packet. Still, not sure if it is correct to + // compute md5 over packet which contains incorrect frames, potentially + // belonging to id3 metadata (TODO: or is it just a missing header issue?), + // but computing md5 hash using ffmpeg will also treat it as audio: + // ffmpeg -i file.mp3 -acodec copy -f md5 - + + len = decode_audio_frame(_audioCtx, buff, &_dataSize, &_packet); + + if (len < 0) { + char errstring[1204]; + av_strerror(len, errstring, sizeof(errstring)); + ostringstream msg; + + if (_audioCtx->codec_id == AV_CODEC_ID_MP3) { + msg << "AudioLoader: invalid frame, skipping it: " << errstring; + // mp3 streams can have tag frames (id3v2?) which libavcodec tries to + // read as audio anyway, and we probably don't want print an error + // message for that... + // TODO: Are these frames really id3 tags? + + //E_DEBUG(EAlgorithm, msg); + E_WARNING(msg.str()); + } + else { + msg << "AudioLoader: error while decoding, skipping frame: " << errstring; + E_WARNING(msg.str()); + } + return 0; + } + + if (len != _packet.size) { + // https://www.ffmpeg.org/doxygen/trunk/group__lavc__decoding.html#ga834bb1b062fbcc2de4cf7fb93f154a3e + + // Some decoders may support multiple frames in a single AVPacket. Such + // decoders would then just decode the first frame and the return value + // would be less than the packet size. In this case, avcodec_decode_audio4 + // has to be called again with an AVPacket containing the remaining data + // in order to decode the second frame, etc... Even if no frames are + // returned, the packet needs to be fed to the decoder with remaining + // data until it is completely consumed or an error occurs. + + E_WARNING("AudioLoader: more than 1 frame in packet, decoding remaining bytes..."); + E_WARNING("at sample index: " << output("audio").totalProduced()); + E_WARNING("decoded samples: " << len); + E_WARNING("packet size: " << _packet.size); + } + + // update packet data pointer to data left undecoded (if any) + _packet.size -= len; + _packet.data += len; + + if (_dataSize <= 0) { + // No data yet, get more frames + // cout << "no data yet, get more frames" << endl; + _dataSize = 0; + } +#endif + + return len; + } + + /* + inline Real scale(int16_t value) { + return value / (Real)32767; + } + */ + + void AudioLoader::copyFFmpegOutput() { +#if defined(USE_ACCELERATE) +#else + int nsamples = _dataSize / (av_get_bytes_per_sample(AV_SAMPLE_FMT_FLT) * _nChannels); + + if (nsamples == 0) return; + + // acquire necessary data + bool ok = _audio.acquire(nsamples); + if (!ok) { + throw EssentiaException("AudioLoader: could not acquire output for audio"); + } + + vector& audio = *((vector*)_audio.getTokens()); + + if (_nChannels == 1) { + for (int i=0; ichannels, _audioCtx->sample_rate); + pushCodecInfo(_audioCodec->name, _audioCtx->bit_rate); +#endif + } + + } // namespace streaming +} // namespace essentia + + +namespace essentia { + namespace standard { + + const char* AudioLoader::name = "AudioLoader"; + const char* AudioLoader::category = "Input/output"; + const char* AudioLoader::description = DOC("This algorithm loads the single audio stream contained in a given audio or video file. Supported formats are all those supported by the FFmpeg library including wav, aiff, flac, ogg and mp3.\n" + "\n" + "This algorithm will throw an exception if it was not properly configured which is normally due to not specifying a valid filename. Invalid names comprise those with extensions different than the supported formats and non existent files. If using this algorithm on Windows, you must ensure that the filename is encoded as UTF-8\n\n" + "Note: ogg files are decoded in reverse phase, due to be using ffmpeg library.\n" + "\n" + "References:\n" + " [1] WAV - Wikipedia, the free encyclopedia,\n" + " http://en.wikipedia.org/wiki/Wav\n" + " [2] Audio Interchange File Format - Wikipedia, the free encyclopedia,\n" + " http://en.wikipedia.org/wiki/Aiff\n" + " [3] Free Lossless Audio Codec - Wikipedia, the free encyclopedia,\n" + " http://en.wikipedia.org/wiki/Flac\n" + " [4] Vorbis - Wikipedia, the free encyclopedia,\n" + " http://en.wikipedia.org/wiki/Vorbis\n" + " [5] MP3 - Wikipedia, the free encyclopedia,\n" + " http://en.wikipedia.org/wiki/Mp3"); + + + void AudioLoader::createInnerNetwork() { + _loader = streaming::AlgorithmFactory::create("AudioLoader"); + _audioStorage = new streaming::VectorOutput(); + + _loader->output("audio") >> _audioStorage->input("data"); + _loader->output("sampleRate") >> PC(_pool, "internal.sampleRate"); + _loader->output("numberChannels") >> PC(_pool, "internal.numberChannels"); + _loader->output("md5") >> PC(_pool, "internal.md5"); + _loader->output("codec") >> PC(_pool, "internal.codec"); + _loader->output("bit_rate") >> PC(_pool, "internal.bit_rate"); + _network = new scheduler::Network(_loader); + } + + void AudioLoader::configure() { + _loader->configure(INHERIT("filename"), + INHERIT("computeMD5"), + INHERIT("audioStream")); + } + + void AudioLoader::compute() { + if (!parameter("filename").isConfigured()) { + throw EssentiaException("AudioLoader: Trying to call compute() on an " + "AudioLoader algo which hasn't been correctly configured."); + } + + Real& sampleRate = _sampleRate.get(); + int& numberChannels = _channels.get(); + string& md5 = _md5.get(); + int& bit_rate = _bit_rate.get(); + string& codec = _codec.get(); + vector& audio = _audio.get(); + + _audioStorage->setVector(&audio); + // TODO: is using VectorInput indeed faster than using Pool? + + // FIXME: + // _audio.reserve(sth_meaningful); + + _network->run(); + + sampleRate = _pool.value("internal.sampleRate"); + numberChannels = (int) _pool.value("internal.numberChannels"); + md5 = _pool.value("internal.md5"); + bit_rate = (int) _pool.value("internal.bit_rate"); + codec = _pool.value("internal.codec"); + + // reset, so it is ready to load audio again + reset(); + } + + void AudioLoader::reset() { + _network->reset(); + _pool.remove("internal.md5"); + _pool.remove("internal.sampleRate"); + _pool.remove("internal.numberChannels"); + _pool.remove("internal.codec"); + _pool.remove("internal.bit_rate"); + } + + } // namespace standard +} // namespace essentia diff --git a/src/internal/audioloader_ios_native.h b/src/internal/audioloader_ios_native.h new file mode 100755 index 0000000..4481aff --- /dev/null +++ b/src/internal/audioloader_ios_native.h @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2006-2016 Music Technology Group - Universitat Pompeu Fabra + * + * This file is part of Essentia + * + * Essentia is free software: you can redistribute it and/or modify it under + * the terms of the GNU Affero General Public License as published by the Free + * Software Foundation (FSF), either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the Affero GNU General Public License + * version 3 along with this program. If not, see http://www.gnu.org/licenses/ + */ + +#ifndef ESSENTIA_STREAMING_AUDIOLOADER_H +#define ESSENTIA_STREAMING_AUDIOLOADER_H + +#if __APPLE__ +#import "TargetConditionals.h" +#endif + +#if (TARGET_OS_IPHONE_SIMULATOR) || (TARGET_OS_IPHONE) || (TARGET_IPHONE) +#define TARGET_IOS +#endif + +#include "streamingalgorithm.h" +#include "network.h" +#include "poolstorage.h" + +#if defined(USE_ACCELERATE) +#include +#else +#include "ffmpegapi.h" + +extern AVInputFormat ff_wav_demuxer; +#endif + +#define MAX_AUDIO_FRAME_SIZE 192000 + +namespace essentia { +namespace streaming { + +class AudioLoader : public Algorithm { + protected: + Source _audio; + AbsoluteSource _sampleRate; + AbsoluteSource _channels; + AbsoluteSource _md5; + AbsoluteSource _bit_rate; + AbsoluteSource _codec; + + int _nChannels; + + // MAX_AUDIO_FRAME_SIZE is in bytes, multiply it by 2 to get some margin, + // because we might want to decode multiple frames in this buffer (all the + // frames contained in a packet, which can be more than 1 as in flac), and + // each time we decode a frame we need to have at least a full buffer of free space. + const static int FFMPEG_BUFFER_SIZE = MAX_AUDIO_FRAME_SIZE * 2; + +// float* _buffer; +// int _dataSize; + +#if defined(USE_ACCELERATE) + ExtAudioFileRef _file; +#else + AVFormatContext* _demuxCtx; + AVCodecContext* _audioCtx; + AVCodec* _audioCodec; + AVPacket _packet; + AVFrame* _decodedFrame; + AVMD5 *_md5Encoded; +#endif + uint8_t _checksum[16]; + bool _computeMD5; + +#ifndef TARGET_IOS + struct AVAudioResampleContext* _convertCtxAv; +#else +#if defined(USE_ACCELERATE) +#else + struct SwrContext* _convertCtxAv; + + int _streamIdx; // index of the audio stream among all the streams contained in the file + std::vector _streams; + bool _configured; +#endif + int _selectedStream; +#endif + + void openAudioFile(const std::string& filename); + void closeAudioFile(); + + void pushChannelsSampleRateInfo(int nChannels, Real sampleRate); + void pushCodecInfo(std::string codec, int bit_rate); + +#if defined(USE_ACCELERATE) +#else + int decode_audio_frame(AVCodecContext* audioCtx, float* output, + int* outputSize, AVPacket* packet); +#endif + + int decodePacket(); + void flushPacket(); + void copyFFmpegOutput(); + + + public: + AudioLoader() + : Algorithm() +#if defined(USE_ACCELERATE) +#else + , _buffer(0) + , _demuxCtx(0), + _audioCtx(0), + _audioCodec(0), + _decodedFrame(0), + _convertCtxAv(0), + _configured(false) +#endif + + { + + declareOutput(_audio, 1, "audio", "the input audio signal"); + declareOutput(_sampleRate, 0, "sampleRate", "the sampling rate of the audio signal [Hz]"); + declareOutput(_channels, 0, "numberChannels", "the number of channels"); + declareOutput(_md5, 0, "md5", "the MD5 checksum of raw undecoded audio payload"); + declareOutput(_bit_rate, 0, "bit_rate", "the bit rate of the input audio, as reported by the decoder codec"); + declareOutput(_codec, 0, "codec", "the codec that is used to decode the input audio"); + + _audio.setBufferType(BufferUsage::forLargeAudioStream); + +#if defined(USE_ACCELERATE) + // TODO: (16 bit alignment) + + // TODO: MD5 encoding +#else + // Register all formats and codecs + av_register_all(); + + // use av_malloc, because we _need_ the buffer to be 16-byte aligned + _buffer = (float*)av_malloc(FFMPEG_BUFFER_SIZE); + + _md5Encoded = av_md5_alloc(); + if (!_md5Encoded) { + throw EssentiaException("Error allocating the MD5 context"); + } + + #endif + } + + ~AudioLoader(); + + AlgorithmStatus process(); + void reset(); + + void declareParameters() { + declareParameter("filename", "the name of the file from which to read", "", Parameter::STRING); + declareParameter("computeMD5", "compute the MD5 checksum", "{true,false}", false); + declareParameter("audioStream", "audio stream index to be loaded. Other streams are not taken into account (e.g. if stream 0 is video and 1 is audio use index 0 to access it.)", "[0,inf)", 0); + } + + void configure(); + + static const char* name; + static const char* category; + static const char* description; + +}; + +} // namespace streaming +} // namespace essentia + + +#include "vectoroutput.h" +#include "algorithm.h" + +namespace essentia { +namespace standard { + +// Standard non-streaming algorithm comes after the streaming one as it +// depends on it +class AudioLoader : public Algorithm { + + protected: + Output > _audio; + Output _sampleRate; + Output _channels; + Output _md5; + Output _bit_rate; + Output _codec; + + streaming::Algorithm* _loader; + streaming::VectorOutput* _audioStorage; + + scheduler::Network* _network; + Pool _pool; + + void createInnerNetwork(); + + public: + AudioLoader() { + declareOutput(_audio, "audio", "the input audio signal"); + declareOutput(_sampleRate, "sampleRate", "the sampling rate of the audio signal [Hz]"); + declareOutput(_channels, "numberChannels", "the number of channels"); + declareOutput(_md5, "md5", "the MD5 checksum of raw undecoded audio payload"); + declareOutput(_bit_rate, "bit_rate", "the bit rate of the input audio, as reported by the decoder codec"); + declareOutput(_codec, "codec", "the codec that is used to decode the input audio"); + + createInnerNetwork(); + } + + ~AudioLoader() { + // NB: this will also delete all the algorithms as the Network took ownership of them + delete _network; + } + + void declareParameters() { + declareParameter("filename", "the name of the file from which to read", "", Parameter::STRING); + declareParameter("computeMD5", "compute the MD5 checksum", "{true,false}", false); + declareParameter("audioStream", "audio stream index to be loaded. Other streams are no taken into account (e.g. if stream 0 is video and 1 is audio use index 0 to access it.)", "[0,inf)", 0); + } + + void configure(); + + void compute(); + void reset(); + + static const char* name; + static const char* category; + static const char* description; +}; + +} // namespace standard +} // namespace essentia + +#endif // ESSENTIA_STREAMING_AUDIOLOADER_H From fbad324ab057bb2685e2676ba66fb3d90103aa61 Mon Sep 17 00:00:00 2001 From: ragnaringi Date: Fri, 24 Aug 2018 18:36:52 +0000 Subject: [PATCH 02/18] Temp --- ESMusicExtractor.xcodeproj/project.pbxproj | 16 +- src/internal/audioloader_ios.h | 174 +++----- src/internal/audioloader_ios_native.cpp | 470 +-------------------- src/internal/audioloader_ios_native.h | 241 ----------- 4 files changed, 77 insertions(+), 824 deletions(-) delete mode 100755 src/internal/audioloader_ios_native.h diff --git a/ESMusicExtractor.xcodeproj/project.pbxproj b/ESMusicExtractor.xcodeproj/project.pbxproj index 18eee95..4cea08e 100644 --- a/ESMusicExtractor.xcodeproj/project.pbxproj +++ b/ESMusicExtractor.xcodeproj/project.pbxproj @@ -493,7 +493,7 @@ C0245CD4212DBC6C00DBFEA3 /* extractor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C01377DB211A06090046020A /* extractor.cpp */; }; C0245CD5212DBC7200DBFEA3 /* keyextractor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C01377DF211A06090046020A /* keyextractor.cpp */; }; C0245CD6212DBC7500DBFEA3 /* levelextractor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C01377E1211A06090046020A /* levelextractor.cpp */; }; - C037276621304F4D00E9C15C /* audioloader_ios_native.h in Headers */ = {isa = PBXBuildFile; fileRef = C037276421304F4C00E9C15C /* audioloader_ios_native.h */; }; + C037276621304F4D00E9C15C /* audioloader_ios.h in Headers */ = {isa = PBXBuildFile; fileRef = C037276421304F4C00E9C15C /* audioloader_ios.h */; }; C088C619211B54C3009ED9DB /* taglib_config.h in Headers */ = {isa = PBXBuildFile; fileRef = C088C618211B54C3009ED9DB /* taglib_config.h */; }; C088CB10211B561C009ED9DB /* checked.h in Headers */ = {isa = PBXBuildFile; fileRef = C088C961211B561C009ED9DB /* checked.h */; }; C088CB11211B561C009ED9DB /* core.h in Headers */ = {isa = PBXBuildFile; fileRef = C088C962211B561C009ED9DB /* core.h */; }; @@ -723,14 +723,13 @@ C0B559F1211B20C5007817F6 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C0B559F0211B20C4007817F6 /* AudioToolbox.framework */; }; C0B559F3211B20CB007817F6 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C0B559F2211B20CB007817F6 /* Accelerate.framework */; }; C0B559F4211B23C4007817F6 /* libsamplerate.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C0F3BE8D211B0EED00073488 /* libsamplerate.a */; }; - C0B55A07211B27D0007817F6 /* audioloader_ios.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C0B559FE211B27D0007817F6 /* audioloader_ios.cpp */; }; - C0B55A08211B27D0007817F6 /* audioloader_ios.h in Headers */ = {isa = PBXBuildFile; fileRef = C0B559FF211B27D0007817F6 /* audioloader_ios.h */; }; C0B55A09211B27D0007817F6 /* essentia_algorithms_reg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C0B55A00211B27D0007817F6 /* essentia_algorithms_reg.cpp */; }; C0B55A0A211B27D0007817F6 /* ffmpegapi.h in Headers */ = {isa = PBXBuildFile; fileRef = C0B55A01211B27D0007817F6 /* ffmpegapi.h */; }; C0B55A0B211B27D0007817F6 /* extractor_utils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C0B55A03211B27D0007817F6 /* extractor_utils.cpp */; }; C0B55A0C211B27D0007817F6 /* extractor_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = C0B55A04211B27D0007817F6 /* extractor_utils.h */; }; C0B55A0D211B27D0007817F6 /* streaming_extractor_music.h in Headers */ = {isa = PBXBuildFile; fileRef = C0B55A05211B27D0007817F6 /* streaming_extractor_music.h */; }; C0B55A0E211B27D0007817F6 /* version.h in Headers */ = {isa = PBXBuildFile; fileRef = C0B55A06211B27D0007817F6 /* version.h */; }; + C0B9C2662130872E0029DA6F /* audioloader_ios_native.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C037276321304F4C00E9C15C /* audioloader_ios_native.cpp */; }; C0BA3B8E211A31E90038D19F /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = C0BA3B8D211A31E90038D19F /* libc++.tbd */; }; C0E7A20C211A2F6700711656 /* libavcodec.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C0E7A1A3211A2F6600711656 /* libavcodec.a */; }; C0E7A20F211A2F6700711656 /* libavformat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C0E7A1A6211A2F6600711656 /* libavformat.a */; }; @@ -1346,7 +1345,7 @@ C0138400211A06BD0046020A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; C0138401211A06BD0046020A /* ESMusicExtractor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ESMusicExtractor.h; sourceTree = ""; }; C037276321304F4C00E9C15C /* audioloader_ios_native.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = audioloader_ios_native.cpp; sourceTree = ""; }; - C037276421304F4C00E9C15C /* audioloader_ios_native.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = audioloader_ios_native.h; sourceTree = ""; }; + C037276421304F4C00E9C15C /* audioloader_ios.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = audioloader_ios.h; sourceTree = ""; }; C088C618211B54C3009ED9DB /* taglib_config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = taglib_config.h; sourceTree = ""; }; C088C961211B561C009ED9DB /* checked.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = checked.h; sourceTree = ""; }; C088C962211B561C009ED9DB /* core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = core.h; sourceTree = ""; }; @@ -1578,7 +1577,6 @@ C0B559F9211B2451007817F6 /* polartocartesian.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = polartocartesian.h; sourceTree = ""; }; C0B559FA211B2451007817F6 /* polartocartesian.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = polartocartesian.cpp; sourceTree = ""; }; C0B559FE211B27D0007817F6 /* audioloader_ios.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = audioloader_ios.cpp; sourceTree = ""; }; - C0B559FF211B27D0007817F6 /* audioloader_ios.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = audioloader_ios.h; sourceTree = ""; }; C0B55A00211B27D0007817F6 /* essentia_algorithms_reg.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = essentia_algorithms_reg.cpp; sourceTree = ""; }; C0B55A01211B27D0007817F6 /* ffmpegapi.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ffmpegapi.h; sourceTree = ""; }; C0B55A03211B27D0007817F6 /* extractor_utils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = extractor_utils.cpp; sourceTree = ""; }; @@ -2918,9 +2916,8 @@ C0B55A02211B27D0007817F6 /* music_extractor */, C088C618211B54C3009ED9DB /* taglib_config.h */, C0B559FE211B27D0007817F6 /* audioloader_ios.cpp */, - C0B559FF211B27D0007817F6 /* audioloader_ios.h */, C037276321304F4C00E9C15C /* audioloader_ios_native.cpp */, - C037276421304F4C00E9C15C /* audioloader_ios_native.h */, + C037276421304F4C00E9C15C /* audioloader_ios.h */, C0B55A00211B27D0007817F6 /* essentia_algorithms_reg.cpp */, C0B55A01211B27D0007817F6 /* ffmpegapi.h */, C0B55A05211B27D0007817F6 /* streaming_extractor_music.h */, @@ -3053,7 +3050,6 @@ C0137FAC211A060B0046020A /* mfcc.h in Headers */, C0137F4E211A060B0046020A /* bpmrubato.h in Headers */, C0B55A0C211B27D0007817F6 /* extractor_utils.h in Headers */, - C0B55A08211B27D0007817F6 /* audioloader_ios.h in Headers */, C0137FA6211A060B0046020A /* hpcp.h in Headers */, C017D4C2212DD70B003009FE /* poolaggregator.h in Headers */, C017D4AC212DD426003009FE /* instantpower.h in Headers */, @@ -3271,7 +3267,7 @@ C0137FD0211A060B0046020A /* constantq.h in Headers */, C0137FF6211A060B0046020A /* multiplexer.h in Headers */, C0137F56211A060B0046020A /* noveltycurve.h in Headers */, - C037276621304F4D00E9C15C /* audioloader_ios_native.h in Headers */, + C037276621304F4D00E9C15C /* audioloader_ios.h in Headers */, C01380FC211A060C0046020A /* asciidag.h in Headers */, C01380EB211A060C0046020A /* sinkproxy.h in Headers */, C0137FCA211A060B0046020A /* bpf.h in Headers */, @@ -3588,7 +3584,6 @@ C0137F87211A060B0046020A /* maxtototal.cpp in Sources */, C088CBB1211B561D009ED9DB /* xingheader.cpp in Sources */, C01380BE211A060C0046020A /* debugging.cpp in Sources */, - C0B55A07211B27D0007817F6 /* audioloader_ios.cpp in Sources */, C0137ECD211A060B0046020A /* kiss_fftnd.c in Sources */, C088CB88211B561D009ED9DB /* relativevolumeframe.cpp in Sources */, C088CB49211B561C009ED9DB /* fileref.cpp in Sources */, @@ -3762,6 +3757,7 @@ C0137F7D211A060B0046020A /* tempotapticks.cpp in Sources */, C088CBAA211B561D009ED9DB /* mpegfile.cpp in Sources */, C0137FAD211A060B0046020A /* panning.cpp in Sources */, + C0B9C2662130872E0029DA6F /* audioloader_ios_native.cpp in Sources */, C088CBD2211B561D009ED9DB /* wavfile.cpp in Sources */, C01380F0211A060C0046020A /* streamingalgorithm.cpp in Sources */, C0137F4F211A060B0046020A /* harmonicbpm.cpp in Sources */, diff --git a/src/internal/audioloader_ios.h b/src/internal/audioloader_ios.h index d11c5f5..e96c41b 100755 --- a/src/internal/audioloader_ios.h +++ b/src/internal/audioloader_ios.h @@ -17,18 +17,13 @@ * version 3 along with this program. If not, see http://www.gnu.org/licenses/ */ -#ifndef ESSENTIA_STREAMING_AUDIOLOADER_H -#define ESSENTIA_STREAMING_AUDIOLOADER_H +#pragma once #include "streamingalgorithm.h" #include "network.h" #include "poolstorage.h" -#include "ffmpegapi.h" - -extern AVInputFormat ff_wav_demuxer; - -#define MAX_AUDIO_FRAME_SIZE 192000 +#include namespace essentia { namespace streaming { @@ -44,48 +39,20 @@ class AudioLoader : public Algorithm { int _nChannels; - // MAX_AUDIO_FRAME_SIZE is in bytes, multiply it by 2 to get some margin, - // because we might want to decode multiple frames in this buffer (all the - // frames contained in a packet, which can be more than 1 as in flac), and - // each time we decode a frame we need to have at least a full buffer of free space. - const static int FFMPEG_BUFFER_SIZE = MAX_AUDIO_FRAME_SIZE * 2; - - float* _buffer; - int _dataSize; - - AVFormatContext* _demuxCtx; - AVCodecContext* _audioCtx; - AVCodec* _audioCodec; - AVPacket _packet; - AVMD5 *_md5Encoded; - AVFrame* _decodedFrame; + ExtAudioFileRef _file; + +// AVMD5 *_md5Encoded; uint8_t _checksum[16]; bool _computeMD5; - struct SwrContext* _convertCtxAv; - - int _streamIdx; // index of the audio stream among all the streams contained in the file - std::vector _streams; - int _selectedStream; - bool _configured; - void openAudioFile(const std::string& filename); void closeAudioFile(); void pushChannelsSampleRateInfo(int nChannels, Real sampleRate); void pushCodecInfo(std::string codec, int bit_rate); - int decode_audio_frame(AVCodecContext* audioCtx, float* output, - int* outputSize, AVPacket* packet); - - int decodePacket(); - void flushPacket(); - void copyFFmpegOutput(); - public: - AudioLoader() : Algorithm(), _buffer(0), _demuxCtx(0), - _audioCtx(0), _audioCodec(0), _decodedFrame(0), - _convertCtxAv(0), _configured(false) { + AudioLoader() : Algorithm() { declareOutput(_audio, 1, "audio", "the input audio signal"); declareOutput(_sampleRate, 0, "sampleRate", "the sampling rate of the audio signal [Hz]"); @@ -96,16 +63,10 @@ class AudioLoader : public Algorithm { _audio.setBufferType(BufferUsage::forLargeAudioStream); - // Register all formats and codecs - av_register_all(); - - // use av_malloc, because we _need_ the buffer to be 16-byte aligned - _buffer = (float*)av_malloc(FFMPEG_BUFFER_SIZE); - - _md5Encoded = av_md5_alloc(); - if (!_md5Encoded) { - throw EssentiaException("Error allocating the MD5 context"); - } +// _md5Encoded = av_md5_alloc(); +// if (!_md5Encoded) { +// throw EssentiaException("Error allocating the MD5 context"); +// } } ~AudioLoader(); @@ -130,67 +91,64 @@ class AudioLoader : public Algorithm { } // namespace streaming } // namespace essentia - #include "vectoroutput.h" #include "algorithm.h" namespace essentia { -namespace standard { - -// Standard non-streaming algorithm comes after the streaming one as it -// depends on it -class AudioLoader : public Algorithm { - - protected: - Output > _audio; - Output _sampleRate; - Output _channels; - Output _md5; - Output _bit_rate; - Output _codec; - - streaming::Algorithm* _loader; - streaming::VectorOutput* _audioStorage; - - scheduler::Network* _network; - Pool _pool; - - void createInnerNetwork(); - - public: - AudioLoader() { - declareOutput(_audio, "audio", "the input audio signal"); - declareOutput(_sampleRate, "sampleRate", "the sampling rate of the audio signal [Hz]"); - declareOutput(_channels, "numberChannels", "the number of channels"); - declareOutput(_md5, "md5", "the MD5 checksum of raw undecoded audio payload"); - declareOutput(_bit_rate, "bit_rate", "the bit rate of the input audio, as reported by the decoder codec"); - declareOutput(_codec, "codec", "the codec that is used to decode the input audio"); - - createInnerNetwork(); - } - - ~AudioLoader() { - // NB: this will also delete all the algorithms as the Network took ownership of them - delete _network; - } - - void declareParameters() { - declareParameter("filename", "the name of the file from which to read", "", Parameter::STRING); - declareParameter("computeMD5", "compute the MD5 checksum", "{true,false}", false); - declareParameter("audioStream", "audio stream index to be loaded. Other streams are no taken into account (e.g. if stream 0 is video and 1 is audio use index 0 to access it.)", "[0,inf)", 0); - } - - void configure(); - - void compute(); - void reset(); - - static const char* name; - static const char* category; - static const char* description; -}; - -} // namespace standard + namespace standard { + + // Standard non-streaming algorithm comes after the streaming one as it + // depends on it + class AudioLoader : public Algorithm { + + protected: + Output > _audio; + Output _sampleRate; + Output _channels; + Output _md5; + Output _bit_rate; + Output _codec; + + streaming::Algorithm* _loader; + streaming::VectorOutput* _audioStorage; + + scheduler::Network* _network; + Pool _pool; + + void createInnerNetwork(); + + public: + AudioLoader() { + declareOutput(_audio, "audio", "the input audio signal"); + declareOutput(_sampleRate, "sampleRate", "the sampling rate of the audio signal [Hz]"); + declareOutput(_channels, "numberChannels", "the number of channels"); + declareOutput(_md5, "md5", "the MD5 checksum of raw undecoded audio payload"); + declareOutput(_bit_rate, "bit_rate", "the bit rate of the input audio, as reported by the decoder codec"); + declareOutput(_codec, "codec", "the codec that is used to decode the input audio"); + + createInnerNetwork(); + } + + ~AudioLoader() { + // NB: this will also delete all the algorithms as the Network took ownership of them + delete _network; + } + + void declareParameters() { + declareParameter("filename", "the name of the file from which to read", "", Parameter::STRING); + declareParameter("computeMD5", "compute the MD5 checksum", "{true,false}", false); + declareParameter("audioStream", "audio stream index to be loaded. Other streams are no taken into account (e.g. if stream 0 is video and 1 is audio use index 0 to access it.)", "[0,inf)", 0); + } + + void configure(); + + void compute(); + void reset(); + + static const char* name; + static const char* category; + static const char* description; + }; + + } // namespace standard } // namespace essentia - -#endif // ESSENTIA_STREAMING_AUDIOLOADER_H diff --git a/src/internal/audioloader_ios_native.cpp b/src/internal/audioloader_ios_native.cpp index 4d81eca..1ad9153 100644 --- a/src/internal/audioloader_ios_native.cpp +++ b/src/internal/audioloader_ios_native.cpp @@ -41,32 +41,18 @@ namespace essentia { AudioLoader::~AudioLoader() { closeAudioFile(); -#if defined(USE_ACCELERATE) -// free(_buffer); -#else - av_freep(&_buffer); - av_freep(&_md5Encoded); - av_freep(&_decodedFrame); -#endif } void AudioLoader::configure() { - // set ffmpeg to be silent by default, so we don't have these annoying - // "invalid new backstep" messages anymore, when everything is actually fine -#if defined(USE_ACCELERATE) -#else - av_log_set_level(AV_LOG_QUIET); -#endif - //av_log_set_level(AV_LOG_VERBOSE); _computeMD5 = parameter("computeMD5").toBool(); - _selectedStream = parameter("audioStream").toInt(); + // _selectedStream = parameter("audioStream").toInt(); reset(); } void AudioLoader::openAudioFile(const string& filename) { E_DEBUG(EAlgorithm, "AudioLoader: opening file: " << filename); -#if defined(USE_ACCELERATE) + auto path = CFStringCreateWithCString(kCFAllocatorDefault, filename.c_str(),kCFStringEncodingUTF8); auto url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path, kCFURLPOSIXPathStyle, false); @@ -74,158 +60,16 @@ namespace essentia { if ( result != noErr ) { throw EssentiaException("AudioLoader: Could not open file \"", filename, "\", error = ", result); } -#else - // Open file - int errnum; - if ((errnum = avformat_open_input(&_demuxCtx, filename.c_str(), NULL, NULL)) != 0) { - char errorstr[128]; - string error = "Unknown error"; - if (av_strerror(errnum, errorstr, 128) == 0) error = errorstr; - throw EssentiaException("AudioLoader: Could not open file \"", filename, "\", error = ", error); - } - - // Retrieve stream information - if ((errnum = avformat_find_stream_info(_demuxCtx, NULL)) < 0) { - char errorstr[128]; - string error = "Unknown error"; - if (av_strerror(errnum, errorstr, 128) == 0) error = errorstr; - avformat_close_input(&_demuxCtx); - _demuxCtx = 0; - throw EssentiaException("AudioLoader: Could not find stream information, error = ", error); - } - - // Dump information about file onto standard error - //dump_format(_demuxCtx, 0, filename.c_str(), 0); - - // Check that we have only 1 audio stream in the file - _streams.clear(); - for (int i=0; i<(int)_demuxCtx->nb_streams; i++) { - if (_demuxCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) { - _streams.push_back(i); - } - } - int nAudioStreams = _streams.size(); - - if (nAudioStreams == 0) { - avformat_close_input(&_demuxCtx); - _demuxCtx = 0; - throw EssentiaException("AudioLoader ERROR: found 0 streams in the file, expecting one or more audio streams"); - } - - if (_selectedStream >= nAudioStreams) { - avformat_close_input(&_demuxCtx); - _demuxCtx = 0; - throw EssentiaException("AudioLoader ERROR: 'audioStream' parameter set to ", _selectedStream ,". It should be smaller than the audio streams count, ", nAudioStreams); - } - - _streamIdx = _streams[_selectedStream]; - - // Load corresponding audio codec - _audioCtx = _demuxCtx->streams[_streamIdx]->codec; - _audioCodec = avcodec_find_decoder(_audioCtx->codec_id); - - if (!_audioCodec) { - throw EssentiaException("AudioLoader: Unsupported codec!"); - } - - if (avcodec_open2(_audioCtx, _audioCodec, NULL) < 0) { - throw EssentiaException("AudioLoader: Unable to instantiate codec..."); - } - - // Configure format convertion (no samplerate conversion yet) - int64_t layout = av_get_default_channel_layout(_audioCtx->channels); - - /* - const char* fmt = 0; - get_format_from_sample_fmt(&fmt, _audioCtx->sample_fmt); - E_DEBUG(EAlgorithm, "AudioLoader: converting from " << (fmt ? fmt : "unknown") << " to FLT"); - */ - - E_DEBUG(EAlgorithm, "AudioLoader: using sample format conversion from libavresample"); - -#ifndef TARGET_IOS - _convertCtxAv = avresample_alloc(); -#else - _convertCtxAv = swr_alloc(); -#endif - - av_opt_set_int(_convertCtxAv, "in_channel_layout", layout, 0); - av_opt_set_int(_convertCtxAv, "out_channel_layout", layout, 0); - av_opt_set_int(_convertCtxAv, "in_sample_rate", _audioCtx->sample_rate, 0); - av_opt_set_int(_convertCtxAv, "out_sample_rate", _audioCtx->sample_rate, 0); - av_opt_set_int(_convertCtxAv, "in_sample_fmt", _audioCtx->sample_fmt, 0); - av_opt_set_int(_convertCtxAv, "out_sample_fmt", AV_SAMPLE_FMT_FLT, 0); - -#ifndef TARGET_IOS - if (avresample_open(_convertCtxAv) < 0) { - throw EssentiaException("AudioLoader: Could not initialize avresample context"); - } -#else - #if defined(USE_ACCELERATE) -#warning TODO - #else - swr_init(_convertCtxAv); - - if (swr_is_initialized(_convertCtxAv) == 0) { - throw EssentiaException("AudioLoader: Could not initialize swresample context"); - } - #endif -#endif - av_init_packet(&_packet); - _decodedFrame = av_frame_alloc(); - if (!_decodedFrame) { - throw EssentiaException("AudioLoader: Could not allocate audio frame"); - } - - av_md5_init(_md5Encoded); // TODO -#endif -#warning TODO +// av_md5_init(_md5Encoded); // TODO } void AudioLoader::closeAudioFile() { -#if defined(USE_ACCELERATE) -#else - if (!_demuxCtx) { - return; - } -#endif - -#ifndef TARGET_IOS - if (_convertCtxAv) { - avresample_close(_convertCtxAv); - avresample_free(&_convertCtxAv); - } -#else - #if defined(USE_ACCELERATE) - #else - if (_convertCtxAv) { - swr_close(_convertCtxAv); - swr_free(&_convertCtxAv); - } - #endif -#endif - -#if defined(USE_ACCELERATE) if ( _file != NULL ) { ExtAudioFileDispose( _file ); _file = NULL; } -#else - // Close the codec - if (_audioCtx) avcodec_close(_audioCtx); - // Close the audio file - if (_demuxCtx) avformat_close_input(&_demuxCtx); - - // free AVPacket - // TODO: use a variable for whether _packet is initialized or not - av_free_packet(&_packet); - _demuxCtx = 0; - _audioCtx = 0; - - _streams.clear(); -#endif } @@ -264,7 +108,6 @@ namespace essentia { throw EssentiaException("AudioLoader: Trying to call process() on an AudioLoader algo which hasn't been correctly configured."); } -#if defined(USE_ACCELERATE) auto audioFormat = ({ AudioStreamBasicDescription audioDescription; memset(&audioDescription, 0, sizeof(audioDescription)); @@ -279,11 +122,7 @@ namespace essentia { audioDescription; }); - SInt64 frameCount = FFMPEG_BUFFER_SIZE / (audioFormat.mBytesPerFrame * audioFormat.mChannelsPerFrame); -// UInt32 propertySize = sizeof(frameCount); -// if ( ExtAudioFileGetProperty(_file, kExtAudioFileProperty_FileLengthFrames, &propertySize, &frameCount) != noErr ) { -// throw EssentiaException("AudioLoader: Error getting file size"); -// } + const SInt64 frameCount = 4096; // Create temporary audio bufferlist int numberOfBuffers = audioFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved ? audioFormat.mChannelsPerFrame : 1; @@ -340,24 +179,13 @@ namespace essentia { if (_nChannels == 1) { for (int i=0; imBuffers[0].mData)[i]; - //audio[i].left() = scale(_buffer[i]); } } else { // _nChannels == 2 - // The output format is always AV_SAMPLE_FMT_FLT, which is interleaved for (int i=0; imBuffers[0].mData)[i]; audio[i].right() = ((float *)bufferList->mBuffers[1].mData)[i]; - //audio[i].left() = scale(_buffer[2*i]); - //audio[i].right() = scale(_buffer[2*i+1]); } - /* - // planar - for (int i=0; i 0) { - if (!decodePacket()) break; - copyFFmpegOutput(); - } - // neds to be freed !! - av_free_packet(&_packet); - -#endif - return OK; - } - - -#if defined(USE_ACCELERATE) -#warning TODO -#else - int AudioLoader::decode_audio_frame(AVCodecContext* audioCtx, - float* output, - int* outputSize, - AVPacket* packet) { - - // _dataSize input = number of bytes available for write in buff - // output = number of bytes actually written (actual: FLT data) - //E_DEBUG(EAlgorithm, "decode_audio_frame, available bytes in buffer = " << _dataSize); - int gotFrame = 0; - av_frame_unref(_decodedFrame); //avcodec_get_frame_defaults(_decodedFrame); - - int len = avcodec_decode_audio4(audioCtx, _decodedFrame, &gotFrame, packet); - - if (len < 0) return len; // error handling should be done outside - - if (gotFrame) { - int inputSamples = _decodedFrame->nb_samples; - int inputPlaneSize = av_samples_get_buffer_size(NULL, _nChannels, inputSamples, - audioCtx->sample_fmt, 1); - int outputPlaneSize = av_samples_get_buffer_size(NULL, _nChannels, inputSamples, - AV_SAMPLE_FMT_FLT, 1); - // the size of the output buffer in samples - int outputBufferSamples = *outputSize / - (av_get_bytes_per_sample(AV_SAMPLE_FMT_FLT) * _nChannels); - - if (outputBufferSamples < inputSamples) { - // this should never happen, throw exception here - throw EssentiaException("AudioLoader: Insufficient buffer size for format conversion"); - } - - if (audioCtx->sample_fmt == AV_SAMPLE_FMT_FLT) { - // TODO: no need in this check? Not many of common formats support FLT - // no conversion needed, direct copy from our frame to output buffer - memcpy(output, _decodedFrame->data[0], inputPlaneSize); - } - else { -#ifndef TARGET_IOS - int samplesWrittern = avresample_convert(_convertCtxAv, - (uint8_t**) &output, - outputPlaneSize, - outputBufferSamples, - (uint8_t**)_decodedFrame->data, - inputPlaneSize, - inputSamples); -#else - int samplesWrittern = swr_convert(_convertCtxAv, - (uint8_t**)&output, - outputBufferSamples, - (const uint8_t**)_decodedFrame->data, - inputSamples); -#endif - if (samplesWrittern < inputSamples) { - // TODO: there may be data remaining in the internal FIFO buffer - // to get this data: call avresample_convert() with NULL input - // Test if this happens in practice - ostringstream msg; - msg << "AudioLoader: Incomplete format conversion (some samples missing)" - << " from " << av_get_sample_fmt_name(_audioCtx->sample_fmt) - << " to " << av_get_sample_fmt_name(AV_SAMPLE_FMT_FLT); - throw EssentiaException(msg); - } - } - *outputSize = outputPlaneSize; - } - else { - E_DEBUG(EAlgorithm, "AudioLoader: tried to decode packet but didn't get any frame..."); - *outputSize = 0; - } - - return len; - } -#endif - - - void AudioLoader::flushPacket() { -#if defined(USE_ACCELERATE) -#warning TODO -#else - AVPacket empty; - av_init_packet(&empty); - do { - _dataSize = FFMPEG_BUFFER_SIZE; - empty.data = NULL; - empty.size = 0; - - int len = decode_audio_frame(_audioCtx, _buffer, &_dataSize, &empty); - if (len < 0) { - char errstring[1204]; - av_strerror(len, errstring, sizeof(errstring)); - ostringstream msg; - msg << "AudioLoader: decoding error while flushing a packet:" << errstring; - E_WARNING(msg.str()); - } - copyFFmpegOutput(); - - } while (_dataSize > 0); -#endif - } - - - /** - * Gets the AVPacket stored in _packet, and decodes all the samples it can from it, - * putting them in _buffer, the total number of bytes written begin stored in _dataSize. - */ - int AudioLoader::decodePacket() { - /* - E_DEBUG(EAlgorithm, "-----------------------------------------------------"); - E_DEBUG(EAlgorithm, "decoding packet of " << _packet.size << " bytes"); - E_DEBUG(EAlgorithm, "pts: " << _packet.pts << " - dts: " << _packet.dts); //" - pos: " << pkt->pos); - E_DEBUG(EAlgorithm, "flags: " << _packet.flags); - E_DEBUG(EAlgorithm, "duration: " << _packet.duration); - */ - int len = 0; -#if defined(USE_ACCELERATE) -#warning TODO -#else - - // buff is an offset in our output buffer, it points to where we should start - // writing the next decoded samples - float* buff = _buffer; - - // _dataSize gets the size of the buffer, in bytes - _dataSize = FFMPEG_BUFFER_SIZE; - - // Note: md5 should be computed before decoding frame, as the decoding may - // change the content of a packet. Still, not sure if it is correct to - // compute md5 over packet which contains incorrect frames, potentially - // belonging to id3 metadata (TODO: or is it just a missing header issue?), - // but computing md5 hash using ffmpeg will also treat it as audio: - // ffmpeg -i file.mp3 -acodec copy -f md5 - - - len = decode_audio_frame(_audioCtx, buff, &_dataSize, &_packet); - - if (len < 0) { - char errstring[1204]; - av_strerror(len, errstring, sizeof(errstring)); - ostringstream msg; - - if (_audioCtx->codec_id == AV_CODEC_ID_MP3) { - msg << "AudioLoader: invalid frame, skipping it: " << errstring; - // mp3 streams can have tag frames (id3v2?) which libavcodec tries to - // read as audio anyway, and we probably don't want print an error - // message for that... - // TODO: Are these frames really id3 tags? - - //E_DEBUG(EAlgorithm, msg); - E_WARNING(msg.str()); - } - else { - msg << "AudioLoader: error while decoding, skipping frame: " << errstring; - E_WARNING(msg.str()); - } - return 0; - } - - if (len != _packet.size) { - // https://www.ffmpeg.org/doxygen/trunk/group__lavc__decoding.html#ga834bb1b062fbcc2de4cf7fb93f154a3e - - // Some decoders may support multiple frames in a single AVPacket. Such - // decoders would then just decode the first frame and the return value - // would be less than the packet size. In this case, avcodec_decode_audio4 - // has to be called again with an AVPacket containing the remaining data - // in order to decode the second frame, etc... Even if no frames are - // returned, the packet needs to be fed to the decoder with remaining - // data until it is completely consumed or an error occurs. - - E_WARNING("AudioLoader: more than 1 frame in packet, decoding remaining bytes..."); - E_WARNING("at sample index: " << output("audio").totalProduced()); - E_WARNING("decoded samples: " << len); - E_WARNING("packet size: " << _packet.size); - } - - // update packet data pointer to data left undecoded (if any) - _packet.size -= len; - _packet.data += len; - - if (_dataSize <= 0) { - // No data yet, get more frames - // cout << "no data yet, get more frames" << endl; - _dataSize = 0; - } -#endif - - return len; - } - - /* - inline Real scale(int16_t value) { - return value / (Real)32767; - } - */ - - void AudioLoader::copyFFmpegOutput() { -#if defined(USE_ACCELERATE) -#else - int nsamples = _dataSize / (av_get_bytes_per_sample(AV_SAMPLE_FMT_FLT) * _nChannels); - if (nsamples == 0) return; - - // acquire necessary data - bool ok = _audio.acquire(nsamples); - if (!ok) { - throw EssentiaException("AudioLoader: could not acquire output for audio"); - } - - vector& audio = *((vector*)_audio.getTokens()); - - if (_nChannels == 1) { - for (int i=0; ichannels, _audioCtx->sample_rate); - pushCodecInfo(_audioCodec->name, _audioCtx->bit_rate); -#endif } } // namespace streaming diff --git a/src/internal/audioloader_ios_native.h b/src/internal/audioloader_ios_native.h deleted file mode 100755 index 4481aff..0000000 --- a/src/internal/audioloader_ios_native.h +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Copyright (C) 2006-2016 Music Technology Group - Universitat Pompeu Fabra - * - * This file is part of Essentia - * - * Essentia is free software: you can redistribute it and/or modify it under - * the terms of the GNU Affero General Public License as published by the Free - * Software Foundation (FSF), either version 3 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the Affero GNU General Public License - * version 3 along with this program. If not, see http://www.gnu.org/licenses/ - */ - -#ifndef ESSENTIA_STREAMING_AUDIOLOADER_H -#define ESSENTIA_STREAMING_AUDIOLOADER_H - -#if __APPLE__ -#import "TargetConditionals.h" -#endif - -#if (TARGET_OS_IPHONE_SIMULATOR) || (TARGET_OS_IPHONE) || (TARGET_IPHONE) -#define TARGET_IOS -#endif - -#include "streamingalgorithm.h" -#include "network.h" -#include "poolstorage.h" - -#if defined(USE_ACCELERATE) -#include -#else -#include "ffmpegapi.h" - -extern AVInputFormat ff_wav_demuxer; -#endif - -#define MAX_AUDIO_FRAME_SIZE 192000 - -namespace essentia { -namespace streaming { - -class AudioLoader : public Algorithm { - protected: - Source _audio; - AbsoluteSource _sampleRate; - AbsoluteSource _channels; - AbsoluteSource _md5; - AbsoluteSource _bit_rate; - AbsoluteSource _codec; - - int _nChannels; - - // MAX_AUDIO_FRAME_SIZE is in bytes, multiply it by 2 to get some margin, - // because we might want to decode multiple frames in this buffer (all the - // frames contained in a packet, which can be more than 1 as in flac), and - // each time we decode a frame we need to have at least a full buffer of free space. - const static int FFMPEG_BUFFER_SIZE = MAX_AUDIO_FRAME_SIZE * 2; - -// float* _buffer; -// int _dataSize; - -#if defined(USE_ACCELERATE) - ExtAudioFileRef _file; -#else - AVFormatContext* _demuxCtx; - AVCodecContext* _audioCtx; - AVCodec* _audioCodec; - AVPacket _packet; - AVFrame* _decodedFrame; - AVMD5 *_md5Encoded; -#endif - uint8_t _checksum[16]; - bool _computeMD5; - -#ifndef TARGET_IOS - struct AVAudioResampleContext* _convertCtxAv; -#else -#if defined(USE_ACCELERATE) -#else - struct SwrContext* _convertCtxAv; - - int _streamIdx; // index of the audio stream among all the streams contained in the file - std::vector _streams; - bool _configured; -#endif - int _selectedStream; -#endif - - void openAudioFile(const std::string& filename); - void closeAudioFile(); - - void pushChannelsSampleRateInfo(int nChannels, Real sampleRate); - void pushCodecInfo(std::string codec, int bit_rate); - -#if defined(USE_ACCELERATE) -#else - int decode_audio_frame(AVCodecContext* audioCtx, float* output, - int* outputSize, AVPacket* packet); -#endif - - int decodePacket(); - void flushPacket(); - void copyFFmpegOutput(); - - - public: - AudioLoader() - : Algorithm() -#if defined(USE_ACCELERATE) -#else - , _buffer(0) - , _demuxCtx(0), - _audioCtx(0), - _audioCodec(0), - _decodedFrame(0), - _convertCtxAv(0), - _configured(false) -#endif - - { - - declareOutput(_audio, 1, "audio", "the input audio signal"); - declareOutput(_sampleRate, 0, "sampleRate", "the sampling rate of the audio signal [Hz]"); - declareOutput(_channels, 0, "numberChannels", "the number of channels"); - declareOutput(_md5, 0, "md5", "the MD5 checksum of raw undecoded audio payload"); - declareOutput(_bit_rate, 0, "bit_rate", "the bit rate of the input audio, as reported by the decoder codec"); - declareOutput(_codec, 0, "codec", "the codec that is used to decode the input audio"); - - _audio.setBufferType(BufferUsage::forLargeAudioStream); - -#if defined(USE_ACCELERATE) - // TODO: (16 bit alignment) - - // TODO: MD5 encoding -#else - // Register all formats and codecs - av_register_all(); - - // use av_malloc, because we _need_ the buffer to be 16-byte aligned - _buffer = (float*)av_malloc(FFMPEG_BUFFER_SIZE); - - _md5Encoded = av_md5_alloc(); - if (!_md5Encoded) { - throw EssentiaException("Error allocating the MD5 context"); - } - - #endif - } - - ~AudioLoader(); - - AlgorithmStatus process(); - void reset(); - - void declareParameters() { - declareParameter("filename", "the name of the file from which to read", "", Parameter::STRING); - declareParameter("computeMD5", "compute the MD5 checksum", "{true,false}", false); - declareParameter("audioStream", "audio stream index to be loaded. Other streams are not taken into account (e.g. if stream 0 is video and 1 is audio use index 0 to access it.)", "[0,inf)", 0); - } - - void configure(); - - static const char* name; - static const char* category; - static const char* description; - -}; - -} // namespace streaming -} // namespace essentia - - -#include "vectoroutput.h" -#include "algorithm.h" - -namespace essentia { -namespace standard { - -// Standard non-streaming algorithm comes after the streaming one as it -// depends on it -class AudioLoader : public Algorithm { - - protected: - Output > _audio; - Output _sampleRate; - Output _channels; - Output _md5; - Output _bit_rate; - Output _codec; - - streaming::Algorithm* _loader; - streaming::VectorOutput* _audioStorage; - - scheduler::Network* _network; - Pool _pool; - - void createInnerNetwork(); - - public: - AudioLoader() { - declareOutput(_audio, "audio", "the input audio signal"); - declareOutput(_sampleRate, "sampleRate", "the sampling rate of the audio signal [Hz]"); - declareOutput(_channels, "numberChannels", "the number of channels"); - declareOutput(_md5, "md5", "the MD5 checksum of raw undecoded audio payload"); - declareOutput(_bit_rate, "bit_rate", "the bit rate of the input audio, as reported by the decoder codec"); - declareOutput(_codec, "codec", "the codec that is used to decode the input audio"); - - createInnerNetwork(); - } - - ~AudioLoader() { - // NB: this will also delete all the algorithms as the Network took ownership of them - delete _network; - } - - void declareParameters() { - declareParameter("filename", "the name of the file from which to read", "", Parameter::STRING); - declareParameter("computeMD5", "compute the MD5 checksum", "{true,false}", false); - declareParameter("audioStream", "audio stream index to be loaded. Other streams are no taken into account (e.g. if stream 0 is video and 1 is audio use index 0 to access it.)", "[0,inf)", 0); - } - - void configure(); - - void compute(); - void reset(); - - static const char* name; - static const char* category; - static const char* description; -}; - -} // namespace standard -} // namespace essentia - -#endif // ESSENTIA_STREAMING_AUDIOLOADER_H From 0701cba9623fe6bd1fae79ce93aea516bf63fda2 Mon Sep 17 00:00:00 2001 From: ragnaringi Date: Fri, 24 Aug 2018 20:41:15 +0000 Subject: [PATCH 03/18] Project housekeeping --- ESMusicExtractor.xcodeproj/project.pbxproj | 14 +++++++++++--- src/internal/{ => 3rdparty}/ffmpegapi.h | 0 src/internal/{ => 3rdparty}/taglib_config.h | 0 src/internal/{ => 3rdparty}/version.h | 0 4 files changed, 11 insertions(+), 3 deletions(-) rename src/internal/{ => 3rdparty}/ffmpegapi.h (100%) rename src/internal/{ => 3rdparty}/taglib_config.h (100%) rename src/internal/{ => 3rdparty}/version.h (100%) diff --git a/ESMusicExtractor.xcodeproj/project.pbxproj b/ESMusicExtractor.xcodeproj/project.pbxproj index 4cea08e..9279ac7 100644 --- a/ESMusicExtractor.xcodeproj/project.pbxproj +++ b/ESMusicExtractor.xcodeproj/project.pbxproj @@ -2914,14 +2914,12 @@ isa = PBXGroup; children = ( C0B55A02211B27D0007817F6 /* music_extractor */, - C088C618211B54C3009ED9DB /* taglib_config.h */, + C0B9C2672130A4140029DA6F /* 3rdparty */, C0B559FE211B27D0007817F6 /* audioloader_ios.cpp */, C037276321304F4C00E9C15C /* audioloader_ios_native.cpp */, C037276421304F4C00E9C15C /* audioloader_ios.h */, C0B55A00211B27D0007817F6 /* essentia_algorithms_reg.cpp */, - C0B55A01211B27D0007817F6 /* ffmpegapi.h */, C0B55A05211B27D0007817F6 /* streaming_extractor_music.h */, - C0B55A06211B27D0007817F6 /* version.h */, ); path = internal; sourceTree = ""; @@ -2935,6 +2933,16 @@ path = music_extractor; sourceTree = ""; }; + C0B9C2672130A4140029DA6F /* 3rdparty */ = { + isa = PBXGroup; + children = ( + C0B55A01211B27D0007817F6 /* ffmpegapi.h */, + C088C618211B54C3009ED9DB /* taglib_config.h */, + C0B55A06211B27D0007817F6 /* version.h */, + ); + path = 3rdparty; + sourceTree = ""; + }; C0BA3B8C211A31E90038D19F /* Frameworks */ = { isa = PBXGroup; children = ( diff --git a/src/internal/ffmpegapi.h b/src/internal/3rdparty/ffmpegapi.h similarity index 100% rename from src/internal/ffmpegapi.h rename to src/internal/3rdparty/ffmpegapi.h diff --git a/src/internal/taglib_config.h b/src/internal/3rdparty/taglib_config.h similarity index 100% rename from src/internal/taglib_config.h rename to src/internal/3rdparty/taglib_config.h diff --git a/src/internal/version.h b/src/internal/3rdparty/version.h similarity index 100% rename from src/internal/version.h rename to src/internal/3rdparty/version.h From 839e3ff893e946852f95423e5a8687b280128c90 Mon Sep 17 00:00:00 2001 From: ragnaringi Date: Fri, 24 Aug 2018 20:47:55 +0000 Subject: [PATCH 04/18] MD5 hash computed using CommonCrypto. Output is identical to AVMD5 but differs from Essentia implementation which uses AVPacket data. --- src/internal/audioloader_ios.h | 13 ++++------- src/internal/audioloader_ios_native.cpp | 29 ++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/src/internal/audioloader_ios.h b/src/internal/audioloader_ios.h index e96c41b..c88b177 100755 --- a/src/internal/audioloader_ios.h +++ b/src/internal/audioloader_ios.h @@ -19,12 +19,13 @@ #pragma once +#include +#include + #include "streamingalgorithm.h" #include "network.h" #include "poolstorage.h" -#include - namespace essentia { namespace streaming { @@ -41,8 +42,7 @@ class AudioLoader : public Algorithm { ExtAudioFileRef _file; -// AVMD5 *_md5Encoded; - uint8_t _checksum[16]; + CC_MD5_CTX hashObject; bool _computeMD5; void openAudioFile(const std::string& filename); @@ -62,11 +62,6 @@ class AudioLoader : public Algorithm { declareOutput(_codec, 0, "codec", "the codec that is used to decode the input audio"); _audio.setBufferType(BufferUsage::forLargeAudioStream); - -// _md5Encoded = av_md5_alloc(); -// if (!_md5Encoded) { -// throw EssentiaException("Error allocating the MD5 context"); -// } } ~AudioLoader(); diff --git a/src/internal/audioloader_ios_native.cpp b/src/internal/audioloader_ios_native.cpp index 1ad9153..2171e6d 100644 --- a/src/internal/audioloader_ios_native.cpp +++ b/src/internal/audioloader_ios_native.cpp @@ -28,6 +28,8 @@ #include "audioloader_ios.h" #include "algorithmfactory.h" #include // setw() +// Cryptography +#include using namespace std; @@ -61,7 +63,9 @@ namespace essentia { throw EssentiaException("AudioLoader: Could not open file \"", filename, "\", error = ", result); } -// av_md5_init(_md5Encoded); // TODO + if ( !CC_MD5_Init(&hashObject) ) { + throw EssentiaException("Error allocating the MD5 context"); + } } @@ -176,15 +180,21 @@ namespace essentia { vector& audio = *((vector*)_audio.getTokens()); + float interleaved[nsamples * _nChannels]; + if (_nChannels == 1) { for (int i=0; imBuffers[0].mData)[i]; + interleaved[i] = audio[i].left(); } } else { // _nChannels == 2 for (int i=0; imBuffers[0].mData)[i]; audio[i].right() = ((float *)bufferList->mBuffers[1].mData)[i]; + + interleaved[2*i] = audio[i].left(); + interleaved[2*i+1] = audio[i].right(); } } @@ -197,8 +207,25 @@ namespace essentia { } free(bufferList); + // compute md5 first + if (_computeMD5) { + CC_MD5_Update(&hashObject, + (const void *)interleaved, + (CC_LONG)nsamples*_nChannels); + } + if ( eof ) { closeAudioFile(); + if (_computeMD5) { + // Compute the hash digest + unsigned char digest[CC_MD5_DIGEST_LENGTH]; + CC_MD5_Final(digest, &hashObject); + _md5.push(uint8_t_to_hex(digest, 16)); + } + else { + string md5 = ""; + _md5.push(md5); + } } return OK; From 14e6d774406f7918af9bf8006835e684cfa578f5 Mon Sep 17 00:00:00 2001 From: ragnaringi Date: Wed, 29 Aug 2018 08:56:40 +0000 Subject: [PATCH 05/18] Correct MD5 calculation for wav files --- src/internal/audioloader_ios_native.cpp | 430 ++++++++++++------------ 1 file changed, 224 insertions(+), 206 deletions(-) diff --git a/src/internal/audioloader_ios_native.cpp b/src/internal/audioloader_ios_native.cpp index 2171e6d..e690cfe 100644 --- a/src/internal/audioloader_ios_native.cpp +++ b/src/internal/audioloader_ios_native.cpp @@ -25,247 +25,265 @@ * version 3 along with this program. If not, see http://www.gnu.org/licenses/ */ -#include "audioloader_ios.h" -#include "algorithmfactory.h" + +#include #include // setw() -// Cryptography #include +#include "audioloader_ios.h" +#include "algorithmfactory.h" + using namespace std; -namespace essentia { - namespace streaming { - - const char* AudioLoader::name = essentia::standard::AudioLoader::name; - const char* AudioLoader::category = essentia::standard::AudioLoader::category; - const char* AudioLoader::description = essentia::standard::AudioLoader::description; - +static std::vector readAllBytes(char const* filename) { + ifstream ifs(filename, ios::binary|ios::ate); + ifstream::pos_type pos = ifs.tellg(); + + vector result(pos); + + ifs.seekg(0, ios::beg); + ifs.read(result.data(), pos); + + string header(result.begin(), result.begin() + 4); + + if (header == "RIFF") { // WAVE file + return vector( result.begin() + 44, result.end() ); + } else if (header == "FORM") { + return result; + } + // TODO: Handle other formats + return result; +} + +string uint8_t_to_hex(uint8_t* input, int size) { + ostringstream result; + for(int i=0; i 2) { + throw EssentiaException("AudioLoader: could not load audio. Audio file has more than 2 channels."); + } + if (sampleRate <= 0) { + throw EssentiaException("AudioLoader: could not load audio. Audio sampling rate must be greater than 0."); } + _nChannels = nChannels; - void AudioLoader::closeAudioFile() { - if ( _file != NULL ) { - ExtAudioFileDispose( _file ); - _file = NULL; - } + _channels.push(nChannels); + _sampleRate.push(sampleRate); + } + + void AudioLoader::pushCodecInfo(std::string codec, int bit_rate) { + _codec.push(codec); + _bit_rate.push(bit_rate); + } + + AlgorithmStatus AudioLoader::process() { + if (!parameter("filename").isConfigured()) { + throw EssentiaException("AudioLoader: Trying to call process() on an AudioLoader algo which hasn't been correctly configured."); } + auto audioFormat = ({ + AudioStreamBasicDescription audioDescription; + memset(&audioDescription, 0, sizeof(audioDescription)); + audioDescription.mFormatID = kAudioFormatLinearPCM; + audioDescription.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked | kAudioFormatFlagIsNonInterleaved; + audioDescription.mChannelsPerFrame = 2; + audioDescription.mBytesPerPacket = sizeof(float); + audioDescription.mFramesPerPacket = 1; + audioDescription.mBytesPerFrame = sizeof(float); + audioDescription.mBitsPerChannel = 8 * sizeof(float); + audioDescription.mSampleRate = 44100.0; + audioDescription; + }); - void AudioLoader::pushChannelsSampleRateInfo(int nChannels, Real sampleRate) { - if (nChannels > 2) { - throw EssentiaException("AudioLoader: could not load audio. Audio file has more than 2 channels."); - } - if (sampleRate <= 0) { - throw EssentiaException("AudioLoader: could not load audio. Audio sampling rate must be greater than 0."); + const SInt64 frameCount = 4096; + + // Create temporary audio bufferlist + int numberOfBuffers = audioFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved ? audioFormat.mChannelsPerFrame : 1; + int channelsPerBuffer = audioFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved ? 1 : audioFormat.mChannelsPerFrame; + int bytesPerBuffer = audioFormat.mBytesPerFrame * frameCount; + + AudioBufferList *bufferList = (AudioBufferList *)malloc(sizeof(AudioBufferList) + (numberOfBuffers-1)*sizeof(AudioBuffer)); + if ( !bufferList ) { + throw EssentiaException("AudioLoader: Error creating AudioBufferList"); + } + bufferList->mNumberBuffers = numberOfBuffers; + for ( int i=0; i 0 ) { + bufferList->mBuffers[i].mData = calloc(bytesPerBuffer, 1); + if ( !bufferList->mBuffers[i].mData ) { + for ( int j=0; jmBuffers[j].mData); + free(bufferList); + throw EssentiaException("AudioLoader: Error allocating data for AudioBufferList"); + } + } else { + bufferList->mBuffers[i].mData = NULL; } - - _nChannels = nChannels; - - _channels.push(nChannels); - _sampleRate.push(sampleRate); + bufferList->mBuffers[i].mDataByteSize = bytesPerBuffer; + bufferList->mBuffers[i].mNumberChannels = channelsPerBuffer; } + // Set destination format + if ( ExtAudioFileSetProperty(_file, kExtAudioFileProperty_ClientDataFormat, sizeof(audioFormat), &audioFormat) != noErr ) { + throw EssentiaException("AudioLoader: Error setting client data format"); + } - void AudioLoader::pushCodecInfo(std::string codec, int bit_rate) { - _codec.push(codec); - _bit_rate.push(bit_rate); + // Read audio + UInt32 readFrames = frameCount; + if ( ExtAudioFileRead(_file, &readFrames, bufferList) != noErr ) { + throw EssentiaException("AudioLoader: Error reading file" ); + } + + bool eof = readFrames < frameCount; + if ( eof ) { // EOF + shouldStop(true); } + int nsamples = readFrames; - string uint8_t_to_hex(uint8_t* input, int size) { - ostringstream result; - for(int i=0; i& audio = *((vector*)_audio.getTokens()); - AlgorithmStatus AudioLoader::process() { - if (!parameter("filename").isConfigured()) { - throw EssentiaException("AudioLoader: Trying to call process() on an AudioLoader algo which hasn't been correctly configured."); - } - - auto audioFormat = ({ - AudioStreamBasicDescription audioDescription; - memset(&audioDescription, 0, sizeof(audioDescription)); - audioDescription.mFormatID = kAudioFormatLinearPCM; - audioDescription.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked | kAudioFormatFlagIsNonInterleaved; - audioDescription.mChannelsPerFrame = 2; - audioDescription.mBytesPerPacket = sizeof(float); - audioDescription.mFramesPerPacket = 1; - audioDescription.mBytesPerFrame = sizeof(float); - audioDescription.mBitsPerChannel = 8 * sizeof(float); - audioDescription.mSampleRate = 44100.0; - audioDescription; - }); - - const SInt64 frameCount = 4096; - - // Create temporary audio bufferlist - int numberOfBuffers = audioFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved ? audioFormat.mChannelsPerFrame : 1; - int channelsPerBuffer = audioFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved ? 1 : audioFormat.mChannelsPerFrame; - int bytesPerBuffer = audioFormat.mBytesPerFrame * frameCount; - - AudioBufferList *bufferList = (AudioBufferList *)malloc(sizeof(AudioBufferList) + (numberOfBuffers-1)*sizeof(AudioBuffer)); - if ( !bufferList ) { - throw EssentiaException("AudioLoader: Error creating AudioBufferList"); - } - bufferList->mNumberBuffers = numberOfBuffers; - for ( int i=0; i 0 ) { - bufferList->mBuffers[i].mData = calloc(bytesPerBuffer, 1); - if ( !bufferList->mBuffers[i].mData ) { - for ( int j=0; jmBuffers[j].mData); - free(bufferList); - throw EssentiaException("AudioLoader: Error allocating data for AudioBufferList"); - } - } else { - bufferList->mBuffers[i].mData = NULL; - } - bufferList->mBuffers[i].mDataByteSize = bytesPerBuffer; - bufferList->mBuffers[i].mNumberChannels = channelsPerBuffer; - } - - // Set destination format - if ( ExtAudioFileSetProperty(_file, kExtAudioFileProperty_ClientDataFormat, sizeof(audioFormat), &audioFormat) != noErr ) { - throw EssentiaException("AudioLoader: Error setting client data format"); + if (_nChannels == 1) { + for (int i=0; imBuffers[0].mData)[i]; } - - // Read audio - // TODO: Read in chunks instead of whole file at once - UInt32 readFrames = frameCount; - if ( ExtAudioFileRead(_file, &readFrames, bufferList) != noErr ) { - throw EssentiaException("AudioLoader: Error reading file" ); - } - // assert( readFrames == frameCount && "Read frames don't match file size" ); - bool eof = readFrames < frameCount; - if ( eof ) { // EOF - shouldStop(true); - } - - int nsamples = readFrames; - - // acquire necessary data - bool ok = _audio.acquire(nsamples); - if (!ok) { - throw EssentiaException("AudioLoader: could not acquire output for audio"); - } - - vector& audio = *((vector*)_audio.getTokens()); - - float interleaved[nsamples * _nChannels]; - - if (_nChannels == 1) { - for (int i=0; imBuffers[0].mData)[i]; - interleaved[i] = audio[i].left(); - } - } - else { // _nChannels == 2 - for (int i=0; imBuffers[0].mData)[i]; - audio[i].right() = ((float *)bufferList->mBuffers[1].mData)[i]; - - interleaved[2*i] = audio[i].left(); - interleaved[2*i+1] = audio[i].right(); - } - } - - // release data - _audio.release(nsamples); - - // Free bufferlist - for ( int i=0; imNumberBuffers; i++ ) { - if ( bufferList->mBuffers[i].mData ) free(bufferList->mBuffers[i].mData); - } - free(bufferList); - - // compute md5 first - if (_computeMD5) { - CC_MD5_Update(&hashObject, - (const void *)interleaved, - (CC_LONG)nsamples*_nChannels); - } - - if ( eof ) { - closeAudioFile(); - if (_computeMD5) { - // Compute the hash digest - unsigned char digest[CC_MD5_DIGEST_LENGTH]; - CC_MD5_Final(digest, &hashObject); - _md5.push(uint8_t_to_hex(digest, 16)); - } - else { - string md5 = ""; - _md5.push(md5); - } + } + else { // _nChannels == 2 + for (int i=0; imBuffers[0].mData)[i]; + audio[i].right() = ((float *)bufferList->mBuffers[1].mData)[i]; } - - return OK; } - void AudioLoader::reset() { - Algorithm::reset(); - - if (!parameter("filename").isConfigured()) return; - - string filename = parameter("filename").toString(); - + // release data + _audio.release(nsamples); + + // Free bufferlist + for ( int i=0; imNumberBuffers; i++ ) { + if ( bufferList->mBuffers[i].mData ) free(bufferList->mBuffers[i].mData); + } + free(bufferList); + + if ( eof ) { closeAudioFile(); - openAudioFile(filename); - - AudioStreamBasicDescription asbd; - UInt32 propertySize = sizeof(asbd); - if ( ExtAudioFileGetProperty(_file, kExtAudioFileProperty_FileDataFormat, &propertySize, &asbd) != noErr ) { - throw EssentiaException("AudioLoader: Error getting audio file channel and sample rate info"); - } - pushChannelsSampleRateInfo(asbd.mChannelsPerFrame, asbd.mSampleRate); - - OSStatus status = noErr; - AudioFileID audioFileId = NULL; - - UInt32 size = sizeof(audioFileId); - status = ExtAudioFileGetProperty(_file, kExtAudioFileProperty_AudioFile, &size, &audioFileId); - assert(status == noErr); - - UInt32 bitRate = 0; - size = sizeof(bitRate); - if ( AudioFileGetProperty(audioFileId, kAudioFilePropertyBitRate, &size, &bitRate) != noErr ) { - throw EssentiaException("AudioLoader: Error getting audio file bitRate"); - } - - E_DEBUG(EAlgorithm, "AudioLoader: TODO: get audio codec"); - pushCodecInfo("pcm_s16le", bitRate); + } + + return OK; + } + + void AudioLoader::reset() { + Algorithm::reset(); + + if (!parameter("filename").isConfigured()) return; + + string filename = parameter("filename").toString(); + + closeAudioFile(); + openAudioFile(filename); + + // Get audio file data format + AudioStreamBasicDescription asbd; + UInt32 propertySize = sizeof(asbd); + if ( ExtAudioFileGetProperty(_file, kExtAudioFileProperty_FileDataFormat, &propertySize, &asbd) != noErr ) { + throw EssentiaException("AudioLoader: Error getting audio file channel and sample rate info"); + } + pushChannelsSampleRateInfo(asbd.mChannelsPerFrame, asbd.mSampleRate); + + // Get bit rate + OSStatus status = noErr; + AudioFileID audioFileId = NULL; + + UInt32 size = sizeof(audioFileId); + status = ExtAudioFileGetProperty(_file, kExtAudioFileProperty_AudioFile, &size, &audioFileId); + assert(status == noErr); + + UInt32 bitRate = 0; + size = sizeof(bitRate); + if ( AudioFileGetProperty(audioFileId, kAudioFilePropertyBitRate, &size, &bitRate) != noErr ) { + throw EssentiaException("AudioLoader: Error getting audio file bitRate"); } - } // namespace streaming + E_DEBUG(EAlgorithm, "AudioLoader: TODO: get audio codec"); + pushCodecInfo("pcm_s16le", bitRate); + } + +} // namespace streaming } // namespace essentia From 33a3f67178183598eec8ba2d6ceb50d0cdfba6ef Mon Sep 17 00:00:00 2001 From: ragnaringi Date: Wed, 29 Aug 2018 11:08:51 +0000 Subject: [PATCH 06/18] Correct MD5 calculation for AIFF file --- src/internal/audioloader_ios_native.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/internal/audioloader_ios_native.cpp b/src/internal/audioloader_ios_native.cpp index e690cfe..27d08fd 100644 --- a/src/internal/audioloader_ios_native.cpp +++ b/src/internal/audioloader_ios_native.cpp @@ -49,7 +49,11 @@ static std::vector readAllBytes(char const* filename) { if (header == "RIFF") { // WAVE file return vector( result.begin() + 44, result.end() ); } else if (header == "FORM") { - return result; + for ( int i = 4; i < result.size(); i++ ) { + if ( string(result.begin()+i, result.begin()+i+4) == "SSND" ) { + return vector( result.begin()+i+16, result.end() ); + } + } } // TODO: Handle other formats return result; From 26d9a77edc50d841657649dbd53ebd6c00eef69d Mon Sep 17 00:00:00 2001 From: ragnaringi Date: Wed, 29 Aug 2018 12:34:44 +0000 Subject: [PATCH 07/18] Correct MD5 calculation for m4a file --- src/internal/audioloader_ios_native.cpp | 28 +++++++++++++++++-------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/src/internal/audioloader_ios_native.cpp b/src/internal/audioloader_ios_native.cpp index 27d08fd..706da7d 100644 --- a/src/internal/audioloader_ios_native.cpp +++ b/src/internal/audioloader_ios_native.cpp @@ -44,18 +44,28 @@ static std::vector readAllBytes(char const* filename) { ifs.seekg(0, ios::beg); ifs.read(result.data(), pos); - string header(result.begin(), result.begin() + 4); - - if (header == "RIFF") { // WAVE file + auto header = string(result.begin(), result.begin() + 4); + if ( header == "RIFF" ) { // WAVE file return vector( result.begin() + 44, result.end() ); - } else if (header == "FORM") { + } else if ( header == "FORM" ) { // AIFF for ( int i = 4; i < result.size(); i++ ) { - if ( string(result.begin()+i, result.begin()+i+4) == "SSND" ) { + auto str = string(result.begin()+i, result.begin()+i+4); + if ( str == "SSND" ) { return vector( result.begin()+i+16, result.end() ); } } + } else { + auto str = string(result.begin()+4, result.begin()+11); + if ( str == "ftypM4A" ) { // m4a + for ( int i = 0; i < result.size()-4; i++ ) { + if ( result[i] == '!' && result[i+3] == '@' && result[i+4] == 'h' ) { + return vector( result.begin()+i, result.end() ); + } + } + } else { + cout << "File format not supported" << endl; + } } - // TODO: Handle other formats return result; } @@ -99,9 +109,9 @@ namespace essentia { namespace streaming { } auto fileData = readAllBytes(filename.c_str()); - for ( int i = 0; i < 100; i++ ) { - std::cout << i << " : " << fileData[i] << std::endl; - } +// for ( int i = 0; i < 100; i++ ) { +// std::cout << i << " : " << fileData[i] << std::endl; +// } // compute md5 first if (_computeMD5) { From a227ef61c93cb5af01475c6e891fba40acb4e3d9 Mon Sep 17 00:00:00 2001 From: ragnaringi Date: Wed, 29 Aug 2018 15:00:47 +0000 Subject: [PATCH 08/18] Correct MD5 calculation for mp3 file --- src/internal/audioloader_ios_native.cpp | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/internal/audioloader_ios_native.cpp b/src/internal/audioloader_ios_native.cpp index 706da7d..8cc0095 100644 --- a/src/internal/audioloader_ios_native.cpp +++ b/src/internal/audioloader_ios_native.cpp @@ -34,7 +34,7 @@ #include "algorithmfactory.h" using namespace std; - +// https://codereview.stackexchange.com/a/22907 static std::vector readAllBytes(char const* filename) { ifstream ifs(filename, ios::binary|ios::ate); ifstream::pos_type pos = ifs.tellg(); @@ -55,8 +55,19 @@ static std::vector readAllBytes(char const* filename) { } } } else { - auto str = string(result.begin()+4, result.begin()+11); - if ( str == "ftypM4A" ) { // m4a + auto str = string(result.begin(), result.begin()+11); + if ( str.substr(0,3) == "ID3" ) { // mp3 + // Extract ID3 tag size from synchsafe integer + // https://stackoverflow.com/a/5652842 + uint8_t* sync_safe = (uint8_t *)result.data()+6; + uint32_t byte0 = sync_safe[0]; + uint32_t byte1 = sync_safe[1]; + uint32_t byte2 = sync_safe[2]; + uint32_t byte3 = sync_safe[3]; + int len = byte0 << 21 | byte1 << 14 | byte2 << 7 | byte3; + int tagSize = len + 10; // + return vector( result.begin()+tagSize, result.end() ); + } else if ( str.substr(4, 11) == "ftypM4A" ) { // m4a for ( int i = 0; i < result.size()-4; i++ ) { if ( result[i] == '!' && result[i+3] == '@' && result[i+4] == 'h' ) { return vector( result.begin()+i, result.end() ); From 2a8f10bd1c7443913cf426b9544f7934278be798 Mon Sep 17 00:00:00 2001 From: ragnaringi Date: Wed, 29 Aug 2018 16:55:04 +0000 Subject: [PATCH 09/18] Adds missing return statement --- src/internal/audioloader_ios_native.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/internal/audioloader_ios_native.cpp b/src/internal/audioloader_ios_native.cpp index 8cc0095..9544a15 100644 --- a/src/internal/audioloader_ios_native.cpp +++ b/src/internal/audioloader_ios_native.cpp @@ -267,6 +267,8 @@ namespace essentia { namespace streaming { if ( eof ) { closeAudioFile(); + + return FINISHED; } return OK; From 3151fbe82faa5eedfe853b32f3f99066a39868f4 Mon Sep 17 00:00:00 2001 From: ragnaringi Date: Wed, 29 Aug 2018 17:03:51 +0000 Subject: [PATCH 10/18] Import correct audioloader version --- src/internal/essentia_algorithms_reg.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/internal/essentia_algorithms_reg.cpp b/src/internal/essentia_algorithms_reg.cpp index 0a7272c..fc753f3 100644 --- a/src/internal/essentia_algorithms_reg.cpp +++ b/src/internal/essentia_algorithms_reg.cpp @@ -148,13 +148,13 @@ #include "algorithms/temporal/duration.h" #include "algorithms/standard/cubicspline.h" #include "algorithms/extractor/musicextractor.h" -#include "algorithms/io/audioloader.h" #include "algorithms/io/easyloader.h" #include "algorithms/io/eqloudloader.h" #include "algorithms/io/monoloader.h" #include "algorithms/io/metadatareader.h" #include "algorithms/io/yamloutput.h" #include "algorithms/standard/resample.h" +#include "audioloader_ios.h" namespace essentia { namespace standard { From c13149ff6de0c1d94340d0934d757ce4c8b3c4c2 Mon Sep 17 00:00:00 2001 From: ragnaringi Date: Wed, 29 Aug 2018 17:04:23 +0000 Subject: [PATCH 11/18] Removes unused libav dependencies --- ESMusicExtractor.xcodeproj/project.pbxproj | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/ESMusicExtractor.xcodeproj/project.pbxproj b/ESMusicExtractor.xcodeproj/project.pbxproj index 9279ac7..5a6c6f1 100644 --- a/ESMusicExtractor.xcodeproj/project.pbxproj +++ b/ESMusicExtractor.xcodeproj/project.pbxproj @@ -731,10 +731,6 @@ C0B55A0E211B27D0007817F6 /* version.h in Headers */ = {isa = PBXBuildFile; fileRef = C0B55A06211B27D0007817F6 /* version.h */; }; C0B9C2662130872E0029DA6F /* audioloader_ios_native.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C037276321304F4C00E9C15C /* audioloader_ios_native.cpp */; }; C0BA3B8E211A31E90038D19F /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = C0BA3B8D211A31E90038D19F /* libc++.tbd */; }; - C0E7A20C211A2F6700711656 /* libavcodec.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C0E7A1A3211A2F6600711656 /* libavcodec.a */; }; - C0E7A20F211A2F6700711656 /* libavformat.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C0E7A1A6211A2F6600711656 /* libavformat.a */; }; - C0E7A210211A2F6700711656 /* libavutil.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C0E7A1A7211A2F6600711656 /* libavutil.a */; }; - C0E7A211211A2F6700711656 /* libswresample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C0E7A1A8211A2F6600711656 /* libswresample.a */; }; C0E7A21D211A30FE00711656 /* ESExtractor.h in Headers */ = {isa = PBXBuildFile; fileRef = C0E7A21A211A30FE00711656 /* ESExtractor.h */; settings = {ATTRIBUTES = (Public, ); }; }; C0E7A21E211A30FE00711656 /* ESExtractor.mm in Sources */ = {isa = PBXBuildFile; fileRef = C0E7A21B211A30FE00711656 /* ESExtractor.mm */; }; /* End PBXBuildFile section */ @@ -1576,7 +1572,6 @@ C0B559F2211B20CB007817F6 /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; }; C0B559F9211B2451007817F6 /* polartocartesian.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = polartocartesian.h; sourceTree = ""; }; C0B559FA211B2451007817F6 /* polartocartesian.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = polartocartesian.cpp; sourceTree = ""; }; - C0B559FE211B27D0007817F6 /* audioloader_ios.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = audioloader_ios.cpp; sourceTree = ""; }; C0B55A00211B27D0007817F6 /* essentia_algorithms_reg.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = essentia_algorithms_reg.cpp; sourceTree = ""; }; C0B55A01211B27D0007817F6 /* ffmpegapi.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ffmpegapi.h; sourceTree = ""; }; C0B55A03211B27D0007817F6 /* extractor_utils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = extractor_utils.cpp; sourceTree = ""; }; @@ -1606,10 +1601,6 @@ C0B559ED211B20A2007817F6 /* libbz2.tbd in Frameworks */, C0B559EB211B2079007817F6 /* libiconv.tbd in Frameworks */, C0BA3B8E211A31E90038D19F /* libc++.tbd in Frameworks */, - C0E7A210211A2F6700711656 /* libavutil.a in Frameworks */, - C0E7A20C211A2F6700711656 /* libavcodec.a in Frameworks */, - C0E7A211211A2F6700711656 /* libswresample.a in Frameworks */, - C0E7A20F211A2F6700711656 /* libavformat.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2915,7 +2906,6 @@ children = ( C0B55A02211B27D0007817F6 /* music_extractor */, C0B9C2672130A4140029DA6F /* 3rdparty */, - C0B559FE211B27D0007817F6 /* audioloader_ios.cpp */, C037276321304F4C00E9C15C /* audioloader_ios_native.cpp */, C037276421304F4C00E9C15C /* audioloader_ios.h */, C0B55A00211B27D0007817F6 /* essentia_algorithms_reg.cpp */, From ab5e6d4a8660c71d0b9f55bf51e06022021bb617 Mon Sep 17 00:00:00 2001 From: ragnaringi Date: Wed, 29 Aug 2018 17:44:53 +0000 Subject: [PATCH 12/18] Removes extra dependencies --- ESMusicExtractor.xcodeproj/project.pbxproj | 40 ---------------------- 1 file changed, 40 deletions(-) diff --git a/ESMusicExtractor.xcodeproj/project.pbxproj b/ESMusicExtractor.xcodeproj/project.pbxproj index 5a6c6f1..322a902 100644 --- a/ESMusicExtractor.xcodeproj/project.pbxproj +++ b/ESMusicExtractor.xcodeproj/project.pbxproj @@ -717,9 +717,6 @@ C088CC0D211B561D009ED9DB /* xmproperties.h in Headers */ = {isa = PBXBuildFile; fileRef = C088CA7D211B561C009ED9DB /* xmproperties.h */; }; C088CC9C211B57F1009ED9DB /* metadatareader.h in Headers */ = {isa = PBXBuildFile; fileRef = C088CC9A211B57F0009ED9DB /* metadatareader.h */; }; C088CC9D211B57F1009ED9DB /* metadatareader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C088CC9B211B57F1009ED9DB /* metadatareader.cpp */; }; - C0B559EB211B2079007817F6 /* libiconv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = C0B559EA211B2079007817F6 /* libiconv.tbd */; }; - C0B559ED211B20A2007817F6 /* libbz2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = C0B559EC211B20A2007817F6 /* libbz2.tbd */; }; - C0B559EF211B20A9007817F6 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = C0B559EE211B20A9007817F6 /* libz.tbd */; }; C0B559F1211B20C5007817F6 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C0B559F0211B20C4007817F6 /* AudioToolbox.framework */; }; C0B559F3211B20CB007817F6 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C0B559F2211B20CB007817F6 /* Accelerate.framework */; }; C0B559F4211B23C4007817F6 /* libsamplerate.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C0F3BE8D211B0EED00073488 /* libsamplerate.a */; }; @@ -730,7 +727,6 @@ C0B55A0D211B27D0007817F6 /* streaming_extractor_music.h in Headers */ = {isa = PBXBuildFile; fileRef = C0B55A05211B27D0007817F6 /* streaming_extractor_music.h */; }; C0B55A0E211B27D0007817F6 /* version.h in Headers */ = {isa = PBXBuildFile; fileRef = C0B55A06211B27D0007817F6 /* version.h */; }; C0B9C2662130872E0029DA6F /* audioloader_ios_native.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C037276321304F4C00E9C15C /* audioloader_ios_native.cpp */; }; - C0BA3B8E211A31E90038D19F /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = C0BA3B8D211A31E90038D19F /* libc++.tbd */; }; C0E7A21D211A30FE00711656 /* ESExtractor.h in Headers */ = {isa = PBXBuildFile; fileRef = C0E7A21A211A30FE00711656 /* ESExtractor.h */; settings = {ATTRIBUTES = (Public, ); }; }; C0E7A21E211A30FE00711656 /* ESExtractor.mm in Sources */ = {isa = PBXBuildFile; fileRef = C0E7A21B211A30FE00711656 /* ESExtractor.mm */; }; /* End PBXBuildFile section */ @@ -1565,9 +1561,6 @@ C088CA7D211B561C009ED9DB /* xmproperties.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xmproperties.h; sourceTree = ""; }; C088CC9A211B57F0009ED9DB /* metadatareader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = metadatareader.h; sourceTree = ""; }; C088CC9B211B57F1009ED9DB /* metadatareader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = metadatareader.cpp; sourceTree = ""; }; - C0B559EA211B2079007817F6 /* libiconv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libiconv.tbd; path = usr/lib/libiconv.tbd; sourceTree = SDKROOT; }; - C0B559EC211B20A2007817F6 /* libbz2.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libbz2.tbd; path = usr/lib/libbz2.tbd; sourceTree = SDKROOT; }; - C0B559EE211B20A9007817F6 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; C0B559F0211B20C4007817F6 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; }; C0B559F2211B20CB007817F6 /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; }; C0B559F9211B2451007817F6 /* polartocartesian.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = polartocartesian.h; sourceTree = ""; }; @@ -1579,11 +1572,6 @@ C0B55A05211B27D0007817F6 /* streaming_extractor_music.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = streaming_extractor_music.h; sourceTree = ""; }; C0B55A06211B27D0007817F6 /* version.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = version.h; sourceTree = ""; }; C0B55A0F211B2E0F007817F6 /* ESMusicExtractor.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = ESMusicExtractor.xcconfig; sourceTree = ""; }; - C0BA3B8D211A31E90038D19F /* libc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; }; - C0E7A1A3211A2F6600711656 /* libavcodec.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libavcodec.a; sourceTree = ""; }; - C0E7A1A6211A2F6600711656 /* libavformat.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libavformat.a; sourceTree = ""; }; - C0E7A1A7211A2F6600711656 /* libavutil.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libavutil.a; sourceTree = ""; }; - C0E7A1A8211A2F6600711656 /* libswresample.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libswresample.a; sourceTree = ""; }; C0E7A21A211A30FE00711656 /* ESExtractor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ESExtractor.h; sourceTree = ""; }; C0E7A21B211A30FE00711656 /* ESExtractor.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ESExtractor.mm; sourceTree = ""; }; C0F3BE87211B0EED00073488 /* samplerate.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = samplerate.xcodeproj; path = samplerate/samplerate.xcodeproj; sourceTree = ""; }; @@ -1597,10 +1585,6 @@ C0B559F4211B23C4007817F6 /* libsamplerate.a in Frameworks */, C0B559F3211B20CB007817F6 /* Accelerate.framework in Frameworks */, C0B559F1211B20C5007817F6 /* AudioToolbox.framework in Frameworks */, - C0B559EF211B20A9007817F6 /* libz.tbd in Frameworks */, - C0B559ED211B20A2007817F6 /* libbz2.tbd in Frameworks */, - C0B559EB211B2079007817F6 /* libiconv.tbd in Frameworks */, - C0BA3B8E211A31E90038D19F /* libc++.tbd in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1633,7 +1617,6 @@ children = ( C088C95A211B561C009ED9DB /* taglib */, C0F3BE87211B0EED00073488 /* samplerate.xcodeproj */, - C0E7A139211A2F6600711656 /* ffmpeg-ios-static-libs */, C0137605211A06080046020A /* essentia */, ); path = libs; @@ -2938,33 +2921,10 @@ children = ( C0B559F2211B20CB007817F6 /* Accelerate.framework */, C0B559F0211B20C4007817F6 /* AudioToolbox.framework */, - C0B559EE211B20A9007817F6 /* libz.tbd */, - C0B559EC211B20A2007817F6 /* libbz2.tbd */, - C0B559EA211B2079007817F6 /* libiconv.tbd */, - C0BA3B8D211A31E90038D19F /* libc++.tbd */, ); name = Frameworks; sourceTree = ""; }; - C0E7A139211A2F6600711656 /* ffmpeg-ios-static-libs */ = { - isa = PBXGroup; - children = ( - C0E7A1A2211A2F6600711656 /* lib */, - ); - path = "ffmpeg-ios-static-libs"; - sourceTree = ""; - }; - C0E7A1A2211A2F6600711656 /* lib */ = { - isa = PBXGroup; - children = ( - C0E7A1A3211A2F6600711656 /* libavcodec.a */, - C0E7A1A6211A2F6600711656 /* libavformat.a */, - C0E7A1A7211A2F6600711656 /* libavutil.a */, - C0E7A1A8211A2F6600711656 /* libswresample.a */, - ); - path = lib; - sourceTree = ""; - }; C0E7A219211A30FE00711656 /* src */ = { isa = PBXGroup; children = ( From 570dc60ec317032f4fc13af6938baa1805881076 Mon Sep 17 00:00:00 2001 From: ragnaringi Date: Wed, 29 Aug 2018 19:00:37 +0000 Subject: [PATCH 13/18] Removes txt files from copy resources phase --- ESMusicExtractor.xcodeproj/project.pbxproj | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/ESMusicExtractor.xcodeproj/project.pbxproj b/ESMusicExtractor.xcodeproj/project.pbxproj index 322a902..c93d3f8 100644 --- a/ESMusicExtractor.xcodeproj/project.pbxproj +++ b/ESMusicExtractor.xcodeproj/project.pbxproj @@ -497,7 +497,6 @@ C088C619211B54C3009ED9DB /* taglib_config.h in Headers */ = {isa = PBXBuildFile; fileRef = C088C618211B54C3009ED9DB /* taglib_config.h */; }; C088CB10211B561C009ED9DB /* checked.h in Headers */ = {isa = PBXBuildFile; fileRef = C088C961211B561C009ED9DB /* checked.h */; }; C088CB11211B561C009ED9DB /* core.h in Headers */ = {isa = PBXBuildFile; fileRef = C088C962211B561C009ED9DB /* core.h */; }; - C088CB30211B561C009ED9DB /* ape-tag-format.txt in Resources */ = {isa = PBXBuildFile; fileRef = C088C989211B561C009ED9DB /* ape-tag-format.txt */; }; C088CB31211B561C009ED9DB /* apefile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C088C98A211B561C009ED9DB /* apefile.cpp */; }; C088CB32211B561C009ED9DB /* apefile.h in Headers */ = {isa = PBXBuildFile; fileRef = C088C98B211B561C009ED9DB /* apefile.h */; }; C088CB33211B561C009ED9DB /* apefooter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C088C98C211B561C009ED9DB /* apefooter.cpp */; }; @@ -600,10 +599,6 @@ C088CB95211B561D009ED9DB /* unsynchronizedlyricsframe.h in Headers */ = {isa = PBXBuildFile; fileRef = C088C9F8211B561C009ED9DB /* unsynchronizedlyricsframe.h */; }; C088CB96211B561D009ED9DB /* urllinkframe.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C088C9F9211B561C009ED9DB /* urllinkframe.cpp */; }; C088CB97211B561D009ED9DB /* urllinkframe.h in Headers */ = {isa = PBXBuildFile; fileRef = C088C9FA211B561C009ED9DB /* urllinkframe.h */; }; - C088CB98211B561D009ED9DB /* id3v2.2.0.txt in Resources */ = {isa = PBXBuildFile; fileRef = C088C9FB211B561C009ED9DB /* id3v2.2.0.txt */; }; - C088CB99211B561D009ED9DB /* id3v2.3.0.txt in Resources */ = {isa = PBXBuildFile; fileRef = C088C9FC211B561C009ED9DB /* id3v2.3.0.txt */; }; - C088CB9A211B561D009ED9DB /* id3v2.4.0-frames.txt in Resources */ = {isa = PBXBuildFile; fileRef = C088C9FD211B561C009ED9DB /* id3v2.4.0-frames.txt */; }; - C088CB9B211B561D009ED9DB /* id3v2.4.0-structure.txt in Resources */ = {isa = PBXBuildFile; fileRef = C088C9FE211B561C009ED9DB /* id3v2.4.0-structure.txt */; }; C088CB9C211B561D009ED9DB /* id3v2extendedheader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C088C9FF211B561C009ED9DB /* id3v2extendedheader.cpp */; }; C088CB9D211B561D009ED9DB /* id3v2extendedheader.h in Headers */ = {isa = PBXBuildFile; fileRef = C088CA00211B561C009ED9DB /* id3v2extendedheader.h */; }; C088CB9E211B561D009ED9DB /* id3v2footer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C088CA01211B561C009ED9DB /* id3v2footer.cpp */; }; @@ -1341,7 +1336,6 @@ C088C618211B54C3009ED9DB /* taglib_config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = taglib_config.h; sourceTree = ""; }; C088C961211B561C009ED9DB /* checked.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = checked.h; sourceTree = ""; }; C088C962211B561C009ED9DB /* core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = core.h; sourceTree = ""; }; - C088C989211B561C009ED9DB /* ape-tag-format.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "ape-tag-format.txt"; sourceTree = ""; }; C088C98A211B561C009ED9DB /* apefile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = apefile.cpp; sourceTree = ""; }; C088C98B211B561C009ED9DB /* apefile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = apefile.h; sourceTree = ""; }; C088C98C211B561C009ED9DB /* apefooter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = apefooter.cpp; sourceTree = ""; }; @@ -1444,10 +1438,6 @@ C088C9F8211B561C009ED9DB /* unsynchronizedlyricsframe.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = unsynchronizedlyricsframe.h; sourceTree = ""; }; C088C9F9211B561C009ED9DB /* urllinkframe.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = urllinkframe.cpp; sourceTree = ""; }; C088C9FA211B561C009ED9DB /* urllinkframe.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = urllinkframe.h; sourceTree = ""; }; - C088C9FB211B561C009ED9DB /* id3v2.2.0.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = id3v2.2.0.txt; sourceTree = ""; }; - C088C9FC211B561C009ED9DB /* id3v2.3.0.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = id3v2.3.0.txt; sourceTree = ""; }; - C088C9FD211B561C009ED9DB /* id3v2.4.0-frames.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "id3v2.4.0-frames.txt"; sourceTree = ""; }; - C088C9FE211B561C009ED9DB /* id3v2.4.0-structure.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "id3v2.4.0-structure.txt"; sourceTree = ""; }; C088C9FF211B561C009ED9DB /* id3v2extendedheader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = id3v2extendedheader.cpp; sourceTree = ""; }; C088CA00211B561C009ED9DB /* id3v2extendedheader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = id3v2extendedheader.h; sourceTree = ""; }; C088CA01211B561C009ED9DB /* id3v2footer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = id3v2footer.cpp; sourceTree = ""; }; @@ -2503,7 +2493,6 @@ C088C988211B561C009ED9DB /* ape */ = { isa = PBXGroup; children = ( - C088C989211B561C009ED9DB /* ape-tag-format.txt */, C088C98A211B561C009ED9DB /* apefile.cpp */, C088C98B211B561C009ED9DB /* apefile.h */, C088C98C211B561C009ED9DB /* apefooter.cpp */, @@ -2643,10 +2632,6 @@ isa = PBXGroup; children = ( C088C9D8211B561C009ED9DB /* frames */, - C088C9FB211B561C009ED9DB /* id3v2.2.0.txt */, - C088C9FC211B561C009ED9DB /* id3v2.3.0.txt */, - C088C9FD211B561C009ED9DB /* id3v2.4.0-frames.txt */, - C088C9FE211B561C009ED9DB /* id3v2.4.0-structure.txt */, C088C9FF211B561C009ED9DB /* id3v2extendedheader.cpp */, C088CA00211B561C009ED9DB /* id3v2extendedheader.h */, C088CA01211B561C009ED9DB /* id3v2footer.cpp */, @@ -3442,11 +3427,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - C088CB99211B561D009ED9DB /* id3v2.3.0.txt in Resources */, - C088CB30211B561C009ED9DB /* ape-tag-format.txt in Resources */, - C088CB98211B561D009ED9DB /* id3v2.2.0.txt in Resources */, - C088CB9B211B561D009ED9DB /* id3v2.4.0-structure.txt in Resources */, - C088CB9A211B561D009ED9DB /* id3v2.4.0-frames.txt in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; From 1820876aaafe6c07dda1e16d1eb95ea1ee29a77f Mon Sep 17 00:00:00 2001 From: ragnaringi Date: Thu, 30 Aug 2018 12:54:18 +0000 Subject: [PATCH 14/18] Removes unnecessary ffmpeg header --- ESMusicExtractor.xcodeproj/project.pbxproj | 4 -- src/internal/3rdparty/ffmpegapi.h | 47 ---------------------- 2 files changed, 51 deletions(-) delete mode 100644 src/internal/3rdparty/ffmpegapi.h diff --git a/ESMusicExtractor.xcodeproj/project.pbxproj b/ESMusicExtractor.xcodeproj/project.pbxproj index c93d3f8..67beaef 100644 --- a/ESMusicExtractor.xcodeproj/project.pbxproj +++ b/ESMusicExtractor.xcodeproj/project.pbxproj @@ -716,7 +716,6 @@ C0B559F3211B20CB007817F6 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C0B559F2211B20CB007817F6 /* Accelerate.framework */; }; C0B559F4211B23C4007817F6 /* libsamplerate.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C0F3BE8D211B0EED00073488 /* libsamplerate.a */; }; C0B55A09211B27D0007817F6 /* essentia_algorithms_reg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C0B55A00211B27D0007817F6 /* essentia_algorithms_reg.cpp */; }; - C0B55A0A211B27D0007817F6 /* ffmpegapi.h in Headers */ = {isa = PBXBuildFile; fileRef = C0B55A01211B27D0007817F6 /* ffmpegapi.h */; }; C0B55A0B211B27D0007817F6 /* extractor_utils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C0B55A03211B27D0007817F6 /* extractor_utils.cpp */; }; C0B55A0C211B27D0007817F6 /* extractor_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = C0B55A04211B27D0007817F6 /* extractor_utils.h */; }; C0B55A0D211B27D0007817F6 /* streaming_extractor_music.h in Headers */ = {isa = PBXBuildFile; fileRef = C0B55A05211B27D0007817F6 /* streaming_extractor_music.h */; }; @@ -1556,7 +1555,6 @@ C0B559F9211B2451007817F6 /* polartocartesian.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = polartocartesian.h; sourceTree = ""; }; C0B559FA211B2451007817F6 /* polartocartesian.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = polartocartesian.cpp; sourceTree = ""; }; C0B55A00211B27D0007817F6 /* essentia_algorithms_reg.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = essentia_algorithms_reg.cpp; sourceTree = ""; }; - C0B55A01211B27D0007817F6 /* ffmpegapi.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ffmpegapi.h; sourceTree = ""; }; C0B55A03211B27D0007817F6 /* extractor_utils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = extractor_utils.cpp; sourceTree = ""; }; C0B55A04211B27D0007817F6 /* extractor_utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = extractor_utils.h; sourceTree = ""; }; C0B55A05211B27D0007817F6 /* streaming_extractor_music.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = streaming_extractor_music.h; sourceTree = ""; }; @@ -2894,7 +2892,6 @@ C0B9C2672130A4140029DA6F /* 3rdparty */ = { isa = PBXGroup; children = ( - C0B55A01211B27D0007817F6 /* ffmpegapi.h */, C088C618211B54C3009ED9DB /* taglib_config.h */, C0B55A06211B27D0007817F6 /* version.h */, ); @@ -3068,7 +3065,6 @@ C01380E6211A060C0046020A /* phantombuffer.h in Headers */, C088CBCE211B561D009ED9DB /* rifffile.h in Headers */, C0138132211A060C0046020A /* tnt_fortran_array1d_utils.h in Headers */, - C0B55A0A211B27D0007817F6 /* ffmpegapi.h in Headers */, C017D4B6212DD556003009FE /* crest.h in Headers */, C088CB93211B561D009ED9DB /* unknownframe.h in Headers */, C013811C211A060C0046020A /* metadatautils.h in Headers */, diff --git a/src/internal/3rdparty/ffmpegapi.h b/src/internal/3rdparty/ffmpegapi.h deleted file mode 100644 index 9e53302..0000000 --- a/src/internal/3rdparty/ffmpegapi.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2006-2016 Music Technology Group - Universitat Pompeu Fabra - * - * This file is part of Essentia - * - * Essentia is free software: you can redistribute it and/or modify it under - * the terms of the GNU Affero General Public License as published by the Free - * Software Foundation (FSF), either version 3 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the Affero GNU General Public License - * version 3 along with this program. If not, see http://www.gnu.org/licenses/ - */ - -#ifndef ESSENTIA_FFMPEGAPI_H -#define ESSENTIA_FFMPEGAPI_H - -extern "C" { -#include -#include -#include -#include -#include -} - - -// --- from audiocontext - -#ifndef AV_PKT_FLAG_KEY -# define AV_PKT_FLAG_KEY PKT_FLAG_KEY -#endif - -#ifndef AVIO_FLAG_WRITE -# define AVIO_FLAG_WRITE URL_WRONLY -# define avio_open url_fopen -# define avio_close url_fclose -# define avformat_write_header(c, o) av_write_header(c) -#endif - - -#endif // ESSENTIA_FFMPEGAPI_H - From 565b415a8789dd1a82b892bb82cab3c645a10931 Mon Sep 17 00:00:00 2001 From: ragnaringi Date: Thu, 30 Aug 2018 12:58:56 +0000 Subject: [PATCH 15/18] Removes unused member variables --- src/internal/audioloader_ios.h | 3 --- src/internal/audioloader_ios_native.cpp | 13 +++++++------ 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/internal/audioloader_ios.h b/src/internal/audioloader_ios.h index c88b177..06bfc27 100755 --- a/src/internal/audioloader_ios.h +++ b/src/internal/audioloader_ios.h @@ -42,9 +42,6 @@ class AudioLoader : public Algorithm { ExtAudioFileRef _file; - CC_MD5_CTX hashObject; - bool _computeMD5; - void openAudioFile(const std::string& filename); void closeAudioFile(); diff --git a/src/internal/audioloader_ios_native.cpp b/src/internal/audioloader_ios_native.cpp index 9544a15..08b02d8 100644 --- a/src/internal/audioloader_ios_native.cpp +++ b/src/internal/audioloader_ios_native.cpp @@ -99,7 +99,6 @@ namespace essentia { namespace streaming { } void AudioLoader::configure() { - _computeMD5 = parameter("computeMD5").toBool(); // _selectedStream = parameter("audioStream").toInt(); reset(); } @@ -114,10 +113,6 @@ namespace essentia { namespace streaming { if ( result != noErr ) { throw EssentiaException("AudioLoader: Could not open file \"", filename, "\", error = ", result); } - - if ( !CC_MD5_Init(&hashObject) ) { - throw EssentiaException("Error allocating the MD5 context"); - } auto fileData = readAllBytes(filename.c_str()); // for ( int i = 0; i < 100; i++ ) { @@ -125,7 +120,13 @@ namespace essentia { namespace streaming { // } // compute md5 first - if (_computeMD5) { + if ( parameter("computeMD5").toBool() ) { + CC_MD5_CTX hashObject; + + if ( !CC_MD5_Init(&hashObject) ) { + throw EssentiaException("Error allocating the MD5 context"); + } + size_t chunkSize = 4096; for ( size_t i = 0; i < fileData.size(); i += chunkSize ) { auto len = std::min(fileData.size()-i, chunkSize); From d55c233815ae1ea7704f3aa213cb2f33e5e29682 Mon Sep 17 00:00:00 2001 From: ragnaringi Date: Thu, 30 Aug 2018 13:32:31 +0000 Subject: [PATCH 16/18] Renames audioloader_ios file --- ESMusicExtractor.xcodeproj/project.pbxproj | 8 +- src/internal/audioloader_ios.cpp | 664 ++++++++------------- src/internal/audioloader_ios_native.cpp | 401 ------------- 3 files changed, 247 insertions(+), 826 deletions(-) delete mode 100644 src/internal/audioloader_ios_native.cpp diff --git a/ESMusicExtractor.xcodeproj/project.pbxproj b/ESMusicExtractor.xcodeproj/project.pbxproj index 67beaef..7bcc5d3 100644 --- a/ESMusicExtractor.xcodeproj/project.pbxproj +++ b/ESMusicExtractor.xcodeproj/project.pbxproj @@ -720,7 +720,7 @@ C0B55A0C211B27D0007817F6 /* extractor_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = C0B55A04211B27D0007817F6 /* extractor_utils.h */; }; C0B55A0D211B27D0007817F6 /* streaming_extractor_music.h in Headers */ = {isa = PBXBuildFile; fileRef = C0B55A05211B27D0007817F6 /* streaming_extractor_music.h */; }; C0B55A0E211B27D0007817F6 /* version.h in Headers */ = {isa = PBXBuildFile; fileRef = C0B55A06211B27D0007817F6 /* version.h */; }; - C0B9C2662130872E0029DA6F /* audioloader_ios_native.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C037276321304F4C00E9C15C /* audioloader_ios_native.cpp */; }; + C0B9C2662130872E0029DA6F /* audioloader_ios.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C037276321304F4C00E9C15C /* audioloader_ios.cpp */; }; C0E7A21D211A30FE00711656 /* ESExtractor.h in Headers */ = {isa = PBXBuildFile; fileRef = C0E7A21A211A30FE00711656 /* ESExtractor.h */; settings = {ATTRIBUTES = (Public, ); }; }; C0E7A21E211A30FE00711656 /* ESExtractor.mm in Sources */ = {isa = PBXBuildFile; fileRef = C0E7A21B211A30FE00711656 /* ESExtractor.mm */; }; /* End PBXBuildFile section */ @@ -1330,7 +1330,7 @@ C0137A36211A06090046020A /* tnt_version.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tnt_version.h; sourceTree = ""; }; C0138400211A06BD0046020A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; C0138401211A06BD0046020A /* ESMusicExtractor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ESMusicExtractor.h; sourceTree = ""; }; - C037276321304F4C00E9C15C /* audioloader_ios_native.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = audioloader_ios_native.cpp; sourceTree = ""; }; + C037276321304F4C00E9C15C /* audioloader_ios.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = audioloader_ios.cpp; sourceTree = ""; }; C037276421304F4C00E9C15C /* audioloader_ios.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = audioloader_ios.h; sourceTree = ""; }; C088C618211B54C3009ED9DB /* taglib_config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = taglib_config.h; sourceTree = ""; }; C088C961211B561C009ED9DB /* checked.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = checked.h; sourceTree = ""; }; @@ -2872,7 +2872,7 @@ children = ( C0B55A02211B27D0007817F6 /* music_extractor */, C0B9C2672130A4140029DA6F /* 3rdparty */, - C037276321304F4C00E9C15C /* audioloader_ios_native.cpp */, + C037276321304F4C00E9C15C /* audioloader_ios.cpp */, C037276421304F4C00E9C15C /* audioloader_ios.h */, C0B55A00211B27D0007817F6 /* essentia_algorithms_reg.cpp */, C0B55A05211B27D0007817F6 /* streaming_extractor_music.h */, @@ -3691,7 +3691,7 @@ C0137F7D211A060B0046020A /* tempotapticks.cpp in Sources */, C088CBAA211B561D009ED9DB /* mpegfile.cpp in Sources */, C0137FAD211A060B0046020A /* panning.cpp in Sources */, - C0B9C2662130872E0029DA6F /* audioloader_ios_native.cpp in Sources */, + C0B9C2662130872E0029DA6F /* audioloader_ios.cpp in Sources */, C088CBD2211B561D009ED9DB /* wavfile.cpp in Sources */, C01380F0211A060C0046020A /* streamingalgorithm.cpp in Sources */, C0137F4F211A060B0046020A /* harmonicbpm.cpp in Sources */, diff --git a/src/internal/audioloader_ios.cpp b/src/internal/audioloader_ios.cpp index f6dec76..08b02d8 100644 --- a/src/internal/audioloader_ios.cpp +++ b/src/internal/audioloader_ios.cpp @@ -25,471 +25,293 @@ * version 3 along with this program. If not, see http://www.gnu.org/licenses/ */ + +#include +#include // setw() +#include + #include "audioloader_ios.h" #include "algorithmfactory.h" -#include // setw() using namespace std; - -namespace essentia { - namespace streaming { - - const char* AudioLoader::name = essentia::standard::AudioLoader::name; - const char* AudioLoader::category = essentia::standard::AudioLoader::category; - const char* AudioLoader::description = essentia::standard::AudioLoader::description; - - AudioLoader::~AudioLoader() { - closeAudioFile(); - - av_freep(&_buffer); - av_freep(&_md5Encoded); - av_freep(&_decodedFrame); - } - - void AudioLoader::configure() { - // set ffmpeg to be silent by default, so we don't have these annoying - // "invalid new backstep" messages anymore, when everything is actually fine - av_log_set_level(AV_LOG_QUIET); - //av_log_set_level(AV_LOG_VERBOSE); - _computeMD5 = parameter("computeMD5").toBool(); - _selectedStream = parameter("audioStream").toInt(); - reset(); - } - - - void AudioLoader::openAudioFile(const string& filename) { - E_DEBUG(EAlgorithm, "AudioLoader: opening file: " << filename); - - // Open file - int errnum; - if ((errnum = avformat_open_input(&_demuxCtx, filename.c_str(), NULL, NULL)) != 0) { - char errorstr[128]; - string error = "Unknown error"; - if (av_strerror(errnum, errorstr, 128) == 0) error = errorstr; - throw EssentiaException("AudioLoader: Could not open file \"", filename, "\", error = ", error); - } - - // Retrieve stream information - if ((errnum = avformat_find_stream_info(_demuxCtx, NULL)) < 0) { - char errorstr[128]; - string error = "Unknown error"; - if (av_strerror(errnum, errorstr, 128) == 0) error = errorstr; - avformat_close_input(&_demuxCtx); - _demuxCtx = 0; - throw EssentiaException("AudioLoader: Could not find stream information, error = ", error); +// https://codereview.stackexchange.com/a/22907 +static std::vector readAllBytes(char const* filename) { + ifstream ifs(filename, ios::binary|ios::ate); + ifstream::pos_type pos = ifs.tellg(); + + vector result(pos); + + ifs.seekg(0, ios::beg); + ifs.read(result.data(), pos); + + auto header = string(result.begin(), result.begin() + 4); + if ( header == "RIFF" ) { // WAVE file + return vector( result.begin() + 44, result.end() ); + } else if ( header == "FORM" ) { // AIFF + for ( int i = 4; i < result.size(); i++ ) { + auto str = string(result.begin()+i, result.begin()+i+4); + if ( str == "SSND" ) { + return vector( result.begin()+i+16, result.end() ); } - - // Dump information about file onto standard error - //dump_format(_demuxCtx, 0, filename.c_str(), 0); - - // Check that we have only 1 audio stream in the file - _streams.clear(); - for (int i=0; i<(int)_demuxCtx->nb_streams; i++) { - if (_demuxCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) { - _streams.push_back(i); + } + } else { + auto str = string(result.begin(), result.begin()+11); + if ( str.substr(0,3) == "ID3" ) { // mp3 + // Extract ID3 tag size from synchsafe integer + // https://stackoverflow.com/a/5652842 + uint8_t* sync_safe = (uint8_t *)result.data()+6; + uint32_t byte0 = sync_safe[0]; + uint32_t byte1 = sync_safe[1]; + uint32_t byte2 = sync_safe[2]; + uint32_t byte3 = sync_safe[3]; + int len = byte0 << 21 | byte1 << 14 | byte2 << 7 | byte3; + int tagSize = len + 10; // + return vector( result.begin()+tagSize, result.end() ); + } else if ( str.substr(4, 11) == "ftypM4A" ) { // m4a + for ( int i = 0; i < result.size()-4; i++ ) { + if ( result[i] == '!' && result[i+3] == '@' && result[i+4] == 'h' ) { + return vector( result.begin()+i, result.end() ); } } - int nAudioStreams = _streams.size(); - - if (nAudioStreams == 0) { - avformat_close_input(&_demuxCtx); - _demuxCtx = 0; - throw EssentiaException("AudioLoader ERROR: found 0 streams in the file, expecting one or more audio streams"); - } - - if (_selectedStream >= nAudioStreams) { - avformat_close_input(&_demuxCtx); - _demuxCtx = 0; - throw EssentiaException("AudioLoader ERROR: 'audioStream' parameter set to ", _selectedStream ,". It should be smaller than the audio streams count, ", nAudioStreams); - } - - _streamIdx = _streams[_selectedStream]; - - // Load corresponding audio codec - _audioCtx = _demuxCtx->streams[_streamIdx]->codec; - _audioCodec = avcodec_find_decoder(_audioCtx->codec_id); - - if (!_audioCodec) { - throw EssentiaException("AudioLoader: Unsupported codec!"); - } - - if (avcodec_open2(_audioCtx, _audioCodec, NULL) < 0) { - throw EssentiaException("AudioLoader: Unable to instantiate codec..."); - } - - // Configure format convertion (no samplerate conversion yet) - int64_t layout = av_get_default_channel_layout(_audioCtx->channels); - - /* - const char* fmt = 0; - get_format_from_sample_fmt(&fmt, _audioCtx->sample_fmt); - E_DEBUG(EAlgorithm, "AudioLoader: converting from " << (fmt ? fmt : "unknown") << " to FLT"); - */ - - E_DEBUG(EAlgorithm, "AudioLoader: using sample format conversion from libavresample"); - - _convertCtxAv = swr_alloc(); - - av_opt_set_int(_convertCtxAv, "in_channel_layout", layout, 0); - av_opt_set_int(_convertCtxAv, "out_channel_layout", layout, 0); - av_opt_set_int(_convertCtxAv, "in_sample_rate", _audioCtx->sample_rate, 0); - av_opt_set_int(_convertCtxAv, "out_sample_rate", _audioCtx->sample_rate, 0); - av_opt_set_int(_convertCtxAv, "in_sample_fmt", _audioCtx->sample_fmt, 0); - av_opt_set_int(_convertCtxAv, "out_sample_fmt", AV_SAMPLE_FMT_FLT, 0); - - swr_init(_convertCtxAv); + } else { + cout << "File format not supported" << endl; + } + } + return result; +} + +string uint8_t_to_hex(uint8_t* input, int size) { + ostringstream result; + for(int i=0; i 2) { + throw EssentiaException("AudioLoader: could not load audio. Audio file has more than 2 channels."); + } + if (sampleRate <= 0) { + throw EssentiaException("AudioLoader: could not load audio. Audio sampling rate must be greater than 0."); } + _nChannels = nChannels; + + _channels.push(nChannels); + _sampleRate.push(sampleRate); + } + + void AudioLoader::pushCodecInfo(std::string codec, int bit_rate) { + _codec.push(codec); + _bit_rate.push(bit_rate); + } + + AlgorithmStatus AudioLoader::process() { + if (!parameter("filename").isConfigured()) { + throw EssentiaException("AudioLoader: Trying to call process() on an AudioLoader algo which hasn't been correctly configured."); + } - void AudioLoader::pushChannelsSampleRateInfo(int nChannels, Real sampleRate) { - if (nChannels > 2) { - throw EssentiaException("AudioLoader: could not load audio. Audio file has more than 2 channels."); - } - if (sampleRate <= 0) { - throw EssentiaException("AudioLoader: could not load audio. Audio sampling rate must be greater than 0."); + auto audioFormat = ({ + AudioStreamBasicDescription audioDescription; + memset(&audioDescription, 0, sizeof(audioDescription)); + audioDescription.mFormatID = kAudioFormatLinearPCM; + audioDescription.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked | kAudioFormatFlagIsNonInterleaved; + audioDescription.mChannelsPerFrame = 2; + audioDescription.mBytesPerPacket = sizeof(float); + audioDescription.mFramesPerPacket = 1; + audioDescription.mBytesPerFrame = sizeof(float); + audioDescription.mBitsPerChannel = 8 * sizeof(float); + audioDescription.mSampleRate = 44100.0; + audioDescription; + }); + + const SInt64 frameCount = 4096; + + // Create temporary audio bufferlist + int numberOfBuffers = audioFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved ? audioFormat.mChannelsPerFrame : 1; + int channelsPerBuffer = audioFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved ? 1 : audioFormat.mChannelsPerFrame; + int bytesPerBuffer = audioFormat.mBytesPerFrame * frameCount; + + AudioBufferList *bufferList = (AudioBufferList *)malloc(sizeof(AudioBufferList) + (numberOfBuffers-1)*sizeof(AudioBuffer)); + if ( !bufferList ) { + throw EssentiaException("AudioLoader: Error creating AudioBufferList"); + } + bufferList->mNumberBuffers = numberOfBuffers; + for ( int i=0; i 0 ) { + bufferList->mBuffers[i].mData = calloc(bytesPerBuffer, 1); + if ( !bufferList->mBuffers[i].mData ) { + for ( int j=0; jmBuffers[j].mData); + free(bufferList); + throw EssentiaException("AudioLoader: Error allocating data for AudioBufferList"); + } + } else { + bufferList->mBuffers[i].mData = NULL; } - - _nChannels = nChannels; - - _channels.push(nChannels); - _sampleRate.push(sampleRate); + bufferList->mBuffers[i].mDataByteSize = bytesPerBuffer; + bufferList->mBuffers[i].mNumberChannels = channelsPerBuffer; } + // Set destination format + if ( ExtAudioFileSetProperty(_file, kExtAudioFileProperty_ClientDataFormat, sizeof(audioFormat), &audioFormat) != noErr ) { + throw EssentiaException("AudioLoader: Error setting client data format"); + } - void AudioLoader::pushCodecInfo(std::string codec, int bit_rate) { - _codec.push(codec); - _bit_rate.push(bit_rate); + // Read audio + UInt32 readFrames = frameCount; + if ( ExtAudioFileRead(_file, &readFrames, bufferList) != noErr ) { + throw EssentiaException("AudioLoader: Error reading file" ); + } + + bool eof = readFrames < frameCount; + if ( eof ) { // EOF + shouldStop(true); } + int nsamples = readFrames; - string uint8_t_to_hex(uint8_t* input, int size) { - ostringstream result; - for(int i=0; i& audio = *((vector*)_audio.getTokens()); - AlgorithmStatus AudioLoader::process() { - if (!parameter("filename").isConfigured()) { - throw EssentiaException("AudioLoader: Trying to call process() on an AudioLoader algo which hasn't been correctly configured."); + if (_nChannels == 1) { + for (int i=0; imBuffers[0].mData)[i]; } - - // read frames until we get a good one - do { - int result = av_read_frame(_demuxCtx, &_packet); - //E_DEBUG(EAlgorithm, "AudioLoader: called av_read_frame(), got result = " << result); - if (result != 0) { - // 0 = OK, < 0 = error or EOF - if (result != AVERROR_EOF) { - char errstring[1204]; - av_strerror(result, errstring, sizeof(errstring)); - ostringstream msg; - msg << "AudioLoader: Error reading frame: " << errstring; - E_WARNING(msg.str()); - } - // TODO: should try reading again on EAGAIN error? - // https://github.com/FFmpeg/FFmpeg/blob/master/ffmpeg.c - shouldStop(true); - flushPacket(); - closeAudioFile(); - if (_computeMD5) { - av_md5_final(_md5Encoded, _checksum); - _md5.push(uint8_t_to_hex(_checksum, 16)); - } - else { - string md5 = ""; - _md5.push(md5); - } - return FINISHED; - } - } while (_packet.stream_index != _streamIdx); - - // compute md5 first - if (_computeMD5) { - av_md5_update(_md5Encoded, _packet.data, _packet.size); - } - - // decode frames in packet - while(_packet.size > 0) { - if (!decodePacket()) break; - copyFFmpegOutput(); + } + else { // _nChannels == 2 + for (int i=0; imBuffers[0].mData)[i]; + audio[i].right() = ((float *)bufferList->mBuffers[1].mData)[i]; } - // neds to be freed !! - av_free_packet(&_packet); - - return OK; } + // release data + _audio.release(nsamples); - int AudioLoader::decode_audio_frame(AVCodecContext* audioCtx, - float* output, - int* outputSize, - AVPacket* packet) { - - // _dataSize input = number of bytes available for write in buff - // output = number of bytes actually written (actual: FLT data) - //E_DEBUG(EAlgorithm, "decode_audio_frame, available bytes in buffer = " << _dataSize); - int gotFrame = 0; - av_frame_unref(_decodedFrame); //avcodec_get_frame_defaults(_decodedFrame); - - int len = avcodec_decode_audio4(audioCtx, _decodedFrame, &gotFrame, packet); - - if (len < 0) return len; // error handling should be done outside - - if (gotFrame) { - int inputSamples = _decodedFrame->nb_samples; - int inputPlaneSize = av_samples_get_buffer_size(NULL, _nChannels, inputSamples, - audioCtx->sample_fmt, 1); - int outputPlaneSize = av_samples_get_buffer_size(NULL, _nChannels, inputSamples, - AV_SAMPLE_FMT_FLT, 1); - // the size of the output buffer in samples - int outputBufferSamples = *outputSize / - (av_get_bytes_per_sample(AV_SAMPLE_FMT_FLT) * _nChannels); - - if (outputBufferSamples < inputSamples) { - // this should never happen, throw exception here - throw EssentiaException("AudioLoader: Insufficient buffer size for format conversion"); - } - - if (audioCtx->sample_fmt == AV_SAMPLE_FMT_FLT) { - // TODO: no need in this check? Not many of common formats support FLT - // no conversion needed, direct copy from our frame to output buffer - memcpy(output, _decodedFrame->data[0], inputPlaneSize); - } - else { - - int samplesWritten = swr_convert(_convertCtxAv, - (uint8_t**)&output, - outputBufferSamples, - (const uint8_t**)_decodedFrame->data, - inputSamples); - - if (samplesWritten < inputSamples) { - // TODO: there may be data remaining in the internal FIFO buffer - // to get this data: call avresample_convert() with NULL input - // Test if this happens in practice - ostringstream msg; - msg << "AudioLoader: Incomplete format conversion (some samples missing)" - << " from " << av_get_sample_fmt_name(_audioCtx->sample_fmt) - << " to " << av_get_sample_fmt_name(AV_SAMPLE_FMT_FLT); - throw EssentiaException(msg); - } - } - *outputSize = outputPlaneSize; - } - else { - E_DEBUG(EAlgorithm, "AudioLoader: tried to decode packet but didn't get any frame..."); - *outputSize = 0; - } + // Free bufferlist + for ( int i=0; imNumberBuffers; i++ ) { + if ( bufferList->mBuffers[i].mData ) free(bufferList->mBuffers[i].mData); + } + free(bufferList); + + if ( eof ) { + closeAudioFile(); - return len; + return FINISHED; } + + return OK; + } + + void AudioLoader::reset() { + Algorithm::reset(); + if (!parameter("filename").isConfigured()) return; - void AudioLoader::flushPacket() { - AVPacket empty; - av_init_packet(&empty); - do { - _dataSize = FFMPEG_BUFFER_SIZE; - empty.data = NULL; - empty.size = 0; - - int len = decode_audio_frame(_audioCtx, _buffer, &_dataSize, &empty); - if (len < 0) { - char errstring[1204]; - av_strerror(len, errstring, sizeof(errstring)); - ostringstream msg; - msg << "AudioLoader: decoding error while flushing a packet:" << errstring; - E_WARNING(msg.str()); - } - copyFFmpegOutput(); - - } while (_dataSize > 0); - } + string filename = parameter("filename").toString(); + closeAudioFile(); + openAudioFile(filename); - /** - * Gets the AVPacket stored in _packet, and decodes all the samples it can from it, - * putting them in _buffer, the total number of bytes written begin stored in _dataSize. - */ - int AudioLoader::decodePacket() { - /* - E_DEBUG(EAlgorithm, "-----------------------------------------------------"); - E_DEBUG(EAlgorithm, "decoding packet of " << _packet.size << " bytes"); - E_DEBUG(EAlgorithm, "pts: " << _packet.pts << " - dts: " << _packet.dts); //" - pos: " << pkt->pos); - E_DEBUG(EAlgorithm, "flags: " << _packet.flags); - E_DEBUG(EAlgorithm, "duration: " << _packet.duration); - */ - int len = 0; - - // buff is an offset in our output buffer, it points to where we should start - // writing the next decoded samples - float* buff = _buffer; - - // _dataSize gets the size of the buffer, in bytes - _dataSize = FFMPEG_BUFFER_SIZE; - - // Note: md5 should be computed before decoding frame, as the decoding may - // change the content of a packet. Still, not sure if it is correct to - // compute md5 over packet which contains incorrect frames, potentially - // belonging to id3 metadata (TODO: or is it just a missing header issue?), - // but computing md5 hash using ffmpeg will also treat it as audio: - // ffmpeg -i file.mp3 -acodec copy -f md5 - - - len = decode_audio_frame(_audioCtx, buff, &_dataSize, &_packet); - - if (len < 0) { - char errstring[1204]; - av_strerror(len, errstring, sizeof(errstring)); - ostringstream msg; - - if (_audioCtx->codec_id == AV_CODEC_ID_MP3) { - msg << "AudioLoader: invalid frame, skipping it: " << errstring; - // mp3 streams can have tag frames (id3v2?) which libavcodec tries to - // read as audio anyway, and we probably don't want print an error - // message for that... - // TODO: Are these frames really id3 tags? - - //E_DEBUG(EAlgorithm, msg); - E_WARNING(msg.str()); - } - else { - msg << "AudioLoader: error while decoding, skipping frame: " << errstring; - E_WARNING(msg.str()); - } - return 0; - } - - if (len != _packet.size) { - // https://www.ffmpeg.org/doxygen/trunk/group__lavc__decoding.html#ga834bb1b062fbcc2de4cf7fb93f154a3e - - // Some decoders may support multiple frames in a single AVPacket. Such - // decoders would then just decode the first frame and the return value - // would be less than the packet size. In this case, avcodec_decode_audio4 - // has to be called again with an AVPacket containing the remaining data - // in order to decode the second frame, etc... Even if no frames are - // returned, the packet needs to be fed to the decoder with remaining - // data until it is completely consumed or an error occurs. - - E_WARNING("AudioLoader: more than 1 frame in packet, decoding remaining bytes..."); - E_WARNING("at sample index: " << output("audio").totalProduced()); - E_WARNING("decoded samples: " << len); - E_WARNING("packet size: " << _packet.size); - } - - // update packet data pointer to data left undecoded (if any) - _packet.size -= len; - _packet.data += len; - - - if (_dataSize <= 0) { - // No data yet, get more frames - // cout << "no data yet, get more frames" << endl; - _dataSize = 0; - } - - return len; + // Get audio file data format + AudioStreamBasicDescription asbd; + UInt32 propertySize = sizeof(asbd); + if ( ExtAudioFileGetProperty(_file, kExtAudioFileProperty_FileDataFormat, &propertySize, &asbd) != noErr ) { + throw EssentiaException("AudioLoader: Error getting audio file channel and sample rate info"); } + pushChannelsSampleRateInfo(asbd.mChannelsPerFrame, asbd.mSampleRate); - /* - inline Real scale(int16_t value) { - return value / (Real)32767; - } - */ + // Get bit rate + OSStatus status = noErr; + AudioFileID audioFileId = NULL; - void AudioLoader::copyFFmpegOutput() { - int nsamples = _dataSize / (av_get_bytes_per_sample(AV_SAMPLE_FMT_FLT) * _nChannels); - if (nsamples == 0) return; - - // acquire necessary data - bool ok = _audio.acquire(nsamples); - if (!ok) { - throw EssentiaException("AudioLoader: could not acquire output for audio"); - } - - vector& audio = *((vector*)_audio.getTokens()); - - if (_nChannels == 1) { - for (int i=0; ichannels, _audioCtx->sample_rate); - pushCodecInfo(_audioCodec->name, _audioCtx->bit_rate); + UInt32 bitRate = 0; + size = sizeof(bitRate); + if ( AudioFileGetProperty(audioFileId, kAudioFilePropertyBitRate, &size, &bitRate) != noErr ) { + throw EssentiaException("AudioLoader: Error getting audio file bitRate"); } - } // namespace streaming + E_DEBUG(EAlgorithm, "AudioLoader: TODO: get audio codec"); + pushCodecInfo("pcm_s16le", bitRate); + } + +} // namespace streaming } // namespace essentia diff --git a/src/internal/audioloader_ios_native.cpp b/src/internal/audioloader_ios_native.cpp deleted file mode 100644 index 08b02d8..0000000 --- a/src/internal/audioloader_ios_native.cpp +++ /dev/null @@ -1,401 +0,0 @@ -// -// audioloader_ios.cpp -// ESMusicExtractor -// -// Created by Ragnar Hrafnkelsson on 07/08/2018. -// Copyright © 2018 Reactify. All rights reserved. -// - -/* - * Copyright (C) 2006-2016 Music Technology Group - Universitat Pompeu Fabra - * - * This file is part of Essentia - * - * Essentia is free software: you can redistribute it and/or modify it under - * the terms of the GNU Affero General Public License as published by the Free - * Software Foundation (FSF), either version 3 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the Affero GNU General Public License - * version 3 along with this program. If not, see http://www.gnu.org/licenses/ - */ - - -#include -#include // setw() -#include - -#include "audioloader_ios.h" -#include "algorithmfactory.h" - -using namespace std; -// https://codereview.stackexchange.com/a/22907 -static std::vector readAllBytes(char const* filename) { - ifstream ifs(filename, ios::binary|ios::ate); - ifstream::pos_type pos = ifs.tellg(); - - vector result(pos); - - ifs.seekg(0, ios::beg); - ifs.read(result.data(), pos); - - auto header = string(result.begin(), result.begin() + 4); - if ( header == "RIFF" ) { // WAVE file - return vector( result.begin() + 44, result.end() ); - } else if ( header == "FORM" ) { // AIFF - for ( int i = 4; i < result.size(); i++ ) { - auto str = string(result.begin()+i, result.begin()+i+4); - if ( str == "SSND" ) { - return vector( result.begin()+i+16, result.end() ); - } - } - } else { - auto str = string(result.begin(), result.begin()+11); - if ( str.substr(0,3) == "ID3" ) { // mp3 - // Extract ID3 tag size from synchsafe integer - // https://stackoverflow.com/a/5652842 - uint8_t* sync_safe = (uint8_t *)result.data()+6; - uint32_t byte0 = sync_safe[0]; - uint32_t byte1 = sync_safe[1]; - uint32_t byte2 = sync_safe[2]; - uint32_t byte3 = sync_safe[3]; - int len = byte0 << 21 | byte1 << 14 | byte2 << 7 | byte3; - int tagSize = len + 10; // - return vector( result.begin()+tagSize, result.end() ); - } else if ( str.substr(4, 11) == "ftypM4A" ) { // m4a - for ( int i = 0; i < result.size()-4; i++ ) { - if ( result[i] == '!' && result[i+3] == '@' && result[i+4] == 'h' ) { - return vector( result.begin()+i, result.end() ); - } - } - } else { - cout << "File format not supported" << endl; - } - } - return result; -} - -string uint8_t_to_hex(uint8_t* input, int size) { - ostringstream result; - for(int i=0; i 2) { - throw EssentiaException("AudioLoader: could not load audio. Audio file has more than 2 channels."); - } - if (sampleRate <= 0) { - throw EssentiaException("AudioLoader: could not load audio. Audio sampling rate must be greater than 0."); - } - - _nChannels = nChannels; - - _channels.push(nChannels); - _sampleRate.push(sampleRate); - } - - void AudioLoader::pushCodecInfo(std::string codec, int bit_rate) { - _codec.push(codec); - _bit_rate.push(bit_rate); - } - - AlgorithmStatus AudioLoader::process() { - if (!parameter("filename").isConfigured()) { - throw EssentiaException("AudioLoader: Trying to call process() on an AudioLoader algo which hasn't been correctly configured."); - } - - auto audioFormat = ({ - AudioStreamBasicDescription audioDescription; - memset(&audioDescription, 0, sizeof(audioDescription)); - audioDescription.mFormatID = kAudioFormatLinearPCM; - audioDescription.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked | kAudioFormatFlagIsNonInterleaved; - audioDescription.mChannelsPerFrame = 2; - audioDescription.mBytesPerPacket = sizeof(float); - audioDescription.mFramesPerPacket = 1; - audioDescription.mBytesPerFrame = sizeof(float); - audioDescription.mBitsPerChannel = 8 * sizeof(float); - audioDescription.mSampleRate = 44100.0; - audioDescription; - }); - - const SInt64 frameCount = 4096; - - // Create temporary audio bufferlist - int numberOfBuffers = audioFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved ? audioFormat.mChannelsPerFrame : 1; - int channelsPerBuffer = audioFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved ? 1 : audioFormat.mChannelsPerFrame; - int bytesPerBuffer = audioFormat.mBytesPerFrame * frameCount; - - AudioBufferList *bufferList = (AudioBufferList *)malloc(sizeof(AudioBufferList) + (numberOfBuffers-1)*sizeof(AudioBuffer)); - if ( !bufferList ) { - throw EssentiaException("AudioLoader: Error creating AudioBufferList"); - } - bufferList->mNumberBuffers = numberOfBuffers; - for ( int i=0; i 0 ) { - bufferList->mBuffers[i].mData = calloc(bytesPerBuffer, 1); - if ( !bufferList->mBuffers[i].mData ) { - for ( int j=0; jmBuffers[j].mData); - free(bufferList); - throw EssentiaException("AudioLoader: Error allocating data for AudioBufferList"); - } - } else { - bufferList->mBuffers[i].mData = NULL; - } - bufferList->mBuffers[i].mDataByteSize = bytesPerBuffer; - bufferList->mBuffers[i].mNumberChannels = channelsPerBuffer; - } - - // Set destination format - if ( ExtAudioFileSetProperty(_file, kExtAudioFileProperty_ClientDataFormat, sizeof(audioFormat), &audioFormat) != noErr ) { - throw EssentiaException("AudioLoader: Error setting client data format"); - } - - // Read audio - UInt32 readFrames = frameCount; - if ( ExtAudioFileRead(_file, &readFrames, bufferList) != noErr ) { - throw EssentiaException("AudioLoader: Error reading file" ); - } - - bool eof = readFrames < frameCount; - if ( eof ) { // EOF - shouldStop(true); - } - - int nsamples = readFrames; - - // acquire necessary data - bool ok = _audio.acquire(nsamples); - if (!ok) { - throw EssentiaException("AudioLoader: could not acquire output for audio"); - } - - vector& audio = *((vector*)_audio.getTokens()); - - if (_nChannels == 1) { - for (int i=0; imBuffers[0].mData)[i]; - } - } - else { // _nChannels == 2 - for (int i=0; imBuffers[0].mData)[i]; - audio[i].right() = ((float *)bufferList->mBuffers[1].mData)[i]; - } - } - - // release data - _audio.release(nsamples); - - // Free bufferlist - for ( int i=0; imNumberBuffers; i++ ) { - if ( bufferList->mBuffers[i].mData ) free(bufferList->mBuffers[i].mData); - } - free(bufferList); - - if ( eof ) { - closeAudioFile(); - - return FINISHED; - } - - return OK; - } - - void AudioLoader::reset() { - Algorithm::reset(); - - if (!parameter("filename").isConfigured()) return; - - string filename = parameter("filename").toString(); - - closeAudioFile(); - openAudioFile(filename); - - // Get audio file data format - AudioStreamBasicDescription asbd; - UInt32 propertySize = sizeof(asbd); - if ( ExtAudioFileGetProperty(_file, kExtAudioFileProperty_FileDataFormat, &propertySize, &asbd) != noErr ) { - throw EssentiaException("AudioLoader: Error getting audio file channel and sample rate info"); - } - pushChannelsSampleRateInfo(asbd.mChannelsPerFrame, asbd.mSampleRate); - - // Get bit rate - OSStatus status = noErr; - AudioFileID audioFileId = NULL; - - UInt32 size = sizeof(audioFileId); - status = ExtAudioFileGetProperty(_file, kExtAudioFileProperty_AudioFile, &size, &audioFileId); - assert(status == noErr); - - UInt32 bitRate = 0; - size = sizeof(bitRate); - if ( AudioFileGetProperty(audioFileId, kAudioFilePropertyBitRate, &size, &bitRate) != noErr ) { - throw EssentiaException("AudioLoader: Error getting audio file bitRate"); - } - - E_DEBUG(EAlgorithm, "AudioLoader: TODO: get audio codec"); - pushCodecInfo("pcm_s16le", bitRate); - } - -} // namespace streaming -} // namespace essentia - - -namespace essentia { - namespace standard { - - const char* AudioLoader::name = "AudioLoader"; - const char* AudioLoader::category = "Input/output"; - const char* AudioLoader::description = DOC("This algorithm loads the single audio stream contained in a given audio or video file. Supported formats are all those supported by the FFmpeg library including wav, aiff, flac, ogg and mp3.\n" - "\n" - "This algorithm will throw an exception if it was not properly configured which is normally due to not specifying a valid filename. Invalid names comprise those with extensions different than the supported formats and non existent files. If using this algorithm on Windows, you must ensure that the filename is encoded as UTF-8\n\n" - "Note: ogg files are decoded in reverse phase, due to be using ffmpeg library.\n" - "\n" - "References:\n" - " [1] WAV - Wikipedia, the free encyclopedia,\n" - " http://en.wikipedia.org/wiki/Wav\n" - " [2] Audio Interchange File Format - Wikipedia, the free encyclopedia,\n" - " http://en.wikipedia.org/wiki/Aiff\n" - " [3] Free Lossless Audio Codec - Wikipedia, the free encyclopedia,\n" - " http://en.wikipedia.org/wiki/Flac\n" - " [4] Vorbis - Wikipedia, the free encyclopedia,\n" - " http://en.wikipedia.org/wiki/Vorbis\n" - " [5] MP3 - Wikipedia, the free encyclopedia,\n" - " http://en.wikipedia.org/wiki/Mp3"); - - - void AudioLoader::createInnerNetwork() { - _loader = streaming::AlgorithmFactory::create("AudioLoader"); - _audioStorage = new streaming::VectorOutput(); - - _loader->output("audio") >> _audioStorage->input("data"); - _loader->output("sampleRate") >> PC(_pool, "internal.sampleRate"); - _loader->output("numberChannels") >> PC(_pool, "internal.numberChannels"); - _loader->output("md5") >> PC(_pool, "internal.md5"); - _loader->output("codec") >> PC(_pool, "internal.codec"); - _loader->output("bit_rate") >> PC(_pool, "internal.bit_rate"); - _network = new scheduler::Network(_loader); - } - - void AudioLoader::configure() { - _loader->configure(INHERIT("filename"), - INHERIT("computeMD5"), - INHERIT("audioStream")); - } - - void AudioLoader::compute() { - if (!parameter("filename").isConfigured()) { - throw EssentiaException("AudioLoader: Trying to call compute() on an " - "AudioLoader algo which hasn't been correctly configured."); - } - - Real& sampleRate = _sampleRate.get(); - int& numberChannels = _channels.get(); - string& md5 = _md5.get(); - int& bit_rate = _bit_rate.get(); - string& codec = _codec.get(); - vector& audio = _audio.get(); - - _audioStorage->setVector(&audio); - // TODO: is using VectorInput indeed faster than using Pool? - - // FIXME: - // _audio.reserve(sth_meaningful); - - _network->run(); - - sampleRate = _pool.value("internal.sampleRate"); - numberChannels = (int) _pool.value("internal.numberChannels"); - md5 = _pool.value("internal.md5"); - bit_rate = (int) _pool.value("internal.bit_rate"); - codec = _pool.value("internal.codec"); - - // reset, so it is ready to load audio again - reset(); - } - - void AudioLoader::reset() { - _network->reset(); - _pool.remove("internal.md5"); - _pool.remove("internal.sampleRate"); - _pool.remove("internal.numberChannels"); - _pool.remove("internal.codec"); - _pool.remove("internal.bit_rate"); - } - - } // namespace standard -} // namespace essentia From 3d6fdff615a56d42c0ba6ca185400cff1adf982c Mon Sep 17 00:00:00 2001 From: ragnaringi Date: Thu, 30 Aug 2018 15:04:38 +0000 Subject: [PATCH 17/18] audioloader_ios cleanup --- src/internal/audioloader_ios.cpp | 97 +++++++++++--------------------- src/internal/audioloader_ios.h | 3 - 2 files changed, 32 insertions(+), 68 deletions(-) diff --git a/src/internal/audioloader_ios.cpp b/src/internal/audioloader_ios.cpp index 08b02d8..5af8715 100644 --- a/src/internal/audioloader_ios.cpp +++ b/src/internal/audioloader_ios.cpp @@ -99,7 +99,6 @@ namespace essentia { namespace streaming { } void AudioLoader::configure() { - // _selectedStream = parameter("audioStream").toInt(); reset(); } @@ -162,8 +161,6 @@ namespace essentia { namespace streaming { throw EssentiaException("AudioLoader: could not load audio. Audio sampling rate must be greater than 0."); } - _nChannels = nChannels; - _channels.push(nChannels); _sampleRate.push(sampleRate); } @@ -174,49 +171,31 @@ namespace essentia { namespace streaming { } AlgorithmStatus AudioLoader::process() { - if (!parameter("filename").isConfigured()) { + if ( !parameter("filename").isConfigured() ) { throw EssentiaException("AudioLoader: Trying to call process() on an AudioLoader algo which hasn't been correctly configured."); } - auto audioFormat = ({ - AudioStreamBasicDescription audioDescription; - memset(&audioDescription, 0, sizeof(audioDescription)); - audioDescription.mFormatID = kAudioFormatLinearPCM; - audioDescription.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked | kAudioFormatFlagIsNonInterleaved; - audioDescription.mChannelsPerFrame = 2; - audioDescription.mBytesPerPacket = sizeof(float); - audioDescription.mFramesPerPacket = 1; - audioDescription.mBytesPerFrame = sizeof(float); - audioDescription.mBitsPerChannel = 8 * sizeof(float); - audioDescription.mSampleRate = 44100.0; - audioDescription; - }); + AudioStreamBasicDescription audioFormat = {0}; + audioFormat.mFormatID = kAudioFormatLinearPCM; + audioFormat.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked | kAudioFormatFlagIsNonInterleaved; + audioFormat.mChannelsPerFrame = lastTokenProduced(_channels); + audioFormat.mBytesPerPacket = sizeof(float); + audioFormat.mFramesPerPacket = 1; + audioFormat.mBytesPerFrame = sizeof(float); + audioFormat.mBitsPerChannel = 8 * sizeof(float); + audioFormat.mSampleRate = 44100.0; const SInt64 frameCount = 4096; + const UInt32 channels = audioFormat.mChannelsPerFrame; + const UInt32 bytesPerBuffer = audioFormat.mBytesPerFrame * frameCount; // Create temporary audio bufferlist - int numberOfBuffers = audioFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved ? audioFormat.mChannelsPerFrame : 1; - int channelsPerBuffer = audioFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved ? 1 : audioFormat.mChannelsPerFrame; - int bytesPerBuffer = audioFormat.mBytesPerFrame * frameCount; - - AudioBufferList *bufferList = (AudioBufferList *)malloc(sizeof(AudioBufferList) + (numberOfBuffers-1)*sizeof(AudioBuffer)); - if ( !bufferList ) { - throw EssentiaException("AudioLoader: Error creating AudioBufferList"); - } - bufferList->mNumberBuffers = numberOfBuffers; - for ( int i=0; i 0 ) { - bufferList->mBuffers[i].mData = calloc(bytesPerBuffer, 1); - if ( !bufferList->mBuffers[i].mData ) { - for ( int j=0; jmBuffers[j].mData); - free(bufferList); - throw EssentiaException("AudioLoader: Error allocating data for AudioBufferList"); - } - } else { - bufferList->mBuffers[i].mData = NULL; - } - bufferList->mBuffers[i].mDataByteSize = bytesPerBuffer; - bufferList->mBuffers[i].mNumberChannels = channelsPerBuffer; + auto bufferList = (AudioBufferList *)malloc(sizeof(AudioBufferList) * channels); + bufferList->mNumberBuffers = channels; + for ( auto i = 0; i < bufferList->mNumberBuffers; i++ ) { + bufferList->mBuffers[i].mNumberChannels = 1; + bufferList->mBuffers[i].mDataByteSize = frameCount * audioFormat.mBytesPerFrame; + bufferList->mBuffers[i].mData = calloc(bytesPerBuffer, 1); } // Set destination format @@ -229,45 +208,33 @@ namespace essentia { namespace streaming { if ( ExtAudioFileRead(_file, &readFrames, bufferList) != noErr ) { throw EssentiaException("AudioLoader: Error reading file" ); } - - bool eof = readFrames < frameCount; - if ( eof ) { // EOF - shouldStop(true); - } - - int nsamples = readFrames; - // acquire necessary data - bool ok = _audio.acquire(nsamples); - if (!ok) { + // Acquire necessary data + if ( !_audio.acquire(readFrames) ) { throw EssentiaException("AudioLoader: could not acquire output for audio"); } - vector& audio = *((vector*)_audio.getTokens()); + auto& audio = *((vector*)_audio.getTokens()); - if (_nChannels == 1) { - for (int i=0; imBuffers[0].mData)[i]; - } - } - else { // _nChannels == 2 - for (int i=0; imBuffers[0].mData)[i]; - audio[i].right() = ((float *)bufferList->mBuffers[1].mData)[i]; + for ( auto i = 0; i < readFrames; i++ ) { + switch (channels) { + case 2: + audio[i].right() = ((float *)bufferList->mBuffers[1].mData)[i]; + default: + audio[i].left() = ((float *)bufferList->mBuffers[0].mData)[i]; } } - // release data - _audio.release(nsamples); + _audio.release(readFrames); // Free bufferlist - for ( int i=0; imNumberBuffers; i++ ) { - if ( bufferList->mBuffers[i].mData ) free(bufferList->mBuffers[i].mData); + for ( auto i = 0; i < bufferList->mNumberBuffers; i++ ) { + free(bufferList->mBuffers[i].mData); } free(bufferList); - if ( eof ) { - closeAudioFile(); + if ( readFrames < frameCount /* EOF */ ) { + shouldStop(true); return FINISHED; } diff --git a/src/internal/audioloader_ios.h b/src/internal/audioloader_ios.h index 06bfc27..85cffea 100755 --- a/src/internal/audioloader_ios.h +++ b/src/internal/audioloader_ios.h @@ -19,7 +19,6 @@ #pragma once -#include #include #include "streamingalgorithm.h" @@ -38,8 +37,6 @@ class AudioLoader : public Algorithm { AbsoluteSource _bit_rate; AbsoluteSource _codec; - int _nChannels; - ExtAudioFileRef _file; void openAudioFile(const std::string& filename); From 52b9e31456f06deb6ebe368ba24ea5e2cc568516 Mon Sep 17 00:00:00 2001 From: ragnaringi Date: Thu, 30 Aug 2018 15:22:24 +0000 Subject: [PATCH 18/18] Read audio bytes using AudioFileRead for MD5 calculation. Closes #6 & #4 --- src/internal/audioloader_ios.cpp | 83 ++++++++------------------------ 1 file changed, 20 insertions(+), 63 deletions(-) diff --git a/src/internal/audioloader_ios.cpp b/src/internal/audioloader_ios.cpp index 5af8715..3b1ade1 100644 --- a/src/internal/audioloader_ios.cpp +++ b/src/internal/audioloader_ios.cpp @@ -34,51 +34,6 @@ #include "algorithmfactory.h" using namespace std; -// https://codereview.stackexchange.com/a/22907 -static std::vector readAllBytes(char const* filename) { - ifstream ifs(filename, ios::binary|ios::ate); - ifstream::pos_type pos = ifs.tellg(); - - vector result(pos); - - ifs.seekg(0, ios::beg); - ifs.read(result.data(), pos); - - auto header = string(result.begin(), result.begin() + 4); - if ( header == "RIFF" ) { // WAVE file - return vector( result.begin() + 44, result.end() ); - } else if ( header == "FORM" ) { // AIFF - for ( int i = 4; i < result.size(); i++ ) { - auto str = string(result.begin()+i, result.begin()+i+4); - if ( str == "SSND" ) { - return vector( result.begin()+i+16, result.end() ); - } - } - } else { - auto str = string(result.begin(), result.begin()+11); - if ( str.substr(0,3) == "ID3" ) { // mp3 - // Extract ID3 tag size from synchsafe integer - // https://stackoverflow.com/a/5652842 - uint8_t* sync_safe = (uint8_t *)result.data()+6; - uint32_t byte0 = sync_safe[0]; - uint32_t byte1 = sync_safe[1]; - uint32_t byte2 = sync_safe[2]; - uint32_t byte3 = sync_safe[3]; - int len = byte0 << 21 | byte1 << 14 | byte2 << 7 | byte3; - int tagSize = len + 10; // - return vector( result.begin()+tagSize, result.end() ); - } else if ( str.substr(4, 11) == "ftypM4A" ) { // m4a - for ( int i = 0; i < result.size()-4; i++ ) { - if ( result[i] == '!' && result[i+3] == '@' && result[i+4] == 'h' ) { - return vector( result.begin()+i, result.end() ); - } - } - } else { - cout << "File format not supported" << endl; - } - } - return result; -} string uint8_t_to_hex(uint8_t* input, int size) { ostringstream result; @@ -113,25 +68,28 @@ namespace essentia { namespace streaming { throw EssentiaException("AudioLoader: Could not open file \"", filename, "\", error = ", result); } - auto fileData = readAllBytes(filename.c_str()); -// for ( int i = 0; i < 100; i++ ) { -// std::cout << i << " : " << fileData[i] << std::endl; -// } - - // compute md5 first + // Compute md5 first if ( parameter("computeMD5").toBool() ) { CC_MD5_CTX hashObject; if ( !CC_MD5_Init(&hashObject) ) { - throw EssentiaException("Error allocating the MD5 context"); + throw EssentiaException("AudioLoader: Error allocating the MD5 context"); } - size_t chunkSize = 4096; - for ( size_t i = 0; i < fileData.size(); i += chunkSize ) { - auto len = std::min(fileData.size()-i, chunkSize); + AudioFileID file; + if ( AudioFileOpenURL(url, kAudioFileReadPermission, 0, &file) != noErr ) { + throw EssentiaException("AudioLoader: Error reading file for MD5 hash"); + } + + SInt64 readPos = 0; + UInt32 chunkSize = 4096; + while ( chunkSize == 4096 ) { + void *buffer[chunkSize]; + AudioFileReadBytes(file, false, readPos, &chunkSize, buffer); CC_MD5_Update(&hashObject, - fileData.data()+i, - (CC_LONG)len); + buffer, + (CC_LONG)chunkSize); + readPos += chunkSize; } // Compute the hash digest @@ -245,7 +203,7 @@ namespace essentia { namespace streaming { void AudioLoader::reset() { Algorithm::reset(); - if (!parameter("filename").isConfigured()) return; + if ( !parameter("filename").isConfigured() ) return; string filename = parameter("filename").toString(); @@ -253,7 +211,7 @@ namespace essentia { namespace streaming { openAudioFile(filename); // Get audio file data format - AudioStreamBasicDescription asbd; + AudioStreamBasicDescription asbd = {0}; UInt32 propertySize = sizeof(asbd); if ( ExtAudioFileGetProperty(_file, kExtAudioFileProperty_FileDataFormat, &propertySize, &asbd) != noErr ) { throw EssentiaException("AudioLoader: Error getting audio file channel and sample rate info"); @@ -261,12 +219,11 @@ namespace essentia { namespace streaming { pushChannelsSampleRateInfo(asbd.mChannelsPerFrame, asbd.mSampleRate); // Get bit rate - OSStatus status = noErr; AudioFileID audioFileId = NULL; - UInt32 size = sizeof(audioFileId); - status = ExtAudioFileGetProperty(_file, kExtAudioFileProperty_AudioFile, &size, &audioFileId); - assert(status == noErr); + if ( ExtAudioFileGetProperty(_file, kExtAudioFileProperty_AudioFile, &size, &audioFileId) != noErr ) { + throw EssentiaException("AudioLoader: Error getting audiofile id for bitrate calculation"); + } UInt32 bitRate = 0; size = sizeof(bitRate);