Implement mp3 support
This commit is contained in:
parent
3255aaf15d
commit
e517e14986
6 changed files with 2010 additions and 2 deletions
|
|
@ -1,7 +1,7 @@
|
|||
find_package(SDL2_mixer REQUIRED)
|
||||
|
||||
file(GLOB_RECURSE PSEMEK_AUDIO_HEADERS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "include/*.hpp")
|
||||
file(GLOB_RECURSE PSEMEK_AUDIO_SOURCES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "source/*.cpp")
|
||||
file(GLOB_RECURSE PSEMEK_AUDIO_HEADERS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "include/*.hpp" "include/*.h")
|
||||
file(GLOB_RECURSE PSEMEK_AUDIO_SOURCES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "source/*.cpp" "source/*.c")
|
||||
|
||||
psemek_add_library(psemek-audio ${PSEMEK_AUDIO_HEADERS} ${PSEMEK_AUDIO_SOURCES})
|
||||
target_include_directories(psemek-audio PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||
|
|
|
|||
1865
libs/audio/include/psemek/audio/minimp3/minimp3.h
Normal file
1865
libs/audio/include/psemek/audio/minimp3/minimp3.h
Normal file
File diff suppressed because it is too large
Load diff
4
libs/audio/include/psemek/audio/minimp3/options.h
Normal file
4
libs/audio/include/psemek/audio/minimp3/options.h
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
#pragma once
|
||||
|
||||
#define MINIMP3_NO_SIMD
|
||||
#define MINIMP3_FLOAT_OUTPUT
|
||||
|
|
@ -25,4 +25,7 @@ namespace psemek::audio
|
|||
track_ptr load_wav(util::span<char const> data);
|
||||
track_ptr load_wav(std::vector<char> const & data);
|
||||
|
||||
track_ptr load_mp3(util::span<char const> data);
|
||||
track_ptr load_mp3(std::vector<char> data);
|
||||
|
||||
}
|
||||
|
|
|
|||
3
libs/audio/source/minimp3/minimp3.c
Normal file
3
libs/audio/source/minimp3/minimp3.c
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
#define MINIMP3_IMPLEMENTATION
|
||||
#include <psemek/audio/minimp3/options.h>
|
||||
#include <psemek/audio/minimp3/minimp3.h>
|
||||
133
libs/audio/source/track_mp3.cpp
Normal file
133
libs/audio/source/track_mp3.cpp
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
#include <psemek/audio/track.hpp>
|
||||
|
||||
#include <psemek/audio/minimp3/options.h>
|
||||
#include <psemek/audio/minimp3/minimp3.h>
|
||||
|
||||
#include <atomic>
|
||||
|
||||
namespace psemek::audio
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
struct mp3_data_holder
|
||||
{
|
||||
std::vector<char> storage;
|
||||
util::span<char const> data;
|
||||
|
||||
mp3_data_holder(std::vector<char> storage)
|
||||
: storage(std::move(storage))
|
||||
, data(this->storage)
|
||||
{}
|
||||
|
||||
mp3_data_holder(util::span<char const> data)
|
||||
: data(data)
|
||||
{}
|
||||
};
|
||||
|
||||
struct mp3_stream_impl
|
||||
: stream
|
||||
{
|
||||
mp3_stream_impl(std::shared_ptr<mp3_data_holder> data)
|
||||
: data_(std::move(data))
|
||||
, buffer_(MINIMP3_MAX_SAMPLES_PER_FRAME)
|
||||
{
|
||||
mp3dec_init(&decoder_);
|
||||
}
|
||||
|
||||
std::optional<std::size_t> length() const override
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::size_t read(float * data, std::size_t sample_count) override
|
||||
{
|
||||
auto input = reinterpret_cast<std::uint8_t const *>(data_->data.data());
|
||||
|
||||
std::size_t result = 0;
|
||||
while (result < sample_count)
|
||||
{
|
||||
if (buffer_pos_ < buffer_size_)
|
||||
{
|
||||
std::size_t size = std::min(buffer_size_ - buffer_pos_, sample_count - result);
|
||||
std::copy(buffer_.data() + buffer_pos_, buffer_.data() + buffer_pos_ + size, data + result);
|
||||
buffer_pos_ += size;
|
||||
result += size;
|
||||
}
|
||||
else
|
||||
{
|
||||
mp3dec_frame_info_t frame_info;
|
||||
buffer_size_ = mp3dec_decode_frame(&decoder_, input + read_bytes_, data_->data.size() - read_bytes_, buffer_.data(), &frame_info);
|
||||
buffer_pos_ = 0;
|
||||
read_bytes_ += frame_info.frame_bytes;
|
||||
|
||||
if (frame_info.channels == 1)
|
||||
{
|
||||
for (std::size_t i = buffer_size_ * 2; i > 0;)
|
||||
{
|
||||
i -= 2;
|
||||
buffer_[i + 0] = buffer_[i / 2];
|
||||
buffer_[i + 1] = buffer_[i / 2];
|
||||
}
|
||||
buffer_size_ *= 2;
|
||||
}
|
||||
|
||||
if (frame_info.frame_bytes == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::size_t played() const override
|
||||
{
|
||||
return played_.load();
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<mp3_data_holder> data_;
|
||||
std::size_t read_bytes_{0};
|
||||
|
||||
mp3dec_t decoder_;
|
||||
std::vector<float> buffer_;
|
||||
std::size_t buffer_pos_{0};
|
||||
std::size_t buffer_size_{0};
|
||||
std::atomic<std::size_t> played_{0};
|
||||
};
|
||||
|
||||
struct mp3_track_impl
|
||||
: track
|
||||
{
|
||||
mp3_track_impl(std::shared_ptr<mp3_data_holder> data)
|
||||
: data_(std::move(data))
|
||||
{}
|
||||
|
||||
stream_ptr stream() const override
|
||||
{
|
||||
return std::make_shared<mp3_stream_impl>(data_);
|
||||
}
|
||||
|
||||
std::optional<std::size_t> length() const override
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<mp3_data_holder> data_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
track_ptr load_mp3(util::span<char const> data)
|
||||
{
|
||||
return std::make_shared<mp3_track_impl>(std::make_shared<mp3_data_holder>(data));
|
||||
}
|
||||
|
||||
track_ptr load_mp3(std::vector<char> data)
|
||||
{
|
||||
return std::make_shared<mp3_track_impl>(std::make_shared<mp3_data_holder>(std::move(data)));
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue