M config/bootstrap_config => config/bootstrap_config +3 -1
@@ 41,7 41,9 @@ INSTALL_PACKAGES="
mtools \
ninja-build \
pkg-config \
- portaudio19-dev \
+ pulseaudio \
+ libpulse0 \
+ libpulse-dev \
python3-magic \
python3-pip \
python3-requests \
M module-audio/Audio/test/CMakeLists.txt => module-audio/Audio/test/CMakeLists.txt +1 -0
@@ 4,6 4,7 @@ add_catch2_executable(
NAME
audio-test
SRCS
+ DummyAudioDevice.cpp
unittest_audio.cpp
LIBS
module-audio
A module-audio/Audio/test/DummyAudioDevice.cpp => module-audio/Audio/test/DummyAudioDevice.cpp +71 -0
@@ 0,0 1,71 @@
+// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include "DummyAudioDevice.hpp"
+
+using namespace audio;
+
+class DummyAudioFactory : public AudioDeviceFactory
+{
+ public:
+ std::shared_ptr<AudioDevice> createCellularAudioDevice() override
+ {
+ return nullptr;
+ }
+
+ protected:
+ std::shared_ptr<AudioDevice> getDevice([[maybe_unused]] const audio::Profile &profile) override
+ {
+ if (profile.GetAudioDeviceType() == AudioDevice::Type::Audiocodec) {
+ return std::make_shared<DummyAudioDevice>();
+ }
+ return nullptr;
+ }
+};
+
+std::unique_ptr<AudioDeviceFactory> AudioPlatform::GetDeviceFactory()
+{
+ return std::make_unique<DummyAudioFactory>();
+}
+
+auto DummyAudioDevice::Start() -> AudioDevice::RetCode
+{
+ return AudioDevice::RetCode::Success;
+}
+auto DummyAudioDevice::Stop() -> AudioDevice::RetCode
+{
+ return AudioDevice::RetCode::Success;
+}
+auto DummyAudioDevice::setOutputVolume(float vol) -> AudioDevice::RetCode
+{
+ return AudioDevice::RetCode::Success;
+}
+auto DummyAudioDevice::setInputGain(float gain) -> AudioDevice::RetCode
+{
+ return AudioDevice::RetCode::Success;
+}
+auto DummyAudioDevice::getTraits() const -> Endpoint::Traits
+{
+ return Endpoint::Traits();
+}
+auto DummyAudioDevice::getSupportedFormats() -> std::vector<audio::AudioFormat>
+{
+ return std::vector<audio::AudioFormat>();
+}
+auto DummyAudioDevice::getSourceFormat() -> audio::AudioFormat
+{
+ return audio::AudioFormat();
+}
+
+void DummyAudioDevice::onDataSend()
+{}
+void DummyAudioDevice::onDataReceive()
+{}
+void DummyAudioDevice::enableInput()
+{}
+void DummyAudioDevice::enableOutput()
+{}
+void DummyAudioDevice::disableInput()
+{}
+void DummyAudioDevice::disableOutput()
+{}
A module-audio/Audio/test/DummyAudioDevice.hpp => module-audio/Audio/test/DummyAudioDevice.hpp +29 -0
@@ 0,0 1,29 @@
+// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+
+#include "Audio/Audio.hpp"
+
+class DummyAudioDevice : public audio::AudioDevice
+{
+ public:
+ auto Start() -> RetCode override;
+ auto Stop() -> RetCode override;
+
+ auto setOutputVolume(float vol) -> RetCode override;
+
+ auto setInputGain(float gain) -> RetCode override;
+
+ auto getTraits() const -> Traits override;
+ auto getSupportedFormats() -> std::vector<audio::AudioFormat> override;
+ auto getSourceFormat() -> audio::AudioFormat override;
+
+ // Endpoint control methods
+ void onDataSend() override;
+ void onDataReceive() override;
+ void enableInput() override;
+ void enableOutput() override;
+ void disableInput() override;
+ void disableOutput() override;
+};
M module-audio/Audio/test/unittest_audio.cpp => module-audio/Audio/test/unittest_audio.cpp +3 -6
@@ 4,13 4,7 @@
#include <catch2/catch.hpp>
#include "Audio/decoder/Decoder.hpp"
-
-#include "Audio/decoder/decoderMP3.hpp"
-#include "Audio/decoder/decoderFLAC.hpp"
-#include "Audio/decoder/decoderWAV.hpp"
-
#include "Audio/AudioCommon.hpp"
-
#include "Audio/AudioMux.hpp"
#include "Audio/Audio.hpp"
#include "Audio/Operation/Operation.hpp"
@@ 18,6 12,9 @@
using namespace audio;
+#include <memory>
+#include <utility>
+
TEST_CASE("Audio Decoder")
{
std::vector<std::string> testExtensions = {"flac", "wav", "mp3"};
M module-audio/board/linux/CMakeLists.txt => module-audio/board/linux/CMakeLists.txt +2 -0
@@ 4,10 4,12 @@
set(AUDIO_LINUX_SOURCES
LinuxAudioPlatform.cpp
LinuxAudioDevice.cpp
+ PulseAudioWrapper.cpp
)
add_library(${AUDIO_BOARD_LIBRARY} STATIC ${AUDIO_LINUX_SOURCES})
target_include_directories(${AUDIO_BOARD_LIBRARY} INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(${AUDIO_BOARD_LIBRARY}
module-os
+ pulse
)
M module-audio/board/linux/LinuxAudioDevice.cpp => module-audio/board/linux/LinuxAudioDevice.cpp +26 -122
@@ 8,64 8,15 @@
namespace audio
{
- namespace
- {
- class PortAudio
- {
- public:
- PortAudio();
- PortAudio(const PortAudio &) = delete;
- PortAudio(PortAudio &&) = delete;
- PortAudio &operator=(const PortAudio &) = delete;
- PortAudio &operator=(PortAudio &&) = delete;
- ~PortAudio() noexcept;
- };
-
- PortAudio::PortAudio()
- {
- if (const auto errorCode = Pa_Initialize(); errorCode == paNoError) {
- LOG_INFO("Portaudio initialized successfully");
- }
- else {
- LOG_ERROR("Error (code %d) initiializing Portaudio: %s", errorCode, Pa_GetErrorText(errorCode));
- }
- }
-
- PortAudio::~PortAudio() noexcept
- {
- if (const auto errorCode = Pa_Terminate(); errorCode == paNoError) {
- LOG_INFO("Portaudio terminated successfully");
- }
- else {
- LOG_ERROR("Error (code %d) while terminating Portaudio: %s", errorCode, Pa_GetErrorText(errorCode));
- }
- }
- } // namespace
-
LinuxAudioDevice::LinuxAudioDevice(const float initialVolume)
: supportedFormats(
- audio::AudioFormat::makeMatrix(supportedSampleRates, supportedBitWidths, supportedChannelModes))
+ audio::AudioFormat::makeMatrix(supportedSampleRates, supportedBitWidths, supportedChannelModes)),
+ audioProxy("audioProxy", [this](const auto &data) {
+ requestedBytes = data;
+ onDataSend();
+ })
{
setOutputVolume(initialVolume);
-
- static PortAudio portAudio;
- }
-
- LinuxAudioDevice::~LinuxAudioDevice()
- {
- if (stream != nullptr) {
- closeStream();
- }
- }
-
- void LinuxAudioDevice::closeStream()
- {
- if (const auto errorCode = Pa_AbortStream(stream); errorCode != paNoError) {
- LOG_ERROR("Error (code %d) while stopping Portaudio stream: %s", errorCode, Pa_GetErrorText(errorCode));
- }
- if (const auto errorCode = Pa_CloseStream(stream); errorCode != paNoError) {
- LOG_ERROR("Error (code %d) while closing Portaudio stream: %s", errorCode, Pa_GetErrorText(errorCode));
- }
}
auto LinuxAudioDevice::Start() -> RetCode
@@ 113,10 64,20 @@ namespace audio
void LinuxAudioDevice::onDataSend()
{
audio::Stream::Span dataSpan;
+ if (!isSinkConnected()) {
+ return;
+ }
Sink::_stream->peek(dataSpan);
- auto streamData = reinterpret_cast<std::int16_t *>(dataSpan.data);
- cache.insert(cache.end(), &streamData[0], &streamData[dataSpan.dataSize / sizeof(std::int16_t)]);
+ scaleVolume(dataSpan);
+ pulseAudioWrapper->insert(dataSpan);
Sink::_stream->consume();
+
+ if (pulseAudioWrapper->bytes() >= requestedBytes) {
+ pulseAudioWrapper->consume();
+ }
+ else {
+ audioProxy.post(requestedBytes);
+ }
}
void LinuxAudioDevice::onDataReceive()
@@ 127,39 88,15 @@ namespace audio
void LinuxAudioDevice::enableOutput()
{
- LOG_INFO("Enabling audio output...");
if (!isSinkConnected()) {
LOG_ERROR("Output stream is not connected!");
return;
}
- currentFormat = Sink::_stream->getOutputTraits().format;
- const auto numOutputChannels = currentFormat.getChannels();
- auto callback = [](const void *input,
- void *output,
- unsigned long frameCount,
- const PaStreamCallbackTimeInfo *timeInfo,
- PaStreamCallbackFlags statusFlags,
- void *userData) -> int {
- LinuxAudioDevice *dev = static_cast<LinuxAudioDevice *>(userData);
- return dev->streamCallback(input, output, frameCount, timeInfo, statusFlags);
- };
- auto errorCode = Pa_OpenDefaultStream(&stream,
- 0,
- numOutputChannels,
- paInt16,
- currentFormat.getSampleRate(),
- paFramesPerBufferUnspecified,
- callback,
- this);
- if (errorCode != paNoError) {
- LOG_ERROR("Error (code %d) while creating portaudio stream: %s", errorCode, Pa_GetErrorText(errorCode));
- return;
- }
- if (errorCode = Pa_StartStream(stream); errorCode != paNoError) {
- LOG_ERROR("Error (code %d) while starting portaudio stream: %s", errorCode, Pa_GetErrorText(errorCode));
- return;
- }
+ currentFormat = Sink::_stream->getOutputTraits().format;
+
+ pulseAudioWrapper = std::make_unique<PulseAudioWrapper>(
+ [this](const std::size_t size) { audioProxy.post(size); }, currentFormat);
}
void LinuxAudioDevice::disableInput()
@@ 167,50 104,17 @@ namespace audio
void LinuxAudioDevice::disableOutput()
{
- LOG_INFO("Disabling audio output...");
if (!isSinkConnected()) {
LOG_ERROR("Error while stopping Linux Audio Device! Null stream.");
return;
}
-
- closeStream();
- stream = nullptr;
+ close = true;
currentFormat = {};
}
-
- int LinuxAudioDevice::streamCallback([[maybe_unused]] const void *input,
- void *output,
- unsigned long frameCount,
- [[maybe_unused]] const PaStreamCallbackTimeInfo *timeInfo,
- [[maybe_unused]] PaStreamCallbackFlags statusFlags)
- {
- if (!isSinkConnected()) {
- return paAbort;
- }
-
- const auto expectedBufferSize = frameCount * currentFormat.getChannels();
- if (!isCacheReady(expectedBufferSize)) {
- onDataSend();
- }
-
- const auto dataReadySize = std::min(expectedBufferSize, cache.size());
- cacheToOutputBuffer(static_cast<std::int16_t *>(output), dataReadySize);
-
- return paContinue;
- }
-
- bool LinuxAudioDevice::isCacheReady(std::size_t expectedSize) const noexcept
+ void LinuxAudioDevice::scaleVolume(audio::AbstractStream::Span data)
{
- return cache.size() >= expectedSize;
- }
-
- void LinuxAudioDevice::cacheToOutputBuffer(std::int16_t *buffer, std::size_t size)
- {
- for (size_t i = 0; i < size; ++i) {
- const auto adjustedValue = static_cast<float>(cache[i]) * volumeFactor;
- *(buffer) = static_cast<std::int16_t>(adjustedValue);
- buffer++;
- }
- cache.erase(cache.begin(), cache.begin() + size);
+ const auto samplesBeg = reinterpret_cast<std::int16_t *>(data.data);
+ const auto samplesEnd = samplesBeg + data.dataSize / 2;
+ std::for_each(samplesBeg, samplesEnd, [this](auto &sample) { sample *= volumeFactor; });
}
} // namespace audio
M module-audio/board/linux/LinuxAudioDevice.hpp => module-audio/board/linux/LinuxAudioDevice.hpp +12 -20
@@ 3,21 3,21 @@
#pragma once
+#include <module-bsp/WorkerQueue.hpp>
+#include <Audio/AbstractStream.hpp>
#include <Audio/AudioDevice.hpp>
#include <Audio/AudioFormat.hpp>
#include <Audio/codec.hpp>
+#include "PulseAudioWrapper.hpp"
-#include <portaudio.h>
-
-#include <deque>
+#include <variant>
namespace audio
{
class LinuxAudioDevice : public audio::AudioDevice
{
public:
- LinuxAudioDevice(const float initialVolume);
- virtual ~LinuxAudioDevice();
+ explicit LinuxAudioDevice(const float initialVolume);
auto Start() -> RetCode override;
auto Stop() -> RetCode override;
@@ 45,15 45,8 @@ namespace audio
void disableOutput() override;
private:
- int streamCallback(const void *input,
- void *output,
- unsigned long frameCount,
- const PaStreamCallbackTimeInfo *timeInfo,
- PaStreamCallbackFlags statusFlags);
- bool isCacheReady(std::size_t expectedSize) const noexcept;
- void cacheToOutputBuffer(std::int16_t *buffer, std::size_t size);
-
- void closeStream();
+ using AudioProxy = WorkerQueue<std::size_t>;
+ void scaleVolume(audio::AbstractStream::Span data);
constexpr static std::initializer_list<unsigned int> supportedSampleRates = {44100, 48000};
constexpr static std::initializer_list<unsigned int> supportedBitWidths = {16};
@@ 62,13 55,12 @@ namespace audio
std::vector<audio::AudioFormat> supportedFormats;
audio::AudioFormat currentFormat;
- /// pointer to portaudio stream
- PaStream *stream = nullptr;
-
- /// Local cache to store data read from Pure stream
- std::deque<std::int16_t> cache;
-
float volumeFactor = 1.0f;
+
+ std::unique_ptr<PulseAudioWrapper> pulseAudioWrapper;
+ AudioProxy audioProxy;
+ std::size_t requestedBytes{};
+ bool close{false};
};
} // namespace audio
A module-audio/board/linux/PulseAudioWrapper.cpp => module-audio/board/linux/PulseAudioWrapper.cpp +159 -0
@@ 0,0 1,159 @@
+// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include "PulseAudioWrapper.hpp"
+#include <pthread.h>
+#include <iostream>
+#include <utility>
+
+namespace audio
+{
+ PulseAudioWrapper::PulseAudioWrapper(WriteCallback write_cb, AudioFormat audio_format)
+ : write_cb{std::move(write_cb)}, audio_format(audio_format)
+ {
+ /// PulseAudio requests at least SampleRate bytes of data to start playback
+ /// Due to how module-audio's streams operate we can load a bit more data to the cache than needed.
+ /// Doubling the buffer size is cheap and simple solution.
+ cache.reserve(audio_format.getSampleRate() * 2);
+
+ pthread_create(
+ &tid,
+ nullptr,
+ [](void *arg) -> void * {
+ /// Mask all available signals to not interfere with the FreeRTOS simulator port
+ sigset_t set;
+ sigfillset(&set);
+ pthread_sigmask(SIG_SETMASK, &set, NULL);
+
+ auto *inst = static_cast<PulseAudioWrapper *>(arg);
+ return inst->worker();
+ },
+ this);
+ }
+
+ void *PulseAudioWrapper::worker()
+ {
+ mainloop = pa_mainloop_new();
+ mainloop_api = pa_mainloop_get_api(mainloop);
+ context = pa_context_new(mainloop_api, "PureOSContext");
+ pa_context_set_state_callback(
+ context,
+ [](pa_context *, void *arg) {
+ auto *inst = static_cast<PulseAudioWrapper *>(arg);
+ inst->context_state_callback();
+ },
+ this);
+ pa_context_connect(context, nullptr, {}, nullptr);
+
+ int ret = 1;
+ if (pa_mainloop_run(mainloop, &ret) < 0) {
+ fprintf(stderr, "pa_mainloop_run() failed.\n");
+ std::abort();
+ }
+
+ return nullptr;
+ }
+
+ void PulseAudioWrapper::context_state_callback()
+ {
+ assert(context);
+
+ switch (pa_context_get_state(context)) {
+ case PA_CONTEXT_CONNECTING:
+ case PA_CONTEXT_AUTHORIZING:
+ case PA_CONTEXT_SETTING_NAME:
+ break;
+
+ case PA_CONTEXT_READY: {
+ int r;
+ pa_sample_spec sample_spec;
+ sample_spec.channels = audio_format.getChannels();
+ sample_spec.rate = audio_format.getSampleRate();
+ sample_spec.format = PA_SAMPLE_S16LE;
+
+ if ((stream = pa_stream_new(context, "PureOSStream", &sample_spec, nullptr)) == nullptr) {
+ fprintf(stderr, "pa_stream_new() failed: %s\n", pa_strerror(pa_context_errno(context)));
+ return;
+ }
+
+ pa_stream_set_write_callback(
+ stream,
+ [](pa_stream *, size_t length, void *arg) {
+ auto *inst = static_cast<PulseAudioWrapper *>(arg);
+ inst->stream_write_cb(length);
+ },
+ this);
+
+ if ((r = pa_stream_connect_playback(stream, nullptr, nullptr, {}, nullptr, nullptr)) < 0) {
+ fprintf(stderr, "pa_stream_connect_playback() failed: %s\n", pa_strerror(pa_context_errno(context)));
+ return;
+ }
+
+ break;
+ }
+
+ case PA_CONTEXT_TERMINATED:
+ quit(0);
+ fprintf(stderr, "PulseAudio connection terminated.\n");
+ break;
+
+ case PA_CONTEXT_FAILED:
+ default:
+ fprintf(stderr, "Connection failure: %s\n", pa_strerror(pa_context_errno(context)));
+ quit(1);
+ }
+ }
+
+ void PulseAudioWrapper::stream_write_cb(size_t length)
+ {
+ write_cb(length);
+ }
+
+ PulseAudioWrapper::~PulseAudioWrapper()
+ {
+ quit();
+ pthread_join(tid, nullptr);
+
+ if (stream != nullptr) {
+ pa_stream_unref(stream);
+ }
+
+ if (context != nullptr) {
+ pa_context_unref(context);
+ context = nullptr;
+ }
+
+ if (mainloop != nullptr) {
+ pa_mainloop_free(mainloop);
+ mainloop = nullptr;
+ mainloop_api = nullptr;
+ }
+ }
+
+ void PulseAudioWrapper::quit(int ret)
+ {
+ if (mainloop_api != nullptr) {
+ mainloop_api->quit(mainloop_api, ret);
+ }
+ }
+
+ void PulseAudioWrapper::insert(audio::AbstractStream::Span span)
+ {
+ std::copy(span.data, span.dataEnd(), &cache[cache_pos]);
+ cache_pos += span.dataSize;
+ }
+
+ void PulseAudioWrapper::consume()
+ {
+ if (pa_stream_write(stream, cache.data(), cache_pos, NULL, 0, PA_SEEK_RELATIVE) < 0) {
+ fprintf(stderr, "pa_stream_write() failed\n");
+ return;
+ }
+ cache_pos = 0;
+ }
+
+ std::size_t PulseAudioWrapper::bytes() const
+ {
+ return cache_pos;
+ }
+} // namespace audio
A module-audio/board/linux/PulseAudioWrapper.hpp => module-audio/board/linux/PulseAudioWrapper.hpp +47 -0
@@ 0,0 1,47 @@
+// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+
+#include <pulse/pulseaudio.h>
+#include <Audio/AudioFormat.hpp>
+#include <Audio/AbstractStream.hpp>
+
+#include <cstdint>
+#include <functional>
+#include <deque>
+#include <vector>
+
+namespace audio
+{
+ class PulseAudioWrapper
+ {
+ public:
+ using WriteCallback = std::function<void(const std::size_t size)>;
+ PulseAudioWrapper(WriteCallback write_cb, AudioFormat audio_format);
+ ~PulseAudioWrapper();
+
+ void insert(audio::AbstractStream::Span span);
+ void consume();
+ std::size_t bytes() const;
+
+ private:
+ void context_state_callback();
+ void *worker();
+ void stream_write_cb(size_t length);
+ void quit(int ret = 0);
+
+ pthread_t tid{};
+ pa_stream *stream{nullptr};
+ pa_mainloop *mainloop{nullptr};
+ pa_mainloop_api *mainloop_api{nullptr};
+ pa_context *context{nullptr};
+
+ WriteCallback write_cb;
+ AudioFormat audio_format;
+
+ std::vector<std::uint8_t> cache{};
+ std::uint32_t cache_pos{};
+ };
+
+} // namespace audio
R module-bsp/board/rt1051/common/WorkerQueue.hpp => module-bsp/WorkerQueue.hpp +15 -7
@@ 6,9 6,11 @@
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
+#include <log/log.hpp>
#include <functional>
#include <utility>
+#include <cstdint>
/*
* Worker queue implementation is used to defer work from interrupt routines.
@@ 46,6 48,7 @@ template <typename Message> class WorkerQueue
xQueueHandle queueHandle{};
xTaskHandle taskHandle{};
WorkerHandle workerHandle{};
+ xTaskHandle callerHandle{};
void worker();
};
@@ 59,6 62,7 @@ WorkerQueue<Message>::WorkerQueue(const char *name, WorkerHandle workerHandle, c
[](void *pvp) {
WorkerQueue *inst = static_cast<WorkerQueue *>(pvp);
inst->worker();
+ vTaskDelete(nullptr);
},
name,
stackSize / sizeof(std::uint32_t),
@@ 69,12 73,17 @@ WorkerQueue<Message>::WorkerQueue(const char *name, WorkerHandle workerHandle, c
template <typename Message> WorkerQueue<Message>::~WorkerQueue()
{
- if (queueHandle && taskHandle) {
- const InternalMessage killMsg{.kill = true};
+ if ((queueHandle != nullptr) && (taskHandle != nullptr)) {
+ const InternalMessage killMsg{{}, true};
InternalMessage responseMsg;
- xQueueSend(queueHandle, &killMsg, pdMS_TO_TICKS(100));
+ callerHandle = xTaskGetCurrentTaskHandle();
+ xQueueReset(queueHandle);
+ if (xQueueSend(queueHandle, &killMsg, pdMS_TO_TICKS(500)) != pdPASS) {
+ LOG_FATAL("xQueueSend error will result in aborting worker thread");
+ }
+
/// Wait 500ms for a response from the worker. If it does not arrive, kill it.
- if (const auto result = xQueueReceive(queueHandle, &responseMsg, pdMS_TO_TICKS(500)); result != pdTRUE) {
+ if (const auto result = ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(500)); result == pdFALSE) {
vTaskDelete(taskHandle);
}
vQueueDelete(queueHandle);
@@ 87,9 96,8 @@ template <typename Message> void WorkerQueue<Message>::worker()
xQueueReceive(queueHandle, &msg, portMAX_DELAY);
if (msg.kill) {
- InternalMessage killMsg{.kill = true};
- xQueueSend(queueHandle, &killMsg, pdMS_TO_TICKS(100));
- vTaskDelete(nullptr);
+ xTaskNotifyGive(callerHandle);
+ return;
}
else {
workerHandle(msg.msg);
M module-bsp/board/rt1051/bellpx/hal/battery_charger/BatteryCharger.cpp => module-bsp/board/rt1051/bellpx/hal/battery_charger/BatteryCharger.cpp +1 -1
@@ 2,7 2,7 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#include "BatteryChargerIRQ.hpp"
-#include "common/WorkerQueue.hpp"
+#include "WorkerQueue.hpp"
#include "common/soc_scaler.hpp"
#include "FreeRTOS.h"
M module-bsp/board/rt1051/puretx/hal/battery_charger/BatteryCharger.cpp => module-bsp/board/rt1051/puretx/hal/battery_charger/BatteryCharger.cpp +1 -1
@@ 2,7 2,7 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#include "BatteryChargerIRQ.hpp"
-#include "common/WorkerQueue.hpp"
+#include "WorkerQueue.hpp"
#include "common/soc_scaler.hpp"
#if ENABLE_CURRENT_MEASUREMENT_SCOPE