mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 07:02:44 +00:00 
			
		
		
		
	SoundPlayer: Start working on a GUI sound player application
This can play anything that AWavLoader can load (so obviously only WAV files at the moment.) It works by having a timer that wakes up every 100ms and tries to send a sample buffer to the AudioServer. If our server-side queue is full then we wait until the next timer iteration and try again. We display the most recently enqueued sample buffer in a nice little widget that just plots the samples in green-on-black. :^)
This commit is contained in:
		
							parent
							
								
									6693e56603
								
							
						
					
					
						commit
						1188a036e9
					
				
					 6 changed files with 148 additions and 0 deletions
				
			
		
							
								
								
									
										9
									
								
								Applications/SoundPlayer/Makefile
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								Applications/SoundPlayer/Makefile
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | |||
| include ../../Makefile.common | ||||
| 
 | ||||
| OBJS = \
 | ||||
|     SampleWidget.o \
 | ||||
|     main.o | ||||
| 
 | ||||
| APP = SoundPlayer | ||||
| 
 | ||||
| include ../Makefile.common | ||||
							
								
								
									
										45
									
								
								Applications/SoundPlayer/SampleWidget.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								Applications/SoundPlayer/SampleWidget.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,45 @@ | |||
| #include "SampleWidget.h" | ||||
| #include <LibAudio/ABuffer.h> | ||||
| #include <LibGUI/GPainter.h> | ||||
| 
 | ||||
| SampleWidget::SampleWidget(GWidget* parent) | ||||
|     : GFrame(parent) | ||||
| { | ||||
|     set_frame_shape(FrameShape::Container); | ||||
|     set_frame_shadow(FrameShadow::Sunken); | ||||
|     set_frame_thickness(2); | ||||
| } | ||||
| 
 | ||||
| SampleWidget::~SampleWidget() | ||||
| { | ||||
| } | ||||
| 
 | ||||
