xiaoyafeng has asked for the wisdom of the Perl Monks concerning the following question:

Hi monks, I intend to write a mp3 player with fmod, Inline::C, and active perl 5.10. I'm stuck a compiler problem.
use strict; use warnings; use Inline C => Config => CC => 'gcc'; use Inline C => Config => INC => '-I.'; use Inline C => Config => LD => 'ld'; use Inline C => Config => LIBS => '-L. -lfmodex'; use Inline C => << 'TEST'; #include <windows.h> #include <stdio.h> #include <conio.h> #include "fmod.h" #include "fmod_errors.h" void ERRCHECK(FMOD_RESULT result) { if (result != FMOD_OK) { printf("FMOD error! (%d) %s\n", result, FMOD_ErrorString(resul +t)); exit(-1); } } int main() { FMOD_SYSTEM *system; FMOD_SOUND *sound; FMOD_CHANNEL *channel = 0; FMOD_RESULT result; int key; unsigned int version; /* Create a System object and initialize. */ result = FMOD_System_Create(&system); ERRCHECK(result); result = FMOD_System_GetVersion(system, &version); ERRCHECK(result); if (version < FMOD_VERSION) { printf("Error! You are using an old version of FMOD %08x. Th +is program requires %08x\n", version, FMOD_VERSION); return 0; } result = FMOD_System_Init(system, 1, FMOD_INIT_NORMAL, NULL); ERRCHECK(result); result = FMOD_System_CreateStream(system, "wave.mp3", FMOD_HARDWAR +E | FMOD_LOOP_NORMAL | FMOD_2D, 0, &sound); ERRCHECK(result); printf("========================================================== +==========\n"); printf("PlayStream Example. Copyright (c) Firelight Technologies +2004-2008.\n"); printf("========================================================== +==========\n"); printf("\n"); printf("Press space to pause, Esc to quit\n"); printf("\n"); /* Play the sound. */ result = FMOD_System_PlaySound(system, FMOD_CHANNEL_FREE, sound, 0 +, &channel); ERRCHECK(result); /* Main loop. */ do { if (kbhit()) { key = getch(); switch (key) { case ' ' : { int paused; FMOD_Channel_GetPaused(channel, &paused); FMOD_Channel_SetPaused(channel, !paused); break; } } } FMOD_System_Update(system); if (channel) { unsigned int ms; unsigned int lenms; int playing; int paused; result = FMOD_Channel_IsPlaying(channel, &playing); if ((result != FMOD_OK) && (result != FMOD_ERR_INVALID_HAN +DLE) && (result != FMOD_ERR_CHANNEL_STOLEN)) { ERRCHECK(result); } result = FMOD_Channel_GetPaused(channel, &paused); if ((result != FMOD_OK) && (result != FMOD_ERR_INVALID_HAN +DLE) && (result != FMOD_ERR_CHANNEL_STOLEN)) { ERRCHECK(result); } result = FMOD_Channel_GetPosition(channel, &ms, FMOD_TIMEU +NIT_MS); if ((result != FMOD_OK) && (result != FMOD_ERR_INVALID_HAN +DLE) && (result != FMOD_ERR_CHANNEL_STOLEN)) { ERRCHECK(result); } result = FMOD_Sound_GetLength(sound, &lenms, FMOD_TIMEUNIT +_MS); if ((result != FMOD_OK) && (result != FMOD_ERR_INVALID_HAN +DLE) && (result != FMOD_ERR_CHANNEL_STOLEN)) { ERRCHECK(result); } printf("Time %02d:%02d:%02d/%02d:%02d:%02d : %s\r", ms / 1 +000 / 60, ms / 1000 % 60, ms / 10 % 100, lenms / 1000 / 60, lenms / 1 +000 % 60, lenms / 10 % 100, paused ? "Paused " : playing ? "Playing" +: "Stopped"); } Sleep(10); } while (key != 27); printf("\n"); /* Shut down */ result = FMOD_Sound_Release(sound); ERRCHECK(result); result = FMOD_System_Close(system); ERRCHECK(result); result = FMOD_System_Release(system); ERRCHECK(result); return 0; } TEST main();
I've copy fmodex.h, fmodexerr.h, libfmodex.a and fmodex.dll to the directory the script run. And

Dev-C++, MinGW and CygWin users - libfmodex.a.

AS fmod help file mentioned above, my LIBS config is
use Inline C => Config => LIBS => '-L. -lfmodex'; not use Inline C => Config => LIBS => '-L. -lfmodex_vc';
strangely, perl throw below error:
|| || Microsoft (R) Program Maintenance Utility Version 1.50 || Copyright (c) Microsoft Corp 1988-94. All rights reserved. || || C:\Perl\bin\perl.exe C:\Perl\lib\ExtUtils\xsubpp -typemap C:\P +erl\lib\ExtUtils\typemap test_pl_0ca9.xs > test_pl_0ca9.xsc && C:\Pe +rl\bin\perl.exe -MExtUtils::Command -e mv test_pl_0ca9.xsc test_pl_0c +a9.c || gcc -c -IC:/test -DNDEBUG -DWIN32 -D_CONSOLE -DNO_STRICT -DHA +VE_DES_FCRYPT -DUSE_SITECUSTOMIZE -DPRIVLIB_LAST_IN_INC -DPERL_IMPLIC +IT_CONTEXT -DPERL_IMPLICIT_SYS -DUSE_PERLIO -DPERL_MSVCRT_READFIX -DH +ASATTRIBUTE -fno-strict-aliasing -mms-bitfields -O2 -DVERSION=\"0. +00\" -DXS_VERSION=\"0.00\" "-IC:\Perl\lib\CORE" test_pl_0ca9.c || Running Mkbootstrap for test_pl_0ca9 () || C:\Perl\bin\perl.exe -MExtUtils::Command -e chmod 644 test_pl_0 +ca9.bs || C:\Perl\bin\perl.exe -MExtUtils::Mksymlists -e "Mksymlists('NA +ME'=>\"test_pl_0ca9\", 'DLBASE' => 'test_pl_0ca9', 'DL_FUNCS' => { } +, 'FUNCLIST' => [], 'IMPORTS' => { }, 'DL_VARS' => []);" || Set up gcc environment - 3.4.5 (mingw special) || dlltool --def test_pl_0ca9.def --output-exp dll.exp || g++ -o blib\arch\auto\test_pl_0ca9\test_pl_0ca9.dll -Wl,--base- +file -Wl,dll.base -mdll -L"C:\Perl\lib\CORE" test_pl_0ca9.o -Wl,--ima +ge-base,0x2b150000 C:\Perl\lib\CORE\perl510.lib C:\MinGW\lib\libkern +el32.a C:\MinGW\lib\libuser32.a C:\MinGW\lib\libgdi32.a C:\MinGW\lib\ +libwinspool.a C:\MinGW\lib\libcomdlg32.a C:\MinGW\lib\libadvapi32.a C +:\MinGW\lib\libshell32.a C:\MinGW\lib\libole32.a C:\MinGW\lib\libolea +ut32.a C:\MinGW\lib\libnetapi32.a C:\MinGW\lib\libuuid.a C:\MinGW\lib +\libws2_32.a C:\MinGW\lib\libmpr.a C:\MinGW\lib\libwinmm.a C:\MinGW\l +ib\libversion.a C:\MinGW\lib\libodbc32.a C:\MinGW\lib\libodbccp32.a C +:\MinGW\lib\libmsvcrt.a dll.exp || test_pl_0ca9.o:test_pl_0ca9.c:(.text+0x3f9): undefined reference to + `FMOD_System_Create@4' || test_pl_0ca9.o:test_pl_0ca9.c:(.text+0x416): undefined reference to + `FMOD_System_GetVersion@8' || test_pl_0ca9.o:test_pl_0ca9.c:(.text+0x473): undefined reference to + `FMOD_System_Init@16' || test_pl_0ca9.o:test_pl_0ca9.c:(.text+0x4a8): undefined reference to + `FMOD_System_CreateStream@20' || test_pl_0ca9.o:test_pl_0ca9.c:(.text+0x523): undefined reference to + `FMOD_System_PlaySound@20' || test_pl_0ca9.o:test_pl_0ca9.c:(.text+0x53b): undefined reference to + `FMOD_System_Update@4' || test_pl_0ca9.o:test_pl_0ca9.c:(.text+0x584): undefined reference to + `FMOD_Channel_GetPaused@8' || test_pl_0ca9.o:test_pl_0ca9.c:(.text+0x59f): undefined reference to + `FMOD_Channel_SetPaused@8' || test_pl_0ca9.o:test_pl_0ca9.c:(.text+0x5ad): undefined reference to + `FMOD_System_Update@4' || test_pl_0ca9.o:test_pl_0ca9.c:(.text+0x5cb): undefined reference to + `FMOD_Channel_IsPlaying@8' || test_pl_0ca9.o:test_pl_0ca9.c:(.text+0x60e): undefined reference to + `FMOD_Channel_GetPaused@8' || test_pl_0ca9.o:test_pl_0ca9.c:(.text+0x657): undefined reference to + `FMOD_Channel_GetPosition@12' || test_pl_0ca9.o:test_pl_0ca9.c:(.text+0x697): undefined reference to + `FMOD_Sound_GetLength@12' || test_pl_0ca9.o:test_pl_0ca9.c:(.text+0x7e3): undefined reference to + `FMOD_Sound_Release@4' || test_pl_0ca9.o:test_pl_0ca9.c:(.text+0x7f9): undefined reference to + `FMOD_System_Close@4' || test_pl_0ca9.o:test_pl_0ca9.c:(.text+0x80f): undefined reference to + `FMOD_System_Release@4' || collect2: ld returned 1 exit status || NMAKE : fatal error U1077: 'C:\WINDOWS\system32\cmd.exe' : return c +ode '0x1' || Stop. || || A problem was encountered while attempting to compile and install y +our Inline || C code. The command that failed was: || nmake > out.make 2>&1 || || The build directory was: || C:\test\_Inline\build\test_pl_0ca9 || || To debug the problem, cd to the build directory, and inspect the ou +tput files. || || at C:\test\test.pl line 28 test.pl|167| BEGIN failed--compilation aborted
any suggestions?

I am trying to improve my English skills, if you see a mistake please feel free to reply or /msg me a correction

Replies are listed 'Best First'.
Re: Inline::C with minGW?
by syphilis (Archbishop) on Oct 29, 2008 at 04:47 UTC
    use Inline C => Config => LIBS => '-L. -lfmodex';

    You don't want that - '.' (ie the current working directory) is not what you think it is. Inline changes it. Instead do:
    use Inline C => Config => LIBS => '-LC:/full/path_to/lib -lfmodex';
    Cheers,
    Rob
      So I guess that means
      use Cwd qw( realpath ); use File::Basename qw( dirname ); my $lib_path = dirname(realpath($0)); $lib_path =~ s{\\}{/}g; # Needed? And what about spaces? use Inline C => Config => LIBS => "-L$lib_path -lfmodex";
        looks like I have to add that to fixed my script.

        I am trying to improve my English skills, if you see a mistake please feel free to reply or /msg me a correction
      How Stupid I am! thanks. But does it means I can't change working directory?

      I am trying to improve my English skills, if you see a mistake please feel free to reply or /msg me a correction
        But does it means I can't change working directory?

        If you know only that the library will be in the same folder as the script, but don't know the fully-qualified location of the script and library, then you'll have to use something like the approach that ikegami showed us.

        By the way, in the example you gave in your original post, the actual working directory (from Inline's point of view) was C:\test\_Inline\build\test_pl_0ca9. The "\test\_Inline\build\" part is known in advance, and you can even configure things so that "test\_Inline" is replaced by the location of your choice. The "test_pl_0ca9" could also be determined in advance - but it will change everytime you make a change to the C code in the script.

        Cheers,
        Rob
Re: Inline::C with minGW? (Dynamic Loading)
by Corion (Patriarch) on Oct 29, 2008 at 21:38 UTC

    This post is as good as any to put some more code of me online. In this case, I wanted to use fmod myself some time ago but wasn't ready for the battle with Inline::C or XS and hence used dynamic loading and Win32::API to access the functions in fmod.dll:

    package Sound::Fmod; use strict; use Win32::API; =head1 NAME Sound::Fmod - interface to the Fmod sound library =head1 SYNOPSIS use Sound::Fmod; my $player = Sound::Fmod->new(); $player->init(32000, 64, 0) or die $player->get_error; my $mod = $player->load_song('../../media/invtro94.s3m') or die "Couldn't load song: " . $player->get_error; $mod->play_song(); while (1) { print "...\n"; sleep 10; }; =cut sub new { my ($package) = @_; my $self = {}; bless $self, $package; $self; }; sub load_song { my ($self,$filename) = @_; warn "Loading $filename"; my $buffer = " " x 1024; $buffer = $self->_load_song($filename); print length $buffer; Sound::Fmod::Song->new($buffer); }; sub load_samples { my $self = shift; my @result; for my $filename (@_) { #$filename =~ s!/!\\!g; my $handle; if ($filename =~ /.pcm/i) { $handle = $self->_load_sample(-1,$filename,0x10051,0,0); } else { $handle = $self->_load_sample(-1,$filename,0x02000,0,0); }; die "Couldn't load $filename :" . $self->get_error unless $handle; push @result, Sound::Fmod::Sample->new($handle); }; @result; }; # My own lame DLL loader, ugly hacked together # because I'm too stupid to use Inline::C and too # lazy to learn XS: my $library = 'fmod'; my %functions = ( init => ['FSOUND_Init','LLL','L'], DESTROY => ['FSOUND_Close','',''], _load_song => ['FMUSIC_LoadSong','P','L'], _load_sample => ['FSOUND_Sample_Load','LPLLL','L'], get_error => ['FSOUND_GetError','','L'], __PACKAGE__ . "::Song::_play_song" => ['FMUSIC_PlaySong','L',''], __PACKAGE__ . "::Song::_DESTROY" => ['FMUSIC_FreeSong','L',''], __PACKAGE__ . "::Sample::_play" => ['FSOUND_PlaySound','LL',''] +, __PACKAGE__ . "::Sample::_DESTROY" => ['FSOUND_Sample_Free','L','' +], ); # Simplicistic fudging of names sub mangle { my ($name,$args) = @_; die "Unknown argument '$args'" unless $args =~ m!^[LP]*$!; my $size = (length $args) * 4; "_$name\@$size" }; sub load_function { my ($f) = @_; die "Unknown function $f" unless exists $functions{$f}; my @args = ('fmod.dll', @{$functions{$f}}); $args[1] = mangle(@args[1,2]); my $code = Win32::API->new(@args); die "Couldn't load $f from fmod: $! / $^E" unless $code; no strict 'refs'; *{$f} = sub { my $self = shift; $code->Call(@_) }; }; load_function($_) for keys %functions; package Sound::Fmod::Song; sub new { my ($package,$handle) = @_; my $self = \$handle; bless $self, $package; $self; }; sub play_song { my ($self) = @_; $self->_play_song($$self); }; sub DESTROY { my $self = shift; $self->_DESTROY($$self); }; package Sound::Fmod::Sample; sub new { my ($package,$handle) = @_; my $self = \$handle; bless $self, $package; $self; }; sub play { my ($self) = @_; $self->_play(-1,$$self); }; sub DESTROY { my $self = shift; $self->_DESTROY($$self); }; 1;
      Good post, I was to wrap fmod as a package with Inline::C AUTOWRAP, But it seems your idea is simple and clean enough than me.

      I am trying to improve my English skills, if you see a mistake please feel free to reply or /msg me a correction
Re: Inline::C with minGW?
by lostjimmy (Chaplain) on Oct 29, 2008 at 14:22 UTC

    I guess I don't understand the point. If all you are using Perl for is to call your main function, why not just write the whole thing in C and avoid the confusion all together?

    Not trying to troll here or anything, so if there is a really good reason for doing this, I would like to know.

      If all you are using Perl for is to call your main function ....

      Often, the ultimate aim is to do more from Perl than just call the main function. Otherwise, your point is valid - and it just boils down to the general C versus Perl consideration.

      ...write the whole thing in C and avoid the confusion all together

      Personally, I generally don't experience any extra confusion from writing C code in Inline::C. In fact, I mostly find it less confusing to write my C code in Inline, than to write it in C. (Admittedly, I have been using Inline::C on a regular basis for a few years.)

      Cheers,
      Rob