| // Copyright 2020 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 <lib/ddk/platform-defs.h> |
| #include <lib/simple-codec/simple-codec-server.h> |
| #include <zircon/errors.h> |
| |
| #include <algorithm> |
| #include <memory> |
| |
| #include <bind/fuchsia/cpp/bind.h> |
| #include <fbl/algorithm.h> |
| #include <fbl/alloc_checker.h> |
| #include <fbl/auto_lock.h> |
| #include <fbl/string_printf.h> |
| |
| namespace audio { |
| |
| namespace audio_fidl = ::fuchsia::hardware::audio; |
| namespace signal_fidl = ::fuchsia::hardware::audio::signalprocessing; |
| |
| // SimpleCodecServer methods. |
| zx_status_t SimpleCodecServer::CreateAndAddToDdkInternal() { |
| simple_codec_ = inspect_.GetRoot().CreateChild("simple_codec"); |
| state_ = simple_codec_.CreateString("state", "created"); |
| start_time_ = simple_codec_.CreateInt("start_time", 0); |
| |
| number_of_channels_ = simple_codec_.CreateUint("number_of_channels", 0); |
| channels_to_use_bitmask_ = simple_codec_.CreateUint("channels_to_use_bitmask", 0); |
| frame_rate_ = simple_codec_.CreateUint("frame_rate", 0); |
| bits_per_slot_ = simple_codec_.CreateUint("bits_per_slot", 0); |
| bits_per_sample_ = simple_codec_.CreateUint("bits_per_sample", 0); |
| sample_format_ = simple_codec_.CreateString("sample_format", "not_set"); |
| frame_format_ = simple_codec_.CreateString("frame_format", "not_set"); |
| |
| auto res = Initialize(); |
| if (res.is_error()) { |
| return res.error_value(); |
| } |
| loop_->StartThread(); |
| |
| driver_ids_ = res.value(); |
| Info info = GetInfo(); |
| simple_codec_.CreateString("manufacturer", info.manufacturer, &inspect_); |
| simple_codec_.CreateString("product", info.product_name, &inspect_); |
| if (info.unique_id) { |
| auto uid_str = fbl::StringPrintf( // Convert array into 32-char string displaying hex values. |
| "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", info.unique_id->at(0), |
| info.unique_id->at(1), info.unique_id->at(2), info.unique_id->at(3), info.unique_id->at(4), |
| info.unique_id->at(5), info.unique_id->at(6), info.unique_id->at(7), info.unique_id->at(8), |
| info.unique_id->at(9), info.unique_id->at(10), info.unique_id->at(11), |
| info.unique_id->at(12), info.unique_id->at(13), info.unique_id->at(14), |
| info.unique_id->at(15)); |
| simple_codec_.CreateString("unique_id", uid_str.c_str(), &inspect_); |
| } |
| |
| async_dispatcher_t* dispatcher = |
| fdf_dispatcher_get_async_dispatcher(fdf_dispatcher_get_current_dispatcher()); |
| outgoing_ = component::OutgoingDirectory(dispatcher); |
| auto endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>(); |
| if (endpoints.is_error()) { |
| return endpoints.status_value(); |
| } |
| |
| fuchsia_hardware_audio::CodecService::InstanceHandler handler({ |
| .codec = |
| [this](fidl::ServerEnd<fuchsia_hardware_audio::Codec> server_end) { |
| this->CodecConnect(server_end.TakeChannel()); |
| }, |
| }); |
| auto result = outgoing_->AddService<fuchsia_hardware_audio::CodecService>(std::move(handler)); |
| if (result.is_error()) { |
| zxlogf(ERROR, "Failed to add service to the outgoing directory"); |
| return result.status_value(); |
| } |
| result = outgoing_->Serve(std::move(endpoints->server)); |
| if (result.is_error()) { |
| zxlogf(ERROR, "Failed to add service to the outgoing directory"); |
| return result.status_value(); |
| } |
| std::array offers = { |
| fuchsia_hardware_audio::CodecService::Name, |
| }; |
| |
| if (driver_ids_.instance_count != 0) { |
| if (!info.unique_id) { |
| simple_codec_.CreateString("unique_id", std::to_string(driver_ids_.instance_count), |
| &inspect_); |
| } |
| zx_device_str_prop_t props[] = { |
| ddk::MakeStrProperty(bind_fuchsia::PLATFORM_DEV_VID, driver_ids_.vendor_id), |
| ddk::MakeStrProperty(bind_fuchsia::PLATFORM_DEV_DID, driver_ids_.device_id), |
| ddk::MakeStrProperty(bind_fuchsia::CODEC_INSTANCE, driver_ids_.instance_count), |
| }; |
| return DdkAdd(ddk::DeviceAddArgs(info.product_name.c_str()) |
| .set_str_props(props) |
| .set_proto_id(ZX_PROTOCOL_CODEC) |
| .set_fidl_service_offers(offers) |
| .set_outgoing_dir(endpoints->client.TakeChannel()) |
| .set_inspect_vmo(inspect_.DuplicateVmo()) |
| .set_flags(DEVICE_ADD_ALLOW_MULTI_COMPOSITE)); |
| } |
| zx_device_str_prop_t props[] = { |
| ddk::MakeStrProperty(bind_fuchsia::PLATFORM_DEV_VID, driver_ids_.vendor_id), |
| ddk::MakeStrProperty(bind_fuchsia::PLATFORM_DEV_DID, driver_ids_.device_id), |
| }; |
| return DdkAdd(ddk::DeviceAddArgs(info.product_name.c_str()) |
| .set_str_props(props) |
| .set_proto_id(ZX_PROTOCOL_CODEC) |
| .set_fidl_service_offers(offers) |
| .set_outgoing_dir(endpoints->client.TakeChannel()) |
| .set_inspect_vmo(inspect_.DuplicateVmo()) |
| .set_flags(DEVICE_ADD_ALLOW_MULTI_COMPOSITE)); |
| } |
| |
| zx_status_t SimpleCodecServer::CodecConnect(zx::channel channel) { |
| return BindClient(std::move(channel), loop_->dispatcher()); |
| } |
| |
| void SimpleCodecServer::SignalProcessingConnect( |
| fidl::InterfaceRequest<signal_fidl::SignalProcessing> signal_processing) { |
| signal_processing.Close(ZX_ERR_NOT_SUPPORTED); |
| } |
| |
| // SimpleCodecServerInternal methods. |
| template <class T> |
| SimpleCodecServerInternal<T>::SimpleCodecServerInternal() { |
| plug_time_ = zx::clock::get_monotonic().get(); |
| } |
| |
| template <class T> |
| zx_status_t SimpleCodecServerInternal<T>::BindClient(zx::channel channel, |
| async_dispatcher_t* dispatcher) { |
| // Acquire the lock before creating the new instance in case OnUnbound is called before the |
| // instance has been added to the list. |
| fbl::AutoLock lock(&instances_lock_); |
| auto instance = std::make_unique<SimpleCodecServerInstance<SimpleCodecServer>>(std::move(channel), |
| dispatcher, this); |
| instances_.push_back(std::move(instance)); |
| return ZX_OK; |
| } |
| |
| template <class T> |
| void SimpleCodecServerInternal<T>::OnUnbound(SimpleCodecServerInstance<T>* instance) { |
| fbl::AutoLock lock(&instances_lock_); |
| instances_.erase(*instance); |
| } |
| |
| template <class T> |
| void SimpleCodecServerInternal<T>::Reset(Codec::ResetCallback callback, |
| SimpleCodecServerInstance<T>* instance) { |
| auto status = static_cast<T*>(this)->Reset(); |
| if (status != ZX_OK) { |
| instance->binding_.Unbind(); |
| fbl::AutoLock lock(&instances_lock_); |
| instances_.erase(*instance); |
| } |
| callback(); |
| } |
| |
| template <class T> |
| void SimpleCodecServerInternal<T>::Stop(Codec::StopCallback callback, |
| SimpleCodecServerInstance<T>* instance) { |
| auto status = static_cast<T*>(this)->Stop(); |
| if (status != ZX_OK) { |
| static_cast<T*>(this)->state_.Set("stopped error"); |
| instance->binding_.Unbind(); |
| fbl::AutoLock lock(&instances_lock_); |
| instances_.erase(*instance); |
| } else { |
| static_cast<T*>(this)->state_.Set("stopped"); |
| } |
| callback(zx::clock::get_monotonic().get()); |
| } |
| |
| template <class T> |
| void SimpleCodecServerInternal<T>::Start(Codec::StartCallback callback, |
| SimpleCodecServerInstance<T>* instance) { |
| auto status = static_cast<T*>(this)->Start(); |
| if (status != ZX_OK) { |
| static_cast<T*>(this)->state_.Set("start error"); |
| instance->binding_.Unbind(); |
| fbl::AutoLock lock(&instances_lock_); |
| instances_.erase(*instance); |
| } else { |
| static_cast<T*>(this)->state_.Set("started"); |
| } |
| int64_t start_time = zx::clock::get_monotonic().get(); |
| static_cast<T*>(this)->start_time_.Set(start_time); |
| callback(start_time); |
| } |
| |
| template <class T> |
| void SimpleCodecServerInternal<T>::GetProperties(Codec::GetPropertiesCallback callback) { |
| Info info = static_cast<T*>(this)->GetInfo(); |
| fuchsia::hardware::audio::CodecProperties properties; |
| if (info.unique_id) { |
| properties.set_unique_id(*info.unique_id); |
| } |
| properties.set_product(info.product_name); |
| properties.set_manufacturer(info.manufacturer); |
| properties.set_plug_detect_capabilities(audio_fidl::PlugDetectCapabilities::HARDWIRED); |
| callback(std::move(properties)); |
| } |
| |
| template <class T> |
| void SimpleCodecServerInternal<T>::SignalProcessingConnect( |
| fidl::InterfaceRequest<signal_fidl::SignalProcessing> signal_processing) { |
| static_cast<T*>(this)->SignalProcessingConnect(std::move(signal_processing)); |
| } |
| |
| template <class T> |
| void SimpleCodecServerInternal<T>::GetElements( |
| signal_fidl::SignalProcessing::GetElementsCallback callback) { |
| std::vector<signal_fidl::Element> pes; |
| auto format = static_cast<T*>(this)->GetGainFormat(); |
| { |
| signal_fidl::Element pe; |
| pe.set_id(kGainPeId); |
| pe.set_type(signal_fidl::ElementType::GAIN); |
| signal_fidl::Gain gain; |
| gain.set_type(signal_fidl::GainType::DECIBELS); // Only decibels in simple codec. |
| gain.set_min_gain(format.min_gain); |
| gain.set_max_gain(format.max_gain); |
| gain.set_min_gain_step(format.gain_step); |
| pe.set_type_specific(signal_fidl::TypeSpecificElement::WithGain(std::move(gain))); |
| pes.emplace_back(std::move(pe)); |
| } |
| if (format.can_mute) { |
| signal_fidl::Element pe; |
| pe.set_id(kMutePeId); |
| pe.set_type(signal_fidl::ElementType::MUTE); |
| pes.emplace_back(std::move(pe)); |
| } |
| if (format.can_agc) { |
| signal_fidl::Element pe; |
| pe.set_id(kAgcPeId); |
| pe.set_type(signal_fidl::ElementType::AUTOMATIC_GAIN_CONTROL); |
| pes.emplace_back(std::move(pe)); |
| } |
| |
| signal_fidl::Reader_GetElements_Response response(std::move(pes)); |
| signal_fidl::Reader_GetElements_Result result; |
| result.set_response(std::move(response)); |
| callback(std::move(result)); |
| } |
| |
| template <class T> |
| void SimpleCodecServerInternal<T>::SetElementState( |
| uint64_t processing_element_id, signal_fidl::SettableElementState state, |
| signal_fidl::SignalProcessing::SetElementStateCallback callback, |
| SimpleCodecServerInstance<T>* unused_instance) { |
| // The unused_instance parameter indicates which instance triggered this call, |
| // but we must update all appropriate instances callbacks with the potentially new state. |
| switch (processing_element_id) { |
| case kGainPeId: |
| |
| if (!state.has_type_specific() || !state.type_specific().is_gain() || |
| !state.type_specific().gain().has_gain()) { |
| callback( |
| signal_fidl::SignalProcessing_SetElementState_Result::WithErr(ZX_ERR_INVALID_ARGS)); |
| return; |
| } |
| gain_state_.gain = state.type_specific().gain().gain(); |
| |
| { |
| fbl::AutoLock lock(&instances_lock_); |
| for (auto& instance : instances_) { |
| if (instance.gain_callback_) { |
| signal_fidl::GainElementState gain_state; |
| gain_state.set_gain(gain_state_.gain); |
| signal_fidl::ElementState state = {}; |
| state.set_type_specific( |
| signal_fidl::TypeSpecificElementState::WithGain(std::move(gain_state))); |
| state.set_started(true).set_bypassed(false); |
| (*instance.gain_callback_)(std::move(state)); |
| instance.gain_callback_.reset(); |
| instance.gain_updated_ = false; |
| } else { |
| // Setting true here triggers WatchElementState to immediately reply with the latest |
| // gain state next the time it is called. |
| instance.gain_updated_ = true; |
| } |
| } |
| } |
| callback(signal_fidl::SignalProcessing_SetElementState_Result::WithResponse( |
| signal_fidl::SignalProcessing_SetElementState_Response())); |
| break; |
| |
| case kMutePeId: |
| gain_state_.muted = state.started() && !state.bypassed(); |
| |
| { |
| fbl::AutoLock lock(&instances_lock_); |
| for (auto& instance : instances_) { |
| if (instance.mute_callback_) { |
| signal_fidl::ElementState state = {}; |
| state.set_started(true).set_bypassed(!gain_state_.muted); |
| (*instance.mute_callback_)(std::move(state)); |
| instance.mute_callback_.reset(); |
| instance.mute_updated_ = false; |
| } else { |
| // Setting true here triggers WatchElementState to immediately reply with the latest |
| // mute state next the time it is called. |
| instance.mute_updated_ = true; |
| } |
| } |
| } |
| callback(signal_fidl::SignalProcessing_SetElementState_Result::WithResponse( |
| signal_fidl::SignalProcessing_SetElementState_Response())); |
| break; |
| |
| case kAgcPeId: |
| gain_state_.agc_enabled = state.started() && !state.bypassed(); |
| { |
| fbl::AutoLock lock(&instances_lock_); |
| for (auto& instance : instances_) { |
| if (instance.agc_callback_) { |
| signal_fidl::ElementState state = {}; |
| state.set_started(true).set_bypassed(!gain_state_.agc_enabled); |
| (*instance.agc_callback_)(std::move(state)); |
| instance.agc_callback_.reset(); |
| instance.agc_updated_ = false; |
| } else { |
| // Setting true here triggers WatchElementState to immediately reply with the latest |
| // AGC state next the time it is called. |
| instance.agc_updated_ = true; |
| } |
| } |
| } |
| callback(signal_fidl::SignalProcessing_SetElementState_Result::WithResponse( |
| signal_fidl::SignalProcessing_SetElementState_Response())); |
| break; |
| |
| default: |
| callback(signal_fidl::SignalProcessing_SetElementState_Result::WithErr(ZX_ERR_INVALID_ARGS)); |
| break; |
| } |
| if (!last_gain_state_.has_value() || last_gain_state_->gain != gain_state_.gain || |
| last_gain_state_->muted != gain_state_.muted || |
| last_gain_state_->agc_enabled != gain_state_.agc_enabled) { |
| auto* thiz = static_cast<T*>(this); |
| thiz->SetGainState(gain_state_); |
| if (!thiz->gain_db_) { |
| thiz->gain_db_ = thiz->simple_codec_.CreateDouble("gain_db", gain_state_.gain); |
| } else { |
| thiz->gain_db_.Set(gain_state_.gain); |
| } |
| if (!thiz->muted_) { |
| thiz->muted_ = thiz->simple_codec_.CreateBool("muted", gain_state_.muted); |
| } else { |
| thiz->muted_.Set(gain_state_.muted); |
| } |
| last_gain_state_.emplace(gain_state_); |
| } |
| } |
| |
| template <class T> |
| void SimpleCodecServerInternal<T>::WatchElementState( |
| uint64_t processing_element_id, |
| signal_fidl::SignalProcessing::WatchElementStateCallback callback, |
| SimpleCodecServerInstance<T>* instance) { |
| if (processing_element_id != kGainPeId && processing_element_id != kMutePeId && |
| processing_element_id != kAgcPeId) { |
| return; |
| } |
| |
| if (load_gain_state_first_time_) { |
| auto* thiz = static_cast<T*>(this); |
| gain_state_ = thiz->GetGainState(); |
| if (!thiz->gain_db_) { |
| thiz->gain_db_ = thiz->simple_codec_.CreateDouble("gain_db", gain_state_.gain); |
| } else { |
| thiz->gain_db_.Set(gain_state_.gain); |
| } |
| if (!thiz->muted_) { |
| thiz->muted_ = thiz->simple_codec_.CreateBool("muted", gain_state_.muted); |
| } else { |
| thiz->muted_.Set(gain_state_.muted); |
| } |
| last_gain_state_.emplace(gain_state_); |
| load_gain_state_first_time_ = false; |
| } |
| |
| // Reply immediately if the either the gain, mute or AGC has been updated since the last call. |
| // Otherwise store the callback and reply the next time the gain state is updated. Only one |
| // hanging get may beoutstanding at a time. |
| switch (processing_element_id) { |
| case kGainPeId: |
| if (instance->gain_updated_) { |
| instance->gain_updated_ = false; |
| |
| signal_fidl::GainElementState gain_state; |
| gain_state.set_gain(gain_state_.gain); |
| signal_fidl::ElementState state = {}; |
| state.set_type_specific( |
| signal_fidl::TypeSpecificElementState::WithGain(std::move(gain_state))); |
| state.set_started(true).set_bypassed(false); |
| callback(std::move(state)); |
| } else if (!instance->gain_callback_) { |
| instance->gain_callback_.emplace(std::move(callback)); |
| } else { |
| // The client called WatchElementState when another hanging get was pending. |
| // This is an error condition and hence we unbind and remove the instance. |
| instance->binding_.Close(ZX_ERR_BAD_STATE); |
| instance->gain_callback_.reset(); |
| fbl::AutoLock lock(&instances_lock_); |
| instances_.erase(*instance); |
| } |
| break; |
| |
| case kMutePeId: |
| if (instance->mute_updated_) { |
| instance->mute_updated_ = false; |
| |
| signal_fidl::ElementState state = {}; |
| state.set_started(true).set_bypassed(!gain_state_.muted); |
| callback(std::move(state)); |
| } else if (!instance->mute_callback_) { |
| instance->mute_callback_.emplace(std::move(callback)); |
| } else { |
| // The client called WatchElementState when another hanging get was pending. |
| // This is an error condition and hence we unbind and remove the instance. |
| instance->binding_.Close(ZX_ERR_BAD_STATE); |
| instance->mute_callback_.reset(); |
| fbl::AutoLock lock(&instances_lock_); |
| instances_.erase(*instance); |
| } |
| break; |
| |
| case kAgcPeId: |
| if (instance->agc_updated_) { |
| instance->agc_updated_ = false; |
| |
| signal_fidl::ElementState state = {}; |
| state.set_started(true).set_bypassed(!gain_state_.agc_enabled); |
| callback(std::move(state)); |
| } else if (!instance->agc_callback_) { |
| instance->agc_callback_.emplace(std::move(callback)); |
| } else { |
| // The client called WatchElementState when another hanging get was pending. |
| // This is an error condition and hence we unbind and remove the instance. |
| instance->binding_.Close(ZX_ERR_BAD_STATE); |
| instance->agc_callback_.reset(); |
| fbl::AutoLock lock(&instances_lock_); |
| instances_.erase(*instance); |
| } |
| break; |
| |
| default: |
| zxlogf(ERROR, "Ignoring WatchElementState for unknown processing_element_id: %zx", |
| processing_element_id); |
| } |
| } |
| |
| template <class T> |
| void SimpleCodecServerInternal<T>::GetTopologies( |
| signal_fidl::SignalProcessing::GetTopologiesCallback callback) { |
| std::vector<signal_fidl::EdgePair> edges; |
| { |
| signal_fidl::EdgePair edge; |
| edge.processing_element_id_from = kGainPeId; |
| edge.processing_element_id_to = kMutePeId; |
| edges.emplace_back(edge); |
| } |
| { |
| signal_fidl::EdgePair edge; |
| edge.processing_element_id_from = kMutePeId; |
| edge.processing_element_id_to = kAgcPeId; |
| edges.emplace_back(edge); |
| } |
| |
| signal_fidl::Topology topology; |
| topology.set_id(kTopologyId); |
| topology.set_processing_elements_edge_pairs(edges); |
| |
| std::vector<signal_fidl::Topology> topologies; |
| topologies.emplace_back(std::move(topology)); |
| |
| signal_fidl::Reader_GetTopologies_Response response(std::move(topologies)); |
| signal_fidl::Reader_GetTopologies_Result result; |
| result.set_response(std::move(response)); |
| callback(std::move(result)); |
| } |
| |
| template <class T> |
| void SimpleCodecServerInternal<T>::WatchTopology( |
| signal_fidl::SignalProcessing::WatchTopologyCallback callback, |
| SimpleCodecServerInstance<T>* instance) { |
| if (!instance->responded_to_watch_topology_) { |
| instance->responded_to_watch_topology_ = true; |
| callback(signal_fidl::Reader_WatchTopology_Result::WithResponse( |
| signal_fidl::Reader_WatchTopology_Response(kTopologyId))); |
| } else if (!instance->topology_callback_) { |
| instance->topology_callback_ = std::move(callback); |
| } else { |
| // The client called WatchTopology when another hanging get was pending. |
| // This is an error condition and hence we unbind and remove the instance. |
| instance->binding_.Close(ZX_ERR_BAD_STATE); |
| fbl::AutoLock lock(&instances_lock_); |
| instances_.erase(*instance); |
| } |
| } |
| |
| template <class T> |
| void SimpleCodecServerInternal<T>::SetTopology( |
| uint64_t topology_id, signal_fidl::SignalProcessing::SetTopologyCallback callback) { |
| // We only support one topology, return error if any mismatch. |
| if (topology_id != kTopologyId) { |
| callback(signal_fidl::SignalProcessing_SetTopology_Result::WithErr(ZX_ERR_INVALID_ARGS)); |
| return; |
| } |
| callback(signal_fidl::SignalProcessing_SetTopology_Result::WithResponse( |
| signal_fidl::SignalProcessing_SetTopology_Response())); |
| } |
| |
| template <class T> |
| void SimpleCodecServerInternal<T>::WatchPlugState(Codec::WatchPlugStateCallback callback, |
| SimpleCodecServerInstance<T>* instance) { |
| if (instance->plug_callback_) { |
| // The client called WatchPlugState when another hanging get was pending. |
| // This is an error condition and hence we unbind and remove the instance. |
| instance->binding_.Close(ZX_ERR_BAD_STATE); |
| instance->plug_callback_.reset(); |
| fbl::AutoLock lock(&instances_lock_); |
| instances_.erase(*instance); |
| } |
| |
| // Since the library only advertises a hardwired codec, it returns that the codec is always |
| // plugged and only replies to this hanging-get with the plugged state once per instance. |
| // Hence for the first WatchPlugState call reply immediately, otherwise do not reply. |
| if (instance->plug_state_updated_) { |
| instance->plug_state_updated_ = false; |
| fuchsia::hardware::audio::PlugState plug_state; |
| plug_state.set_plugged(true); |
| plug_state.set_plug_state_time(plug_time_); |
| callback(std::move(plug_state)); |
| } |
| } |
| |
| template <class T> |
| void SimpleCodecServerInternal<T>::GetDaiFormats(Codec::GetDaiFormatsCallback callback) { |
| auto formats = static_cast<T*>(this)->GetDaiFormats(); |
| std::vector<audio_fidl::DaiFrameFormat> frame_formats; |
| for (FrameFormat i : formats.frame_formats) { |
| audio_fidl::DaiFrameFormat frame_format; |
| frame_format.set_frame_format_standard(i); |
| frame_formats.emplace_back(std::move(frame_format)); |
| } |
| audio_fidl::Codec_GetDaiFormats_Response response; |
| response.formats.emplace_back(audio_fidl::DaiSupportedFormats{ |
| .number_of_channels = std::move(formats.number_of_channels), |
| .sample_formats = std::move(formats.sample_formats), |
| .frame_formats = std::move(frame_formats), |
| .frame_rates = std::move(formats.frame_rates), |
| .bits_per_slot = std::move(formats.bits_per_slot), |
| .bits_per_sample = std::move(formats.bits_per_sample), |
| }); |
| audio_fidl::Codec_GetDaiFormats_Result result; |
| result.set_response(std::move(response)); |
| callback(std::move(result)); |
| } |
| |
| template <class T> |
| void SimpleCodecServerInternal<T>::SetDaiFormat(audio_fidl::DaiFormat format, |
| Codec::SetDaiFormatCallback callback) { |
| DaiFormat format2 = {}; |
| format2.number_of_channels = format.number_of_channels; |
| format2.channels_to_use_bitmask = format.channels_to_use_bitmask; |
| format2.sample_format = format.sample_format; |
| format2.frame_format = format.frame_format.frame_format_standard(); |
| format2.frame_rate = format.frame_rate; |
| format2.bits_per_slot = format.bits_per_slot; |
| format2.bits_per_sample = format.bits_per_sample; |
| auto* thiz = static_cast<T*>(this); |
| thiz->number_of_channels_.Set(format2.number_of_channels); |
| thiz->channels_to_use_bitmask_.Set(format2.channels_to_use_bitmask); |
| thiz->frame_rate_.Set(format2.frame_rate); |
| thiz->bits_per_slot_.Set(format2.bits_per_slot); |
| thiz->bits_per_sample_.Set(format2.bits_per_sample); |
| using FidlSampleFormat = audio_fidl::DaiSampleFormat; |
| // clang-format off |
| switch (format2.sample_format) { |
| case FidlSampleFormat::PDM: thiz->sample_format_.Set("PDM"); break; |
| case FidlSampleFormat::PCM_SIGNED: thiz->sample_format_.Set("PCM_signed"); break; |
| case FidlSampleFormat::PCM_UNSIGNED: thiz->sample_format_.Set("PCM_unsigned"); break; |
| case FidlSampleFormat::PCM_FLOAT: thiz->sample_format_.Set("PCM_float"); break; |
| } |
| // clang-format on |
| using FidlFrameFormat = audio_fidl::DaiFrameFormatStandard; |
| // clang-format off |
| switch (format2.frame_format) { |
| case FidlFrameFormat::NONE: thiz->frame_format_.Set("NONE"); break; |
| case FidlFrameFormat::I2S: thiz->frame_format_.Set("I2S"); break; |
| case FidlFrameFormat::STEREO_LEFT: thiz->frame_format_.Set("Stereo_left"); break; |
| case FidlFrameFormat::STEREO_RIGHT: thiz->frame_format_.Set("Stereo_right"); break; |
| case FidlFrameFormat::TDM1: thiz->frame_format_.Set("TDM1"); break; |
| case FidlFrameFormat::TDM2: thiz->frame_format_.Set("TDM2"); break; |
| case FidlFrameFormat::TDM3: thiz->frame_format_.Set("TDM3"); break; |
| } |
| // clang-format on |
| audio_fidl::Codec_SetDaiFormat_Result result = {}; |
| zx::result<CodecFormatInfo> format_info = thiz->SetDaiFormat(format2); |
| if (!format_info.is_ok()) { |
| thiz->state_.Set(std::string("Set DAI format error: ") + format_info.status_string()); |
| result.set_err(format_info.status_value()); |
| callback(std::move(result)); |
| return; |
| } |
| audio_fidl::Codec_SetDaiFormat_Response response = {}; |
| audio_fidl::CodecFormatInfo codec_state = {}; |
| if (format_info->has_external_delay()) { |
| codec_state.set_external_delay(format_info->external_delay()); |
| } |
| if (format_info->has_turn_on_delay()) { |
| codec_state.set_turn_on_delay(format_info->turn_on_delay()); |
| } |
| if (format_info->has_turn_off_delay()) { |
| codec_state.set_turn_off_delay(format_info->turn_off_delay()); |
| } |
| response.state = std::move(codec_state); |
| result.set_response(std::move(response)); |
| callback(std::move(result)); |
| } |
| |
| // SimpleCodecServerInstance methods. |
| |
| template <class T> |
| void SimpleCodecServerInstance<T>::SignalProcessingConnect( |
| fidl::InterfaceRequest<signal_fidl::SignalProcessing> signal_processing) { |
| if (parent_->SupportsSignalProcessing()) { |
| parent_->SignalProcessingConnect(std::move(signal_processing)); |
| } else { |
| if (signal_processing_binding_.has_value()) { |
| signal_processing.Close(ZX_ERR_ALREADY_BOUND); |
| return; |
| } |
| signal_processing_binding_.emplace(this, std::move(signal_processing), |
| parent_->loop_->dispatcher()); |
| } |
| } |
| |
| template class SimpleCodecServerInternal<SimpleCodecServer>; |
| |
| } // namespace audio |