manual opus decoding + alsa. warning: music very distorted
This commit is contained in:
parent
20685a8d2e
commit
2eaa032305
11 changed files with 127 additions and 100 deletions
|
@ -5,12 +5,15 @@ project(
|
||||||
)
|
)
|
||||||
|
|
||||||
raven_dep = dependency('raven')
|
raven_dep = dependency('raven')
|
||||||
mpv_dep = dependency('mpv')
|
opus_dep = dependency('opus')
|
||||||
|
opusfile_dep = dependency('opusfile')
|
||||||
|
alsa_dep = dependency('alsa')
|
||||||
|
|
||||||
executable(
|
executable(
|
||||||
'waffle',
|
'waffle',
|
||||||
'./src/MPVClient.cpp',
|
'./src/OpusFile.cpp',
|
||||||
|
'./src/ALSAOutput.cpp',
|
||||||
'./src/AppWidget.cpp',
|
'./src/AppWidget.cpp',
|
||||||
'./src/main.cpp',
|
'./src/main.cpp',
|
||||||
dependencies : [raven_dep, mpv_dep]
|
dependencies : [raven_dep, opus_dep, opusfile_dep, alsa_dep]
|
||||||
)
|
)
|
44
src/ALSAOutput.cpp
Normal file
44
src/ALSAOutput.cpp
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
#include "ALSAOutput.hpp"
|
||||||
|
#include <alsa/asoundlib.h>
|
||||||
|
#include <alsa/pcm.h>
|
||||||
|
#include <cstring>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
16
src/ALSAOutput.hpp
Normal file
16
src/ALSAOutput.hpp
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "src/AbstractMusicFile.hpp"
|
||||||
|
#include <alsa/asoundlib.h>
|
||||||
|
|
||||||
|
|
||||||
|
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 };
|
||||||
|
};
|
15
src/AbstractMusicFile.hpp
Normal file
15
src/AbstractMusicFile.hpp
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
|
||||||
|
class AbstractMusicFile {
|
||||||
|
public:
|
||||||
|
AbstractMusicFile() {}
|
||||||
|
virtual ~AbstractMusicFile() {}
|
||||||
|
|
||||||
|
virtual int open(std::string file_path) = 0;
|
||||||
|
virtual int read(int16_t *pcm, int buf_size) = 0;
|
||||||
|
};
|
||||||
|
|
|
@ -9,24 +9,14 @@
|
||||||
#include "raven/BoxLayout.hpp"
|
#include "raven/BoxLayout.hpp"
|
||||||
#include "raven/Label.hpp"
|
#include "raven/Label.hpp"
|
||||||
#include "raven/DocumentLayout.hpp"
|
#include "raven/DocumentLayout.hpp"
|
||||||
#include "MPVClient.hpp"
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
||||||
static void populate_song_list(std::filesystem::path songs_directory, std::shared_ptr<Raven::Widget> song_list_widget, MPVClient *mpv_client) {
|
static void populate_song_list(std::filesystem::path songs_directory, std::shared_ptr<Raven::Widget> song_list_widget) {
|
||||||
song_list_widget->clear_children();
|
song_list_widget->clear_children();
|
||||||
for (const auto &entry : std::filesystem::directory_iterator(songs_directory)) {
|
for (const auto &entry : std::filesystem::directory_iterator(songs_directory)) {
|
||||||
std::string filename = entry.path().filename();
|
std::string filename = entry.path().filename();
|
||||||
std::string path_string = entry.path().string();
|
std::string path_string = entry.path().string();
|
||||||
auto button = song_list_widget->add<Raven::Button>(filename);
|
auto button = song_list_widget->add<Raven::Button>(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);
|
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();
|
reflow();
|
||||||
set_did_init(true);
|
set_did_init(true);
|
||||||
|
|
|
@ -3,17 +3,14 @@
|
||||||
#include "raven/ScrollContainer.hpp"
|
#include "raven/ScrollContainer.hpp"
|
||||||
#include "raven/Widget.hpp"
|
#include "raven/Widget.hpp"
|
||||||
#include "raven/TextInput.hpp"
|
#include "raven/TextInput.hpp"
|
||||||
#include "src/MPVClient.hpp"
|
|
||||||
|
|
||||||
class AppWidget : public Raven::Widget {
|
class AppWidget : public Raven::Widget {
|
||||||
public:
|
public:
|
||||||
AppWidget(MPVClient *mpv_client)
|
AppWidget()
|
||||||
: Raven::Widget(),
|
: Raven::Widget() {}
|
||||||
m_mpv_client(mpv_client) {}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void on_init() override;
|
void on_init() override;
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<Raven::ScrollContainer> m_song_list;
|
std::shared_ptr<Raven::ScrollContainer> m_song_list;
|
||||||
MPVClient *m_mpv_client { nullptr };
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
#include "MPVClient.hpp"
|
|
||||||
#include <mpv/client.h>
|
|
||||||
|
|
||||||
static void handle_mpv_wakeup_callback(void *mpv_client_ptr) {
|
|
||||||
MPVClient *mpv_client = static_cast<MPVClient*>(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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Application.hpp"
|
|
||||||
#include "Window.hpp"
|
|
||||||
#include <mpv/client.h>
|
|
||||||
|
|
||||||
|
|
||||||
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 };
|
|
||||||
};
|
|
16
src/OpusFile.cpp
Normal file
16
src/OpusFile.cpp
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#include "OpusFile.hpp"
|
||||||
|
#include "opus/opus.h"
|
||||||
|
#include "opus_types.h"
|
||||||
|
#include "opusfile.h"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
16
src/OpusFile.hpp
Normal file
16
src/OpusFile.hpp
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "opusfile.h"
|
||||||
|
#include "AbstractMusicFile.hpp"
|
||||||
|
#include <string>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
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 };
|
||||||
|
};
|
15
src/main.cpp
15
src/main.cpp
|
@ -4,19 +4,24 @@
|
||||||
#include "raven/Widget.hpp"
|
#include "raven/Widget.hpp"
|
||||||
#include "raven/Button.hpp"
|
#include "raven/Button.hpp"
|
||||||
#include "AppWidget.hpp"
|
#include "AppWidget.hpp"
|
||||||
#include "MPVClient.hpp"
|
#include "src/ALSAOutput.hpp"
|
||||||
|
#include "src/OpusFile.hpp"
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
|
OpusFile file;
|
||||||
|
file.open("song");
|
||||||
|
ALSAOutput output;
|
||||||
|
output.open();
|
||||||
|
output.configure(48000, 1, 10, 100000);
|
||||||
|
output.play(&file);
|
||||||
|
|
||||||
Raven::Application application(true);
|
Raven::Application application(true);
|
||||||
auto window = application.add_window<Raven::Window>();
|
auto window = application.add_window<Raven::Window>();
|
||||||
|
|
||||||
MPVClient client { &application, window.get() };
|
|
||||||
client.initialize();
|
|
||||||
|
|
||||||
window->spawn_window();
|
window->spawn_window();
|
||||||
|
|
||||||
window->set_main_widget<AppWidget>(&client);
|
window->set_main_widget<AppWidget>();
|
||||||
|
|
||||||
return application.run();
|
return application.run();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue