M module-audio/Audio/AudioDevice.hpp => module-audio/Audio/AudioDevice.hpp +7 -1
@@ 16,6 16,9 @@ namespace audio
{
public:
+ static constexpr auto minVolume = .0f;
+ static constexpr auto maxVolume = 10.0f;
+
enum class RetCode
{
Success = 0,
@@ 62,11 65,14 @@ namespace audio
/// Set device input gain
/// @param gain desired input gain from 0 to 100
/// @return RetCode::Success if OK, or RetCode::Failure otherwise
- virtual RetCode setInputGain(float gain) = 0;
+ virtual RetCode setInputGain(float gain) = 0;
auto getSinkFormat() -> AudioFormat override
{
return getSourceFormat();
}
+
+ private:
+ static_assert(maxVolume > 0, "maxVolume needs to be bigger than 0");
};
} // namespace audio
M module-audio/board/linux/LinuxAudioDevice.cpp => module-audio/board/linux/LinuxAudioDevice.cpp +5 -4
@@ 4,6 4,7 @@
#include "LinuxAudioDevice.hpp"
#include <Audio/Stream.hpp>
#include <log/log.hpp>
+#include <cmath>
namespace audio
{
@@ 82,10 83,10 @@ namespace audio
auto LinuxAudioDevice::setOutputVolume(float vol) -> RetCode
{
- constexpr auto minVolume = .0f;
- constexpr auto maxVolume = 10.0f;
- vol = std::clamp(vol, minVolume, maxVolume);
- volumeFactor = 1.0f * (vol / maxVolume);
+ 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;
}
M module-audio/board/rt1051/SAIAudioDevice.cpp => module-audio/board/rt1051/SAIAudioDevice.cpp +19 -0
@@ 5,6 5,8 @@
#include <Audio/Stream.hpp>
+#include <cmath>
+
using namespace audio;
SAIAudioDevice::SAIAudioDevice(I2S_Type *base, sai_edma_handle_t *rxHandle, sai_edma_handle_t *txHandle)
@@ 41,6 43,7 @@ void SAIAudioDevice::onDataSend()
/// 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);
@@ 97,3 100,19 @@ void SAIAudioDevice::disableOutput()
{
txEnabled = false;
}
+AudioDevice::RetCode SAIAudioDevice::setOutputVolume(float vol)
+{
+ 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 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; });
+ }
+}
M module-audio/board/rt1051/SAIAudioDevice.hpp => module-audio/board/rt1051/SAIAudioDevice.hpp +8 -0
@@ 4,6 4,7 @@
#pragma once
#include <Audio/AudioDevice.hpp>
+#include <Audio/Stream.hpp>
#include "fsl_sai_edma.h"
@@ 25,11 26,18 @@ namespace audio
protected:
void initiateRxTransfer();
void initiateTxTransfer();
+ RetCode setOutputVolume(float vol) override;
+
I2S_Type *_base;
sai_edma_handle_t *rx = nullptr;
sai_edma_handle_t *tx = nullptr;
bool txEnabled = false;
bool rxEnabled = false;
+
+ private:
+ void scaleOutputVolume(audio::Stream::Span &span);
+
+ float volumeFactor{1.0};
};
} // namespace audio
M module-audio/board/rt1051/bellpx/BellPxAudioCodec.cpp => module-audio/board/rt1051/bellpx/BellPxAudioCodec.cpp +6 -24
@@ 25,7 25,7 @@ namespace audio
BellPxAudioCodec::~BellPxAudioCodec()
{
Stop();
- DeinitBsp();
+ bsp::audio::deinit();
}
AudioDevice::RetCode BellPxAudioCodec::Start()
@@ 40,7 40,7 @@ namespace audio
return AudioDevice::RetCode::Failure;
};
- InitBsp();
+ bsp::audio::init(currentFormat.sampleRate_Hz);
saiInFormat.bitWidth = currentFormat.bitWidth;
saiInFormat.sampleRate_Hz = currentFormat.sampleRate_Hz;
@@ 71,8 71,10 @@ namespace audio
}
codecParams.sampleRate = sampleRate;
- codecParams.outVolume = currentFormat.outputVolume;
- codecParams.inGain = currentFormat.inputGain;
+ /// Set the output volume to max possible value. Volume control is implemented
+ /// using software scaling instead of hardware gain control.
+ codecParams.outVolume = maxVolume;
+ codecParams.inGain = currentFormat.inputGain;
txEnabled = true;
initiateTxTransfer();
@@ 100,16 102,6 @@ namespace audio
return AudioDevice::RetCode::Success;
}
- AudioDevice::RetCode BellPxAudioCodec::setOutputVolume(float vol)
- {
- currentFormat.outputVolume = vol;
- CodecParamsAW8898 params;
- params.outVolume = vol;
- params.opCmd = CodecParams::Cmd::SetOutVolume;
- codec.Ioctrl(params);
- return AudioDevice::RetCode::Success;
- }
-
AudioDevice::RetCode BellPxAudioCodec::setInputGain(float gain)
{
currentFormat.inputGain = gain;
@@ 120,16 112,6 @@ namespace audio
return AudioDevice::RetCode::Success;
}
- void BellPxAudioCodec::InitBsp()
- {
- bsp::audio::init();
- }
-
- void BellPxAudioCodec::DeinitBsp()
- {
- bsp::audio::deinit();
- }
-
void BellPxAudioCodec::InStart()
{
sai_transfer_format_t sai_format;
M module-audio/board/rt1051/bellpx/BellPxAudioCodec.hpp => module-audio/board/rt1051/bellpx/BellPxAudioCodec.hpp +0 -3
@@ 39,7 39,6 @@ namespace audio
AudioDevice::RetCode Start() final;
AudioDevice::RetCode Stop() final;
- AudioDevice::RetCode setOutputVolume(float vol) final;
AudioDevice::RetCode setInputGain(float gain) final;
auto getSupportedFormats() -> std::vector<AudioFormat> final;
auto getTraits() const -> Traits final;
@@ 76,8 75,6 @@ namespace audio
static AT_NONCACHEABLE_SECTION_INIT(sai_edma_handle_t txHandle);
static AT_NONCACHEABLE_SECTION_INIT(sai_edma_handle_t rxHandle);
- void InitBsp();
- void DeinitBsp();
void OutStart();
void InStart();
void OutStop();
M module-audio/board/rt1051/puretx/PureTxAudioCodec.cpp => module-audio/board/rt1051/puretx/PureTxAudioCodec.cpp +1 -1
@@ 175,7 175,7 @@ namespace audio
void PureTxAudioCodec::InitBsp()
{
- bsp::audio::init();
+ bsp::audio::init(currentFormat.sampleRate_Hz);
}
void PureTxAudioCodec::DeinitBsp()
M module-bsp/board/rt1051/bellpx/audio.cpp => module-bsp/board/rt1051/bellpx/audio.cpp +37 -2
@@ 16,10 16,45 @@ using namespace drivers;
AudioConfig audioConfig;
-void bsp::audio::init()
+namespace
+{
+ constexpr bool isDerivativeOf44100(const std::uint32_t sampleRate)
+ {
+ return sampleRate % 11025 == 0;
+ }
+
+ DriverPLLParams getPLLParams(const std::uint32_t sampleRate)
+ {
+ /// The basic formula for calculating PLL4:
+ /// (fsclk * (loopDivider + numerator/denominator)) / postDivider
+
+ if (isDerivativeOf44100(sampleRate)) {
+ /// PLL4 set to 722.5344MHz, later it will be divided by SAI to generate 11,2896MHz BCLK
+ return {
+ .loopDivider = 30,
+ .postDivider = 1,
+ .numerator = 1056,
+ .denominator = 10000,
+ .src = 0,
+ };
+ }
+ else {
+ /// PLL4 set to 786.48MHz, later it will be divided by SAI to generate 12,28875MHz BCLK
+ return {
+ .loopDivider = 32,
+ .postDivider = 1,
+ .numerator = 77,
+ .denominator = 100,
+ .src = 0,
+ };
+ }
+ }
+} // namespace
+
+void bsp::audio::init(const std::uint32_t sampleRate)
{
audioConfig.pllAudio =
- DriverPLL::Create(static_cast<PLLInstances>(BoardDefinitions ::AUDIO_PLL), DriverPLLParams{});
+ DriverPLL::Create(static_cast<PLLInstances>(BoardDefinitions ::AUDIO_PLL), getPLLParams(sampleRate));
audioConfig.dmamux =
DriverDMAMux::Create(static_cast<DMAMuxInstances>(BoardDefinitions ::AUDIOCODEC_DMAMUX), DriverDMAMuxParams{});
audioConfig.dma =
M module-bsp/board/rt1051/bellpx/board/clock_config.h => module-bsp/board/rt1051/bellpx/board/clock_config.h +1 -1
@@ 125,7 125,7 @@ extern "C"
void clkPLL3_PFD2setup(uint8_t enabled);
void clkPLL3_PFD3setup(uint8_t enabled);
- void clkPLL4setup(uint8_t enabled);
+ void clkPLL4setup(uint8_t enabled, const clock_audio_pll_config_t conf);
void clkPLL5setup(uint8_t enabled);
M module-bsp/board/rt1051/bellpx/bsp/audio/CodecAW8898.cpp => module-bsp/board/rt1051/bellpx/bsp/audio/CodecAW8898.cpp +0 -1
@@ 95,7 95,6 @@ CodecRetCode CodecAW8898::Start(const CodecParams ¶m)
// Store param configuration
currentParams = params;
-
SetOutputVolume(currentParams.outVolume);
AW8898::ReadAllReg();
M module-bsp/board/rt1051/bellpx/clock_config.cpp => module-bsp/board/rt1051/bellpx/clock_config.cpp +3 -10
@@ 393,7 393,7 @@ void BOARD_BootClockRUN(void)
*/
/* DeInit Audio PLL */
- clkPLL4setup(CLK_DISABLE);
+ clkPLL4setup(CLK_DISABLE, {});
CLOCK_SetPllBypass(CCM_ANALOG, kCLOCK_PllAudio, 1);
/*
@@ 966,17 966,10 @@ void clkPLL3_PFD3setup(uint8_t enabled)
}
}
-void clkPLL4setup(uint8_t enabled)
+void clkPLL4setup(uint8_t enabled, const clock_audio_pll_config_t conf)
{
- const clock_audio_pll_config_t audioPllConfig_BOARD_BootClockRUN = {
- .loopDivider = 32, /* PLL loop divider. Valid range for DIV_SELECT divider value: 27~54. */
- .postDivider = 1, /* Divider after the PLL, should only be 1, 2, 4, 8, 16. */
- .numerator = 77, /* 30 bit numerator of fractional loop divider. */
- .denominator = 100, /* 30 bit denominator of fractional loop divider */
- .src = 0,
- };
if (enabled) {
- CLOCK_InitAudioPll(&audioPllConfig_BOARD_BootClockRUN);
+ CLOCK_InitAudioPll(&conf);
}
else {
/* DeInit Audio PLL */
M module-bsp/board/rt1051/common/audio.hpp => module-bsp/board/rt1051/common/audio.hpp +1 -1
@@ 14,7 14,7 @@
namespace bsp::audio
{
- void init();
+ void init(std::uint32_t sampleRate);
void deinit();
struct AudioConfig
M module-bsp/board/rt1051/drivers/RT1051DriverPLL.cpp => module-bsp/board/rt1051/drivers/RT1051DriverPLL.cpp +7 -2
@@ 14,7 14,12 @@ namespace drivers
switch (instance) {
case PLLInstances::PLL4_Audio: {
- clkPLL4setup(CLK_ENABLE);
+ clkPLL4setup(CLK_ENABLE,
+ clock_audio_pll_config_t{.loopDivider = static_cast<uint8_t>(params.loopDivider),
+ .postDivider = static_cast<uint8_t>(params.postDivider),
+ .numerator = params.numerator,
+ .denominator = params.denominator,
+ .src = static_cast<uint8_t>(params.src)});
name = "PLL4_Audio";
} break;
case PLLInstances::PLL3: {
@@ 54,7 59,7 @@ namespace drivers
{
switch (instance) {
case PLLInstances::PLL4_Audio: {
- clkPLL4setup(CLK_DISABLE);
+ clkPLL4setup(CLK_DISABLE, {});
} break;
case PLLInstances::PLL3: {
clkPLL3setup(CLK_DISABLE);
M module-bsp/board/rt1051/puretx/audio.cpp => module-bsp/board/rt1051/puretx/audio.cpp +1 -1
@@ 16,7 16,7 @@ using namespace drivers;
bsp::audio::AudioConfig audioConfig;
-void bsp::audio::init()
+void bsp::audio::init([[maybe_unused]] const std::uint32_t sampleRate)
{
audioConfig.pllAudio =
DriverPLL::Create(static_cast<PLLInstances>(BoardDefinitions ::AUDIO_PLL), DriverPLLParams{});
M module-bsp/board/rt1051/puretx/board/clock_config.h => module-bsp/board/rt1051/puretx/board/clock_config.h +1 -1
@@ 128,7 128,7 @@ extern "C"
void clkPLL3_PFD2setup(uint8_t enabled);
void clkPLL3_PFD3setup(uint8_t enabled);
- void clkPLL4setup(uint8_t enabled);
+ void clkPLL4setup(uint8_t enabled, const clock_audio_pll_config_t config);
void clkPLL5setup(uint8_t enabled);
M module-bsp/board/rt1051/puretx/clock_config.cpp => module-bsp/board/rt1051/puretx/clock_config.cpp +4 -2
@@ 396,7 396,7 @@ void BOARD_BootClockRUN(void)
*/
/* DeInit Audio PLL */
- clkPLL4setup(CLK_DISABLE);
+ clkPLL4setup(CLK_DISABLE, {});
CLOCK_SetPllBypass(CCM_ANALOG, kCLOCK_PllAudio, 1);
/*
@@ 965,8 965,10 @@ void clkPLL3_PFD3setup(uint8_t enabled)
}
}
-void clkPLL4setup(uint8_t enabled)
+void clkPLL4setup(uint8_t enabled, [[maybe_unused]] const clock_audio_pll_config_t config)
{
+ /// Due to used audio codec, fixed PLL configuration can be used. Audio codec's internal PLL can generate
+ /// all needed clocks using provided MCLK.
const clock_audio_pll_config_t audioPllConfig_BOARD_BootClockRUN = {
.loopDivider = 32, /* PLL loop divider. Valid range for DIV_SELECT divider value: 27~54. */
.postDivider = 1, /* Divider after the PLL, should only be 1, 2, 4, 8, 16. */
M module-bsp/drivers/pll/DriverPLL.cpp => module-bsp/drivers/pll/DriverPLL.cpp +1 -3
@@ 7,8 7,6 @@
#if defined(TARGET_RT1051)
#include "board/rt1051/drivers/RT1051DriverPLL.hpp"
#elif defined(TARGET_Linux)
-
-//#include ""
#else
#error "Unsupported target"
#endif
@@ 43,4 41,4 @@ namespace drivers
}
}
-} // namespace drivers>
\ No newline at end of file
+} // namespace drivers
M module-bsp/drivers/pll/DriverPLL.hpp => module-bsp/drivers/pll/DriverPLL.hpp +10 -6
@@ 5,6 5,7 @@
#define PUREPHONE_DRIVERPLL_HPP
#include <memory>
+#include <cstdint>
namespace drivers
{
@@ 23,17 24,20 @@ namespace drivers
};
struct DriverPLLParams
- {};
+ {
+ std::uint32_t loopDivider;
+ std::uint32_t postDivider;
+ std::uint32_t numerator;
+ std::uint32_t denominator;
+ std::uint32_t src;
+ };
class DriverPLL
{
public:
- static std::shared_ptr<DriverPLL> Create(const PLLInstances inst, const DriverPLLParams ¶ms);
-
- DriverPLL(const DriverPLLParams ¶ms) : parameters(params)
- {}
+ static std::shared_ptr<DriverPLL> Create(PLLInstances inst, const DriverPLLParams ¶ms);
- virtual ~DriverPLL()
+ explicit DriverPLL(const DriverPLLParams ¶ms) : parameters(params)
{}
protected: