Tuesday, May 8, 2012

Playing sound on iOS? Anyone have a working Delphi XE2 FireMonkey sample?

I wish I had time for everything... I've tried this one a couple of times, but have run into issues with parsing framework this and that... ;)

So, here it is... Anyone got a simple example that plays a single sound effect and/or plays a background track?

From files that you deploy with the app (resources).

Background track from existing music library on device would be a sweet bonus.

I'll make sure you get credit if I use it in upcoming blogs, webinars, etc.

Thanks!

27 comments:

  1. This may help: https://forums.embarcadero.com/thread.jspa?threadID=68910&stqc=true

    Taken from this forum thread:
    https://forums.embarcadero.com/thread.jspa?threadID=65979

    ReplyDelete
  2. Here is what i used from an exmaple posting on the delphi forum.
    It is not perfect as I patched a memory leak in a dirty way.

    ---------------------------
    unit iossound;

    (*
    adapted from https://forums.embarcadero.com/thread.jspa?threadID=68910&stqc=true
    *)

    {$IFDEF FPC}
    {$mode objfpc}{$H+}
    {$modeswitch objectivec1}
    {$linkframework AVFoundation}
    {$ENDIF}

    interface

    uses
    SysUtils{$IFDEF FPC},iPhoneAll , FMX_Platform_iOS ,NSHelpers{$ENDIF};

    Const optSound : boolean = True;
    Function PlaySound( sndFileName : String) : boolean;

    implementation

    {$IFDEF FPC}

    type
    AVAudioPlayer = objcclass external(NSObject)
    public
    function initWithContentsOfURL_error(url: NSURL; outError: NSErrorPointer): id; message 'initWithContentsOfURL:error:';
    function play: Boolean; message 'play';
    function volume:double; message 'volume';
    //Calling setvolume crashes
    procedure setvolume(newValue:double); message 'setvolume:';
    function isPlaying: Boolean; message 'isPlaying';
    function prepareToPlay: Boolean; message 'prepareToPlay';
    function duration:double; message 'duration';
    end;
    AVAudioSession= objcclass external(NSObject)
    public
    class function sharedInstance:id; message 'sharedInstance';
    function setActive(beActive:boolean;outError:NSError):Boolean;message 'setActive:error:';
    function setCategory(theCategory:NSString;outError:NSError):boolean; message 'setCategory:error:';
    end;
    {$ENDIF}

    {$IFDEF FPC}
    (*
    this was moved from PlaySound
    I still dont understand the garbage collection on iOS
    snd.autorelease; seemed to be the proper way to do it
    but it was causing some sound to not be played.
    *)
    const snd : AVAudioPlayer=Nil;
    {$ENDIF}

    Function PlaySound( sndFileName : String) : boolean;
    {$IFDEF FPC}
    var
    audioSession :AVAudioSession;
    //snd : AVAudioPlayer;
    err : NSError;
    url : NSURL;
    temp : NSString;
    {$ENDIF}
    begin
    result := true;
    if not optSound then exit;

    {$IFDEF FPC}
    try
    AnsiStrToNSStr( sndFileName , temp );
    url := NSURL.fileURLWithPath(temp);

    audioSession:=AVAudioSession.sharedInstance;
    audioSession.setActive(true,err);
    audioSession.setCategory(nsstr(PChar('AVAudioSessionCategoryPlayback')),nil);
    //to avoid memory leak see comment in SND declaration
    if sndNil then
    snd.release;

    snd := AVAudioPlayer.alloc.initWithContentsOfURL_error(url, @err);
    if not snd.prepareToPlay then
    Begin
    result := false;
    exit;
    end;
    //snd.autorelease;
    //snd.setvolume(0.9);
    if snd.play=false then
    result := false;
    except
    result := false;
    end;
    {$ENDIF}
    end;

    end.

    ReplyDelete
  3. Quick follow up: I forgot to mention that the setvolume does not work (crash the app) i do not understand why.
    Also the fact the snd is global is bad, but i could not avoid the memory leak
    using "snd.autorelease;" seemed the right solution and indeed it avoided the leak, but some sound (short ones) where not playing... There is obviously something i am missing.

    ReplyDelete
  4. Thanks guys! I will check this stuff out soon.

    ReplyDelete
  5. @anders: I'd hate to be "that guy", but a component for this would be cool! :)

    ReplyDelete
  6. lol, Was testing with controlling volume and got errors, Then I though I try the arm7 compile and patched some more files for bluetooth etc.

    Now i get same error but on other places. I cant even run an application , I think i have to reinstall everything again.

    [UIImage cGImage]: unrecognized selector sent to instance 0x8b5c500'

    I'm sure i messed all up by using IOS 5.1 framework for compile. :)

    ReplyDelete
  7. I got over that bug :)

    Was error in the UImage.inc, I patched the iPhoneAll again after fixing it, Now it seems i'm back in business again ouch.

    ReplyDelete
  8. To get the volume to work. Change procedure setvolume(newValue:double); message ’setvolume:’; to procedure setvolume(newValue:single); message ’setVolume:’;

    ReplyDelete
  9. I guess for picking file, 2 ways, Ipod library something like,

    procedure MediaPickerDelegate .mediaPicker_didPickMediaItems(mediaPicker: MPMediaPickerController; mediaItemCollection: MPMediaItemCollection);
    var
    Items : NSArray;
    selectedItem : MPMediaItem;
    begin

    Items := mediaItemCollection.items;

    selectedItem := Items.objectAtIndex(0);

    fileurl := selectedItem.valueForProperty(MPMediaItemPropertyAssetURL);

    NSLOG(NSSTR('Picked File'));

    NSLOG(fileurl.absoluteString);

    ViewController.DismissModalViewControllerAnimated (true);
    end;

    procedure TForm1.Button1Clic(Sender: TObject);
    var
    MediaPicker : MPMediaPickerController;
    Window : TUIWindowHack;
    MySelector : Sel;
    begin
    Window := UIApplication.sharedApplication.Windows.objectAtIndex(0);
    ViewController := Window.mainController;

    MediaPicker := MPMediaPickerController.alloc.initWithMediaTypes(MPMediaTypeAnyAudio);
    MediaPicker.setAllowsPickingMultipleItems(true);

    MediaPickerDelegateVar := MediaPickerDelegate.alloc;
    MediaPicker.setDelegate(MediaPickerDelegateVar);
    MediaPicker.setPrompt(NSSTR('Pick one'));

    // MySelector := sel_registerName(PChar('mediaPickerDidCancel:')); //Might need next 2
    // ViewController.view.setAnimationDidStopSelector(MySelector);

    ViewController.presentModalViewController_animated(MediaPicker,True);
    MediaPicker.release;
    end;

    or a simple file listing or your dirs.

    type
    TFileList = record
    Count : Integer;
    Items : array of String;
    CanRead : array of boolean;
    CanWrite: array of boolean;
    FileDate: array of String;
    end;

    procedure file_Find( const Directory : String; var List : TFileList; FindDir : Boolean = FALSE );
    var
    i : Integer;
    fileManager : NSFileManager;
    dirContent : NSArray;
    path : NSString;
    fileName : array[ 0..255 ] of Char;
    error : NSErrorPointer;
    isDirectory : Boolean;
    nd : NSDictionary;
    begin
    fileManager := NSFileManager.defaultManager;
    path := NSString( CFStr( PChar( Directory ) ) );
    dirContent := fileManager.contentsOfDirectoryAtPath_error( path , error );
    List.Count := 0;
    fileManager.changeCurrentDirectoryPath( path );
    for i := 0 to dirContent.count() - 1 do
    begin
    if FindDir Then
    begin
    if ( fileManager.fileExistsAtPath_isDirectory( dirContent.objectAtIndex( i ), @isDirectory ) ) and ( not isDirectory ) Then continue;
    end else
    if ( fileManager.fileExistsAtPath_isDirectory( dirContent.objectAtIndex( i ), @isDirectory ) ) and ( isDirectory ) Then continue;

    SetLength( List.Items, List.Count + 1 );
    SetLength( List.CanRead, List.Count + 1 );
    SetLength( List.CanWrite, List.Count + 1 );
    SetLength( List.FileDate, List.Count + 1 );

    FillChar( fileName[ 0 ], 256, 0 );
    CFStringGetCString( CFStringRef( dirContent.objectAtIndex( i ) ), @fileName[ 0 ], 255, kCFStringEncodingUTF8 );
    List.Items[ List.Count ] := PChar( @fileName[ 0 ] );
    try
    nd := fileManager.attributesOfItemAtPath_error(NSSTR(PChar(List.Items[ List.Count ])),nil);
    List.FileDate[ List.Count ] := String(nd.fileCreationDate.description.UTF8String);
    except
    end;
    List.CanRead[ List.Count ] := fileManager.isReadableFileAtPath( NSSTR(PChar(List.Items[ List.Count ])));
    List.CanWrite[ List.Count ] := fileManager.isWritableFileAtPath( NSSTR(PChar(List.Items[ List.Count ])));
    INC( List.Count );
    end;
    fileManager.release;
    end;

    ReplyDelete
  10. And it appears that ou can't use the mediapicker with the URL from the ipod library in AVAudioPlayer (it needs file://) so you have to use ipod player or if you can try to stream the data to local then use it dont know.

    ReplyDelete
  11. I load the music from the resource and play the music from memory.

    http://leoshiang-dev.blogspot.com/2012/05/firemonkey-ios.html

    ReplyDelete
  12. Hi leoshiang!
    Can you provide CopyResourceToMemory function?
    Thanks!

    ReplyDelete
  13. Every weekend i used to pay a visit this website, as i want enjoyment, for the reason that this this website conations actually good funny material too.| www.wattographs.com/js/toms_shoes.asp http://www.wattographs.com/js/toms_shoes.aspx

    ReplyDelete
  14. Henderson-born Ursulina Youmon is intrigued by ヴィトン 財布 rc vehicles, crochet. She's interested in checking out a st louis blues icehockey-game.

    ReplyDelete
  15. We've been a bunch of volunteers in addition to starting a new structure within our community. Your website offered us all by using useful data for you to pictures with. You might have done the challenging occupation in addition to each of our full online community is going to be fortunate to your account.

    ReplyDelete
  16. 頻繁とI 本当に私はブログ| 情報コンテンツ 感謝あなたのあなたのためにありがとうございます。 この偉大な記事 真にた私の関心がピークに達した。 私がします のメモを取ると新しいの詳細をチェックし続ける一度週。あなたのRSSフィード |私はのためにオプトインにサブスクライブ あまりに。| グッチ 長財布グッチ 財布 値段 http://www.eaip.org/tag/%e3%82%b0%e3%83%83%e3%83%81-%e3%82%ab%e3%83%95%e3%82%b9/

    ReplyDelete
  17. This is really interesting, You're an excessively professional blogger. I've joined your rss feed and sit up for in quest of more of your fantastic post. Additionally, I have shared your web site in my social networks

    ReplyDelete
  18. Oahu is the ideal time in making a few programs to the years to come and it's really time for you to be very glad. We've master this particular offered of course, if I'll simply just I wish to advocate you handful of exciting points or maybe guidelines. You may can certainly write subsequent content in regards to this article. I'm going to find out far more aspects of them!

    ReplyDelete
  19. You realize thus significantly with regards to this topic, made me in my opinion consider it from so many various angles. Its like men and women aren't fascinated except it's something to do with Lady gaga! Your individual stuffs nice. All the time take care of it up!

    ReplyDelete
  20. We appreciate you the excellent writeup. Them actually was once a new entertainment profile that. Glimpse intricate to help much increased pleasant of your stuff! Mind you, just how may many of us keep up a correspondence?

    ReplyDelete
  21. London wide Sound Test provider. HIgh quality sound and air pressure testing.

    ReplyDelete
  22. Magnificent website. Lots of useful info here. I am sending it to some buddies ans also sharing in delicious. And of course, thanks in your sweat!

    ReplyDelete
  23. We are a bunch of volunteers and opening a new scheme in our community. Your site offered us with useful info to work on. You've performed an impressive job and our whole group will likely be thankful to you.

    ReplyDelete
  24. It has the like you examine my head! You peer to learn considerably roughly the following, such as you submitted a e-book there as well. I am that can be done with a few Percentage in order to tension what it's all about dwelling slightly, however as an alternative to that will, that is excellent site. A terrific read through. I'm going to certainly return to their office.

    ReplyDelete