Delphiでお手軽プログラミング

Delphiでお手軽プログラミングメニュー

録音再生を行うAndroidアプリケーションを作成する

~Delphiで録音、録音した音の再生を行うAndroidアプリケーションを作成する(TMediaPlayer、TAudioCaptureDevice)


1.Delphiを起動

Delphiを起動して新規FMXプロジェクトを作成し、ターゲットプラットフォームをAndroidに設定して、 フォームにTButtonを2つ、TTrackbarを1つ、TMediaPlayerを1つ、TTimerを1つ配置します。
(スタイル:をAndroidに切り替えると、実行時に近い表示になるので便利です。)
プロジェクトとユニットを保存します。


Delphi XE10.3.xの場合

Delphi XE10.3.xは、C:\Program Files (x86)\Embarcadero\Studio\20.0\source\fmx\FMX.Media.Android.pasに不具合があるので、 このファイルを保存したプロジェクトと同じフォルダ内にコピーし、以下2行を修正します。
修正したら、「プロジェクトに追加(Shift+F11)」で修正したFMX.Media.Android.pasを追加します。
function TAndroidMedia.GetCurrent: TMediaTime;
begin
  1114行目
  Result := Round(FPlayer.getCurrentPosition * MediaTimeScale / MSecsPerSec);}
  ↓以下に修正
  Result := FPlayer.getCurrentPosition * int64(MediaTimeScale) div int64(MSecsPerSec);
end;

function TAndroidMedia.GetDuration: TMediaTime;
begin
  1119行目
  Result := Round(FPlayer.getDuration * MediaTimeScale / MSecsPerSec);
  ↓以下に修正
  Result := FPlayer.getDuration * int64(MediaTimeScale) div int64(MSecsPerSec);
end;

2.権限設定

プロジェクト⇒オプションをクリックし、アプリケーション⇒使用する権限 から 「音声の録音」にチェックを入れて(True)、「保存」ボタンを押します。

3.ソースコードの記述

Button1を押したら録音します。Button2を押したら再生します。

unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Media,
  FMX.StdCtrls, FMX.Controls.Presentation, FMX.ScrollBox,
  System.Permissions,
  FMX.Media.Android;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    TrackBar1: TTrackBar;
    MediaPlayer1: TMediaPlayer;
    Timer1: TTimer;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure TrackBar1Change(Sender: TObject);
  private
    { private 宣言 }
    FFileName:String;
    FAudioCaptureDevice:TAudioCaptureDevice;
    procedure PermissionRequestResult(
      Sender: TObject; const APermissions: TArray<string>;
      const AGrantResults: TArray<TPermissionStatus>);
    procedure RequestPermissions();
  public
    { public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses
  System.IOUtils, Androidapi.Helpers, Androidapi.JNI.Os,
  Androidapi.Jni.Support, FMX.DialogService, FMX.Platform;

{$R *.fmx}

procedure TForm1.Button1Click(Sender: TObject);
begin
  if Button1.Tag=0 then
  begin
    //録音開始
    if FileExists(FFileName) then
      DeleteFile(FFileName);
    FAudioCaptureDevice.StartCapture;
    Button1.Text:='録音停止';
    Button1.Tag:=1;
    Button2.Enabled:=False;
  end
  else
  begin
    //録音停止
    FAudioCaptureDevice.StopCapture;
    Button1.Text:='録音開始';
    Button1.Tag:=0;
    Button2.Enabled:=True;
  end;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  if Button2.Tag=0 then
  begin
    //再生開始
    TImer1.Enabled:=True;
    Button2.Text:='再生停止';
    Button2.Tag:=1;
    Button1.Enabled:=False;
    MediaPlayer1.Clear;
    MediaPlayer1.FileName:=FFileName;
    MediaPlayer1.Play;
  end
  else
  begin
    //再生停止
    Timer1.Enabled:=False;
    Button2.Text:='再生開始';
    Button2.Tag:=0;
    if Assigned(FAudioCaptureDevice) then
      Button1.Enabled:=True;
    MediaPlayer1.Stop;
    MediaPlayer1.Clear;
    TrackBar1.Value:=0;
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Button1.Text:='録音開始';
  Button2.Text:='再生開始';
  Timer1.Enabled:=False;
  TImer1.Interval:=200;
  //録音、再生のファイル名を設定
  {$IF DEFINED(ANDROID) OR DEFINED(IOS)}
  FFileName:=TPath.Combine(
    System.IOUtils.TPath.GetDocumentsPath,'test.mp3');
  {$ELSE}
  FFileName:=TPath.Combine(
    System.IOUtils.TPath.GetDocumentsPath,'test.wav');
  {$ENDIF}
  //再生ファイルが無い場合は再生ボタンを使用不可にする
  if not FileExists(FFileName) then
    Button2.Enabled:=False;
  //録音デバイスの取得
  FAudioCaptureDevice :=
    TCaptureDeviceManager.Current.DefaultAudioCaptureDevice;
  if Assigned(FAudioCaptureDevice) then
    //録音デバイスにファイル名を設定する
    FAudioCaptureDevice.FileName:=FFileName
  else
    //録音デバイスが無い場合は録音ボタンを使用不可にする
    Button1.Enabled:=False;

  //権限の取得 APIレベル23以降
  RequestPermissions();
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  if Button1.Tag=1 then
    FAudioCaptureDevice.StopCapture;
  if Button2.Tag=1 then
    MediaPlayer1.Stop;
end;

procedure TForm1.PermissionRequestResult(Sender: TObject;
  const APermissions: TArray<string>;
  const AGrantResults: TArray<TPermissionStatus>);
begin
  //音声の録音の権限があるか
  if (AGrantResults[0]<>TPermissionStatus.Granted) then
  begin
    //権限がない場合
    //「□今後は表示しない」チェックボックスにチェックが入っているか
    if (TJActivityCompat.JavaClass.shouldShowRequestPermissionRationale(
         TAndroidHelper.Activity,
         TJManifest_permission.JavaClass.RECORD_AUDIO)) then
    begin
      //「□今後は表示しない」チェックボックスにチェックが入っていない場合
      // 非同期でダイアログを表示して説明と許可を要求
      TDialogService.MessageDialog(
        '許可しないとアプリが動作しません。',TMsgDlgType.mtInformation,
        [TMsgDlgBtn.mbOK],TMsgDlgBtn.mbOk,0,
        procedure (const AResult: TModalResult)
        begin
          //2回目以降は「□今後は表示しない」チェックボックスが表示される
          RequestPermissions();
        end);
    end
    else
    begin
      //「□今後は表示しない」チェックボックスにチェックが入っている
      TDialogService.MessageDialog(
        '権限が無いため終了します。「設定⇒アプリと通知」から権限を設定してください。',
        TMsgDlgType.mtError,[TMsgDlgBtn.mbOK],TMsgDlgBtn.mbOK,0,
        procedure(const AResult:TModalResult)
        begin
          Application.Terminate;
        end
      );
    end;
  end;
end;

procedure TForm1.RequestPermissions;
var FPmsRecordAudio: string;
begin
  FPmsRecordAudio:=JStringToString(
    TJManifest_permission.JavaClass.RECORD_AUDIO);
  PermissionsService.RequestPermissions(
    [FPmsRecordAudio],PermissionRequestResult);
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  if MediaPlayer1.State=TMediaState.Playing then
  begin
    TrackBar1.Tag:=1;
    TrackBar1.Min:=0;
    {$IFDEF ANDROID}
      //FMX.Media.Android.pasのバグ対応(XE10.2.x以下)
      //TrackBar1.Max:=MediaPlayer1.Duration;
      //TrackBar1.Value:=MediaPlayer1.CurrentTime;
      //XE10.3以降
      TrackBar1.Max:=
        MediaPlayer1.Duration * MSecsPerSec / MediaTimeScale;
      TrackBar1.Value:=
        MediaPlayer1.CurrentTime * MSecsPerSec / MediaTimeScale;
    {$ELSE}
      TrackBar1.Max:=
        MediaPlayer1.Duration * MSecsPerSec / MediaTimeScale;
      TrackBar1.Value:=
        MediaPlayer1.CurrentTime * MSecsPerSec / MediaTimeScale;
    {$ENDIF}
    TrackBar1.Tag := 0;
    if MediaPlayer1.CurrentTime>=MediaPlayer1.Duration then
      Button2Click(nil);
  end
  else
  begin
    Button2Click(nil);
  end;
end;

procedure TForm1.TrackBar1Change(Sender: TObject);
begin
  if TrackBar1.Tag=0 then
  begin
    {$IFDEF ANDROID}
      //FMX.Media.Android.pasのバグ対応
      MediaPlayer1.CurrentTime := Round(TrackBar1.Value);
    {$ELSE}
      MediaPlayer1.CurrentTime :=
        Round(TrackBar1.Value * MediaTimeScale / MSecsPerSec);
    {$ENDIF}
  end;
end;
end.





Copyright 2019 Mam