diff --git a/lib/SDLx/Sound.pm b/lib/SDLx/Sound.pm index 7cb2ede1..9146f292 100644 --- a/lib/SDLx/Sound.pm +++ b/lib/SDLx/Sound.pm @@ -1,4 +1,5 @@ package SDLx::Sound; + use strict; use warnings; use Carp; @@ -16,19 +17,19 @@ use SDL::Mixer::Music; my $audioInited = undef; sub new { - my $class = shift; - my $self = {@_}; - bless ($self, $class); - _initAudio() unless $audioInited; - $self->{supported} = _initMixer(); - return $self; + my $class = shift; + my $self = {@_}; + bless ($self, $class); + _initAudio() unless $audioInited; # only once per module load + $self->{supported} = _initMixer(); + return $self; } sub _initAudio { SDL::Mixer::open_audio( 44100, AUDIO_S16SYS, 2, 4096 ); my ($status, $freq, $format, $channels) = @{ SDL::Mixer::query_spec() }; $audioInited = 1 if $status == 1; - return ($status, $freq, $format, $channels); #TODO: Save this information in $self; + return ($status, $freq, $format, $channels); # being passed back to $self } sub _initMixer { @@ -48,8 +49,33 @@ sub _initMixer { } sub load { - my $self = shift; - $self->{files} = {@_}; + my $self = shift; + $self->{files} = { @_ }; # user passes key/value pairs + + for my $name (keys %{ $self->{files} }){ + my $obj = {}; + # allow user to pass hash-refs + unless( ref($self->{files}->{$name}) ){ + # print "load: preparing sound object: $name \n"; + $obj = { + file => $self->{files}->{$name}, + volume => 100, + loops => 1, + }; + $self->{files}->{$name} = $obj; + + + } + + if (-e $obj->{file}){ + $obj->{load_ref} = SDL::Mixer::Music::load_MUS($obj->{file}) or Carp::croak "Sound file $obj->{file} not found: " . SDL::get_error(); + $obj->{is_loaded} = 1; + }else{ + carp("Sound file ".$obj->{file}." not found\n"); + next; + } + } + } sub unload { @@ -58,49 +84,72 @@ sub unload { } sub play { - my $self = shift; - $self->{files} = {@_} if $#_ > 0 && @_; - my $play = 1; - if (-e $_[0]) { - my $music = SDL::Mixer::Music::load_MUS($_[0]) - or Carp::croak 'Sound file not found: ' . SDL::get_error(); - SDL::Mixer::Music::volume_music(85); - if (SDL::Mixer::Music::play_music($music, -1)<0) { - print("Can't play!\n". SDL::get_error()."\n"); - $play = 0; - } - } else { - carp("No newline ".$self->{files}."\n".$_[0]."\n"); - $play = 0; - } - return $play; + my $self = shift; +# $self->{files} = {@_} if $#_ > 0 && @_; + + my ($name, %override) = @_; + + my $obj; + if($name){ + unless( exists($self->{files}->{$name}) && $self->{files}->{$name}->{is_loaded} ){ + carp("Sound file with name $name not loaded, autoload with name = path (which is $name) \n"); + + $self->load( $name => $name ); + } + $obj = $self->{files}->{$name}; + }else{ + # if user calls play() do something + ($name, $obj) = each %{ $self->{files} }; + + unless( $name ){ + carp("No sound file loaded, either call load() first or pass a file-path to play()\n"); + return 0; + } + } + + ## execute play + SDL::Mixer::Music::volume_music( (defined($override{volume}) ? $override{volume} : $obj->{volume}) ); + + if( SDL::Mixer::Music::play_music( + $obj->{load_ref}, + ( defined($override{loops}) ? $override{loops} : $obj->{loops} ) + ) < 0 + ){ + carp("Error calling play_music: ". SDL::get_error()."\n"); + return 0; + } + + return 1; } -sub loud { +sub playing { + return SDL::Mixer::Music::playing_music(); +} +sub is_playing { + return SDL::Mixer::Music::playing_music(); } sub pause { - my $self = shift; - SDL::Mixer::Music::pause_music(); - + my $self = shift; + SDL::Mixer::Music::pause_music(); } sub resume { - my $self = shift; - SDL::Mixer::Music::resume_music(); - + my $self = shift; + SDL::Mixer::Music::resume_music(); } sub stop { - my $self = shift; - SDL::Mixer::Music::halt_music(); - #SDL::Mixer::quit(); + my $self = shift; + SDL::Mixer::Music::halt_music(); + #SDL::Mixer::quit(); } -sub fade { +sub loud { } - +sub fade { +} 1; # End of SDLx::Sound diff --git a/lib/pods/SDLx/Sound.pod b/lib/pods/SDLx/Sound.pod index da1a8e60..b72d6384 100644 --- a/lib/pods/SDLx/Sound.pod +++ b/lib/pods/SDLx/Sound.pod @@ -1,7 +1,7 @@ =head1 NAME -SDLx::Sound +SDLx::Sound - Simple audio interface =head1 CATEGORY @@ -10,108 +10,78 @@ Extension =head1 SYNOPSIS use SDLx::Sound; - my $snd = SDLx::Sound->new(); - - # loads and plays a single sound now - $snd->play('myfile.wav'); - - # load a single file - $snd->load('theSound.aif'); - - # plays it or all loaded files - $snd->play(); - - # more sounds - my %files = ( - channel_01 => "/my_sound1.wav", - channel_02 => "/my_sound2.ogg" - ); - # times sounds bangs - my %times = ( - channel_01 => 0, # start - channel_01 => 1256, # milliseconds - channel_02 => 2345 + $snd->load( + arbitrary_name => 'some_sound.wav', ); - - # Load files in channels for realtime play - $snd->load(%files); - - # sets sound channel_01 loudness - $snd->loud('channel_01', 80); # loud at 80% - $snd->play(%times); # play loaded files at times - $snd->play; # play again - - # plays sound channel_01 at 578 milliseconds from now - $snd->play('channel_01', 578); - - # fades sound - $snd->fade('channel_02', 2345, 3456, -20); - - # in a single act do the whole Sound - my $snd = SDLx::Sound->new( - files => ( - channel_01 => "/my_sound1.wav", - channel_02 => "/my_sound2.ogg" - - ), - loud => ( - channel_01 => 80, - channel_02 => 75 - ), - times => ( - channel_01 => 0, # start - channel_01 => 1256, # milliseconds - channel_02 => 2345 - ), - fade => ( - channel_02 => [2345, 3456, -20] - ) - )->play(); - -=head1 DESCRIPTION - - -You can think about the SDLx::Sound at 2 approaches. -=over 4 + $snd->play('arbitrary_name'); -=item * A simple sound or + $snd->play('arbitrary_name', loops => -1); # loop .wav infinitely (default is play once, 1) -=item * The sound of your game or app. + $snd->play('arbitrary_name', loops => 3, volume => 50); # play 3x with volume lowered to 50% -=back + $snd->stop(); -Your application will say what the best approach. +=head1 DESCRIPTION -In a taste that resembles to perl and to SDL, our SDLx:Sound hooks at SDL::Audio and SDL::Mixer with a graceful and simple interface that can offer to monks a modern perlish way to manage sounds. +You may use this module to emit audio / sound / music from your SDL / SDLx application or game. -An SDLx::Sound object can load sounds from filesystem, play it, adjust this loudness level or stops the sound. +It's a thin interface to L, which it wraps/ uses internally, merely rounding off the edges of init, +pre-loading and playback. -Each sound will play in the next available channel, so it can be handled isolately. +Calls to play() are not blocking, each sound will play in the next available channel. That's how SDL::Mixer::Music works. =head1 METHODS =head2 new -Returns a new instance of SDLx::Sound +Returns a new instance of SDLx::Sound, initiating sound with C =head2 load +Processes the passed sound files, checks for existence, and pre-loads the files via SDL::Mixer::Music::load_MUS, +throwing errors if anything fails. In contrast to earlier versions of SDLx::Sound, this version here actually does a +pre-load and keeps files in memory, while play() will rely on this memory structure being prepared and ready to play. + +Each sound file will be prepared with I = 100 and I = 1, one-shot play. That's how this sound will be played +when you call it with -Eplay('some_sound'). =head2 play - $sdlx_sound->play('file.wav'); +Doing something like C<$audio-Eplay('sound_name');> will fetch the prepared sound from the internal C<-E{files}> hash +and play it via C. As previous versions of SDLx::Sound were able to autoload via play(), this +updated version here does something similar: passing it a file_path will trigger the internal load() routine and the given +file will be loaded with the name representing it being the path you passed in to play/load a sound. -Play a file +You may pass additional key/value pairs to override settings from -Eload(), see SYNOPSIS. =head2 pause +SDL::Mixer::Music::pause_music() + =head2 resume +SDL::Mixer::Music::resume_music() + =head2 stop +SDL::Mixer::Music::halt_music() + +=head2 playing() and is_playing() + +Both call SDL::Mixer::Music::playing_music(), returning a true value if the mixer is playing any sound. + +=head1 SEE ALSO + +This here is a simple interface to L, which you may also call directly for additional +bells and whistles. + +And then there's L. While the bundled example C is able to +play something with it, the POD is out of sync with the actual internals. L behaves like this module here +as it prepares defaults for every file, which you may then overrride upon calling play() - nice inspiration. But then +it has a somewhat IMHO convoluted way of loading/managing loaded files and has a less convenient play() method. =head1 AUTHORS