diff --git a/meson.build b/meson.build index d0f4f75..4b58c04 100644 --- a/meson.build +++ b/meson.build @@ -5,12 +5,15 @@ project( ) raven_dep = dependency('raven') -mpv_dep = dependency('mpv') +opus_dep = dependency('opus') +opusfile_dep = dependency('opusfile') +alsa_dep = dependency('alsa') executable( 'waffle', - './src/MPVClient.cpp', + './src/OpusFile.cpp', + './src/ALSAOutput.cpp', './src/AppWidget.cpp', './src/main.cpp', - dependencies : [raven_dep, mpv_dep] + dependencies : [raven_dep, opus_dep, opusfile_dep, alsa_dep] ) \ No newline at end of file diff --git a/src/ALSAOutput.cpp b/src/ALSAOutput.cpp new file mode 100644 index 0000000..fc4842b --- /dev/null +++ b/src/ALSAOutput.cpp @@ -0,0 +1,44 @@ +#include "ALSAOutput.hpp" +#include +#include +#include +#include + + +int ALSAOutput::open() { + if (snd_pcm_open(&m_pcm, "default", SND_PCM_STREAM_PLAYBACK, 0) < 0) return -1; + + return 0; +} + +int ALSAOutput::configure(int sample_rate, int channels, int periods, float period_time) { + snd_pcm_hw_params_t *hw_params; + snd_pcm_hw_params_alloca(&hw_params); + + #ifndef ___CHECK___ + #define ___CHECK___(x) {if ((x) < 0) return -1;} + #endif + + ___CHECK___(snd_pcm_hw_params_any(m_pcm, hw_params)); + ___CHECK___(snd_pcm_hw_params_set_access(m_pcm, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)); + ___CHECK___(snd_pcm_hw_params_set_format(m_pcm, hw_params, SND_PCM_FORMAT_S16_LE)); + ___CHECK___(snd_pcm_hw_params_set_channels(m_pcm, hw_params, channels)); + ___CHECK___(snd_pcm_hw_params_set_rate(m_pcm, hw_params, sample_rate, 0)); + + ___CHECK___(snd_pcm_hw_params(m_pcm, hw_params)); + + #undef __CHECK___ + + return 0; +} + +int ALSAOutput::play(AbstractMusicFile *file) { + while (true) { + int16_t pcm[176400]; + memset(pcm, 0, sizeof(pcm)); + + size_t n_samples = file->read(pcm, 176400); + + snd_pcm_writei(m_pcm, pcm, n_samples); + } +} diff --git a/src/ALSAOutput.hpp b/src/ALSAOutput.hpp new file mode 100644 index 0000000..cb1be31 --- /dev/null +++ b/src/ALSAOutput.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include "src/AbstractMusicFile.hpp" +#include + + +class ALSAOutput { +public: + ALSAOutput() {} + + int open(); + int configure(int sample_rate, int channels, int periods, float period_time); + int play(AbstractMusicFile *file); +private: + snd_pcm_t *m_pcm { nullptr }; +}; diff --git a/src/AbstractMusicFile.hpp b/src/AbstractMusicFile.hpp new file mode 100644 index 0000000..2a46f79 --- /dev/null +++ b/src/AbstractMusicFile.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + + +class AbstractMusicFile { +public: + AbstractMusicFile() {} + virtual ~AbstractMusicFile() {} + + virtual int open(std::string file_path) = 0; + virtual int read(int16_t *pcm, int buf_size) = 0; +}; + diff --git a/src/AppWidget.cpp b/src/AppWidget.cpp index 22344b7..bd643c3 100644 --- a/src/AppWidget.cpp +++ b/src/AppWidget.cpp @@ -9,24 +9,14 @@ #include "raven/BoxLayout.hpp" #include "raven/Label.hpp" #include "raven/DocumentLayout.hpp" -#include "MPVClient.hpp" #include -static void populate_song_list(std::filesystem::path songs_directory, std::shared_ptr song_list_widget, MPVClient *mpv_client) { +static void populate_song_list(std::filesystem::path songs_directory, std::shared_ptr song_list_widget) { song_list_widget->clear_children(); for (const auto &entry : std::filesystem::directory_iterator(songs_directory)) { std::string filename = entry.path().filename(); std::string path_string = entry.path().string(); auto button = song_list_widget->add(filename); - button->on_click = [mpv_client, path_string]() { - const char *command[] = { - "loadfile", - path_string.c_str(), - NULL - }; - - mpv_client->send_command_async(command); - }; } } @@ -48,7 +38,7 @@ void AppWidget::on_init() { layout->set_inherit_secondary_dimension(true); } - populate_song_list("/home/hippoz/music", m_song_list->target(), m_mpv_client); + populate_song_list("/home/hippoz/music", m_song_list->target()); reflow(); set_did_init(true); diff --git a/src/AppWidget.hpp b/src/AppWidget.hpp index 102d576..aa20984 100644 --- a/src/AppWidget.hpp +++ b/src/AppWidget.hpp @@ -3,17 +3,14 @@ #include "raven/ScrollContainer.hpp" #include "raven/Widget.hpp" #include "raven/TextInput.hpp" -#include "src/MPVClient.hpp" class AppWidget : public Raven::Widget { public: - AppWidget(MPVClient *mpv_client) - : Raven::Widget(), - m_mpv_client(mpv_client) {} + AppWidget() + : Raven::Widget() {} protected: void on_init() override; private: std::shared_ptr m_song_list; - MPVClient *m_mpv_client { nullptr }; }; diff --git a/src/MPVClient.cpp b/src/MPVClient.cpp deleted file mode 100644 index a43f7a1..0000000 --- a/src/MPVClient.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include "MPVClient.hpp" -#include - -static void handle_mpv_wakeup_callback(void *mpv_client_ptr) { - MPVClient *mpv_client = static_cast(mpv_client_ptr); - mpv_client->raven_application()->queue_microtask([mpv_client]() { - mpv_client->handle_events(); - }); - mpv_client->raven_application()->wake(); -} - -void MPVClient::handle_events() { - while (1) { - mpv_event *event = mpv_wait_event(m_ctx, 0); - std::cout << "[MPV EVENT] " << mpv_event_name(event->event_id) << std::endl; - if (event->event_id == MPV_EVENT_NONE) { - break; - } - } -} - -int MPVClient::initialize() { - m_ctx = mpv_create(); - if (!m_ctx) { - return -1; - } - - mpv_set_option_string(m_ctx, "vo", "null"); - - if (mpv_initialize(m_ctx) < 0) return -1; - - mpv_set_wakeup_callback(m_ctx, handle_mpv_wakeup_callback, this); - - return 0; -} - -void MPVClient::send_command_async(const char **command) { - mpv_command_async(m_ctx, 1, command); -} - -MPVClient::~MPVClient() { - if (m_ctx) { - mpv_terminate_destroy(m_ctx); - } -} diff --git a/src/MPVClient.hpp b/src/MPVClient.hpp deleted file mode 100644 index 39764f2..0000000 --- a/src/MPVClient.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include "Application.hpp" -#include "Window.hpp" -#include - - -class MPVClient { -public: - MPVClient( - Raven::Application *raven_application, - Raven::Window *raven_window - ) - : m_raven_application(raven_application) - , m_raven_window(raven_window) {} - - ~MPVClient(); - - int initialize(); - void handle_events(); - - void send_command_async(const char **command); - - Raven::Application* raven_application() { return m_raven_application; } - Raven::Window* raven_window() { return m_raven_window; } -private: - mpv_handle *m_ctx { nullptr }; - Raven::Application* m_raven_application { nullptr }; - Raven::Window* m_raven_window { nullptr }; -}; diff --git a/src/OpusFile.cpp b/src/OpusFile.cpp new file mode 100644 index 0000000..e02775e --- /dev/null +++ b/src/OpusFile.cpp @@ -0,0 +1,16 @@ +#include "OpusFile.hpp" +#include "opus/opus.h" +#include "opus_types.h" +#include "opusfile.h" +#include + +int OpusFile::open(std::string file_path) { + m_file = op_open_file(file_path.c_str(), NULL); + if (m_file == NULL) return -1; + + return 0; +} + +int OpusFile::read(int16_t *pcm, int buf_size) { + return op_read_stereo(m_file, pcm, buf_size); +} diff --git a/src/OpusFile.hpp b/src/OpusFile.hpp new file mode 100644 index 0000000..d57d77f --- /dev/null +++ b/src/OpusFile.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include "opusfile.h" +#include "AbstractMusicFile.hpp" +#include +#include + +class OpusFile : public AbstractMusicFile { +public: + OpusFile() {} + + int open(std::string file_path); + int read(int16_t *pcm, int buf_size); +private: + OggOpusFile *m_file { nullptr }; +}; diff --git a/src/main.cpp b/src/main.cpp index 6ae2e0c..21a4ab0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,19 +4,24 @@ #include "raven/Widget.hpp" #include "raven/Button.hpp" #include "AppWidget.hpp" -#include "MPVClient.hpp" +#include "src/ALSAOutput.hpp" +#include "src/OpusFile.hpp" int main() { + OpusFile file; + file.open("song"); + ALSAOutput output; + output.open(); + output.configure(48000, 1, 10, 100000); + output.play(&file); + Raven::Application application(true); auto window = application.add_window(); - MPVClient client { &application, window.get() }; - client.initialize(); - window->spawn_window(); - window->set_main_widget(&client); + window->set_main_widget(); return application.run(); }