Skip to content
Merged
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: 1 addition & 1 deletion deps/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ FetchContent_Declare(gvdi
FetchContent_Declare(
capo
GIT_REPOSITORY https://github.com/capo-devs/capo-lite
GIT_TAG v2.0.1
GIT_TAG v2.1.0
GIT_SHALLOW TRUE
)

Expand Down
18 changes: 18 additions & 0 deletions library/include/juke/core/AudioFile.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#pragma once

#include <capo/buffer.hpp>
#include <juke/core/MediaFile.hpp>

namespace juke {

class AudioFile : public IMediaFile {
public:
explicit AudioFile(std::filesystem::path const& path);

bool bind_to(capo::ISource& source) final { return source.bind_to(m_buffer); }

private:
std::shared_ptr<capo::Buffer> m_buffer{std::make_shared<capo::Buffer>()};
};

} // namespace juke
11 changes: 11 additions & 0 deletions library/include/juke/core/MediaError.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#pragma once

#include <stdexcept>

namespace juke {

struct MediaError : std::runtime_error {
using std::runtime_error::runtime_error;
};

} // namespace juke
11 changes: 6 additions & 5 deletions library/include/juke/core/MediaFile.hpp
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@

#pragma once

#include <capo/engine.hpp>
#include <capo/source.hpp>
#include <filesystem>

namespace juke {

class MediaFile {
class IMediaFile : public capo::Polymorphic {
public:
explicit MediaFile(std::filesystem::path const& path);
[[nodiscard]] capo::Buffer const& get_buffer() const { return m_buffer; }
explicit IMediaFile(std::filesystem::path const& path) : m_filename(path.filename().string()) {}

virtual bool bind_to(capo::ISource& source) = 0;

[[nodiscard]] std::string const& get_filename() const { return m_filename; }

private:
capo::Buffer m_buffer{};
std::string m_filename{};
};

Expand Down
7 changes: 4 additions & 3 deletions library/include/juke/core/MediaPlayer.hpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
#pragma once

#include <capo/source.hpp>
#include <capo/engine.hpp>
#include <juke/core/MediaFile.hpp>
#include <juke/core/XMStream.hpp>
#include <chrono>
#include <filesystem>
#include <optional>
#include <memory>
#include <string>

namespace juke {
Expand All @@ -26,7 +27,7 @@ class MediaPlayer {
MediaStatus m_status{MediaStatus::stopped};
[[maybe_unused]] bool m_trigger{};

std::optional<MediaFile> m_file{};
std::unique_ptr<IMediaFile> m_media_file{};
std::unique_ptr<capo::ISource> m_source{};

std::string m_status_string{"stopped"};
Expand Down
12 changes: 0 additions & 12 deletions library/include/juke/core/XMDecode.hpp

This file was deleted.

20 changes: 20 additions & 0 deletions library/include/juke/core/XMFile.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#pragma once

#include <juke/core/MediaFile.hpp>
#include <juke/core/XMStream.hpp>

namespace juke {

class XMFile : public IMediaFile {
public:
static bool is_xm(std::filesystem::path const& path) { return XMStream::is_xm(path); }

explicit XMFile(std::filesystem::path const& path);

bool bind_to(capo::ISource& source) final { return source.bind_to(m_stream); }

private:
std::shared_ptr<XMStream> m_stream{};
};

} // namespace juke
30 changes: 30 additions & 0 deletions library/include/juke/core/XMStream.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#pragma once

#include <capo/buffer.hpp>
#include <capo/stream_pipe.hpp>
#include <filesystem>
#include <memory>

namespace juke {

class XMStream : public capo::IStreamPipe {
public:
static bool is_xm(std::filesystem::path const& path);

explicit XMStream(std::filesystem::path const& path);

private:
struct Impl;
struct Deleter {
void operator()(Impl* ptr) const noexcept;
};

void push_samples(std::vector<float>& out) final;
[[nodiscard]] auto get_sample_rate() const -> std::uint32_t final { return capo::Buffer::sample_rate_v; }
[[nodiscard]] auto get_channels() const -> std::uint8_t final { return 2; }
auto set_looping(bool looping) -> bool final;

std::unique_ptr<Impl, Deleter> m_impl{};
};

} // namespace juke
11 changes: 11 additions & 0 deletions library/src/core/AudioFile.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#include <juke/core/AudioFile.hpp>
#include <juke/core/MediaError.hpp>

namespace juke {

AudioFile::AudioFile(std::filesystem::path const& path) : IMediaFile(path) {
auto const path_str = path.string();
if (!m_buffer->decode_file(path_str.c_str())) { throw MediaError{"Failed to decode file: " + path_str}; }
}

} // namespace juke
5 changes: 3 additions & 2 deletions library/src/core/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@

target_sources(juke PRIVATE
Application.cpp
MediaFile.cpp
AudioFile.cpp
MediaPlayer.cpp
XMDecode.cpp
XMFile.cpp
XMStream.cpp
)
20 changes: 0 additions & 20 deletions library/src/core/MediaFile.cpp

This file was deleted.

45 changes: 28 additions & 17 deletions library/src/core/MediaPlayer.cpp
Original file line number Diff line number Diff line change
@@ -1,33 +1,40 @@

#include <imgui.h>
#include <capo/engine.hpp>
#include <juke/core/AudioFile.hpp>
#include <juke/core/MediaError.hpp>
#include <juke/core/MediaPlayer.hpp>
#include <juke/core/XMFile.hpp>
#include <juke/graphics/Colors.hpp>
#include <algorithm>
#include <print>

namespace juke {

using namespace std::chrono_literals;

MediaPlayer::MediaPlayer(capo::IEngine& audio_engine) : m_source(audio_engine.create_source()) {
if (!m_source) { throw std::runtime_error{"Failed to create Audio Source"}; }
}

bool MediaPlayer::load_media(std::filesystem::path const& path) {
try {
auto file = MediaFile(path);
m_source->stop(); // if file didn't throw, stop playback as m_file will be replaced
m_file = std::move(file);
} catch (std::exception const& e) {
auto media_file = std::unique_ptr<IMediaFile>{};
if (XMFile::is_xm(path)) {
media_file = std::make_unique<XMFile>(path);
} else {
media_file = std::make_unique<AudioFile>(path);
}
m_source->stop();
if (!media_file->bind_to(*m_source)) {
std::println("ERROR: Failed to bind Source to PCM");
return false;
}
m_media_file = std::move(media_file);
} catch (MediaError const& e) { // only MediaErrors are caught here, others are raised all the way to main.
std::println("ERROR: {}", e.what());
return false;
} catch (...) {
std::println("ERROR: UNKNOWN");
return false;
}
if (!m_source->bind_to(&m_file->get_buffer())) {
std::println("ERROR: Failed to bind Source to PCM");
return false;
}

std::println("Media file loaded: {}", path.string());

m_source->play();
Expand All @@ -40,10 +47,11 @@ void MediaPlayer::handle_input() {
ImGuiWindowFlags flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove;
if (ImGui::Begin("Player", nullptr, flags)) {

/* Progress Bar */
auto fraction = 0.f;
if (m_file) { fraction = m_source->get_cursor() / m_source->get_duration(); }
ImGui::ProgressBar(fraction);
if (auto const duration = m_source->get_duration(); duration > 0s) {
/* Progress Bar */
auto const fraction = m_source->get_cursor() / duration;
ImGui::ProgressBar(fraction);
}

/* Control Buttons */
ImGui::Separator();
Expand All @@ -62,10 +70,13 @@ void MediaPlayer::handle_input() {
m_source->set_cursor({}); // seek to start
m_status = MediaStatus::stopped;
}
ImGui::SameLine();
auto loop = m_source->is_looping();
if (ImGui::Checkbox("loop", &loop)) { m_source->set_looping(loop); }
ImGui::Separator();
ImGui::Text("File Name: ");
ImGui::SameLine();
m_file ? ImGui::TextColored(colors::blue, "%s", m_file->get_filename().c_str()) : ImGui::TextColored(colors::grey, "<no file>");
m_media_file ? ImGui::TextColored(colors::blue, "%s", m_media_file->get_filename().c_str()) : ImGui::TextColored(colors::grey, "<no file>");

m_status_string.clear();
std::format_to(std::back_inserter(m_status_string), "{}", playing() ? "playing" : paused() ? "paused" : "stopped");
Expand Down
77 changes: 0 additions & 77 deletions library/src/core/XMDecode.cpp

This file was deleted.

7 changes: 7 additions & 0 deletions library/src/core/XMFile.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#include <juke/core/XMFile.hpp>

namespace juke {

XMFile::XMFile(std::filesystem::path const& path) : IMediaFile(path), m_stream(std::make_shared<XMStream>(path)) {}

} // namespace juke
Loading