diff options
author | Cesar Torres <shortanemoia@protonmail.com> | 2021-03-26 01:26:12 +0100 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-03-27 10:20:55 +0100 |
commit | 2e28b8ebccd6dceeec058ab3be86e13f10546040 (patch) | |
tree | bbe376eb28a8d9396830546876d688804111a669 /Userland/Applications/SoundPlayer/SoundPlayerWidgetAdvancedView.cpp | |
parent | e4d6a56a28d61d820c36efea0a553072b72ead4d (diff) | |
download | serenity-2e28b8ebccd6dceeec058ab3be86e13f10546040.zip |
SoundPlayer: Add playlist supprt
And a M3U(8) parser
Diffstat (limited to 'Userland/Applications/SoundPlayer/SoundPlayerWidgetAdvancedView.cpp')
-rw-r--r-- | Userland/Applications/SoundPlayer/SoundPlayerWidgetAdvancedView.cpp | 161 |
1 files changed, 137 insertions, 24 deletions
diff --git a/Userland/Applications/SoundPlayer/SoundPlayerWidgetAdvancedView.cpp b/Userland/Applications/SoundPlayer/SoundPlayerWidgetAdvancedView.cpp index 4d11fb4c0c..ee9accbc26 100644 --- a/Userland/Applications/SoundPlayer/SoundPlayerWidgetAdvancedView.cpp +++ b/Userland/Applications/SoundPlayer/SoundPlayerWidgetAdvancedView.cpp @@ -26,13 +26,14 @@ #include "SoundPlayerWidgetAdvancedView.h" #include "BarsVisualizationWidget.h" +#include "Common.h" +#include "M3UParser.h" #include "PlaybackManager.h" -#include "SoundPlayerWidget.h" +#include <AK/LexicalPath.h> #include <AK/SIMD.h> #include <LibGUI/Action.h> #include <LibGUI/BoxLayout.h> #include <LibGUI/Button.h> -#include <LibGUI/DragOperation.h> #include <LibGUI/Label.h> #include <LibGUI/MessageBox.h> #include <LibGUI/Slider.h> @@ -46,11 +47,16 @@ SoundPlayerWidgetAdvancedView::SoundPlayerWidgetAdvancedView(GUI::Window& window , m_window(window) { window.resize(455, 350); - window.set_minimum_size(440, 130); + window.set_minimum_size(600, 130); window.set_resizable(true); - set_fill_with_background_color(true); + set_layout<GUI::VerticalBoxLayout>(); + m_splitter = add<GUI::HorizontalSplitter>(); + m_player_view = m_splitter->add<GUI::Widget>(); + m_playlist_model = adopt(*new PlaylistModel()); + + m_player_view->set_layout<GUI::VerticalBoxLayout>(); m_play_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/play.png"); m_pause_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/pause.png"); @@ -58,9 +64,9 @@ SoundPlayerWidgetAdvancedView::SoundPlayerWidgetAdvancedView(GUI::Window& window m_back_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/go-back.png"); m_next_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/go-forward.png"); - m_visualization = add<BarsVisualizationWidget>(); + m_visualization = m_player_view->add<BarsVisualizationWidget>(); - m_playback_progress_slider = add<Slider>(Orientation::Horizontal); + m_playback_progress_slider = m_player_view->add<AutoSlider>(Orientation::Horizontal); m_playback_progress_slider->set_fixed_height(20); m_playback_progress_slider->set_min(0); m_playback_progress_slider->set_max(this->manager().total_length() * 44100); //this value should be set when we load a new file @@ -68,8 +74,7 @@ SoundPlayerWidgetAdvancedView::SoundPlayerWidgetAdvancedView(GUI::Window& window this->manager().seek(value); }; - auto& toolbar_container = add<GUI::ToolBarContainer>(); - + auto& toolbar_container = m_player_view->add<GUI::ToolBarContainer>(); auto& menubar = toolbar_container.add<GUI::ToolBar>(); m_play_button = menubar.add<GUI::Button>(); @@ -99,16 +104,35 @@ SoundPlayerWidgetAdvancedView::SoundPlayerWidgetAdvancedView(GUI::Window& window // filler_label menubar.add<GUI::Label>(); - m_back_button = menubar.add<GUI::Button>(); m_back_button->set_fixed_width(50); m_back_button->set_icon(*m_back_icon); m_back_button->set_enabled(has_loaded_file()); + m_back_button->on_click = [&](unsigned) { + if (!m_playlist_model.is_null()) { + auto it = m_playlist_model->items().find_if([&](const M3UEntry& e) { return e.path == loaded_filename(); }); + if (it.index() == 0) { + open_file(m_playlist_model->items().at(m_playlist_model->items().size() - 1).path); + } else + open_file((it - 1)->path); + play(); + } + }; m_next_button = menubar.add<GUI::Button>(); m_next_button->set_fixed_width(50); m_next_button->set_icon(*m_next_icon); m_next_button->set_enabled(has_loaded_file()); + m_next_button->on_click = [&](unsigned) { + if (!m_playlist_model.is_null()) { + auto it = m_playlist_model->items().find_if([&](const M3UEntry& e) { return e.path == loaded_filename(); }); + if (it.index() + 1 == m_playlist_model->items().size()) { + open_file(m_playlist_model->items().at(0).path); + } else + open_file((it + 1)->path); + play(); + } + }; m_volume_label = &menubar.add<GUI::Label>(); m_volume_label->set_fixed_width(30); @@ -137,30 +161,46 @@ SoundPlayerWidgetAdvancedView::SoundPlayerWidgetAdvancedView(GUI::Window& window m_playback_progress_slider->set_value(samples_played); dynamic_cast<Visualization*>(m_visualization.ptr())->set_buffer(this->manager().current_buffer()); + dynamic_cast<Visualization*>(m_visualization.ptr())->set_samplerate(loaded_file_samplerate()); + }; + + manager().on_load_sample_buffer = [&](Audio::Buffer&) { + //TODO: Implement an equalizer + return; }; - this->manager().on_load_sample_buffer = [&](Audio::Buffer& buffer) { - if (volume() == 1.) + manager().on_finished_playing = [&] { + m_play_button->set_icon(*m_play_icon); + if (looping()) { + open_file(loaded_filename()); return; - auto sample_count = buffer.sample_count(); - if (sample_count % 4 == 0) { - const int total_iter = sample_count / (sizeof(AK::SIMD::f64x4) / sizeof(double) / 2); - AK::SIMD::f64x4* sample_ptr = const_cast<AK::SIMD::f64x4*>(reinterpret_cast<const AK::SIMD::f64x4*>((buffer.data()))); - for (int i = 0; i < total_iter; ++i) { - sample_ptr[i] = sample_ptr[i] * volume(); - } - } else { - const int total_iter = sample_count / (sizeof(AK::SIMD::f64x2) / sizeof(double) / 2); - AK::SIMD::f64x2* sample_ptr = const_cast<AK::SIMD::f64x2*>(reinterpret_cast<const AK::SIMD::f64x2*>((buffer.data()))); - for (int i = 0; i < total_iter; ++i) { - sample_ptr[i] = sample_ptr[i] * volume(); - } + } + + if (!m_playlist_model.is_null() && !m_playlist_model->items().is_empty()) { + auto it = m_playlist_model->items().find_if([&](const M3UEntry& e) { return e.path == loaded_filename(); }); + if (it.index() + 1 == m_playlist_model->items().size()) { + if (looping_playlist()) { + open_file(m_playlist_model->items().at(0).path); + return; + } + } else + open_file((it + 1)->path); } }; } void SoundPlayerWidgetAdvancedView::open_file(StringView path) { + if (!Core::File::exists(path)) { + GUI::MessageBox::show(window(), String::formatted("File \"{}\" does not exist", path), "Error opening file", GUI::MessageBox::Type::Error); + return; + } + + if (path.ends_with(".m3u", AK::CaseSensitivity::CaseInsensitive) || path.ends_with(".m3u8", AK::CaseSensitivity::CaseInsensitive)) { + read_playlist(path); + return; + } + NonnullRefPtr<Audio::Loader> loader = Audio::Loader::create(path); if (loader->has_error() || !loader->sample_rate()) { const String error_string = loader->error_string(); @@ -208,3 +248,76 @@ void SoundPlayerWidgetAdvancedView::play() set_paused(false); set_stopped(false); } + +void SoundPlayerWidgetAdvancedView::read_playlist(StringView path) +{ + auto parser = M3UParser::from_file(path); + auto items = parser->parse(true); + VERIFY(items->size() > 0); + try_fill_missing_info(*items, path); + for (auto& item : *items) + m_playlist_model->items().append(item); + set_playlist_visible(true); + m_playlist_model->update(); + + open_file(items->at(0).path); + + if (items->size() > 1) { + m_back_button->set_enabled(true); + m_next_button->set_enabled(true); + } else { + m_back_button->set_enabled(false); + m_next_button->set_enabled(false); + } +} + +void SoundPlayerWidgetAdvancedView::set_playlist_visible(bool visible) +{ + if (visible) { + m_playlist_widget = m_player_view->parent_widget()->add<PlaylistWidget>(); + m_playlist_widget->set_data_model(m_playlist_model); + m_playlist_widget->set_fixed_width(150); + } else { + m_playlist_widget->remove_from_parent(); + m_player_view->set_max_width(window()->width()); + } +} + +void SoundPlayerWidgetAdvancedView::try_fill_missing_info(Vector<M3UEntry>& entries, StringView playlist_p) +{ + LexicalPath playlist_path(playlist_p); + Vector<M3UEntry*> to_delete; + for (auto& entry : entries) { + LexicalPath entry_path(entry.path); + if (!entry_path.is_absolute()) { + entry.path = String::formatted("{}/{}", playlist_path.dirname(), entry_path.basename()); + } + + if (!Core::File::exists(entry.path)) { + GUI::MessageBox::show(window(), String::formatted("The file \"{}\" present in the playlist does not exist or was not found. This file will be ignored.", entry_path.basename()), "Error reading playlist", GUI::MessageBox::Type::Warning); + to_delete.append(&entry); + continue; + } + + if (!entry.extended_info->track_display_title.has_value()) + entry.extended_info->track_display_title = LexicalPath(entry.path).title(); + if (!entry.extended_info->track_length_in_seconds.has_value()) { + if (entry_path.has_extension("wav")) { + auto wav_reader = Audio::Loader::create(entry.path); + entry.extended_info->track_length_in_seconds = wav_reader->total_samples() / wav_reader->sample_rate(); + } + //TODO: Implement embedded metadata extractor for other audio formats + } + //TODO: Implement a metadata parser for the uncomfortably numerous popular embedded metadata formats + + if (!entry.extended_info->file_size_in_bytes.has_value()) { + FILE* f = fopen(entry.path.characters(), "r"); + VERIFY(f != nullptr); + fseek(f, 0, SEEK_END); + entry.extended_info->file_size_in_bytes = ftell(f); + fclose(f); + } + } + for (M3UEntry* entry : to_delete) + entries.remove_first_matching([&](M3UEntry& e) { return &e == entry; }); +} |