~aleteoryx/muditaos

ref: 06d16390c21c37457bc875e841bb12039d1123d7 muditaos/module-audio/board/linux/LinuxAudioDevice.cpp -rw-r--r-- 4.0 KiB
06d16390 — Lefucjusz [MOS-1021] Fix blocked passcode behavior 2 years ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "LinuxAudioDevice.hpp"
#include <Audio/Stream.hpp>
#include <log/log.hpp>
#include <cmath>
#include <stdexcept>

namespace
{
    /// Creating and destroying context each time LinuxAudioDevice is spawned seems very unstable. It causes various
    /// problems, from raising sanitizer errors to completely freezing the simulator. As a solution, another approach
    /// has been chosen. Instead of recreating the pulse audio context, we rely on static instance of it, which is valid
    /// throughout the life of the simulator process. During the standard operation, we only create and attach audio
    /// streams, which have proven to be a fast and reliable way of doing things. All the necessary context cleaning is
    /// handled in the context's destructor during the simulator process close.
    static audio::pulse_audio::Context ctx;
    audio::pulse_audio::Context &get_context()
    {
        return ctx;
    }
} // namespace

namespace audio
{
    LinuxAudioDevice::LinuxAudioDevice(const float initialVolume)
        : supportedFormats(
              audio::AudioFormat::makeMatrix(supportedSampleRates, supportedBitWidths, supportedChannelModes)),
          audioProxy("audioProxy", [this](const auto &data) { onDataSend(); })
    {
        setOutputVolume(initialVolume);
    }

    auto LinuxAudioDevice::Start() -> RetCode
    {
        if (!isSinkConnected()) {
            return AudioDevice::RetCode::Failure;
        }
        return AudioDevice::RetCode::Success;
    }

    auto LinuxAudioDevice::Stop() -> RetCode
    {
        return AudioDevice::RetCode::Success;
    }

    auto LinuxAudioDevice::setOutputVolume(float vol) -> RetCode
    {
        vol = std::clamp(vol, minVolume, maxVolume);
        /// Using y=x^4 function as an approximation seems very natural and sufficient
        /// For more info check: https://www.dr-lex.be/info-stuff/volumecontrols.html
        volumeFactor = std::pow(1.0f * (vol / maxVolume), 4);
        return RetCode::Success;
    }

    auto LinuxAudioDevice::setInputGain([[maybe_unused]] float gain) -> RetCode
    {
        return RetCode::Success;
    }

    auto LinuxAudioDevice::getTraits() const -> Traits
    {
        return Traits{};
    }

    auto LinuxAudioDevice::getSupportedFormats() -> std::vector<audio::AudioFormat>
    {
        return supportedFormats;
    }

    auto LinuxAudioDevice::getSourceFormat() -> audio::AudioFormat
    {
        return currentFormat;
    }

    void LinuxAudioDevice::onDataSend()
    {
        audio::Stream::Span dataSpan;
        if (!isSinkConnected()) {
            return;
        }
        Sink::_stream->peek(dataSpan);
        scaleVolume(dataSpan);
        stream->insert(dataSpan);
        Sink::_stream->consume();

        if (stream->bytes() >= requestedBytes) {
            stream->consume();
        }
        else {
            audioProxy.post(requestedBytes);
        }
    }

    void LinuxAudioDevice::onDataReceive()
    {}

    void LinuxAudioDevice::enableInput()
    {}

    void LinuxAudioDevice::enableOutput()
    {
        currentFormat = Sink::_stream->getOutputTraits().format;

        stream = get_context().open_stream(currentFormat, [this](const std::size_t size) {
            requestedBytes = size;
            audioProxy.post(requestedBytes);
        });

        if (stream == nullptr) {
            fprintf(stderr, "Failed to open the audio stream");
            std::abort();
        }
    }

    void LinuxAudioDevice::disableInput()
    {}

    void LinuxAudioDevice::disableOutput()
    {
        get_context().close_stream();
        currentFormat = {};
    }
    void LinuxAudioDevice::scaleVolume(audio::AbstractStream::Span data)
    {
        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