Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
9cb0669
Improve error messages
sbooth May 3, 2024
44a4a81
Restore import
sbooth May 3, 2024
9def3ff
Merge branch 'main' into error-reporting
sbooth May 3, 2024
66784bb
Remove TARGET_OS_MACCATALYST
sbooth May 3, 2024
2f3999a
Merge branch 'main' into error-reporting
sbooth May 3, 2024
a1a9df2
Merge branch 'main' into error-reporting
sbooth May 3, 2024
f6d4a2e
Update file header
sbooth May 3, 2024
e998d75
Merge branch 'main' into error-reporting
sbooth May 3, 2024
75cb2c9
Add SFBStringFormatting to module map
sbooth May 3, 2024
4f023b5
Merge branch 'main' into error-reporting
sbooth May 7, 2024
01f6092
Merge branch 'main' into error-reporting
sbooth May 16, 2024
bcaf740
Align fourcc_*_string implementations
sbooth May 16, 2024
d00f994
Merge branch 'main' into error-reporting
sbooth Sep 25, 2024
d6bc267
Remove erroneous noexcept
sbooth Sep 25, 2024
f8ee7be
Improve performance of `fourcc_fourchar_string`
sbooth Nov 20, 2024
2f41dce
Remove `string_format`
sbooth Nov 20, 2024
fa639f2
Add namespace to `concat`
sbooth Nov 20, 2024
c5e5225
Remove unneeded include
sbooth Nov 20, 2024
a8dff43
Improve `osstatus_string`
sbooth Nov 24, 2024
5dd704f
Merge branch 'main' into error-reporting
sbooth Dec 2, 2024
c577eea
Merge branch 'main' into error-reporting
sbooth Dec 22, 2024
8ddbfc3
Merge branch 'main' into error-reporting
sbooth Dec 22, 2024
dbd903c
Update header
sbooth Dec 22, 2024
34ebcf9
Add `string_from_cftype`
sbooth Dec 22, 2024
9b5cdd4
Add null check
sbooth Dec 22, 2024
6df6cdc
Add nullability specifiers
sbooth Dec 22, 2024
aa7665d
Merge branch 'main' into error-reporting
sbooth Jun 25, 2025
2adf6b2
Merge branch 'main' into error-reporting
sbooth Jun 25, 2025
20f654b
Merge branch 'main' into error-reporting
sbooth Oct 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 124 additions & 0 deletions Sources/CXXAudioUtilities/SFBStringFormatting.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
//
// Copyright (c) 2021-2024 Stephen F. Booth <me@sbooth.org>
// Part of https://github.com/sbooth/CXXAudioUtilities
// MIT license
//

#import <cctype>
#import <memory>

#import "SFBStringFormatting.hpp"

namespace {

/// Returns true if @c i consists of four printing characters
constexpr bool fourcc_isprint(uint32_t i) noexcept
{
return std::isprint(static_cast<unsigned char>(i)) && std::isprint(static_cast<unsigned char>(i >> 8)) && std::isprint(static_cast<unsigned char>(i >> 16)) && std::isprint(static_cast<unsigned char>(i >> 24));
}

/// Creates a @c std::string containing @c fourcc formatted as four characters and returns the result
/// @throw @c std::length_error
/// @throw @c std::bad_alloc
/// @throw @c std::bad_array_new_length
std::string fourcc_fourchar_string(uint32_t fourcc)
{
return {
#if __BIG_ENDIAN__
static_cast<char>(fourcc),
static_cast<char>(fourcc >> 8),
static_cast<char>(fourcc >> 16),
static_cast<char>(fourcc >> 24),
#elif __LITTLE_ENDIAN__
static_cast<char>(fourcc >> 24),
static_cast<char>(fourcc >> 16),
static_cast<char>(fourcc >> 8),
static_cast<char>(fourcc),
#else
#error "Unknown endianness"
#endif
};
}

/// Creates a @c std::string containing @c val formatted as hexadecimal and returns the result
/// @throw @c std::length_error
/// @throw @c std::bad_alloc
/// @throw @c std::bad_array_new_length
template <typename T, typename = std::enable_if_t<std::is_unsigned_v<T>>>
std::string to_hex_string(T val, std::string::size_type len = sizeof(T) << 1)
{
static const char *digits = "0123456789ABCDEF";
std::string result(len, '0');
for(std::string::size_type i = 0, j = (len - 1) * 4; i < len; ++i, j -= 4)
result[i] = digits[(val >> j) & 0x0f];
return result;
}

} // namespace

