Inicio > Android, Delphi, Ejemplos, Embarcadero, sonido > Cuestión de volumen… (parte I)

Cuestión de volumen… (parte I)

Share Button

volumen-audio-10054183Hace unos días se planteaba en el clubdelphi una cuestión acerca de la gestión de volúmenes en los dispositivos Android. Me llamó la atención porque días atrás me había planteado la cuestión de poder disponer de «perfiles de audio» en mi propio teléfono Android. Se trata de un Xiaomi con versión KitKat instalada y una ROM personalizada.

  • ¿Cómo podemos desde Delphi acceder a los volúmenes del dispositivo para las diferentes características (música, alarmas, llamadas,…)?
  • ¿Cómo podemos cambiarlos?

Esta estrada, al final la he dividido en dos. Os adjunto los links a las dos partes; Est a primera más itroductoria y la segunda más práctica y donde generamos una aplicación de ejemplo funcional.

CONTROL DE VOLUMEN

Lo primero es saber cómo podemos acceder a esta información utilizando las clases de Android. Si revisamos la API de Android llegamos a la clase AudioManager,  que es la que se encarga de estos menesteres (y algunos más).
La descripción de la clase dice lo siguiente:

«AudioManager provides access to volume and ringer mode control.»

Si revisamos un poco las clase en una primera lectura, ya podemos ver algunas constantes interesantes como estas:

int    STREAM_ALARM:        The audio stream for alarms
int    STREAM_DTMF:         The audio stream for DTMF Tones
int    STREAM_MUSIC:        The audio stream for music playback
int    STREAM_NOTIFICATION: The audio stream for notifications
int    STREAM_RING:         The audio stream for the phone ring
int    STREAM_SYSTEM:       The audio stream for system sounds
int    STREAM_VOICE_CALL:   The audio stream for phone calls

Que hacen referencia a los diferentes «volúmenes» que podemos gestionar.

Si bajamos un poco más en esa página llegamos a la parte de los métodos. Podemos ver también algunos bastante explicativos como estos:

int    getStreamMaxVolume(int streamType)
  //Returns the maximum volume index for a particular stream.
int    getStreamVolume(int streamType)
  //Returns the current volume index for a particular stream.

Que permiten obtener el volumen máximo y actual para cada uno de los «volúmenes» de los que hablábamos antes.

Desde la descripción y ayuda del método ya se hace referencia a otro método que nos será de mucha utilidad, el setStreamVolume. Su descripción nos dice:

«Sets the volume index for a particular stream.»

Pues ya tenemos nuestro punto de partida.

ACCESO DESDE DELPHI

Lo siguiente sería ver si Delphi posee ya componentes/clases para acceder a estas características, como los trae implementados para acceder a muchas otras (sensores, cámara de foros, bluetooth,…)

Si buscamos en la paleta, no hay ningún componente como TLocationSensor o TBluetooth que nos permita acceder a las características de audio.
No desesperemos, seguimos teniendo más opciones…

ACCESO A CLASES DE JAVA

imagesUtilizando Delphi podemos acceder a las clases java a través de lo que Embarcadero llama «bridge files». Es decir, una series de ficheros escritos en Delphi que hacen de «puente» entre nuestro código Delphi (nuestra aplicación) y las clases de Java.

La instalación de Delphi ya incluye bastantes de estos archivos ya generados, con lo que a tenemos el trabajo hecho. La documentación en la docWiki está accesible aquí

Tal y como se explica ahí, podemos encontrar esas clases en nuestra instalación, en la siguiente ubicación:

  • Delphi units: source\rtl\android (.pas file extension)
  • C++ header files: include\android\rtl (.hpp file extension)

¿Qué pasa si la unit «puente» que necesitamos no se encuentra en esas lista?

Aun así no debemos desesperar. Tenemos varias opciones para generarla nosotros de forma manual. No voy a entrar ahora en este tema, porque tal vez podemos tratar más extensamente en otra entrada en un futuro. Os dejo unos links, por si alguien quiere ir revisando:

En nuestro caso, hemos tenido suerte y en el directorio antes comentado de nuestra instalación tenemos el fichero Androidapi.JNI.Media.pas  que dentro incluye el interface:

JAudioManager = interface;//android.media.AudioManager

Recordáis las constantes que hemos comentado más arriba; Pues dentro de este este fichero podemos encontrar algo como esto (que se parece bastante…):

...
{class} property STREAM_ALARM: Integer read _GetSTREAM_ALARM;
{class} property STREAM_DTMF: Integer read _GetSTREAM_DTMF;
{class} property STREAM_MUSIC: Integer read _GetSTREAM_MUSIC;
{class} property STREAM_NOTIFICATION: Integer read _GetSTREAM_NOTIFICATION;
{class} property STREAM_RING: Integer read _GetSTREAM_RING;
{class} property STREAM_SYSTEM: Integer read _GetSTREAM_SYSTEM;
{class} property STREAM_VOICE_CALL: Integer read _GetSTREAM_VOICE_CALL;
...

Y un poco más abajo que están los procedimientos y funciones, algo como esto:

...
function getStreamMaxVolume(streamType: Integer): Integer; cdecl;
function getStreamVolume(streamType: Integer): Integer; cdecl;
...
procedure setStreamVolume(streamType: Integer; index: Integer; flags: Integer); cdecl;
...

Con esto se nos abre el cielo.  :-D

Una prueba sencilla, nos permitirá saber si vamos por buen camino y ver cómo podemos utilizar esta unit.

Pantalla_Ejemplo

Colocamos un formulario con varios Trackbar y un botón que nos permita recuperar el volumen actual de los diferentes elementos:

procedure TForm1.Button1Click(Sender: TObject);
var
  AudioObj: JObject;
  Audio: JAudioManager;
p:Double;
 
  //·····································································
  procedure InfoAudioLevel(pAudio:JAudioManager; 
         pTracBar: TTrackBar; pStreamType: integer; pEdtValue:TEdit);
  var
    p:Double;
  begin
    pTracBar.Value := pAudio.getStreamVolume(pStreamType);
    pTracBar.Max := pAudio.getStreamMaxVolume(pStreamType);
    p := ((pTracBar.Value / pTracBar.Max) * 100);
    pEdtValue.Text := IntToStr(Round(p)) + ' %';
  end;
  //······································································
 
begin
  // acceso al servicio
  AudioObj:= TAndroidHelper.Activity.getSystemService(TJActivity.JavaClass.AUDIO_SERVICE);
  // acceso a la clase
  Audio := TJAudioManager.Wrap((AudioObj as ILocalObject).GetObjectID);
  // Alarma
  InfoAudioLevel(Audio, tbAlarma, 
    TJAudioManager.JavaClass.STREAM_ALARM, edtAlarma);
  // DTMF
  InfoAudioLevel(Audio, tbDTMF, 
    TJAudioManager.JavaClass.STREAM_DTMF, edtDTMF);
  // MUsica
  InfoAudioLevel(Audio, tbMusica, 
    TJAudioManager.JavaClass.STREAM_MUSIC, edtMusica);
  // notifocaciones
  InfoAudioLevel(Audio, tbNotificaciones, 
    TJAudioManager.JavaClass.STREAM_NOTIFICATION, edtNotificaciones);
  // Ring /llamadas
  InfoAudioLevel(Audio, tbRing, 
    TJAudioManager.JavaClass.STREAM_RING, edtRing);
  // Voz
  InfoAudioLevel(Audio, tbVoz, 
    TJAudioManager.JavaClass.STREAM_VOICE_CALL, edtVoz);
end;

Habrá que añadir las siguientes units al formulario para poder utilizar las clases de Java.

uses
  Androidapi.JNI.Media,
  Androidapi.Helpers,
  Androidapi.JNIBridge,
  AndroidApi.Jni.JavaTypes,
  AndroidApi.Jni.App;

Os adjunto un vídeo de este proyecto funcionando.

Hasta aquí esta entrada.

Como siempre cualquier comentario será bienvenido.

 

Vota este post
Subscribe
Notify of
guest

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

4 Comments
Inline Feedbacks
Ver todos los comentarios
Jhonny
7 years ago

Como siempre, fabuloso. Muchas gracias.

José A.
7 years ago

Maese Neftaly, como siempre, un artículo excelente. Me quito el sombrero.

Asiel
Asiel
7 years ago

Genial¡¡¡ …esta información …Saludos y esperamos más conocimiento ¡¡¡

4
0
Would love your thoughts, please comment.x
()
x