blob: 3ef3a8bfcfbe357fd9d103cad1aafe56249328b3 [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/media/audio/lib/format2/stream_converter.h"
#include <fidl/fuchsia.audio/cpp/common_types.h>
#include <fidl/fuchsia.audio/cpp/natural_ostream.h>
#include <lib/syslog/cpp/macros.h>
#include <cmath>
#include <memory>
#include <type_traits>
#include "src/media/audio/lib/format2/sample_converter.h"
namespace media_audio {
namespace {
using ::fuchsia_audio::SampleType;
template <typename SourceSampleType, typename DestSampleType>
void CopyWithConversion(const void* source_samples, void* dest_samples, int64_t sample_count) {
using SourceConverter = SampleConverter<SourceSampleType>;
using DestConverter = SampleConverter<DestSampleType>;
const auto* source_ptr = static_cast<const SourceSampleType*>(source_samples);
auto* dest_ptr = static_cast<DestSampleType*>(dest_samples);
for (int64_t i = 0; i < sample_count; ++i) {
dest_ptr[i] = DestConverter::FromFloat(SourceConverter::ToFloat(source_ptr[i]));
}
}
template <typename SourceSampleType>
void CopyWithConversion(const Format& dest_format, const void* source_samples, void* dest_samples,
int64_t sample_count) {
switch (dest_format.sample_type()) {
case SampleType::kUint8:
CopyWithConversion<SourceSampleType, uint8_t>(source_samples, dest_samples, sample_count);
break;
case SampleType::kInt16:
CopyWithConversion<SourceSampleType, int16_t>(source_samples, dest_samples, sample_count);
break;
case SampleType::kInt32:
CopyWithConversion<SourceSampleType, int32_t>(source_samples, dest_samples, sample_count);
break;
case SampleType::kFloat32:
CopyWithConversion<SourceSampleType, float>(source_samples, dest_samples, sample_count);
break;
default:
FX_LOGS(FATAL) << dest_format.sample_type();
__builtin_unreachable();
}
}
void CopyWithConversion(const Format& source_format, const Format& dest_format,
const void* source_samples, void* dest_samples, int64_t sample_count) {
switch (source_format.sample_type()) {
case SampleType::kUint8:
CopyWithConversion<uint8_t>(dest_format, source_samples, dest_samples, sample_count);
break;
case SampleType::kInt16:
CopyWithConversion<int16_t>(dest_format, source_samples, dest_samples, sample_count);
break;
case SampleType::kInt32:
CopyWithConversion<int32_t>(dest_format, source_samples, dest_samples, sample_count);
break;
case SampleType::kFloat32:
CopyWithConversion<float>(dest_format, source_samples, dest_samples, sample_count);
break;
default:
FX_LOGS(FATAL) << source_format.sample_type();
__builtin_unreachable();
}
}
template <typename SourceSampleType, typename DestSampleType>
void CopyAndClipWithConversion(const void* source_samples, void* dest_samples,
int64_t sample_count) {
using SourceConverter = SampleConverter<SourceSampleType>;
using DestConverter = SampleConverter<DestSampleType>;
const auto* source_ptr = static_cast<const SourceSampleType*>(source_samples);
auto* dest_ptr = static_cast<DestSampleType*>(dest_samples);
for (int64_t i = 0; i < sample_count; ++i) {
if constexpr (std::is_same_v<SourceSampleType, float> &&
std::is_same_v<DestSampleType, float>) {
float source = DestConverter::FromFloat(SourceConverter::ToFloat(source_ptr[i]));
// Clamp the sample for float -> float conversion. Attempts to handle NAN without actually
// using isnan(), such as comparing the value to itself, were not fruitful.
dest_ptr[i] = std::clamp<float>(source, -1.0f, 1.0f);
} else {
dest_ptr[i] = DestConverter::FromFloat(SourceConverter::ToFloat(source_ptr[i]));
}
}
}
template <typename SourceSampleType>
void CopyAndClipWithConversion(const Format& dest_format, const void* source_samples,
void* dest_samples, int64_t sample_count) {
switch (dest_format.sample_type()) {
case SampleType::kUint8:
CopyAndClipWithConversion<SourceSampleType, uint8_t>(source_samples, dest_samples,
sample_count);
break;
case SampleType::kInt16:
CopyAndClipWithConversion<SourceSampleType, int16_t>(source_samples, dest_samples,
sample_count);
break;
case SampleType::kInt32:
CopyAndClipWithConversion<SourceSampleType, int32_t>(source_samples, dest_samples,
sample_count);
break;
case SampleType::kFloat32:
CopyAndClipWithConversion<SourceSampleType, float>(source_samples, dest_samples,
sample_count);
break;
default:
FX_LOGS(FATAL) << dest_format.sample_type();
__builtin_unreachable();
}
}
void CopyAndClipWithConversion(const Format& source_format, const Format& dest_format,
const void* source_samples, void* dest_samples,
int64_t sample_count) {
switch (source_format.sample_type()) {
case SampleType::kUint8:
CopyAndClipWithConversion<uint8_t>(dest_format, source_samples, dest_samples, sample_count);
break;
case SampleType::kInt16:
CopyAndClipWithConversion<int16_t>(dest_format, source_samples, dest_samples, sample_count);
break;
case SampleType::kInt32:
CopyAndClipWithConversion<int32_t>(dest_format, source_samples, dest_samples, sample_count);
break;
case SampleType::kFloat32:
CopyAndClipWithConversion<float>(dest_format, source_samples, dest_samples, sample_count);
break;
default:
FX_LOGS(FATAL) << source_format.sample_type();
__builtin_unreachable();
}
}
} // namespace
StreamConverter::StreamConverter(const Format& source_format, const Format& dest_format)
: source_format_(source_format),
dest_format_(dest_format),
// Conversion needed iff the formats are different or the source samples need clamping.
should_convert_(source_format_.sample_type() != dest_format_.sample_type() ||
source_format_.sample_type() == SampleType::kFloat32) {}
// static
StreamConverter StreamConverter::CreateFromFloatSource(const Format& dest_format) {
const auto source_format = Format::CreateOrDie({
.sample_type = SampleType::kFloat32,
.channels = dest_format.channels(),
.frames_per_second = dest_format.frames_per_second(),
});
return StreamConverter(source_format, dest_format);
}
void StreamConverter::Copy(const void* source_samples, void* dest_samples,
int64_t frame_count) const {
if (should_convert_) {
CopyWithConversion(source_format_, dest_format_, source_samples, dest_samples,
frame_count * dest_format_.channels());
} else {
std::memmove(dest_samples, source_samples, frame_count * source_format_.bytes_per_frame());
}
}
void StreamConverter::CopyAndClip(const void* source_samples, void* dest_samples,
int64_t frame_count) const {
if (should_convert_) {
CopyAndClipWithConversion(source_format_, dest_format_, source_samples, dest_samples,
frame_count * dest_format_.channels());
} else {
std::memmove(dest_samples, source_samples, frame_count * source_format_.bytes_per_frame());
}
}
void StreamConverter::WriteSilence(void* dest_samples, int64_t frame_count) const {
if (dest_format().sample_type() == SampleType::kUint8) {
std::memset(dest_samples, kInt8ToUint8, frame_count * dest_format().channels());
} else {
// All other sample formats represent silence with zeroes.
std::memset(dest_samples, 0, frame_count * dest_format().bytes_per_frame());
}
}
} // namespace media_audio