/* * Copyright (c) 2021, Cesar Torres * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "SoundPlayerWidgetAdvancedView.h" #include "BarsVisualizationWidget.h" #include "PlaybackManager.h" #include "SoundPlayerWidget.h" #include #include #include #include #include #include #include #include #include #include #include #include SoundPlayerWidgetAdvancedView::SoundPlayerWidgetAdvancedView(GUI::Window& window, Audio::ClientConnection& connection, PlaybackManager& manager) : m_window(window) , m_connection(connection) , m_manager(manager) { window.resize(455, 350); window.set_minimum_size(440, 130); window.set_resizable(true); set_fill_with_background_color(true); set_layout(); 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"); m_stop_icon = Gfx::Bitmap::load_from_file("/res/icons/16x16/stop.png"); 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(); m_playback_progress_slider = add(Orientation::Horizontal); m_playback_progress_slider->set_fixed_height(20); m_playback_progress_slider->set_min(0); m_playback_progress_slider->set_max(m_manager.total_length() * 44100); //this value should be set when we load a new file m_playback_progress_slider->on_knob_released = [&](int value) { m_manager.seek(value); }; auto& toolbar_container = add(); auto& menubar = toolbar_container.add(); m_play_button = menubar.add(); m_play_button->set_icon(*m_play_icon); m_play_button->set_fixed_width(50); m_play_button->on_click = [&](unsigned) { bool paused = m_manager.toggle_pause(); m_play_button->set_icon(paused ? *m_play_icon : *m_pause_icon); m_stop_button->set_enabled(!paused); }; m_stop_button = menubar.add(); m_stop_button->set_icon(*m_stop_icon); m_stop_button->set_fixed_width(50); m_stop_button->on_click = [&](unsigned) { m_manager.stop(); m_play_button->set_icon(*m_play_icon); m_stop_button->set_enabled(false); }; auto& timestamp_label = menubar.add(); timestamp_label.set_fixed_width(110); timestamp_label.set_text("Elapsed: 00:00:00"); // filler_label menubar.add(); auto& back_button = menubar.add(); back_button.set_fixed_width(50); back_button.set_icon(*m_back_icon); auto& next_button = menubar.add(); next_button.set_fixed_width(50); next_button.set_icon(*m_next_icon); m_volume_label = &menubar.add(); m_volume_label->set_fixed_width(30); m_volume_label->set_text("100%"); auto& volume_slider = menubar.add(); volume_slider.set_fixed_width(95); volume_slider.set_min(0); volume_slider.set_max(150); volume_slider.set_value(100); volume_slider.on_change = [&](int value) { double volume = m_nonlinear_volume_slider ? (double)(value * value) / (100 * 100) : value / 100.; m_volume_label->set_text(String::formatted("{}%", (int)(volume * 100))); set_volume(volume); }; set_volume(1.); set_nonlinear_volume_slider(false); m_manager.on_update = [&]() { //TODO: make this program support other sample rates int samples_played = m_connection.get_played_samples() + m_manager.last_seek(); int current_second = samples_played / 44100; timestamp_label.set_text(String::formatted("Elapsed: {:02}:{:02}:{:02}", current_second / 3600, current_second / 60, current_second % 60)); m_playback_progress_slider->set_value(samples_played); dynamic_cast(m_visualization.ptr())->set_buffer(m_manager.current_buffer()); }; m_manager.on_load_sample_buffer = [&](Audio::Buffer& buffer) { if (m_volume == 1.) 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(reinterpret_cast((buffer.data()))); for (int i = 0; i < total_iter; ++i) { sample_ptr[i] = sample_ptr[i] * m_volume; } } else { const int total_iter = sample_count / (sizeof(AK::SIMD::f64x2) / sizeof(double) / 2); AK::SIMD::f64x2* sample_ptr = const_cast(reinterpret_cast((buffer.data()))); for (int i = 0; i < total_iter; ++i) { sample_ptr[i] = sample_ptr[i] * m_volume; } } }; } void SoundPlayerWidgetAdvancedView::set_volume(double value) { m_volume = value; } void SoundPlayerWidgetAdvancedView::open_file(StringView path) { NonnullRefPtr loader = Audio::Loader::create(path); if (loader->has_error() || !loader->sample_rate()) { const String error_string = loader->error_string(); GUI::MessageBox::show(&m_window, String::formatted("Failed to load audio file: {} ({})", path, error_string.is_null() ? "Unknown error" : error_string), "Filetype error", GUI::MessageBox::Type::Error); return; } m_window.set_title(String::formatted("{} - SoundPlayer", loader->file()->filename())); m_playback_progress_slider->set_max(loader->total_samples()); m_manager.set_loader(move(loader)); } void SoundPlayerWidgetAdvancedView::set_nonlinear_volume_slider(bool nonlinear) { m_nonlinear_volume_slider = nonlinear; } void SoundPlayerWidgetAdvancedView::drop_event(GUI::DropEvent& event) { event.accept(); window()->move_to_front(); if (event.mime_data().has_urls()) { auto urls = event.mime_data().urls(); if (urls.is_empty()) return; open_file(urls.first().path()); } } SoundPlayerWidgetAdvancedView::~SoundPlayerWidgetAdvancedView() { m_manager.on_load_sample_buffer = nullptr; }