/* This file is part of Darling. Copyright (C) 2020 Lubos Dolezel Darling is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Darling 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 GNU General Public License along with Darling. If not, see . */ #include "AudioFileFormatManager.h" AudioFileFormatManager::AudioFileFormatManager() { } void AudioFileFormatManager::buildDatabase() { AudioComponentDescription acd = { 0 }; acd.componentType = 'afil'; AudioComponent component = nullptr; while ((component = ::AudioComponentFindNext(component, &acd)) != nullptr) { AudioComponentInstance inst; OSStatus status = ::AudioComponentInstanceNew(component, &inst); if (status != noErr) continue; analyzeAudioFileComponent(inst); ::AudioComponentInstanceDispose(inst); } } static std::vector cfArrayToVector(CFArrayRef array) { std::vector rv; const CFIndex count = CFArrayGetCount(array); rv.reserve(count); for (CFIndex i = 0; i < count; i++) { CFStringRef str = (CFStringRef) CFArrayGetValueAtIndex(array, i); rv.push_back(CFStringGetCStringPtr(str, kCFStringEncodingUTF8)); } return rv; } void AudioFileFormatManager::analyzeAudioFileComponent(AudioFileComponent component) { ComponentInfo ci; UInt32 propSize; ::AudioComponentGetDescription(AudioComponent(component), &ci.acd); ci.fileType = ci.acd.componentSubType; // TODO: Is this correct? { CFStringRef name = nullptr; propSize = sizeof(name); if (::AudioFileComponentGetGlobalInfo(component, kAudioFileComponent_FileTypeName, 0, nullptr, &propSize, &name) == noErr && name) { ci.name = CFStringGetCStringPtr(name, kCFStringEncodingUTF8); CFRelease(name); } } { UInt32 can; propSize = sizeof(can); ci.canRead = ci.canWrite = false; if (::AudioFileComponentGetGlobalInfo(component, kAudioFileComponent_CanRead, 0, nullptr, &propSize, &can) == noErr && can) ci.canRead = true; if (::AudioFileComponentGetGlobalInfo(component, kAudioFileComponent_CanWrite, 0, nullptr, &propSize, &can) == noErr && can) ci.canWrite = true; } CFArrayRef array = nullptr; if (::AudioFileComponentGetGlobalInfo(component, kAudioFileComponent_UTIsForType, 0, nullptr, &propSize, &array) == noErr && array) { ci.utis = cfArrayToVector(array); CFRelease(array); } array = nullptr; if (::AudioFileComponentGetGlobalInfo(component, kAudioFileComponent_MIMETypesForType, 0, nullptr, &propSize, &array) == noErr && array) { ci.mimeTypes = cfArrayToVector(array); CFRelease(array); } array = nullptr; if (::AudioFileComponentGetGlobalInfo(component, kAudioFileComponent_ExtensionsForType, 0, nullptr, &propSize, &array) == noErr && array) { ci.extensions = cfArrayToVector(array); CFRelease(array); } if (::AudioFileComponentGetGlobalInfoSize(component, kAudioFileComponent_AvailableFormatIDs, 0, nullptr, &propSize) == noErr) { const size_t formatCount = propSize / sizeof(UInt32); std::unique_ptr formatIds(new UInt32[formatCount]); if (::AudioFileComponentGetGlobalInfo(component, kAudioFileComponent_AvailableFormatIDs, 0, nullptr, &propSize, formatIds.get()) == noErr) { for (size_t i = 0; i < formatCount; i++) { UInt32 format = formatIds[i]; std::vector asbds; if (::AudioFileComponentGetGlobalInfoSize(component, kAudioFileComponent_AvailableStreamDescriptionsForFormat, sizeof(UInt32), &format, &propSize) == noErr) { asbds.resize(propSize / sizeof(AudioStreamBasicDescription)); if (::AudioFileComponentGetGlobalInfo(component, kAudioFileComponent_AvailableStreamDescriptionsForFormat, sizeof(UInt32), &format, &propSize, asbds.data()) != noErr) asbds.resize(0); ci.formatDescriptions.emplace(format, std::move(asbds)); } else ci.formatDescriptions.emplace(format, asbds); } } } registerComponent(ci); } void AudioFileFormatManager::addToMap(std::unordered_map>& map, const std::string& key, ComponentInfo* ci) { auto it = map.find(key); if (it == map.end()) it = map.emplace(key, std::vector()).first; it->second.push_back(ci); } void AudioFileFormatManager::registerComponent(const ComponentInfo& ci) { m_components.push_back(ci); // Add format into various maps ComponentInfo* pci = &m_components.back(); m_fileTypeMap.emplace(ci.fileType, pci); for (const std::string& uti : ci.utis) addToMap(m_utiMap, uti, pci); for (const std::string& mime : ci.mimeTypes) addToMap(m_mimeMap, mime, pci); for (const std::string& ext : ci.extensions) addToMap(m_extensionMap, ext, pci); } AudioFileFormatManager* AudioFileFormatManager::instance() { static AudioFileFormatManager inst; return &inst; } std::set AudioFileFormatManager::availableFormatIDs(UInt32 fileType) const { std::set rv; auto it = m_fileTypeMap.find(fileType); if (it == m_fileTypeMap.end()) return rv; const ComponentInfo* ci = it->second; for (auto const& [key, value] : ci->formatDescriptions) rv.insert(key); return rv; } const AudioFileFormatManager::ComponentInfo* AudioFileFormatManager::fileType(UInt32 fileType) const { auto it = m_fileTypeMap.find(fileType); if (it == m_fileTypeMap.end()) return nullptr; return it->second; } std::set AudioFileFormatManager::types(bool writableTypes) const { std::set rv; for (const ComponentInfo& ci : m_components) { if (writableTypes && ci.canWrite) rv.insert(ci.fileType); else if (!writableTypes && ci.canRead) rv.insert(ci.fileType); } return rv; } std::set AudioFileFormatManager::findTypes(const std::unordered_map>& map, const char* key) { std::set rv; auto it = map.find(key); if (it == map.end()) return rv; for (const ComponentInfo* ci : it->second) rv.insert(ci->fileType); return rv; } std::set AudioFileFormatManager::typesForMIME(const char* mime) const { return findTypes(m_mimeMap, mime); } std::set AudioFileFormatManager::typesForUTI(const char* uti) const { return findTypes(m_utiMap, uti); } std::set AudioFileFormatManager::typesForExtension(const char* ext) const { return findTypes(m_extensionMap, ext); } std::set AudioFileFormatManager::allMIMEs() const { return allKeys(m_mimeMap); } std::set AudioFileFormatManager::allUTIs() const { return allKeys(m_utiMap); } std::set AudioFileFormatManager::allExtensions() const { return allKeys(m_extensionMap); }