blob: 372239d428ba1d92a2293993f62174274a808710 [file] [log] [blame]
// Copyright 2017 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/format/format.h"
#include <lib/syslog/cpp/macros.h>
#include "src/media/audio/lib/format/constants.h"
#include "src/media/audio/lib/format/traits.h"
namespace media::audio {
namespace {
bool Validate(fuchsia::media::AudioStreamType stream_type) {
// Sanity check the details of the mode request.
if ((stream_type.channels < fuchsia::media::MIN_PCM_CHANNEL_COUNT) ||
(stream_type.channels > fuchsia::media::MAX_PCM_CHANNEL_COUNT)) {
FX_LOGS(ERROR) << "Bad channel count, " << stream_type.channels << " is not in the range ["
<< fuchsia::media::MIN_PCM_CHANNEL_COUNT << ", "
<< fuchsia::media::MAX_PCM_CHANNEL_COUNT << "]";
return false;
}
if ((stream_type.frames_per_second < fuchsia::media::MIN_PCM_FRAMES_PER_SECOND) ||
(stream_type.frames_per_second > fuchsia::media::MAX_PCM_FRAMES_PER_SECOND)) {
FX_LOGS(ERROR) << "Bad frame rate, " << stream_type.frames_per_second
<< " is not in the range [" << fuchsia::media::MIN_PCM_FRAMES_PER_SECOND << ", "
<< fuchsia::media::MAX_PCM_FRAMES_PER_SECOND << "]";
return false;
}
switch (stream_type.sample_format) {
case fuchsia::media::AudioSampleFormat::UNSIGNED_8:
case fuchsia::media::AudioSampleFormat::SIGNED_16:
case fuchsia::media::AudioSampleFormat::SIGNED_24_IN_32:
case fuchsia::media::AudioSampleFormat::FLOAT:
break;
default:
FX_LOGS(ERROR) << "Bad sample format " << fidl::ToUnderlying(stream_type.sample_format);
return false;
}
return true;
}
} // namespace
fpromise::result<Format> Format::Create(fuchsia::media::AudioStreamType stream_type) {
if (!Validate(stream_type)) {
return fpromise::error();
}
return fpromise::ok(Format(stream_type));
}
template <fuchsia::media::AudioSampleFormat SampleFormat>
fpromise::result<TypedFormat<SampleFormat>> Format::Create(int32_t channels,
int32_t frames_per_second) {
fuchsia::media::AudioStreamType stream_type = {
.sample_format = SampleFormat,
.channels = static_cast<uint32_t>(channels),
.frames_per_second = static_cast<uint32_t>(frames_per_second),
};
if (!Validate(stream_type)) {
return fpromise::error();
}
return fpromise::ok(TypedFormat<SampleFormat>(stream_type));
}
Format::Format(fuchsia::media::AudioStreamType stream_type) : stream_type_(stream_type) {
// Precompute some useful timing/format stuff.
//
// Start with the ratio between frames and nanoseconds.
frames_per_ns_ = TimelineRate(stream_type.frames_per_second, ZX_SEC(1));
// Figure out the rate we need to scale by in order to produce our fixed point timestamps.
frame_to_media_ratio_ = TimelineRate(Fixed(1).raw_value(), 1);
switch (stream_type.sample_format) {
case fuchsia::media::AudioSampleFormat::UNSIGNED_8:
bytes_per_frame_ = 1;
valid_bits_per_channel_ = 8;
break;
case fuchsia::media::AudioSampleFormat::SIGNED_16:
bytes_per_frame_ = 2;
valid_bits_per_channel_ = 16;
break;
case fuchsia::media::AudioSampleFormat::SIGNED_24_IN_32:
bytes_per_frame_ = 4;
valid_bits_per_channel_ = 24;
break;
case fuchsia::media::AudioSampleFormat::FLOAT:
bytes_per_frame_ = 4;
valid_bits_per_channel_ = 32;
break;
}
bytes_per_frame_ *= stream_type.channels;
}
bool Format::operator==(const Format& other) const {
// All the other class members are derived from our stream_type, so we don't need to include them
// here.
return fidl::Equals(stream_type_, other.stream_type_);
}
// Explicitly instantiate all possible implementations.
#define INSTANTIATE(T) \
template fpromise::result<TypedFormat<T>> Format::Create<T>(int32_t channels, \
int32_t frames_per_seconds);
INSTANTIATE_FOR_ALL_FORMATS(INSTANTIATE)
} // namespace media::audio