mirror of
https://github.com/mudita/MuditaOS.git
synced 2026-01-26 06:52:14 -05:00
Extended volume scale from 10 to 15 point scale Modified volume approximation function for better user experience
121 lines
3.1 KiB
C++
121 lines
3.1 KiB
C++
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
|
|
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
|
|
|
|
#include "SAIAudioDevice.hpp"
|
|
|
|
#include <Audio/Stream.hpp>
|
|
#include <log/log.hpp>
|
|
|
|
#include <cmath>
|
|
|
|
using namespace audio;
|
|
|
|
SAIAudioDevice::SAIAudioDevice(I2S_Type *base, sai_edma_handle_t *rxHandle, sai_edma_handle_t *txHandle)
|
|
: _base(base), rx(rxHandle), tx(txHandle)
|
|
{}
|
|
|
|
void SAIAudioDevice::initiateRxTransfer()
|
|
{
|
|
audio::Stream::Span dataSpan;
|
|
|
|
Source::_stream->reserve(dataSpan);
|
|
LOG_DEBUG("Initiate rx transfer with %u bytes", dataSpan.dataSize);
|
|
auto xfer = sai_transfer_t{.data = dataSpan.data, .dataSize = dataSpan.dataSize};
|
|
SAI_TransferReceiveEDMA(_base, rx, &xfer);
|
|
}
|
|
|
|
void SAIAudioDevice::initiateTxTransfer()
|
|
{
|
|
audio::Stream::Span dataToSend;
|
|
Sink::_stream->peek(dataToSend);
|
|
|
|
auto xfer = sai_transfer_t{.data = dataToSend.data, .dataSize = dataToSend.dataSize};
|
|
SAI_TransferSendEDMA(_base, tx, &xfer);
|
|
}
|
|
|
|
void SAIAudioDevice::onDataSend()
|
|
{
|
|
audio::Stream::Span dataSpan;
|
|
|
|
if (!txEnabled || !isSinkConnected()) {
|
|
return;
|
|
}
|
|
|
|
/// pop previous read and peek next
|
|
Sink::_stream->consume();
|
|
Sink::_stream->peek(dataSpan);
|
|
scaleOutputVolume(dataSpan);
|
|
|
|
sai_transfer_t xfer{.data = dataSpan.data, .dataSize = dataSpan.dataSize};
|
|
SAI_TransferSendEDMA(_base, tx, &xfer);
|
|
}
|
|
|
|
void SAIAudioDevice::onDataReceive()
|
|
{
|
|
audio::Stream::Span dataSpan;
|
|
|
|
if (!rxEnabled || !isSourceConnected()) {
|
|
return;
|
|
}
|
|
|
|
/// reserve space for the next read commiting previously reserved block before
|
|
Source::_stream->commit();
|
|
Source::_stream->reserve(dataSpan);
|
|
|
|
sai_transfer_t xfer{.data = dataSpan.data, .dataSize = dataSpan.dataSize};
|
|
SAI_TransferReceiveEDMA(_base, rx, &xfer);
|
|
}
|
|
|
|
void SAIAudioDevice::enableInput()
|
|
{
|
|
if (!isSourceConnected()) {
|
|
LOG_FATAL("No output stream connected!");
|
|
return;
|
|
}
|
|
|
|
rxEnabled = true;
|
|
|
|
/// initiate first read
|
|
initiateRxTransfer();
|
|
}
|
|
|
|
void SAIAudioDevice::enableOutput()
|
|
{
|
|
if (!isSinkConnected()) {
|
|
LOG_FATAL("No input stream connected!");
|
|
return;
|
|
}
|
|
|
|
txEnabled = true;
|
|
|
|
/// initiate first write
|
|
initiateTxTransfer();
|
|
}
|
|
|
|
void SAIAudioDevice::disableInput()
|
|
{
|
|
rxEnabled = false;
|
|
}
|
|
|
|
void SAIAudioDevice::disableOutput()
|
|
{
|
|
txEnabled = false;
|
|
}
|
|
AudioDevice::RetCode SAIAudioDevice::setOutputVolume(float vol)
|
|
{
|
|
vol = std::clamp(vol, minVolume, maxVolume);
|
|
/// Using y=x^2 function as an approximation seems very natural and has the most useful range
|
|
/// For more info check: https://www.dr-lex.be/info-stuff/volumecontrols.html
|
|
volumeFactor = std::pow(1.0f * (vol / maxVolume), 2);
|
|
return AudioDevice::RetCode::Success;
|
|
}
|
|
|
|
void SAIAudioDevice::scaleOutputVolume(audio::Stream::Span &span)
|
|
{
|
|
if (volumeFactor < 1.0) {
|
|
const auto samples = reinterpret_cast<std::int16_t *>(span.data);
|
|
const auto samplesCount = samples + span.dataSize / 2;
|
|
std::for_each(samples, samplesCount, [this](auto &sample) { sample *= volumeFactor; });
|
|
}
|
|
}
|