std::string SFB::concat(std::initializer_list<std::string_view> il)
{
std::string::size_type len = 0;
for(auto s : il)
len += s.size();
std::string result;
result.reserve(len);
for(auto s : il)
result.append(s);
return result;
}

std::string SFB::string_from_cfstring(CFStringRef str)
{
if(!str)
return {};

auto range = CFRange{ .location = 0, .length = CFStringGetLength(str) };
auto max_size = CFStringGetMaximumSizeForEncoding(range.length, kCFStringEncodingUTF8);

std::string result;
result.reserve(max_size);

char buf [128];
while(range.length > 0) {
CFIndex bytesWritten = 0;
auto converted = CFStringGetBytes(str, range, kCFStringEncodingUTF8, 0, false, reinterpret_cast<UInt8 *>(buf), sizeof buf, &bytesWritten);
result.append(buf, static_cast<std::string::size_type>(bytesWritten));

range.location += converted;
range.length -= converted;
}

return result;
}

std::string string_from_cftype(CFTypeRef cf)
{
if(!cf)
return {};

struct cf_type_ref_deleter {
void operator()(CFTypeRef cf) { CFRelease(cf); }
};

auto description = std::unique_ptr<std::remove_pointer_t<CFStringRef>, cf_type_ref_deleter>{CFCopyDescription(cf)};
return SFB::string_from_cfstring(description.get());
}

std::string SFB::fourcc_string(uint32_t fourcc)
{
if(fourcc_isprint(fourcc))
return concat({"'", fourcc_fourchar_string(fourcc), "'"});
else
return concat({"0x", to_hex_string(fourcc)});
}

std::string SFB::osstatus_string(int32_t code)
{
if(fourcc_isprint(static_cast<uint32_t>(code)))
return concat({"'", fourcc_fourchar_string(static_cast<uint32_t>(code)), "'"});
else if(code > -200000 && code < 200000)
return std::to_string(code);
else
return concat({"0x", to_hex_string(static_cast<uint32_t>(code))});
}
19 changes: 10 additions & 9 deletions Sources/CXXAudioUtilities/include/SFBCAAudioFile.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#import "SFBCAException.hpp"
#import "SFBCAStreamBasicDescription.hpp"
#import "SFBCFWrapper.hpp"
#import "SFBStringFormatting.hpp"

CF_ASSUME_NONNULL_BEGIN

Expand Down Expand Up @@ -183,7 +184,7 @@ class CAAudioFile
{
UInt32 size;
auto result = AudioFileGetUserDataSize(mAudioFileID, inUserDataID, inIndex, &size);
ThrowIfCAAudioFileError(result, "AudioFileGetUserDataSize");
SFBThrowIfCAAudioFileError(result, concat({"AudioFileGetUserDataSize(", fourcc_string(inUserDataID), ", ", std::to_string(inIndex), ")"}));
return size;
}

Expand All @@ -192,23 +193,23 @@ class CAAudioFile
void GetUserData(UInt32 inUserDataID, UInt32 inIndex, UInt32& ioUserDataSize, void *outUserData)
{
auto result = AudioFileGetUserData(mAudioFileID, inUserDataID, inIndex, &ioUserDataSize, outUserData);
ThrowIfCAAudioFileError(result, "AudioFileGetUserData");
SFBThrowIfCAAudioFileError(result, concat({"AudioFileGetUserData(", fourcc_string(inUserDataID), ", ", std::to_string(inIndex), ")"}));
}

