// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/blob/master/LICENSE.md #pragma once #include "AudioDevice.hpp" #include "Profiles/Profile.hpp" #include #include #include #include #include #include namespace audio { class AudioMux; } namespace audio { inline constexpr Volume defaultVolumeStep{1}; inline constexpr Volume maxVolume{10}; inline constexpr Volume minVolume{0}; inline constexpr Gain maxGain{100}; inline constexpr Gain minGain{0}; inline constexpr auto audioDbPrefix{"audio"}; inline constexpr auto dbPathSeparator{'/'}; inline constexpr std::chrono::seconds defaultMaxFadeDuration{5}; enum class Setting { Volume, Gain, EnableVibration, VibrationLevel, EnableSound, Sound, IsSystemSound }; enum class SettingState : bool { Enabled, Disabled }; enum class PlaybackMode { Single, Loop }; enum class PlaybackType { None, Multimedia, Notifications, System = Notifications, SingleVibration = Notifications, KeypadSound, CallRingtone, TextMessageRingtone, Meditation, Alarm, PreWakeUp, Snooze, FocusTimer, Bedtime, Last = Bedtime }; enum class VolumeChangeRequestSource { A2DP, HFP, HSP, Other }; enum class Fade { Disable, In, InOut }; struct FadeParams { Fade mode; std::chrono::seconds maxFadeDuration{defaultMaxFadeDuration}; std::optional playbackDuration{std::nullopt}; }; enum class VolumeUpdateType { UpdateDB, SkipUpdateDB }; /// Used to describe audio operations using Context = std::pair; struct DbPathElement { Setting setting; PlaybackType playbackType; Profile::Type profileType; }; [[nodiscard]] const std::string str(const PlaybackType &playbackType) noexcept; [[nodiscard]] const std::string str(const Setting &setting) noexcept; [[nodiscard]] const std::string dbPath(const DbPathElement &element); [[nodiscard]] const std::string dbPath(const Setting &setting, const PlaybackType &playbackType, const Profile::Type &profileType); enum class EventType { // HW state change notifications JackState, //!< Jack input plugged / unplugged event MicrophoneState, //!< Microphone presence in headset (3-pole w/o microphone or 4-pole with microphone) BluetoothHSPDeviceState, //!< BT device connected / disconnected event (Headset Profile) BluetoothHFPDeviceState, //!< BT device connected / disconnected event (Headset Profile) BluetoothA2DPDeviceState, //!< BT device connected / disconnected event (Advanced Audio Distribution Profile) // call control CallMute, CallUnmute, CallLoudspeakerOn, CallLoudspeakerOff, }; inline constexpr auto hwStateUpdateMaxEvent = magic_enum::enum_index(EventType::BluetoothA2DPDeviceState); class Event { public: enum class DeviceState { Connected, Disconnected }; explicit Event(EventType eType, DeviceState deviceState = DeviceState::Connected) : eventType(eType), deviceState(deviceState) {} virtual ~Event() = default; [[nodiscard]] EventType getType() const noexcept { return eventType; } [[nodiscard]] DeviceState getDeviceState() const noexcept { return deviceState; } private: const EventType eventType; const DeviceState deviceState; }; class AudioSinkState { public: void UpdateState(std::shared_ptr stateChangeEvent) { auto hwUpdateEventIdx = magic_enum::enum_integer(stateChangeEvent->getType()); if (hwUpdateEventIdx <= hwStateUpdateMaxEvent) { audioSinkState.set(hwUpdateEventIdx, stateChangeEvent->getDeviceState() == Event::DeviceState::Connected); } } [[nodiscard]] std::vector> getUpdateEvents() const { std::vector> updateEvents; for (auto i = 0; i <= hwStateUpdateMaxEvent; i++) { auto isConnected = audioSinkState.test(i) ? Event::DeviceState::Connected : Event::DeviceState::Disconnected; auto updateEvt = magic_enum::enum_cast(i); updateEvents.emplace_back(std::make_unique(updateEvt.value(), isConnected)); } return updateEvents; } [[nodiscard]] bool isConnected(EventType deviceUpdateEvent) const { return audioSinkState.test(magic_enum::enum_integer(deviceUpdateEvent)); } void setConnected(EventType deviceUpdateEvent, bool isConnected) { audioSinkState.set(magic_enum::enum_integer(deviceUpdateEvent), isConnected); } private: std::bitset()> audioSinkState; }; enum class RetCode : std::uint8_t { Success = 0, InvokedInIncorrectState, UnsupportedProfile, UnsupportedEvent, InvalidFormat, OperationCreateFailed, FileDoesntExist, FailedToAllocateMemory, OperationNotSet, ProfileNotSet, DeviceFailure, TokenNotFound, Ignored, Failed }; struct AudioInitException : public std::runtime_error { public: AudioInitException(const char *message, audio::RetCode errorCode) : runtime_error(message), errorCode{errorCode} {} [[nodiscard]] audio::RetCode getErrorCode() const noexcept { return errorCode; } protected: audio::RetCode errorCode = audio::RetCode::Failed; }; class Token { using TokenType = std::int16_t; public: explicit Token(TokenType initValue = tokenUninitialized) : t(initValue) {} bool operator==(const Token &other) const noexcept { return other.t == t; } bool operator!=(const Token &other) const noexcept { return !(other.t == t); } /** * Valid token is one connected with existing sequence of operations * @return True if valid, false otherwise */ [[nodiscard]] bool IsValid() const { return t > tokenUninitialized; } /** * Bad token cannot be used anymore * @return True if token is flagged bad */ [[nodiscard]] bool IsBad() const { return t == tokenBad; } /** * Uninitialized token can be used but it is not connected to any sequence of operations * @return True if token is flagged uninitialized */ [[nodiscard]] bool IsUninitialized() const { return t == tokenUninitialized; } /** * Helper - returns bad Token * @return Unusable bad Token */ static inline Token MakeBadToken() { return Token(tokenBad); } private: static constexpr auto maxToken = std::numeric_limits::max(); Token IncrementToken() { t = (t == maxToken) ? 0 : t + 1; return *this; } static constexpr TokenType tokenUninitialized{-1}; static constexpr TokenType tokenBad{-2}; TokenType t; friend class ::audio::AudioMux; }; RetCode GetDeviceError(AudioDevice::RetCode retCode); const std::string str(RetCode retcode); [[nodiscard]] auto GetVolumeText(const audio::Volume &volume) -> std::string; } // namespace audio namespace AudioServiceMessage { class EndOfFile : public sys::DataMessage { public: explicit EndOfFile(audio::Token &token) : token(token) {} [[nodiscard]] const audio::Token &GetToken() const { return token; } private: audio::Token token = audio::Token::MakeBadToken(); }; class FileDeleted : public sys::DataMessage { public: explicit FileDeleted(audio::Token &token) : token(token) {} [[nodiscard]] const audio::Token &GetToken() const { return token; } private: audio::Token token = audio::Token::MakeBadToken(); }; class FileSystemNoSpace : public sys::DataMessage { public: explicit FileSystemNoSpace(audio::Token &token) : token(token) {} [[nodiscard]] const audio::Token &GetToken() const { return token; } private: audio::Token token = audio::Token::MakeBadToken(); }; class DbRequest : public sys::DataMessage { public: explicit DbRequest(const audio::Setting &setting, const audio::PlaybackType &playback, const audio::Profile::Type &profile) : setting(setting), profile(profile), playback(playback) {} const audio::Setting setting; const audio::Profile::Type profile; const audio::PlaybackType playback; }; class AudioDeviceCreated : public sys::DataMessage { public: explicit AudioDeviceCreated(std::shared_ptr device, audio::AudioDevice::Type type) : device(std::move(device)), type(type) {} [[nodiscard]] auto getDevice() const noexcept -> std::shared_ptr { return device; } [[nodiscard]] auto getDeviceType() const noexcept -> audio::AudioDevice::Type { return type; } private: std::shared_ptr device; audio::AudioDevice::Type type; }; using Callback = std::function(const sys::Message *msg)>; } // namespace AudioServiceMessage