Search code examples
javaandroiddelphimediarecorderdelphi-10.2-tokyo

Problems with Android MediaRecorder JNI (Delphi)


I am using RAD Studio 10.2 Tokyo and am programming in Delphi.

I am having general problems with a Java object using the JNI libraries.

What I am trying to do is find the maximum audio amplitude of an Android audio source between button presses.

Here is the code that I am using.

var
  Form1: TForm1;
  Recorder: JMediaRecorder;

procedure TForm1.Button1Click(Sender: TObject);
begin
  if not assigned(Recorder) then
  begin
    Recorder:= TJMediaRecorder.Create();
    Recorder.setAudioSource(1);
    Recorder.setOutputFormat(1);
    Recorder.setAudioEncoder(1);
    Recorder.setOutputFile(TPath.GetTempPath+'/test');
    Recorder.prepare();
    Recorder.start();
  end;
  Label1.Text:=IntToStr(Recorder.getMaxAmplitude());
end;

This appears to generate multiple issues.

Issue #1:

Although I am aware that in java the Media recorder settings should look something like

Recorder.setAudioSource(MediaRecorder.AudioSource.MIC).

That is not something that I can enter in RAD Studio. I took a look at the API and found that that is supposed to mean an integer of 1. The problem is that it gives me an error saying that it can't find an audio source.

Issue #2:

I need to set a path for the file using

Recorder.setOutputFile()

The problem with this is that it only accepts JStrings as input. I have looked all over the place and found a few people who use

StringToJString('my super cool string')

I think this is a part of a library that I am unaware of though because it doesn't work when I use it.

If you know of another way to get an amplitude reading off an Android audio source I would also accept that as an answer as that is the overall problem I am trying to solve.


Solution

  • In general, within the Delphi Android JNI framework:

    • Java class types are represented in Delphi using classes that are prefixed with TJ, and Java object instances are represented in Delphi using interfaces that are prefixed with J.

    • nested Java classes are represented by Delphi classes and interfaces that are named after the parent class name followed by _ followed by the nested class name.

    • Java static constants/fields/properties/methods are represented in Delphi as sub-properties/methods of a special JavaClass class property.

    So, in your case:

    • the Java MediaRecorder class is represented by the TJMediaRecorder class and JMediaRecorder interface.

    • the Java MediaRecorder.AudioSource class is represented by the TJMediaRecorder_AudioSource class and JMediaRecorder_AudioSource interface.

    • the Java MediaRecorder.AudioSource.MIC constant is represented by the TJMediaRecorder_AudioSource.JavaClass.MIC class property:

      Recorder.setAudioSource(TJMediaRecorder_AudioSource.JavaClass.MIC);
      

    The same thing applies to the MediaRecorder.setOutputFormat() and MediaRecorder.setAudioEncoder() methods:

    Recorder.setOutputFormat(TJMediaRecorder_OutputFormat.JavaClass.THREE_GPP);
    Recorder.setAudioEncoder(TJMediaRecorder_AudioEncoder.JavaClass.AMR_NB);
    

    As for strings, JNI knows nothing about Delphi strings, so you must use StringToJString() to convert Delphi strings to JNI strings, and use JStringToString() to convert JNI strings to Delphi strings. Both functions are in the Delphi RTL, specifically in the Androidapi.Helpers unit (XE6+) or Androidapi.JNI.JavaTypes unit (XE5), depending on which version of Delphi you are using.

    Recorder.setOutputFile(StringToJString(TPath.Combine(TPath.GetTempPath, 'test')));