Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Yann/feature/audio/add audiokit #1423

Open
wants to merge 4 commits into
base: yann/feature/haptic/coredac-spike
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions include/interface/platform/File.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ struct File {
virtual auto read(std::span<uint8_t> buffer) -> std::size_t = 0;
virtual auto write(std::span<uint8_t> data) -> std::size_t = 0;

virtual auto read(std::span<int16_t> buffer) -> std::size_t = 0;

virtual auto read(std::span<char> buffer) -> std::size_t = 0;
virtual auto write(std::span<char> data) -> std::size_t = 0;

Expand Down
24 changes: 24 additions & 0 deletions libs/AudioKit/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Leka - LekaOS
# Copyright 2024 APF France handicap
# SPDX-License-Identifier: Apache-2.0

add_library(AudioKit STATIC)

target_include_directories(AudioKit
PUBLIC
include
)

target_sources(AudioKit
PRIVATE
source/AudioKit.cpp
source/WavFile.cpp
)

target_link_libraries(AudioKit
mbed-os
CoreSTM32Hal
CoreDAC
CoreEventQueue
FileManagerKit
)
53 changes: 53 additions & 0 deletions libs/AudioKit/include/AudioKit.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Leka - LekaOS
// Copyright 2024 APF France handicap
// SPDX-License-Identifier: Apache-2.0

#pragma once

#include "CoreEventQueue.h"
#include "DigitalOut.h"
#include "FileManagerKit.h"
#include "WavFile.h"
#include "interface/drivers/DAC.h"
#include "interface/drivers/STM32HalBasicTimer.h"

namespace leka {

class AudioKit
{
public:
explicit AudioKit(interface::STM32HalBasicTimer &dac_timer, interface::DACDMA &dac)
: _dac_timer(dac_timer), _dac(dac)
{
// nothing to do
}

void initialize();

void enableAudio();
void disableAudio();

void play(const std::filesystem::path &path);
void stop();

void setData(uint32_t offset);
void run();

private:
mbed::DigitalOut _audio_enable {SOUND_ENABLE, 1};

interface::STM32HalBasicTimer &_dac_timer;
interface::DACDMA &_dac;

FileManagerKit::File _file {};
WavFile _wav_file {_file};

CoreEventQueue _event_queue {};

static constexpr uint32_t played_data_size {2000};
std::array<uint16_t, played_data_size> played_data {};

static constexpr uint8_t _repetition {10};
};

} // namespace leka
57 changes: 57 additions & 0 deletions libs/AudioKit/include/WavFile.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Leka - LekaOS
// Copyright 2024 APF France handicap
// SPDX-License-Identifier: Apache-2.0

#pragma once

#include "interface/platform/File.h"

namespace leka {

class WavFile
{
constexpr static uint32_t blockID_fileType = 0x46464952; // corresponds to the letters 'RIFF' in reverse order
constexpr static uint32_t blockID_fileFormat = 0x45564157; // corresponds to the letters 'WAVE' in reverse order
constexpr static uint32_t blockID_format = 0x20746D66; // corresponds to the letters 'fmt ' in reverse order
constexpr static uint32_t blockID_data = 0x61746164; // corresponds to the letters 'data' in reverse order

public:
using WavHeader = struct wavHeader {
uint32_t FileTypeBlockID;
uint32_t FileSize; // Nb bytes in file -8 bytes
uint32_t FileFormatID;
uint32_t FormatBlockID;
uint32_t FormatBlockSize; // Nb bytes in format block -16 bytes
uint16_t AudioFormat;
uint16_t NumChannels;
uint32_t SamplingRate;
uint32_t BytePerSec;
uint16_t BytePerSampleBlock;
uint16_t BitsPerSample;
uint32_t DataBlockID;
uint32_t DataSize;
};

explicit WavFile(interface::File &file) : _file(file) {}

auto open(std::filesystem::path filename) -> bool;

[[nodiscard]] auto getHeader() const -> const WavHeader &;

auto read(std::span<int16_t> buffer) -> bool;
[[nodiscard]] auto isEndOfFile() const -> bool;

private:
void readHeader();

interface::File &_file;

const std::filesystem::path directory {"/fs/home/wav"};
const std::string extension {".wav"};

WavHeader _header {};

bool _is_eof {false};
};

} // namespace leka
86 changes: 86 additions & 0 deletions libs/AudioKit/source/AudioKit.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Leka - LekaOS
// Copyright 2024 APF France handicap
// SPDX-License-Identifier: Apache-2.0

#include "AudioKit.h"

#include "rtos/ThisThread.h"

#include "LogKit.h"

using namespace leka;
using namespace std::chrono_literals;

void AudioKit::initialize()
{
_event_queue.dispatch_forever();

auto onHalfTransfer = [this] { setData(0); };
auto onCompleteTransfer = [this] { setData(played_data_size / 2); };

_dac.registerDMACallbacks([this, onHalfTransfer] { _event_queue.call(onHalfTransfer); },
[this, onCompleteTransfer] { _event_queue.call(onCompleteTransfer); });
_dac.registerDataToPlay(played_data);

constexpr auto file_sample_rate = float {44'100.0};
constexpr auto timer_rate = float {file_sample_rate * _repetition};

_dac_timer.initialize(timer_rate);
_dac.initialize();
}

void AudioKit::enableAudio()
{
_audio_enable = 1;
}

void AudioKit::disableAudio()
{
stop();
_audio_enable = 0;
}

void AudioKit::play(const std::filesystem::path &path)
{
auto is_open = _wav_file.open(path);
if (!is_open) {
return;
}

setData(0);
setData(played_data_size / 2);

enableAudio();

run();
}

void AudioKit::stop()
{
_dac.stop();
}

void AudioKit::setData(uint32_t offset)
{
if (_wav_file.isEndOfFile()) {
stop();
return;
}

constexpr auto file_data_size = played_data_size / _repetition / 2;
auto file_data = std::array<int16_t, file_data_size> {};

_wav_file.read(file_data);

for (uint32_t i = 0; i < file_data.size(); i++) {
auto normalized_value = static_cast<uint16_t>((file_data.at(i) + 0x8000) >> 4);
std::fill_n(played_data.begin() + offset + i * _repetition, _repetition, normalized_value);
}

rtos::ThisThread::sleep_for(2ms); // Related to played_data_size and _repetition
}

void AudioKit::run()
{
_dac.start();
}
56 changes: 56 additions & 0 deletions libs/AudioKit/source/WavFile.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Leka - LekaOS
// Copyright 2024 APF France handicap
// SPDX-License-Identifier: Apache-2.0

#include "WavFile.h"
#include <cstring>

#include "FileManagerKit.h"

using namespace leka;

auto WavFile::open(std::filesystem::path filename) -> bool
{
auto path = directory / filename;
path += extension;

if (FileManagerKit::file_is_missing(path)) {
return false;
}

auto is_open = _file.open(path, "r");

readHeader();

_is_eof = false;

return is_open;
}

void WavFile::readHeader()
{
std::array<int16_t, sizeof(_header)> buffer {};

_file.read(buffer);

std::memcpy(&_header, &buffer, sizeof(_header));
}

auto WavFile::getHeader() const -> const WavHeader &
{
return _header;
}

auto WavFile::read(std::span<int16_t> buffer) -> bool
{
auto bytes_read = _file.read(buffer);

_is_eof = bytes_read != std::size(buffer);
auto is_not_eof = !_is_eof;
return is_not_eof;
}

auto WavFile::isEndOfFile() const -> bool
{
return _is_eof;
}
1 change: 1 addition & 0 deletions libs/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# SPDX-License-Identifier: Apache-2.0

add_subdirectory(${LIBS_DIR}/ActivityKit)
add_subdirectory(${LIBS_DIR}/AudioKit)
add_subdirectory(${LIBS_DIR}/BatteryKit)
add_subdirectory(${LIBS_DIR}/BehaviorKit)
add_subdirectory(${LIBS_DIR}/BLEKit)
Expand Down
2 changes: 2 additions & 0 deletions libs/FileManagerKit/include/FileManagerKit.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ struct File : public interface::File, public mbed::NonCopyable<File> {
auto read(std::span<uint8_t> buffer) -> std::size_t final;
auto write(std::span<uint8_t> data) -> std::size_t final;

auto read(std::span<int16_t> buffer) -> std::size_t final;

auto read(std::span<char> buffer) -> std::size_t final;
auto write(std::span<char> data) -> std::size_t final;

Expand Down
5 changes: 5 additions & 0 deletions libs/FileManagerKit/source/File.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ auto File::read(std::span<uint8_t> buffer) -> std::size_t
return std::fread(buffer.data(), sizeof(uint8_t), buffer.size(), _file.get());
}

auto File::read(std::span<int16_t> buffer) -> std::size_t
{
return std::fread(buffer.data(), sizeof(int16_t), buffer.size(), _file.get());
}

auto File::write(std::span<uint8_t> data) -> std::size_t
{
return std::fwrite(data.data(), sizeof(uint8_t), data.size(), _file.get());
Expand Down
5 changes: 4 additions & 1 deletion spikes/lk_audio/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ target_sources(spike_lk_audio
)

target_link_libraries(spike_lk_audio
FileManagerKit
CoreSTM32Hal
CoreDAC
AudioKit
BLEKit
)

target_link_custom_leka_targets(spike_lk_audio)
Loading