Cuestión de volumen… (parte I)
Hace 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
Utilizando 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.
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.
Embarcadero MVP.
Analista y Programador de Sistemas Informáticos.
Estudios de Informática (Ingeniería Técnica Superior) en la UPC (Universidad Politécnica de Barcelona).
Llevo utilizando Delphi desde su versión 3. Especialista en diseño de componentes, Bases de Datos, Frameworks de Persistencia, Integración Continua, Desarrollo móvil,…
Como siempre, fabuloso. Muchas gracias.
Maese Neftaly, como siempre, un artículo excelente. Me quito el sombrero.
Gracias a ambos.
Espero completarla en breve con otra que modifique perfiles.
Un saludo.
Genial¡¡¡ …esta información …Saludos y esperamos más conocimiento ¡¡¡