Autor Téma: přehrání zvuku ve firemonkey - zamrznutí aplikace na desítky milisekund  (Přečteno 126 krát)

Offline age.new

  • Plnoletý
  • ***
  • Příspěvků: 136
  • Karma: 0
Vážená komunito,

obracím se na Vás s prosbou o radu. Ve Firemonkey aplikaci bych chtěl přehrát jeden malý wav soubor. Doteď k tomu využívám komponentu AudioManager viz kód níže:

Kód: Delphi [Vybrat]
  1. unit AudioManager;
  2.  
  3. interface
  4.  
  5. uses
  6.   System.SysUtils, System.Types, System.UITypes, System.Classes, FMX.Dialogs, FMX.Forms
  7. {$IFDEF ANDROID}
  8.   ,androidapi.jni.media, FMX.Helpers.Android, androidapi.jni.JavaTypes, Androidapi.JNI.GraphicsContentViewText, Androidapi.JNIBridge,
  9.   androidapi.helpers, androidapi.JNI.App, System.Threading
  10. {$ENDIF}
  11. {$IFDEF IOS}
  12.   ,MacApi.CoreFoundation, FMX.Platform.iOS, iOSapi.CocoaTypes, iOSapi.AVFoundation, iOSapi.Foundation
  13. {$ELSE}
  14. {$IFDEF MACOS}
  15.   ,MacApi.CoreFoundation, FMX.Platform.Mac, Macapi.CocoaTypes, Macapi.AppKit, Macapi.Foundation, Macapi.Helpers
  16. {$ENDIF}
  17. {$ENDIF}
  18. {$IFDEF MSWINDOWS}
  19.   ,MMSystem
  20. {$ENDIF}
  21. ;
  22.  
  23. type
  24.   TSoundRec = record
  25.     SFilename: String;
  26.     SName: String;
  27.     SNameExt: string;
  28.     SID: integer;
  29.   end;
  30.   PSoundRec = ^TSoundRec;
  31.  
  32.   TAudioManager = Class
  33.   Private
  34.     fSoundsList: TList;
  35.  
  36. {$IFDEF ANDROID}
  37.     fAudioMgr: JAudioManager;
  38.     fSoundPool: JSoundPool;
  39. {$ENDIF}
  40.  
  41.     function GetSoundsCount: integer;
  42.     function GetSoundFromIndex(Aindex: integer): PSoundRec;
  43.   Public
  44.     Constructor Create;
  45.     Destructor Destroy; override;
  46.  
  47.     function  AddSound(ASoundFile: string): integer;
  48.     procedure DeleteSound(AName: String); overload;
  49.     procedure DeleteSound(AIndex: integer); overload;
  50.     procedure PlaySound(AName: String); overload;
  51.     procedure PlaySound(AIndex: integer); overload;
  52.  
  53.     property  SoundsCount: Integer read GetSoundsCount;
  54.     property  Sounds[AIndex: integer]: PSoundRec read GetSoundFromIndex ;
  55.   end;
  56.  
  57. {$IFDEF IOS}
  58. const
  59.   _libAudioToolbox = '/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox';
  60.  
  61.   procedure AudioServicesPlaySystemSound(inSystemSoundID: nsinteger); cdecl; external _libAudioToolbox name 'AudioServicesPlaySystemSound';
  62.   procedure AudioServicesCreateSystemSoundID(inFileURL: CFURLRef; var SystemSoundID: pnsinteger); cdecl; external _libAudioToolbox name 'AudioServicesCreateSystemSoundID';
  63.   procedure AudioServicesDisposeSystemSoundID(inSystemSoundID: nsinteger); cdecl; external _libAudioToolbox name 'AudioServicesDisposeSystemSoundID';
  64.   procedure AudioServicesAddSystemSoundCompletion(inSystemSoundID : nsinteger; inRunLoop: CFRunLoopRef; inRunLoopMode: CFStringRef; inCompletionRoutine: Pointer; inClientData: CFURLRef); cdecl; external _libAudioToolbox name 'AudioServicesAddSystemSoundCompletion';
  65. {$ENDIF}
  66.  
  67. implementation
  68.  
  69. { TAudioManager }
  70.  
  71. {$IF Defined(IOS) OR Defined(MACOS)}
  72. procedure oncompleteionIosProc(SystemSndID: nsinteger; var AData: Pointer);
  73. begin
  74.   // place here the code to run when a sound finish playing
  75. end;
  76. {$ENDIF}
  77.  
  78. constructor TAudioManager.Create;
  79. begin
  80.   try
  81.     fSoundsList := TList.Create;
  82. {$IFDEF ANDROID}
  83.     fAudioMgr  := TJAudioManager.Wrap((SharedActivity.getSystemService(TJContext.JavaClass.AUDIO_SERVICE) as ILocalObject).GetObjectID);
  84.     fSoundPool := TJSoundPool.JavaClass.init(4, TJAudioManager.JavaClass.STREAM_MUSIC, 0);
  85. {$ENDIF}
  86.  
  87.   except
  88.     On E:Exception do
  89.       Raise Exception.create('[AudioManager.Create] : ' + E.message);
  90.   end;
  91. end;
  92.  
  93. destructor TAudioManager.Destroy;
  94. var
  95.   i: integer;
  96.   wRec: PSoundRec;
  97. begin
  98.   try
  99.     for i := fSoundsList.Count -1 downto 0 do
  100.     begin
  101.       wRec := fSoundsList[i];
  102.       Dispose(wRec);
  103.       fSoundsList.Delete(i);
  104.     end;
  105.     fSoundsList.Free;
  106. {$IFDEF ANDROID}
  107.     fSoundPool := nil;
  108.     fAudioMgr := nil;
  109. {$ENDIF}
  110.     inherited;
  111.   except
  112.     On E:Exception do
  113.       Raise Exception.create('[AudioManager.Destroy] : ' + E.message);
  114.   end;
  115. end;
  116.  
  117. function TAudioManager.AddSound(ASoundFile: string): integer;
  118. var
  119.   wSndRec: PSoundRec;
  120. {$IFDEF IOS}
  121.   wSndID: NSInteger;
  122.   wNSFilename: CFStringRef;
  123.   wNSURL: CFURLRef;
  124.   wCFRunLoopRef: CFRunLoopRef;
  125.   winRunLoopMode: CFStringRef;
  126. {$ENDIF}
  127. begin
  128.   try
  129.     New(wSndRec);
  130.     wSndRec.SFilename := ASoundFile;
  131.     wSndRec.SNameExt  := ExtractFilename(ASoundFile);
  132.     wSndRec.SName     := ChangeFileExt(wSndRec.SNameExt,'');
  133.  
  134. {$IFDEF ANDROID}
  135.     wSndRec.SID := fSoundPool.load(StringToJString(ASoundFile) ,0);
  136. {$ENDIF}
  137. {$IFDEF IOS}
  138.     wNSFilename := CFStringCreateWithCharacters(nil, PChar(ASoundFile), Length(ASoundFile));
  139.     wNSURL      := CFURLCreateWithFileSystemPath(nil, wNSFilename, kCFURLPOSIXPathStyle, False);
  140.     AudioServicesCreateSystemSoundID(wNSURL, PNSInteger(wSndID));
  141.     wSndRec.SID := wSndID;
  142.     AudioServicesAddSystemSoundCompletion(wSndID, nil, nil, @oncompleteionIosProc, nil);
  143. {$ENDIF}
  144.     Result := fSoundsList.Add(wSndRec);
  145.   except
  146.     On E:Exception do
  147.       Raise Exception.create('[AudioManager.AddSound] : ' + E.message);
  148.   end;
  149. end;
  150.  
  151. procedure TAudioManager.DeleteSound(AIndex: integer);
  152. var wRec: PSoundRec;
  153. begin
  154.   try
  155.     if AIndex < fSoundsList.Count then
  156.     begin
  157.       wRec := fSoundsList[AIndex];
  158. {$IFDEF ANDROID}
  159.       fSoundPool.unload(wRec.SID);
  160. {$ENDIF}
  161. {$IFDEF IOS}
  162.       AudioServicesDisposeSystemSoundID(wRec.SID);
  163. {$ENDIF}
  164.       Dispose(wRec);
  165.       fSoundsList.Delete(AIndex);
  166.     end;
  167.   except
  168.     On E:Exception do
  169.       Raise Exception.create('[AudioManager.DeleteSound] : ' + E.message);
  170.   end;
  171. end;
  172.  
  173. procedure TAudioManager.DeleteSound(AName: String);
  174. var i: integer;
  175. begin
  176.   try
  177.     for i := 0 to fSoundsList.Count -1 do
  178.       if CompareText(PSoundRec(fSoundsList[i]).SName , AName) = 0 then
  179.       begin
  180.         DeleteSound(i);
  181.         Break;
  182.       end;
  183.   except
  184.     On E:Exception do
  185.       Raise Exception.create('[AudioManager.DeleteSound] : ' + E.message);
  186.   end;
  187. end;
  188.  
  189. procedure TAudioManager.PlaySound(AIndex: integer);
  190. var
  191.   wRec: PSoundRec;
  192. {$IFDEF ANDROID}
  193.   wCurrVolume, wMaxVolume: Double;
  194.   wVolume: Double;
  195. {$ENDIF}
  196. {$IFNDEF IOS}
  197. {$IFDEF MACOS}
  198.   wNssound: NSSound;
  199. {$ENDIF}
  200. {$ENDIF}
  201. begin
  202.   try
  203.     if AIndex < fSoundsList.Count then
  204.     begin
  205.       wRec := fSoundsList[AIndex];
  206. {$IFDEF ANDROID}
  207.       if Assigned(fAudioMgr) then
  208.       begin
  209.         wCurrVolume := fAudioMgr.getStreamVolume(TJAudioManager.JavaClass.STREAM_MUSIC);
  210.         wMaxVolume  := fAudioMgr.getStreamMaxVolume(TJAudioManager.JavaClass.STREAM_MUSIC);
  211.         wVolume     := wCurrVolume / wMaxVolume;
  212.         fSoundPool.play(wRec.SID, wVolume, wVolume, 1, 0, 1);
  213.       end;
  214. {$ENDIF}
  215. {$IFDEF IOS}
  216.       AudioServicesAddSystemSoundCompletion(wRec.SID, nil, nil, @oncompleteionIosProc, nil);
  217.       AudioServicesPlaySystemSound(wRec.SID)
  218. {$ELSE}
  219. {$IFDEF MACOS}
  220.       wNSSound := TNSSound.Wrap(TNSSound.Alloc.initWithContentsOfFile(StrToNSStr(wRec.SFilename), true));
  221.       try
  222.         wNSSound.setLoops(False);
  223.         wNSSound.play;
  224.       finally
  225.         wNSSound.Release;
  226.       end;
  227. {$ENDIF}
  228. {$ENDIF}
  229. {$IFDEF MSWINDOWS}
  230.       sndPlaySound(Pchar(wRec.SFilename), SND_NODEFAULT Or SND_ASYNC);
  231. {$ENDIF}
  232.     end;
  233.   except
  234.     On E:Exception do
  235.       Raise Exception.create('[AudioManager.PlaySound] : ' + E.message);
  236.   end;
  237. end;
  238.  
  239. procedure TAudioManager.PlaySound(AName: String);
  240. var i: integer;
  241. begin
  242.   try
  243.     for i := 0 to fSoundsList.Count -1 do
  244.       if CompareText(PSoundRec(fSoundsList[i]).SName , AName) = 0 then
  245.       begin
  246.         PlaySound(i);
  247.         Break;
  248.       end;
  249.   except
  250.     On E:Exception do
  251.       Raise Exception.create('[AudioManager.PlaySound] : ' + E.message);
  252.   end;
  253. end;
  254.  
  255. function TAudioManager.GetSoundsCount: integer;
  256. begin
  257.   Result := fSoundsList.Count;
  258. end;
  259.  
  260. function TAudioManager.GetSoundFromIndex(Aindex: integer): PSoundRec;
  261. begin
  262.   if Aindex < fSoundslist.Count then
  263.     Result := fSoundsList[AIndex]
  264.   else
  265.     Result := nil;
  266. end;
  267.  
  268. end.
  269.  