/// Sets the data of a chunk in a file.
/// @throw @c std::system_error
void SetUserData(UInt32 inUserDataID, UInt32 inIndex, UInt32 inUserDataSize, const void *inUserData)
{
auto result = AudioFileSetUserData(mAudioFileID, inUserDataID, inIndex, inUserDataSize, inUserData);
ThrowIfCAAudioFileError(result, "AudioFileGetUserData");
SFBThrowIfCAAudioFileError(result, concat({"AudioFileSetUserData(", fourcc_string(inUserDataID), ", ", std::to_string(inIndex), ")"}));
}

/// Removes a user chunk in a file.
/// @throw @c std::system_error
void RemoveUserData(UInt32 inUserDataID, UInt32 inIndex)
{
auto result = AudioFileRemoveUserData(mAudioFileID, inUserDataID, inIndex);
ThrowIfCAAudioFileError(result, "AudioFileRemoveUserData");
SFBThrowIfCAAudioFileError(result, concat({"AudioFileRemoveUserData(", fourcc_string(inUserDataID), ", ", std::to_string(inIndex), ")"}));
}

/// Gets information about the size of a property of an AudioFile and whether it can be set.
Expand All @@ -217,7 +218,7 @@ class CAAudioFile
{
UInt32 size;
auto result = AudioFileGetPropertyInfo(mAudioFileID, inPropertyID, &size, isWritable);
ThrowIfCAAudioFileError(result, "AudioFileGetPropertyInfo");
SFBThrowIfCAAudioFileError(result, concat({"AudioFileGetPropertyInfo(", fourcc_string(inPropertyID), ")"}));
return size;
}

Expand All @@ -226,15 +227,15 @@ class CAAudioFile
void GetProperty(AudioFilePropertyID inPropertyID, UInt32& ioDataSize, void *outPropertyData) const
{
auto result = AudioFileGetProperty(mAudioFileID, inPropertyID, &ioDataSize, outPropertyData);
ThrowIfCAAudioFileError(result, "AudioFileGetProperty");
SFBThrowIfCAAudioFileError(result, concat({"AudioFileGetProperty(", fourcc_string(inPropertyID), ")"}));
}

/// Sets the value for a property of an AudioFile.
/// @throw @c std::system_error
void SetProperty(AudioFilePropertyID inPropertyID, UInt32 inDataSize, const void *inPropertyData)
{
auto result = AudioFileSetProperty(mAudioFileID, inPropertyID, inDataSize, inPropertyData);
ThrowIfCAAudioFileError(result, "AudioFileSetProperty");
SFBThrowIfCAAudioFileError(result, concat({"AudioFileSetProperty(", fourcc_string(inPropertyID), ")"}));
}

/// Returns the file's format (@c kAudioFilePropertyFileFormat)
Expand Down Expand Up @@ -265,7 +266,7 @@ class CAAudioFile
{
UInt32 size;
auto result = AudioFileGetGlobalInfoSize(inPropertyID, inSpecifierSize, inSpecifier, &size);
ThrowIfCAAudioFileError(result, "AudioFileGetGlobalInfoSize");
SFBThrowIfCAAudioFileError(result, concat({"AudioFileGetGlobalInfoSize(", fourcc_string(inPropertyID), ")"}));
return size;
}

Expand All @@ -274,7 +275,7 @@ class CAAudioFile
static void GetGlobalInfo(AudioFilePropertyID inPropertyID, UInt32 inSpecifierSize, void * _Nullable inSpecifier, UInt32& ioDataSize, void *outPropertyData)
{
auto result = AudioFileGetGlobalInfo(inPropertyID, inSpecifierSize, inSpecifier, &ioDataSize, outPropertyData);
ThrowIfCAAudioFileError(result, "AudioFileGetGlobalInfo");
SFBThrowIfCAAudioFileError(result, concat({"AudioFileGetGlobalInfo(", fourcc_string(inPropertyID), ")"}));
}


Expand Down
Loading