| void SampleWidget::paint_event(GPaintEvent& event) | ||||
| { | ||||
|     GFrame::paint_event(event); | ||||
|     GPainter painter(*this); | ||||
|     painter.add_clip_rect(event.rect()); | ||||
| 
 | ||||
|     painter.fill_rect(frame_inner_rect(), Color::Black); | ||||
| 
 | ||||
|     if (!m_buffer) | ||||
|         return; | ||||
| 
 | ||||
|     // FIXME: Right now we only display as many samples from the buffer as we can fit
 | ||||
|     //        in the frame_inner_rect(). Maybe scale the samples or something?
 | ||||
|     int samples_to_draw = min(m_buffer->sample_count(), frame_inner_rect().width()); | ||||
|     for (int x = 0; x < samples_to_draw; ++x) { | ||||
|         // FIXME: This might look nicer if drawn as lines.
 | ||||
|         auto& sample = m_buffer->samples()[x]; | ||||
|         Point p = { x, frame_inner_rect().center().y() + (int)(sample.left * frame_inner_rect().height()) }; | ||||
|         painter.set_pixel(p, Color::Green); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void SampleWidget::set_buffer(ABuffer* buffer) | ||||
| { | ||||
|     if (m_buffer == buffer) | ||||
|         return; | ||||
|     m_buffer = buffer; | ||||
|     update(); | ||||
| } | ||||
							
								
								
									
										19
									
								
								Applications/SoundPlayer/SampleWidget.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								Applications/SoundPlayer/SampleWidget.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,19 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <LibGUI/GFrame.h> | ||||
| 
 | ||||
| class ABuffer; | ||||
| 
 | ||||
| class SampleWidget final : public GFrame { | ||||
|     C_OBJECT(SampleWidget) | ||||
| public: | ||||
|     explicit SampleWidget(GWidget* parent); | ||||
|     virtual ~SampleWidget() override; | ||||
| 
 | ||||
|     void set_buffer(ABuffer*); | ||||
| 
 | ||||
| private: | ||||
|     virtual void paint_event(GPaintEvent&) override; | ||||
| 
 | ||||
|     RefPtr<ABuffer> m_buffer; | ||||
| }; | ||||
							
								
								
									
										72
									
								
								Applications/SoundPlayer/main.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								Applications/SoundPlayer/main.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,72 @@ | |||
| #include "SampleWidget.h" | ||||
| #include <LibAudio/ABuffer.h> | ||||
| #include <LibAudio/AClientConnection.h> | ||||
| #include <LibAudio/AWavLoader.h> | ||||
| #include <LibCore/CTimer.h> | ||||
| #include <LibGUI/GApplication.h> | ||||
| #include <LibGUI/GBoxLayout.h> | ||||
| #include <LibGUI/GButton.h> | ||||
| #include <LibGUI/GWidget.h> | ||||
| #include <LibGUI/GWindow.h> | ||||
| #include <stdio.h> | ||||
| 
 | ||||
| int main(int argc, char** argv) | ||||
| { | ||||
|     if (argc != 2) { | ||||
|         printf("usage: %s <wav-file>\n", argv[0]); | ||||
|         return 0; | ||||
|     } | ||||
| 
 | ||||
|     GApplication app(argc, argv); | ||||
| 
 | ||||
|     String path = argv[1]; | ||||
|     AWavLoader loader(path); | ||||
| 
 | ||||
|     if (loader.has_error()) { | ||||
|         fprintf(stderr, "Failed to load WAV file: %s (%s)\n", path.characters(), loader.error_string()); | ||||
|         return 1; | ||||
|     } | ||||
| 
 | ||||
|     AClientConnection audio_client; | ||||
|     audio_client.handshake(); | ||||
| 
 | ||||
|     auto* window = new GWindow; | ||||
|     window->set_title("SoundPlayer"); | ||||
|     window->set_rect(300, 300, 300, 200); | ||||
| 
 | ||||
|     auto* widget = new GWidget; | ||||
|     window->set_main_widget(widget); | ||||
| 
 | ||||
|     widget->set_fill_with_background_color(true); | ||||
|     widget->set_layout(make<GBoxLayout>(Orientation::Vertical)); | ||||
|     widget->layout()->set_margins({ 2, 2, 2, 2 }); | ||||
| 
 | ||||
|     auto* sample_widget = new SampleWidget(widget); | ||||
| 
 | ||||
|     auto* button = new GButton("Quit", widget); | ||||
|     button->set_size_policy(SizePolicy::Fill, SizePolicy::Fixed); | ||||
|     button->set_preferred_size(0, 20); | ||||
|     button->on_click = [&](auto&) { | ||||
|         app.quit(); | ||||
|     }; | ||||
| 
 | ||||
|     auto next_sample_buffer = loader.get_more_samples(); | ||||
| 
 | ||||
|     new CTimer(100, [&] { | ||||
|         if (!next_sample_buffer) { | ||||
|             sample_widget->set_buffer(nullptr); | ||||
|             return; | ||||
|         } | ||||
|         bool enqueued = audio_client.try_enqueue(*next_sample_buffer); | ||||
|         if (!enqueued) | ||||
|             return; | ||||
|         sample_widget->set_buffer(next_sample_buffer); | ||||
|         next_sample_buffer = loader.get_more_samples(16 * KB); | ||||
|         if (!next_sample_buffer) { | ||||
|             dbg() << "Exhausted samples :^)"; | ||||
|         } | ||||
|     }); | ||||
| 
 | ||||
|     window->show(); | ||||
|     return app.exec(); | ||||
| } | ||||
|  | @ -85,6 +85,7 @@ cp ../Applications/Piano/Piano mnt/bin/Piano | |||
| cp ../Applications/SystemDialog/SystemDialog mnt/bin/SystemDialog | ||||
| cp ../Applications/ChanViewer/ChanViewer mnt/bin/ChanViewer | ||||
| cp ../Applications/Calculator/Calculator mnt/bin/Calculator | ||||
| cp ../Applications/SoundPlayer/SoundPlayer mnt/bin/SoundPlayer | ||||
| cp ../Demos/HelloWorld/HelloWorld mnt/bin/HelloWorld | ||||
| cp ../Demos/HelloWorld2/HelloWorld2 mnt/bin/HelloWorld2 | ||||
| cp ../Demos/RetroFetch/RetroFetch mnt/bin/RetroFetch | ||||
|  | @ -123,6 +124,7 @@ ln -s SystemDialog mnt/bin/sd | |||
| ln -s ChanViewer mnt/bin/cv | ||||
| ln -s Calculator mnt/bin/calc | ||||
| ln -s Inspector mnt/bin/ins | ||||
| ln -s SoundPlayer mnt/bin/sp | ||||
| echo "done" | ||||
| 
 | ||||
| # Run local sync script, if it exists | ||||
|  |  | |||
|  | @ -55,6 +55,7 @@ build_targets="$build_targets ../Applications/SystemMonitor" | |||
| build_targets="$build_targets ../Applications/Taskbar" | ||||
| build_targets="$build_targets ../Applications/Terminal" | ||||
| build_targets="$build_targets ../Applications/TextEditor" | ||||
| build_targets="$build_targets ../Applications/SoundPlayer" | ||||
| 
 | ||||
| build_targets="$build_targets ../Demos/Fire" | ||||
| build_targets="$build_targets ../Demos/HelloWorld" | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Andreas Kling
						Andreas Kling