fully functional music playback

This commit is contained in:
hippoz 2022-11-07 18:52:23 +02:00
parent a051a8d574
commit 5cf4b965e0
Signed by: hippoz
GPG key ID: 7C52899193467641
11 changed files with 161 additions and 31 deletions

View file

@ -11,9 +11,10 @@ alsa_dep = dependency('alsa')
executable( executable(
'waffle', 'waffle',
'./src/OpusFile.cpp', './src/player/OpusFile.cpp',
'./src/ALSAOutput.cpp', './src/player/ALSAOutput.cpp',
'./src/AppWidget.cpp', './src/player/Player.cpp',
'./src/ui/AppWidget.cpp',
'./src/main.cpp', './src/main.cpp',
dependencies : [raven_dep, opus_dep, opusfile_dep, alsa_dep] dependencies : [raven_dep, opus_dep, opusfile_dep, alsa_dep]
) )

View file

@ -1,27 +1,24 @@
#include <iostream> #include <iostream>
#include <memory>
#include "raven/Window.hpp" #include "raven/Window.hpp"
#include "raven/Application.hpp" #include "raven/Application.hpp"
#include "raven/Widget.hpp" #include "raven/Widget.hpp"
#include "raven/Button.hpp" #include "raven/Button.hpp"
#include "AppWidget.hpp" #include "src/player/Player.hpp"
#include "src/ALSAOutput.hpp" #include "ui/AppWidget.hpp"
#include "src/OpusFile.hpp" #include "player/ALSAOutput.hpp"
#include "player/OpusFile.hpp"
int main() int main()
{ {
OpusFile file; Player player;
file.open("song");
ALSAOutput output;
output.open();
output.configure(48000, 2, 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>();
window->spawn_window(); window->spawn_window();
window->set_main_widget<AppWidget>(); window->set_main_widget<AppWidget>(&player);
return application.run(); return application.run();
} }

View file

@ -5,8 +5,8 @@
#include <iostream> #include <iostream>
int ALSAOutput::open() { int ALSAOutput::open(std::string output_name) {
if (snd_pcm_open(&m_pcm, "default", SND_PCM_STREAM_PLAYBACK, 0) < 0) return -1; if (snd_pcm_open(&m_pcm, output_name.c_str(), SND_PCM_STREAM_PLAYBACK, 0) < 0) return -1;
return 0; return 0;
} }
@ -32,13 +32,17 @@ int ALSAOutput::configure(int sample_rate, int channels, int periods, float peri
return 0; return 0;
} }
int ALSAOutput::play(AbstractMusicFile *file) { int ALSAOutput::write(int16_t *data, size_t samples) {
while (true) { return snd_pcm_writei(m_pcm, data, samples);
int16_t pcm[176400]; }
memset(pcm, 0, sizeof(pcm));
int ALSAOutput::drop() {
size_t n_samples = file->read(pcm, 176400); snd_pcm_drop(m_pcm);
snd_pcm_prepare(m_pcm);
snd_pcm_writei(m_pcm, pcm, n_samples); return 0;
} }
ALSAOutput::~ALSAOutput() {
snd_pcm_drain(m_pcm);
snd_pcm_close(m_pcm);
} }

View file

@ -1,16 +1,19 @@
#pragma once #pragma once
#include "src/AbstractMusicFile.hpp" #include "AbstractMusicFile.hpp"
#include <alsa/asoundlib.h> #include <alsa/asoundlib.h>
#include <alsa/pcm.h>
class ALSAOutput { class ALSAOutput {
public: public:
ALSAOutput() {} ALSAOutput() {}
~ALSAOutput();
int open(); int open(std::string output_name);
int configure(int sample_rate, int channels, int periods, float period_time); int configure(int sample_rate, int channels, int periods, float period_time);
int play(AbstractMusicFile *file); int write(int16_t *data, size_t samples);
int drop();
private: private:
snd_pcm_t *m_pcm { nullptr }; snd_pcm_t *m_pcm { nullptr };
}; };

View file