Při startu aplikace přidám wav soubor pomocí AddSound funkce. Poté jej přehrávám pomocí PlaySound procedury. Vše funguje, zvuk se přehraji i na Androidu. Bohužel přehrání doprovází malé zaseknutí aplikace na desítky milisekund. A to přehrávám jen jedem malý wav soubor. Výše uvedený kód není můj, vzal jsem jej z nějaké hry (pokud si dobře pamatuji). Jen nevím proč dochází k tomu zamrznutí. Určitě to není výkonem HW, protože se to děje i na mém počítači.

Popřípadě existuje jiné řešení?

Děkuji za případné rady.

Offline tomak586

  • Nováček
  • *
  • Příspěvků: 16
  • Karma: 1
Ahoj, ja jsem kdysi na prehravani zvuku pod Androidem pouzil komponentu TMediaPlayer a nevybavuji si ze by byl nejaky problem.

mpl.FileName := System.IOUtils.TPath.GetTempPath + '/win.mp3';
mpl.Play;

Offline age.new

  • Plnoletý
  • ***
  • Příspěvků: 136
  • Karma: 0
Mám pocit, že u MediaPlayeru jsem měl podobný výsledek, tj. docházelo k malému probrždění v momentě, kdy se zavolalo spuštění zvuku.. Proto jsem použil jiný způsob.