From 6d9ad2b9474a3efa929888a874ce920e9dfeda7b Mon Sep 17 00:00:00 2001 From: Silvano Seva Date: Sun, 18 Sep 2022 17:54:02 +0200 Subject: [PATCH] Implementation of audio path manager --- meson.build | 1 + openrtx/include/core/audio_path.h | 13 ++- openrtx/src/core/audio_path.cpp | 184 ++++++++++++++++++++++++++++++ 3 files changed, 192 insertions(+), 6 deletions(-) create mode 100644 openrtx/src/core/audio_path.cpp diff --git a/meson.build b/meson.build index 79cb2cfa..80ff0031 100644 --- a/meson.build +++ b/meson.build @@ -49,6 +49,7 @@ openrtx_src = ['openrtx/src/core/state.c', 'openrtx/src/core/datetime.c', 'openrtx/src/core/openrtx.c', 'openrtx/src/core/audio_codec.c', + 'openrtx/src/core/audio_path.cpp', 'openrtx/src/core/data_conversion.c', 'openrtx/src/core/memory_profiling.cpp', 'openrtx/src/core/voicePrompts.c', diff --git a/openrtx/include/core/audio_path.h b/openrtx/include/core/audio_path.h index 58cf3cd6..7f31c4a7 100644 --- a/openrtx/include/core/audio_path.h +++ b/openrtx/include/core/audio_path.h @@ -21,6 +21,7 @@ #define AUDIO_PATH_H #include +#include #ifdef __cplusplus extern "C" { @@ -41,27 +42,27 @@ typedef int32_t pathId; * with an higher priority. * * @param source: identifier of the input audio peripheral. - * @param destination: identifier of the output audio peripheral. + * @param sink: identifier of the output audio peripheral. * @param prio: priority of the requester. * @return a unique identifier of the opened path or -1 if path is already in use. */ -pathId audioPath_request(enum AudioSource source, enum AudioSink destination, +pathId audioPath_request(enum AudioSource source, enum AudioSink sink, enum AudioPriority prio); /** * Get the current status of an audio path. * - * @param pathId: ID of the audio path. + * @param id: ID of the audio path. * @return status of the path queried. */ -enum PathStatus audioPath_getStatus(const pathId pathId); +enum PathStatus audioPath_getStatus(const pathId id); /** * Release an audio path. * - * @param pathId: identifier of the path. + * @param id: identifier of the path. */ -void audioPath_release(const pathId pathId); +void audioPath_release(const pathId id); #ifdef __cplusplus } diff --git a/openrtx/src/core/audio_path.cpp b/openrtx/src/core/audio_path.cpp new file mode 100644 index 00000000..8c53402e --- /dev/null +++ b/openrtx/src/core/audio_path.cpp @@ -0,0 +1,184 @@ +/*************************************************************************** + * Copyright (C) 2022 by Alain Carlucci, * + * Federico Amedeo Izzo IU2NUO, * + * Niccolò Izzo IU2KIN * + * Silvano Seva IU2KWO * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, see * + ***************************************************************************/ + +#include +#include +#include + +/** + * \internal + * Data structure representing an audio path with source, destination and + * priority. + */ +struct Path +{ + int8_t source = -1; ///< Source endpoint of the path. + int8_t destination = -1; ///< Destination endpoint of the path. + int8_t priority = -1; ///< Path priority level. + + bool isValid() const + { + return (source != -1) && + (destination != -1) && + (priority != -1); + } + + bool operator<(const Path& other) const + { + return (source < other.source) && + (destination < other.destination); + } + + bool isCompatible(const Path& other) const + { + enum AudioSource p1Source = (enum AudioSource) source; + enum AudioSource p2Source = (enum AudioSource) other.source; + enum AudioSink p1Sink = (enum AudioSink) destination; + enum AudioSink p2Sink = (enum AudioSink) other.destination; + + return audio_checkPathCompatibility(p1Source, p1Sink, p2Source, p2Sink); + } +}; + +/** + * \internal + * Data structure representing an established audio route. + */ +struct Route +{ + Path path; ///< Path associated to this route. + std::set< int > suspendList; ///< Suspended paths with lower priority. + std::set< int > suspendBy; ///< List of paths which suspended this route. + + bool isActive() const + { + return suspendBy.empty(); + } +}; + + +static std::set< int > activePaths; // IDs of currently active paths. +static std::map< int, Route > routes; // Route data of currently active paths. +static int pathCounter = 1; // Counter for path ID generation. + + +pathId audioPath_request(enum AudioSource source, enum AudioSink sink, + enum AudioPriority prio) +{ + const Path path{source, sink, prio}; + if (!path.isValid()) + return -1; + + std::set< int > pathsToSuspend; + + // Check if this new path can be activated, otherwise return -1 + for (const auto& i : activePaths) + { + const Path& activePath = routes.at(i).path; + if(path.isCompatible(activePath)) + continue; + + // Not compatible where active one has higher priority + if (activePath.priority >= path.priority) + return -1; + + // Active path has lower priority than this new one + pathsToSuspend.insert(i); + } + + // New path can be activated + const int newPathId = pathCounter; + pathCounter += 1; + const Route newRoute{path, pathsToSuspend, {}}; + + // Move active paths that should be suspended to the suspend-list + for (const auto& i : pathsToSuspend) + { + // Move path to suspended-list + activePaths.erase(i); + + routes.at(i).suspendBy.insert(newPathId); + } + + // Set this new path as active + routes.insert(std::make_pair(newPathId, newRoute)); + activePaths.insert(newPathId); + + return newPathId; +} + +enum PathStatus audioPath_getStatus(const pathId id) +{ + const auto it = routes.find(id); + + if(it == routes.end()) + return PATH_CLOSED; + + if(it->second.isActive()) + return PATH_OPEN; + + return PATH_SUSPENDED; +} + +void audioPath_release(const pathId id) +{ + auto it = routes.find(id); + if (it == routes.end()) // Does not exists + return; + + Route dataToRemove = it->second; + routes.erase(it); + activePaths.erase(id); + + // For each path that suspended me + for (const auto& i : dataToRemove.suspendBy) + { + auto& suspendList = routes.at(i).suspendList; + + // Remove myself from its suspend-list + suspendList.erase(id); + + // Add who I suspended + for (const auto& j : dataToRemove.suspendList) + suspendList.insert(j); + } + + // For each path suspended by me + for (const auto& i : dataToRemove.suspendList) + { + auto& suspendBy = routes.at(i).suspendBy; + + // Remove myself + suspendBy.erase(id); + + if (!dataToRemove.suspendBy.empty()) + { + // If I was suspended, propagate who suspended me + for (const auto& j : dataToRemove.suspendBy) + suspendBy.insert(j); + } + else + { + // This path can be started again + if (suspendBy.empty()) + activePaths.insert(i); + } + } +}