@ -5,6 +5,10 @@
#include <iostream> #include <iostream>
int OpusFile::open(std::string file_path) { int OpusFile::open(std::string file_path) {
if (m_file != NULL) {
op_free(m_file);
}
m_file = op_open_file(file_path.c_str(), NULL); m_file = op_open_file(file_path.c_str(), NULL);
if (m_file == NULL) return -1; if (m_file == NULL) return -1;
@ -14,3 +18,7 @@ int OpusFile::open(std::string file_path) {
int OpusFile::read(int16_t *pcm, int buf_size) { int OpusFile::read(int16_t *pcm, int buf_size) {
return op_read_stereo(m_file, pcm, buf_size); return op_read_stereo(m_file, pcm, buf_size);
} }
OpusFile::~OpusFile() {
op_free(m_file);
}

View file

@ -8,6 +8,7 @@
class OpusFile : public AbstractMusicFile { class OpusFile : public AbstractMusicFile {
public: public:
OpusFile() {} OpusFile() {}
~OpusFile();
int open(std::string file_path); int open(std::string file_path);
int read(int16_t *pcm, int buf_size); int read(int16_t *pcm, int buf_size);

68
src/player/Player.cpp Normal file
View file

@ -0,0 +1,68 @@
#include "Player.hpp"
#include "ALSAOutput.hpp"
#include <thread>
#include <iostream>
#include <fcntl.h>
#include <unistd.h>
#include <exception>
#include <mutex>
void Player::start_playback_thread() {
m_playback_thread = new std::thread([this]() {
playback_loop();
});
}
void Player::set_file(std::shared_ptr<AbstractMusicFile> file) {
std::lock_guard<std::mutex> guard(m_lock);
m_file = file;
if (m_state == State::NoThreadUntilFile) {
{
std::lock_guard<std::mutex> alsa_guard(m_alsa_lock);
m_alsa_output->drop();
}
start_playback_thread();
m_state = State::Playing;
} else if (m_state == State::Playing) {
std::lock_guard<std::mutex> alsa_guard(m_alsa_lock);
m_alsa_output->drop();
}
}
void Player::playback_loop() {
while (true) {
int16_t pcm[176400];
size_t samples_read = 0;
{
std::lock_guard<std::mutex> guard(m_lock);
samples_read = m_file->read(pcm, 176400);
if (samples_read < 1) {
m_state = State::NoThreadUntilFile;
return;
}
}
{
std::lock_guard<std::mutex> guard(m_alsa_lock);
m_alsa_output->write(pcm, samples_read);
}
}
}
Player::Player() {
m_alsa_output = new ALSAOutput();
m_alsa_output->open("pipewire");
m_alsa_output->configure(48000, 2, 1, 100000);
}
Player::~Player() {
if (m_alsa_output) {
delete m_alsa_output;
}
if (m_playback_thread) {
delete m_playback_thread;
}
}

38
src/player/Player.hpp Normal file
View file

@ -0,0 +1,38 @@
#pragma once
#include "AbstractMusicFile.hpp"
#include "ALSAOutput.hpp"
#include <memory>
#include <thread>
#include <mutex>
class Player {
public:
Player();
~Player();
enum class State {
NoThreadUntilFile,
Paused,
Playing
};
std::shared_ptr<AbstractMusicFile> file() { return m_file; }
void set_file(std::shared_ptr<AbstractMusicFile> file);
private:
void playback_loop();
void start_playback_thread();
std::mutex m_lock;
/**/
std::thread *m_playback_thread { nullptr };
State m_state { State::NoThreadUntilFile };
std::shared_ptr<AbstractMusicFile> m_file { nullptr };
/**/
std::mutex m_alsa_lock;
/**/
ALSAOutput *m_alsa_output { nullptr };
/**/
};

View file

@ -9,14 +9,21 @@
#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 "src/player/OpusFile.hpp"
#include "src/player/Player.hpp"
#include <filesystem> #include <filesystem>
static void populate_song_list(std::filesystem::path songs_directory, std::shared_ptr<Raven::Widget> song_list_widget) { static void populate_song_list(std::filesystem::path songs_directory, std::shared_ptr<Raven::Widget> song_list_widget, Player *player) {
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 = [player, path_string]() {
auto file = std::make_shared<OpusFile>();
file->open(path_string);
player->set_file(file);
};
} }
} }
@ -38,7 +45,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()); populate_song_list("/home/hippoz/music", m_song_list->target(), m_player);
reflow(); reflow();
set_did_init(true); set_did_init(true);

View file

@ -3,14 +3,17 @@
#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/player/Player.hpp"
class AppWidget : public Raven::Widget { class AppWidget : public Raven::Widget {
public: public:
AppWidget() AppWidget(Player *player)
: Raven::Widget() {} : Raven::Widget()
, m_player(player) {}
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;
Player *m_player;
}; };