Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 726 lines (517 sloc) 22.128 kB
59c26cb @kthakore Out line for music and sound added
kthakore authored
1 =head0 Sound and Music
18bc16f @kthakore Initial Commit
kthakore authored
2
f54caeb @garu fixing small typo
garu authored
3 Sound and Music in SDL are handled by the C<Audio> and C<SDL_Mixer>
4 components. Enabling C<Audio> devices is provided with the Core SDL
5 Library and only supports wav files. C<SDL_Mixer> supports more audio
6 file formats and has additional features that we need for sound in Game
7 Development.
2896dc1 @kthakore Intro to music and sound chapter
kthakore authored
8
9 Similarly to video in SDL, there are several way for perl developers to access the Sound components of SDL. For the plain C<Audio> component the C<SDL::Audio> and related modules are available. C<SDL_Mixer> is supported with th C<SDL::Mixer> module. There is currently a C<SDLx::Sound> module in the work, but not completed at the time of writing this manual. For that reason this chapter will use C<SDL::Audio> and C<SDL::Mixer>.
10
25a17d9 @kthakore Editing the chapter HEADdings
kthakore authored
11 =head1 Simple Sound Script
c3f956a @kthakore Added '+' back in. Please explain this to the readers
kthakore authored
12
fffeb8f @kthakore Using sound
kthakore authored
13 To begin using sound we must enable and open an audiospec:
18bc16f @kthakore Initial Commit
kthakore authored
14
5f85f83 @kthakore Tabs were messing up code listings
kthakore authored
15 use strict;
16 use warnings;
17 use SDL;
18 use Carp;
19 use SDL::Audio;
20 use SDL::Mixer;
21
22 SDL::init(SDL_INIT_AUDIO);
23
24 unless( SDL::Mixer::open_audio( 44100, AUDIO_S16SYS, 2, 4096 ) == 0 )
25 {
26 Carp::croak "Cannot open audio: ".SDL::get_error();
27 }
28
7c377ad @kthakore Fixed typo
kthakore authored
29
30 C<open_audio> will open an audio device with frequency at 44100 Mhz, audio format AUDIO_S16SYS (Note: This is currently the most portable format, however there are others), 2 channels and a chunk size of 4096. Fiddle with these values if you are comfortable with sound terminology and techniques.
31
25a17d9 @kthakore Editing the chapter HEADdings
kthakore authored
32 =head2 Loading Samples
18bc16f @kthakore Initial Commit
kthakore authored
33
cbe578b @kthakore Finished playing samples
kthakore authored
34 Next we will load sound samples that generally used for sound effects and the like. Currently C<SDL_Mixer> reserves samples for C<.WAV>, C<.AIFF>, C<.RIFF> C<.OGG>, and C<.VOC> formats.
35
b7fa6f4 @kthakore Clarifying the number of channels needed and why.
kthakore authored
36 Samples run on one of the 2 channels that we opened up, while the other channel will be reserved for multiple plays of the sample. To load samples we will be doing the following:
cbe578b @kthakore Finished playing samples
kthakore authored
37
5f85f83 @kthakore Tabs were messing up code listings
kthakore authored
38 +use SDL::Mixer::Samples;
cbe578b @kthakore Finished playing samples
kthakore authored
39
4c3c6cf @pip B4ACw2e: Updated all URLs to be minimal CamelCase && wrapped in U<> P…
pip authored
40 +#Brillant Lazer Sound from U<HTTP://FreeSound.Org/samplesViewSingle.php?id=30935>
5f85f83 @kthakore Tabs were messing up code listings
kthakore authored
41 +my $sample = SDL::Mixer::Samples::load_WAV('data/sample.wav');
cbe578b @kthakore Finished playing samples
kthakore authored
42
5f85f83 @kthakore Tabs were messing up code listings
kthakore authored
43 +unless($sample)
44 +{
45 + Carp::croak "Cannot load file data/sample.wav: ".SDL::get_error();
46 +}
cbe578b @kthakore Finished playing samples
kthakore authored
47
25a17d9 @kthakore Editing the chapter HEADdings
kthakore authored
48 =head2 Playing the sample and closing audio
cbe578b @kthakore Finished playing samples
kthakore authored
49
50 Now we can play that sample on any open channel looping forever:
51
5f85f83 @kthakore Tabs were messing up code listings
kthakore authored
52 use SDL::Mixer::Samples;
53 +use SDL::Mixer::Channels;
54
55 my $sample = SDL::Mixer::Samples::load_WAV('data/sample.wav');
56 unless( $sample)
57 {
58 Carp::croak "Cannot load file data/sample.wav: ".SDL::get_error();
59 }
cbe578b @kthakore Finished playing samples
kthakore authored
60
5f85f83 @kthakore Tabs were messing up code listings
kthakore authored
61 +my $playing_channel = SDL::Mixer::Channels::play_channel( -1, $sample, 0 );
cbe578b @kthakore Finished playing samples
kthakore authored
62
66f2638 @kthakore Added how to control the volume
kthakore authored
63 C<play_channel> allows us to assign a sample to the channel C<-1> which indicates any open channel. C<0> indicates we want to play the sample only once.
cbe578b @kthakore Finished playing samples
kthakore authored
64
65 Note that since the sound will be playing in an external process we will need to keep the perl script running. In a game this is no problem but for a single script like this we can just use a simple C<sleep> function. Once we are done we can go ahead and close the audio device.
59c26cb @kthakore Out line for music and sound added
kthakore authored
66
5f85f83 @kthakore Tabs were messing up code listings
kthakore authored
67 +sleep(1);
68 +SDL::Mixer::close_audio();
59c26cb @kthakore Out line for music and sound added
kthakore authored
69
25a17d9 @kthakore Editing the chapter HEADdings
kthakore authored
70 =head2 Streaming Music
59c26cb @kthakore Out line for music and sound added
kthakore authored
71
95dfad3 @kthakore Using a lazer sound sample. Also finished simple music playback
kthakore authored
72 Next we will use C<SDL::Mixer::Music> to add a background music to our script here.
73
5f85f83 @kthakore Tabs were messing up code listings
kthakore authored
74 use SDL::Mixer::Channels;
95dfad3 @kthakore Using a lazer sound sample. Also finished simple music playback
kthakore authored
75 +use SDL::Mixer::Music;
76
4c3c6cf @pip B4ACw2e: Updated all URLs to be minimal CamelCase && wrapped in U<> P…
pip authored
77 +#Load our awesome music from U<HTTP://8BitCollective.Com>
cb33cd1 @kthakore Firing Laser by pressing space :D
kthakore authored
78 +my $background_music =
79 + SDL::Mixer::Music::load_MUS('data/music/01-PC-Speaker-Sorrow.ogg');
95dfad3 @kthakore Using a lazer sound sample. Also finished simple music playback
kthakore authored
80
81
cb33cd1 @kthakore Firing Laser by pressing space :D
kthakore authored
82 +unless( $background_music )
83 +{
84 + Carp::croak "Cannot load music file data/music/01-PC-Speaker-Sorrow.ogg: ".SDL::get_error() ;
85 +}
95dfad3 @kthakore Using a lazer sound sample. Also finished simple music playback
kthakore authored
86
ea6ea32 @kthakore brush_color is global var. bobross++
kthakore authored
87 Music types in C<SDL::Mixer> run in a separate channel from our samples which allows us to have sound effects (like jump, or lasers etc) to play at the same time.
95dfad3 @kthakore Using a lazer sound sample. Also finished simple music playback
kthakore authored
88
5f85f83 @kthakore Tabs were messing up code listings
kthakore authored
89 +SDL::Mixer::Music::play_music($background_music,0);
95dfad3 @kthakore Using a lazer sound sample. Also finished simple music playback
kthakore authored
90
66f2638 @kthakore Added how to control the volume
kthakore authored
91 C<play_music> also takes a parameter for how many loops you would like to play the song for, where 0 is 1.
92
95dfad3 @kthakore Using a lazer sound sample. Also finished simple music playback
kthakore authored
93 To stop the music we can call C<halt_music>.
94
5f85f83 @kthakore Tabs were messing up code listings
kthakore authored
95 sleep(2);
96 +SDL::Mixer::Music::halt_music();
97 SDL::Mixer::close_audio();
95dfad3 @kthakore Using a lazer sound sample. Also finished simple music playback
kthakore authored
98
25a17d9 @kthakore Editing the chapter HEADdings
kthakore authored
99 =begin sidebar
59c26cb @kthakore Out line for music and sound added
kthakore authored
100
66f2638 @kthakore Added how to control the volume
kthakore authored
101 Controlling Volume can be as simple as:
5f85f83 @kthakore Tabs were messing up code listings
kthakore authored
102
103 #All channels indicated by the -1
104 SDL::Mixer::Channels::volume(-1,10);
66f2638 @kthakore Added how to control the volume
kthakore authored
105
5f85f83 @kthakore Tabs were messing up code listings
kthakore authored
106 #Specifically for the Music
107 SDL::Mixer::Music::volume_music( 10 );
66f2638 @kthakore Added how to control the volume
kthakore authored
108
109 Volumes can be set at anytime and range from C<1-100>.
110
25a17d9 @kthakore Editing the chapter HEADdings
kthakore authored
111 =end sidebar
112
113 =head2 Code so far
114
115 =begin programlisting
116
f112219 @kthakore Applied fixed it
kthakore authored
117 use strict;
118 use warnings;
119 use SDL;
120 use Carp;
121 use SDL::Audio;
122 use SDL::Mixer;
123 use SDL::Mixer::Samples;
124 use SDL::Mixer::Channels;
125 use SDL::Mixer::Music;
126 SDL::init(SDL_INIT_AUDIO);
a7fe3b2 @kthakore Added the final code for the script 1
kthakore authored
127
f112219 @kthakore Applied fixed it
kthakore authored
128 unless( SDL::Mixer::open_audio( 44100, AUDIO_S16SYS, 2, 4096 ) == 0 )
129 {
130 Carp::croak "Cannot open audio: ".SDL::get_error();
131 }
a7fe3b2 @kthakore Added the final code for the script 1
kthakore authored
132
133
f112219 @kthakore Applied fixed it
kthakore authored
134 my $sample = SDL::Mixer::Samples::load_WAV('data/sample.wav');
a7fe3b2 @kthakore Added the final code for the script 1
kthakore authored
135
f112219 @kthakore Applied fixed it
kthakore authored
136 unless( $sample)
137 {
138 Carp::croak "Cannot load file data/sample.wav: ".SDL::get_error();
139 }
a7fe3b2 @kthakore Added the final code for the script 1
kthakore authored
140
f112219 @kthakore Applied fixed it
kthakore authored
141 my $playing_channel = SDL::Mixer::Channels::play_channel( -1, $sample, 0 );
a7fe3b2 @kthakore Added the final code for the script 1
kthakore authored
142
4c3c6cf @pip B4ACw2e: Updated all URLs to be minimal CamelCase && wrapped in U<> P…
pip authored
143 #Load our awesome music from U<HTTP://8BitCollective.Com>
f112219 @kthakore Applied fixed it
kthakore authored
144 my $background_music = SDL::Mixer::Music::load_MUS('data/music/01-PC-Speaker-Sorrow.ogg');
a7fe3b2 @kthakore Added the final code for the script 1
kthakore authored
145
f112219 @kthakore Applied fixed it
kthakore authored
146 unless( $background_music )
147 {
148 Carp::croak "Cannot load music file data/music/01-PC-Speaker-Sorrow.ogg: "
149 .SDL::get_error();
150 }
a7fe3b2 @kthakore Added the final code for the script 1
kthakore authored
151
f112219 @kthakore Applied fixed it
kthakore authored
152 SDL::Mixer::Music::play_music( $background_music,0 );
a7fe3b2 @kthakore Added the final code for the script 1
kthakore authored
153
f112219 @kthakore Applied fixed it
kthakore authored
154 sleep(2);
a7fe3b2 @kthakore Added the final code for the script 1
kthakore authored
155
f112219 @kthakore Applied fixed it
kthakore authored
156 SDL::Mixer::Music::halt_music();
157 SDL::Mixer::close_audio;
25a17d9 @kthakore Editing the chapter HEADdings
kthakore authored
158
159 =end programlisting
160
161
162 =head1 Sound Applications
163
164 Now that we know how to prepare and play simple sounds we will apply it to an C<SDLx::App>.
165
1c38b70 @kthakore Finished the 2nd example of the music stuff
kthakore authored
166 =head2 SDLx::App Audio Initialization
167
168 C<SDLx::App> will initialize everything normally for us. However for a stream line application it is recommend to initialize only the things we need. In this case that is C<SDL_INIT_VIDEO> and C<SDL_INIT_AUDIO>.
169
170 use strict;
171 use warnings;
172 use SDL;
173 use Carp;
174 use SDLx::App;
175 use SDL::Audio;
176 use SDL::Mixer;
177 use SDL::Event;
178 use SDL::Events;
179 use SDL::Mixer::Music;
180 use SDL::Mixer::Samples;
181 use SDL::Mixer::Channels;
182
183 my $app = SDLx::App->new(
184 init => SDL_INIT_AUDIO | SDL_INIT_VIDEO,
185 width => 250,
186 height => 75,
187 title => "Sound Event Demo",
188 eoq => 1
189
190 );
191
192 =head2 Loading Resources
193
194 It is highly recommended to perform all resource allocations before a C<SDLx::App::run()> method is called.
195
196
197 # Initialize the Audio
198 unless ( SDL::Mixer::open_audio( 44100, AUDIO_S16SYS, 2, 4096 ) == 0 ) {
199 Carp::croak "Cannot open audio: " . SDL::get_error();
200 }
201
202 #Something to show while we play music and sounds
203 my $channel_volume = 100;
204 my $music_volume = 100;
205 my $laser_status = 'none';
206 my $music_status = 'not playing';
207
208 # Load our sound resources
209 my $laser = SDL::Mixer::Samples::load_WAV('data/sample.wav');
210 unless ($laser) {
211 Carp::croak "Cannot load sound: " . SDL::get_error();
212 }
213
214 my $background_music =
215 SDL::Mixer::Music::load_MUS('data/music/01-PC-Speaker-Sorrow.ogg');
216 unless ($background_music) {
217 Carp::croak "Cannot load music: " . SDL::get_error();
218 }
219
220
221 =head2 The Show Handler
222
223 For the purposes of describing the current state of the music lets draw text to the screen in a C<show_handler>.
224
225 $app->add_show_handler(
226 sub {
227
228 $app->draw_rect([0,0,$app->w,$app->h], 0 );
229
230 $app->draw_gfx_text( [10,10], [255,0,0,255], "Channel Volume : $channel_volume" );
231 $app->draw_gfx_text( [10,25], [255,0,0,255], "Music Volume : $music_volume" );
232 $app->draw_gfx_text( [10,40], [255,0,0,255], "Laser Status : $laser_status" );
233 $app->draw_gfx_text( [10,55], [255,0,0,255], "Music Status : $music_status" );
234
235 $app->update();
236
237 }
238 );
239
240 This will draw the channel volume of our samples, and the volume of the music. It will also print the status of our two sounds in the application.
241
242 =head2 The Event Handler
243
244 Finally our event handler will do the actual leg work and trigger the music and sound as we need it.
245
246 $app->add_event_handler(
247 sub {
248 my $event = shift;
249
250 if ( $event->type == SDL_KEYDOWN ) {
251 my $keysym = $event->key_sym;
252 my $keyname = SDL::Events::get_key_name($keysym);
253
254 if ( $keyname eq 'space' ) {
255
256 $laser_status = 'PEW!';
257 #fire lasers!
258 SDL::Mixer::Channels::play_channel( -1, $laser, 0 );
259
260 }
261 elsif ( $keyname eq 'up' ) {
262 $channel_volume += 5 unless $channel_volume == 100;
263 }
264 elsif ( $keyname eq 'down' ) {
265 $channel_volume -= 5 unless $channel_volume == 0;
266 }
267 elsif ( $keyname eq 'right' ) {
268 $music_volume += 5 unless $music_volume == 100;
269 }
270 elsif ( $keyname eq 'left' ) {
271 $music_volume -= 5 unless $music_volume == 0;
272 }
273 elsif ( $keyname eq 'return' ) {
274 my $playing = SDL::Mixer::Music::playing_music();
275 my $paused = SDL::Mixer::Music::paused_music();
276
277 if ( $playing == 0 && $paused == 0 ) {
278 SDL::Mixer::Music::play_music( $background_music, 1 );
279 $music_status = 'playing';
280 }
281 elsif ( $playing && !$paused ) {
282 SDL::Mixer::Music::pause_music();
283 $music_status = 'paused'
284 }
285 elsif ( $playing && $paused ) {
286 SDL::Mixer::Music::resume_music();
287 $music_status = 'resumed playing';
288 }
289
290 }
291
292 SDL::Mixer::Channels::volume( -1, $channel_volume );
293 SDL::Mixer::Music::volume_music($music_volume);
294
295 }
296
297 }
298
299 );
300
301 The above event handler fires the laser on pressing the 'Space' key. Go ahead and press it multiple times as if you are firing a gun in a game! You will notice that depending on how fast you fire the laser the application will still manage to overlap the sounds as needed. The sample overlapping is accomplished by requiring multiple channels in the C<open_audio> call. If your game has lots of samples that may play at the same time you may need more channels allocated.
302 Additionally you can see that the volume control is easily managed both on the channels and the music with just incrementing or decrementing a value and calling the appropriate function.
303
304 Finally it is worth noticing the various state the background music can be in.
305
306 Lets run this application and the make sure to clean up the audio on the way out.
307 $app->run();
308 SDL::Mixer::Music::halt_music();
309 SDL::Mixer::close_audio;
310
311
312 =head2 Completed Code
313
314 =begin programlisting
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
315
1c38b70 @kthakore Finished the 2nd example of the music stuff
kthakore authored
316 use strict;
317 use warnings;
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
318
319 use Cwd;
1c38b70 @kthakore Finished the 2nd example of the music stuff
kthakore authored
320 use Carp;
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
321 use File::Spec;
322
323 use threads;
324 use threads::shared;
325
326 use SDL;
1c38b70 @kthakore Finished the 2nd example of the music stuff
kthakore authored
327 use SDL::Event;
328 use SDL::Events;
329
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
330 use SDL::Audio;
331 use SDL::Mixer;
332 use SDL::Mixer::Music;
333 use SDL::Mixer::Effects;
1c38b70 @kthakore Finished the 2nd example of the music stuff
kthakore authored
334
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
335 use SDLx::App;
1c38b70 @kthakore Finished the 2nd example of the music stuff
kthakore authored
336 my $app = SDLx::App->new(
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
337 init => SDL_INIT_AUDIO | SDL_INIT_VIDEO,
338 width => 800,
339 height => 600,
340 depth => 32,
341 title => "Music Visualizer",
342 eoq => 1,
343 dt => 0.2,
1c38b70 @kthakore Finished the 2nd example of the music stuff
kthakore authored
344 );
345
346 # Initialize the Audio
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
347 unless ( SDL::Mixer::open_audio( 44100, AUDIO_S16, 2, 1024 ) == 0 ) {
1c38b70 @kthakore Finished the 2nd example of the music stuff
kthakore authored
348 Carp::croak "Cannot open audio: " . SDL::get_error();
349 }
350
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
351 # Load our music files
352 my $data_dir = '.';
353 my @songs = glob 'data/music/*.ogg';
1c38b70 @kthakore Finished the 2nd example of the music stuff
kthakore authored
354
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
355 my @stream_data : shared;
1c38b70 @kthakore Finished the 2nd example of the music stuff
kthakore authored
356
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
357 # Music Effect to pull Stream Data
358 sub music_data {
359 my ( $channel, $samples, $position, @stream ) = @_;
360
361 {
362 lock(@stream_data);
363 push @stream_data, @stream;
364 }
365
366 return @stream;
1c38b70 @kthakore Finished the 2nd example of the music stuff
kthakore authored
367 }
368
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
369 sub done_music_data { }
1c38b70 @kthakore Finished the 2nd example of the music stuff
kthakore authored
370
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
371 my $music_data_effect_id =
372 SDL::Mixer::Effects::register( MIX_CHANNEL_POST, "main::music_data",
373 "main::done_music_data", 0 );
1c38b70 @kthakore Finished the 2nd example of the music stuff
kthakore authored
374
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
375 # Music Playing Callbacks
376 my $current_song = 0;
377 my $lines = $ARGV[0] || 50;
1c38b70 @kthakore Finished the 2nd example of the music stuff
kthakore authored
378
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
379 my $current_music_callback = sub {
380 my ( $delta, $app ) = @_;
1c38b70 @kthakore Finished the 2nd example of the music stuff
kthakore authored
381
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
382 $app->draw_rect( [ 0, 0, $app->w(), $app->h() ], 0x000000FF );
383 $app->draw_gfx_text(
384 [ 5, $app->h() - 10 ],
385 [ 255, 0, 0, 255 ],
386 "Playing Song: " . $songs[ $current_song - 1 ]
387 );
1c38b70 @kthakore Finished the 2nd example of the music stuff
kthakore authored
388
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
389 my @stream;
390 {
391 lock @stream_data;
392 @stream = @stream_data;
393 @stream_data = ();
394 }
1c38b70 @kthakore Finished the 2nd example of the music stuff
kthakore authored
395
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
396 # To show the right amount of lines we choose a cut of the stream
397 # this is purely for asthetic reasons.
1c38b70 @kthakore Finished the 2nd example of the music stuff
kthakore authored
398
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
399 my $cut = @stream / $lines;
1c38b70 @kthakore Finished the 2nd example of the music stuff
kthakore authored
400
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
401 # The width of each line is calculated to use.
402 my $l_wdt = ( $app->w() / $lines ) / 2;
1c38b70 @kthakore Finished the 2nd example of the music stuff
kthakore authored
403
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
404 for ( my $i = 0 ; $i < $#stream ; $i += $cut ) {
1c38b70 @kthakore Finished the 2nd example of the music stuff
kthakore authored
405
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
406 # In stereo mode the stream is split between two alternating streams
407 my $left = $stream[$i];
408 my $right = $stream[ $i + 1 ];
1c38b70 @kthakore Finished the 2nd example of the music stuff
kthakore authored
409
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
410 # For each bar we calculate a Y point and a X point
411 my $point_y = ( ( ($left) ) * $app->h() / 4 / 32000 ) + ( $app->h / 2 );
412 my $point_y_r =
413 ( ( ($right) ) * $app->h() / 4 / 32000 ) + ( $app->h / 2 );
414 my $point_x = ( $i / @stream ) * $app->w;
1c38b70 @kthakore Finished the 2nd example of the music stuff
kthakore authored
415
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
416 # Using the parameters
417 # Surface, box coordinates and color as RGBA
418 SDL::GFX::Primitives::box_RGBA(
419 $app,
420 $point_x - $l_wdt,
421 $app->h() / 2,
422 $point_x + $l_wdt,
423 $point_y, 40, 0, 255, 128
424 );
425 SDL::GFX::Primitives::box_RGBA(
426 $app,
427 $point_x - $l_wdt,
428 $app->h() / 2,
429 $point_x + $l_wdt,
430 $point_y_r, 255, 0, 40, 128
431 );
1c38b70 @kthakore Finished the 2nd example of the music stuff
kthakore authored
432
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
433 }
1c38b70 @kthakore Finished the 2nd example of the music stuff
kthakore authored
434
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
435 $app->flip();
1c38b70 @kthakore Finished the 2nd example of the music stuff
kthakore authored
436
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
437 };
1c38b70 @kthakore Finished the 2nd example of the music stuff
kthakore authored
438
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
439 my $cms_move_callback_id;
440 my $pns_move_callback_id;
441 my $play_next_song_callback;
442
443 sub music_finished_playing {
444 SDL::Mixer::Music::halt_music();
445
446 $pns_move_callback_id = $app->add_move_handler($play_next_song_callback)
447 if ( defined $play_next_song_callback );
448
449 }
450
451 $play_next_song_callback = sub {
452 return $app->stop() if $current_song >= @songs;
453 my $song = SDL::Mixer::Music::load_MUS( $songs[ $current_song++ ] );
454 SDL::Mixer::Music::play_music( $song, 0 );
455
456 $app->remove_move_handler($pns_move_callback_id)
457 if defined $pns_move_callback_id;
458 };
459
460 $app->add_show_handler($current_music_callback);
461 $pns_move_callback_id = $app->add_move_handler($play_next_song_callback);
462
463 $app->add_move_handler(
464 sub {
465 my $music_playing = SDL::Mixer::Music::playing_music();
466
467 music_finished_playing() unless $music_playing;
468
469 }
470 );
471
472 $app->add_event_handler(
473 sub {
474 my ( $event, $app ) = @_;
475 if ( $event->type == SDL_KEYDOWN && $event->key_sym == SDLK_DOWN ) {
476
477 # Indicate that we are done playing the music_finished_playing
478 music_finished_playing();
479 }
480 }
1c38b70 @kthakore Finished the 2nd example of the music stuff
kthakore authored
481 );
482
483 $app->run();
484
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
485 SDL::Mixer::Effects::unregister( MIX_CHANNEL_POST, $music_data_effect_id );
486 SDL::Mixer::Music::hook_music_finished();
1c38b70 @kthakore Finished the 2nd example of the music stuff
kthakore authored
487 SDL::Mixer::Music::halt_music();
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
488 SDL::Mixer::close_audio();
1c38b70 @kthakore Finished the 2nd example of the music stuff
kthakore authored
489
490 =end programlisting
491
940dcea @kthakore Typo on the label tag
kthakore authored
492 =head1 Music Visualizer
493
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
494 The music visualizer example processes real-time sound data--data as it
495 plays--and displays the wave form on the screen. It will look something like:
940dcea @kthakore Typo on the label tag
kthakore authored
496
497
498 =for figure
499 \includegraphics[width=0.5\textwidth]{../src/images/spectro-1.png}
500 \caption{Simple Music Visualization}
501 \label{fig:Visualization}
502
d55ec08 @kthakore Clean up and bumped the script
kthakore authored
503 =head2 The Code and Comments
940dcea @kthakore Typo on the label tag
kthakore authored
504
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
505 The program begins with the usual boilerplate of an SDL Perl application:
506
437e2c4 @kthakore Organized the code in the chapter a bit
kthakore authored
507 use strict;
508 use warnings;
509
510 use Cwd;
511 use Carp;
512 use File::Spec;
513
514 use threads;
515 use threads::shared;
516
517 use SDL;
518 use SDL::Event;
519 use SDL::Events;
520
521 use SDL::Audio;
522 use SDL::Mixer;
523 use SDL::Mixer::Music;
524 use SDL::Mixer::Effects;
525
526 use SDLx::App;
527
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
528 It then creates an application with both audio and video support:
437e2c4 @kthakore Organized the code in the chapter a bit
kthakore authored
529
530 my $app = SDLx::App->new(
531 init => SDL_INIT_AUDIO | SDL_INIT_VIDEO,
532 width => 800,
533 height => 600,
534 depth => 32,
535 title => "Sound Event Demo",
536 eoq => 1,
537 dt => 0.2,
538 );
539
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
540 The application must initialize the audio system with a format matching the
541 expected audio input. C<AUDIO_S16> provides a 16-bit signed integer array for
542 the stream data:
437e2c4 @kthakore Organized the code in the chapter a bit
kthakore authored
543
544 # Initialize the Audio
545 unless ( SDL::Mixer::open_audio( 44100, AUDIO_S16, 2, 1024 ) == 0 ) {
546 Carp::croak "Cannot open audio: " . SDL::get_error();
547 }
548
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
549 The music player needs the music files from the F<data/music/> directory:
437e2c4 @kthakore Organized the code in the chapter a bit
kthakore authored
550
551 # Load our music files
552 my $data_dir = '.';
553 my @songs = glob 'data/music/*.ogg';
554
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
555 A music effect reads stream data, then serializes it to share between threads:
437e2c4 @kthakore Organized the code in the chapter a bit
kthakore authored
556
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
557 my @stream_data : shared;
437e2c4 @kthakore Organized the code in the chapter a bit
kthakore authored
558
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
559 # Music Effect to pull Stream Data
560 sub music_data {
561 my ( $channel, $samples, $position, @stream ) = @_;
437e2c4 @kthakore Organized the code in the chapter a bit
kthakore authored
562
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
563 {
564 lock(@stream_data);
565 push @stream_data, @stream;
566 }
437e2c4 @kthakore Organized the code in the chapter a bit
kthakore authored
567
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
568 return @stream;
569 }
570
571 sub done_music_data { }
572
573 ... and that effect gets registered as a callback with C<SDL::Mixer::Effects>:
437e2c4 @kthakore Organized the code in the chapter a bit
kthakore authored
574
575 my $music_data_effect_id =
576 SDL::Mixer::Effects::register( MIX_CHANNEL_POST, "main::music_data",
577 "main::done_music_data", 0 );
578
579
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
580 The program's single command-line option governs the number of lines to display
581 in the visualizer. The default is 50.
d55ec08 @kthakore Clean up and bumped the script
kthakore authored
582
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
583 my $lines = $ARGV[0] || 50;
437e2c4 @kthakore Organized the code in the chapter a bit
kthakore authored
584
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
585 The drawing callback for the C<SDLx::App> runs while a song plays. It reads
586 the stream data and displays it on the screen as a wave form. The math behind
587 calculating the graphics to display is more detail than this article intends,
588 but the graphic code is straightforward:
d55ec08 @kthakore Clean up and bumped the script
kthakore authored
589
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
590 # Music Playing Callbacks
591 my $current_song = 0;
437e2c4 @kthakore Organized the code in the chapter a bit
kthakore authored
592
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
593 my $current_music_callback = sub {
594 my ( $delta, $app ) = @_;
437e2c4 @kthakore Organized the code in the chapter a bit
kthakore authored
595
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
596 $app->draw_rect( [ 0, 0, $app->w(), $app->h() ], 0x000000FF );
597 $app->draw_gfx_text(
598 [ 5, $app->h() - 10 ],
599 [ 255, 0, 0, 255 ],
600 "Playing Song: " . $songs[ $current_song - 1 ]
601 );
602
603 my @stream;
604 {
605 lock @stream_data;
606 @stream = @stream_data;
607 @stream_data = ();
608 }
609
610 # To show the right amount of lines we choose a cut of the stream
611 # this is purely for asthetic reasons.
612
613 my $cut = @stream / $lines;
614
615 # The width of each line is calculated to use.
616 my $l_wdt = ( $app->w() / $lines ) / 2;
617
618 for ( my $i = 0 ; $i < $#stream ; $i += $cut ) {
619
620 # In stereo mode the stream is split between two alternating streams
621 my $left = $stream[$i];
622 my $right = $stream[ $i + 1 ];
623
624 # For each bar we calculate a Y point and a X point
625 my $point_y = ( ( ($left) ) * $app->h() / 4 / 32000 ) + ( $app->h / 2 );
626 my $point_y_r =
627 ( ( ($right) ) * $app->h() / 4 / 32000 ) + ( $app->h / 2 );
628 my $point_x = ( $i / @stream ) * $app->w;
437e2c4 @kthakore Organized the code in the chapter a bit
kthakore authored
629
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
630 # Using the parameters
631 # Surface, box coordinates and color as RGBA
632 SDL::GFX::Primitives::box_RGBA(
633 $app,
634 $point_x - $l_wdt,
635 $app->h() / 2,
636 $point_x + $l_wdt,
637 $point_y, 40, 0, 255, 128
638 );
639 SDL::GFX::Primitives::box_RGBA(
640 $app,
641 $point_x - $l_wdt,
642 $app->h() / 2,
643 $point_x + $l_wdt,
644 $point_y_r, 255, 0, 40, 128
645 );
437e2c4 @kthakore Organized the code in the chapter a bit
kthakore authored
646
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
647 }
437e2c4 @kthakore Organized the code in the chapter a bit
kthakore authored
648
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
649 $app->flip();
437e2c4 @kthakore Organized the code in the chapter a bit
kthakore authored
650
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
651 };
d55ec08 @kthakore Clean up and bumped the script
kthakore authored
652
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
653
654
655 Whenever a song finishes C<SDL::Mixer::Music::playing_music> returns C<0>. We detect this change in state
656 and call C<music_finished_playing()> where the program attaches our
657 C<$play_next_song_callback> callback to switch to the next song gracefully:
658
659 my $cms_move_callback_id;
660 my $pns_move_callback_id;
661 my $play_next_song_callback;
662
663 sub music_finished_playing {
664 SDL::Mixer::Music::halt_music();
665 $pns_move_callback_id = $app->add_move_handler($play_next_song_callback)
666 if ( defined $play_next_song_callback );
667
668 }
669
670 $play_next_song_callback = sub {
671 return $app->stop() if $current_song >= @songs;
672 my $song = SDL::Mixer::Music::load_MUS( $songs[ $current_song++ ] );
673 SDL::Mixer::Music::play_music( $song, 0 );
674
675 $app->remove_move_handler($pns_move_callback_id)
676 if defined $pns_move_callback_id;
677 };
678
679 A move handler is attached to detect if music is playing or not:
680
681 $app->add_move_handler(
682 sub {
683 my $music_playing = SDL::Mixer::Music::playing_music();
684 music_finished_playing() unless $music_playing;
685 }
686 )
687
688
689
690 The first callback to trigger the C<$play_next_song_callback> gets the first song:
691
692 $app->add_show_handler($current_music_callback);
693 $pns_move_callback_id = $app->add_move_handler($play_next_song_callback);
694
695 ... and a keyboard event handler for a keypress allows the user to move through
696 songs:
437e2c4 @kthakore Organized the code in the chapter a bit
kthakore authored
697
698 $app->add_event_handler(
699 sub {
700 my ($event, $app) = @_;
701
702 if( $event->type == SDL_KEYDOWN && $event->key_sym == SDLK_DOWN)
703 {
704 #Indicate that we are done playing the music_finished_playing
705 music_finished_playing();
706 }
707
708 }
709 );
710
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
711 From there, the application is ready to run:
712
437e2c4 @kthakore Organized the code in the chapter a bit
kthakore authored
713 $app->run();
714
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
715 ... and the final code gracefully stops C<SDL::Mixer>:
437e2c4 @kthakore Organized the code in the chapter a bit
kthakore authored
716
717 SDL::Mixer::Effects::unregister( MIX_CHANNEL_POST, $music_data_effect_id );
718 SDL::Mixer::Music::hook_music_finished();
719 SDL::Mixer::Music::halt_music();
720 SDL::Mixer::close_audio();
940dcea @kthakore Typo on the label tag
kthakore authored
721
747fea6 @kthakore Fixed code and add chromatic's editing of the visualizer article
kthakore authored
722 The result? Several dozen lines of code to glue together the SDL mixer and
723 display a real-time visualization of the music.
724
18bc16f @kthakore Initial Commit
kthakore authored
725 =for vim: spell
Something went wrong with that request. Please try again.