package Blacknote::Sound; use FFI::Platypus; use FFI::CheckLib 0.06; use strict; use warnings; use Exporter qw(import); use threads; our @EXPORT = qw(load_soundfile play_chunk open_audio close_audio free_chunk); my $ffi = FFI::Platypus->new(api=>1); if($ffi->lib(find_lib_or_die(lib => 'SDL2'), find_lib_or_die lib => "SDL2_mixer")){ $ffi->attach(SDL_Init => ["int"] => "int"); $ffi->attach(Mix_OpenAudio => ["int","int","int","int"] => "int"); $ffi->attach(Mix_AllocateChannels => ["int"] => "int"); #NOTE: Opaque is kind of a 'anything' or 'object' of some sort #use it when it relates to some unknown/undefined object structure $ffi->attach(SDL_RWFromFile => ["string","string"] => "opaque"); $ffi->attach(Mix_LoadWAV_RW => ["opaque","uint"] => "opaque"); $ffi->attach(SDL_GetError => [] => "string"); $ffi->attach(Mix_CloseAudio => [] => "int"); $ffi->attach(Mix_FreeChunk => ["opaque"] => "int"); $ffi->attach(Mix_PlayChannelTimed => ["int","opaque","int","int"] => "int"); $ffi->attach(Mix_Playing => ["int"] => "int"); } sub open_audio () { SDL_Init(0x00000010); # Init audio only # AUDIO_S16LSB = AUDIO_S16SYS my $mixopen = Mix_OpenAudio(44100, 0x8010, 2, 1024); # Open mixer audio die "Failed to open mixer" unless $mixopen != -1; } sub close_audio ($) { Mix_CloseAudio(); } sub load_soundfile ($) { my $filename = shift; my $ptr = SDL_RWFromFile($filename, "rb"); if(defined $ptr){ my $chunk = Mix_LoadWAV_RW($ptr,1); if(defined $chunk){ return $chunk; }else{ die SDL_GetError(); } }else{ die SDL_GetError(); } } sub free_chunk ($) { my $chunk = shift; Mix_FreeChunk($chunk); } sub play_chunk ($) { my $chunk = shift; my $thread = async { Mix_PlayChannelTimed(-1,$chunk,0,-1); }->detach; # Let it run in the background. Makes it so that we dont have to join it return 1; } 1; .