Inicio > Delphi, Tethering, XE7 > Mando a distancia; Tethering con Delphi XE7

Mando a distancia; Tethering con Delphi XE7

viernes, 17 de octubre de 2014 Dejar un comentario Ir a comentarios
Share Button

El tema de Tethering, del que ya he hablado antes en el blog (Tethering, operaciones básicas), se incorporó a la versión XE6 de RAD Studio. Hasta ese momento podíamos utilizar esta característica para conectar aplicaciones y dispositivos utilizando Wifi.

blueT_WifiPara esta nueva versión XE7 se a añadido la posibilidad de conexión mediante Bluetooth.
Además, a las características ya existentes de conexión wifi, se han añadido opciones para poder filtrar o discriminar los dispositivos a los que nos vamos a conectar a partir de máscaras para la IP o para la subred.

Como ya he comentado anteriormente, el Tethering es una característica que podemos utilizar (aunque se haya introducido en las últimas versiones de Delphi) no sólo en aplicaciones FireMonkey (tanto móviles como de escritorio) sino también, en aplicaciones ”antiguas” diseñadas con la VCL.

INTRODUCCIÓN

La idea de esta entrada es repasar más ampliamente en las características de Tethering, de las que ya hablé anteriormente, y profundizar en las posibilidades de esta tecnología, utilizando un proyecto más complejo y completo que el que vimos en la introducción.

Para ello utilizaremos 2 aplicaciones diferentes. Una basada en la VCL y otra desarrollada con Firemonkey para dispositivos móviles.

Reproductor y Mando

PROYECTO VCL

Como proyecto VCL he recuperado un antiguo reproductor que comencé a realizar hace un tiempo y que tenía aparcado pendiente de acabar. Inicialmente está programado en Delphi 6 y debería compilar sin problemas en esta versión y en posteriores (hasta Delphi XE7) a excepción de las características que le he añadido con esta última, como son los estilos y la inclusión de App Tethering.

Las características básicas con las que cuenta son las siguientes:

  • Para la visualización y las funciones básicas utiliza BASS.DLL. La librería BASS,  para los que no la conozcáis, es una librería de audio bastante potente, multiplataforma y  libre para uso no comercial. Para más detalles podéis echarle un vistazo a la web.
  • Utiliza conexión a Internet y los componentes Indy para obtener información de la canción que está reproduciendo en cada momento. Para ello utiliza la información del propio fichero tal y como os mostré en una entrada anterior del blog titulada “Obtener información de una canción”. Todo ello se encapsula en una clase que utiliza Threads para no bloquear el programa principal mientras se hacen las consultas.
  • Utilizando las ultimas versiones de delphi, le he añadido soporte para estilos, de forma que se puede cambiar entre varios de ellos en ejecución.

Os adjunto alguna imagen del proyecto tal y como ha quedado y en el vídeo posterior lo veréis funcionando.

Captura__17_repro_Todo

Al proyecto original (diseñado con Delphi 6/7) utilizando la VCL le he añadido código para poder utilizar Tethering y comunicarse con una aplicación móvil que hará las veces de “mando a distancia”.
Lo primero es incluir los dos componentes nuevos e imprescindibles que nos permitirán trabajar con Tethering; Un TTetheringManager y un TTetheringAppProfile. El primero podríamos decir, que es el encargado de todo lo relacionado con la conexión entre dispositivos y el segundo el que controla todas las acciones y recursos que comparten las aplicaciones una vez que ya están conectadas.

Además de estos dos componentes (que son indispensables), vamos añadir Acciones y recursos a nuestra aplicación. Con ellos podremos completar todas las operaciones/comunicaciones que deseamos realizar entre ambos programas.

AccionesVCLACCIONES

El programa de por si, tiene unas acciones predefinidas propias de un reproductor (incluidas en un TActionList estándar), como son el PLAY, PAUSE, STOP, SIGUIENTE,… como se aprecian el la imagen derecha.

Lo que he hecho es “mapear” estas acciones en el componente TTetheringAppProfile como públicas y con la propiedad Kind a Shared. De esta forma podrán ser llamadas/invocadas desde otra aplicación conectada a esta, utilizando, por ejemplo, el método RunRemoteAction de la clase Action_PlayTTetheringAppProfile.

A la izquierda se puede ver una imagen de cómo está “mapeada” la acción PLAY del reproductor. De forma similar a esta se hace con el resto. Tiene asignado un nombre único “rPlay”, que será con el que se invoque y está asociada a la acción del programa ActionPlay.

Cuando desde el mando a distancia ejecutemos el siguiente código:

ttProfiler.RunRemoteAction(pInfo, 'rPlay');

Se ejecutará la acción ActionPlay definida en nuestro programa. Más adelante cuando hablemos del mando a distancia, repasaremos esto.

En cuanto a las acciones no hay que hacer nada más, tan simple como esto. Debemos/podemos “mapear” las acciones que necesitemos ejecutar utilizando el Inspector de objetos.

RECURSOS

Si utilizando las acciones podemos conseguir ejecutar comandos entre diferentes aplicaciones, utilizando recursos lo que vamos a conseguir es poder compartir información entre los programas que se están comunicando. Utilizando ambos, deberíamos poder hacer todo lo necesario.

En el componente TTetheringAppProfile (igual que con las acciones) vamos a definir los recursos que utilizaremos para enviar información al “Mando a distancia”.

En mi caso, he utilizado diferentes recursos para la información que deseo enviar desde el reproductor y que deseo que reciba el mando a distancia; Es decir, todo lo referente a la canción que se está reproduciendo en ese momento. De esta forma, definiré recursos para el título, el artista, género, duración de la canción, volumen,…

Todos ellos se irán actualizando en cada momento con la información actual de la reproducción.

Recursos

Durante la ejecución de la aplicación (reproductor) lo único que tenemos que hacer es ir actualizando el valor de los recursos a medida que estos van cambiando. Cuando selecciono una canción nueva, modifico los recursos del Título, Artista, Longitud de la canción,… mientras que se está reproduciendo tendré que actualizar el recurso que posee la posición (en tiempo) de la canción o cuando cambio el volumen, el que mantiene este valor.

Para ello bastan unas líneas como estas:

// actualizar los recursos relacionados con la canción actual
ttProfiler.Resources.FindByName('rTiempoTotal').Value := lblDuracion.Caption;
ttProfiler.Resources.FindByName('rMaxCancion').Value := IntToStr(tbCancion.Max);
ttProfiler.Resources.FindByName('rTiempoCancion').Value := lblPosition.Caption;
ttProfiler.Resources.FindByName('rPosCancion').Value := IntToStr(tbCancion.Position);
ttProfiler.Resources.FindByName('rMaxCancion').Value := IntToStr(tbCancion.Max);
...
 
// Al modificar el volumen actualizamos el recurso
ttProfiler.Resources.FindByName('rVolumen').Value := IntToStr(tbVolumen.Position);
...

Aquí accedo a los recursos a partir de su nombre de forma única, utilizando FindByName, aunque también podría hacerlo utilizando la propiedad Items o FindItemID.

En este caso la estrategia que hemos escogido es definir unos recursos donde almacenaremos valores, disponibles para que otras aplicaciones los consulten.

Otra alternativa es enviar directamente los datos a recursos definidos en otras aplicaciones conectadas. Es lo que vamos a hacer para enviar la carátura (imagen JPG) a la aplicación del mando a distancia. Der esta forma podremos ver la alternativa a intercambiar datos.

Para ello vamos a utilizar el método SendStream de la clase TTetheringAppProfile, con un código como este:

// enviar datos a los perfiles remotos (si tenemos alguno) 
  if (ttManager.RemoteProfiles.Count > 0) then begin
 
    // acceder al perfil 
    pInfo := ttManager.RemoteProfiles[0];
 
    // La imagen está en disco
 
    JPEGImage := TJPEGImage.Create(); 
    CaractulaStream := TMemoryStream.Create; 
    try 
      JPEGImage.LoadFromFile(InfoTracks[ID].PathImage); 
      JPEGImage.SaveToStream(CaractulaStream); 
      // Una vez almacenada en un Stream la enviamos 
      ttProfiler.SendStream(pInfo, 'rCaratula', CaractulaStream); 
    finally 
      CaractulaStream.Free; 
      JPEGImage.Free; 
    end; 
  end;

Una vez realizado el envío, en la aplicación que está conectada nos saltará un evento OnResourceReceived que tiene la siguiente cabecera:

procedure TMainForm.ttProfilerResourceReceived
  (const Sender: TObject; const AResource: TRemoteResource);

Basta con comprobar que es el recurso deseado y realizar las acciones pertinentes. En mi caso, como es una imagen, directamente se puede cargar sobre un TImage y mostrar así en el mando a distancia la carátula de la canción que se está reproduciendo en ese momento:

procedure TMainForm.ttProfilerResourceReceived(const Sender: TObject; 
  const AResource: TRemoteResource);
 
begin
 
  _Log('Resource received; ' + AResource.Name); 
  _Log('Hint= ' + AResource.Hint); 
  _Log('Valor= ' + AResource.Value.AsString);
 
  // Es el recurso que esperamos? 
  if (AResource.Hint = 'rCaratula') then begin 
 
    // Cargarlo sobre un TImage 
    imgCaratula.Bitmap.LoadFromStream(AResource.Value.AsStream); 
  end;

Con esto ya tenemos descrito el sistema que utilizamos para enviar información desde el reproductor al mando a distancia conectado.

PROYECTO MÓVIL

El proyecto móvil, como ya se ha visto antes, es un mando a distancia para el reproductor de música. No sólo he intentado que tenga las funciones básicas para controlar la aplicación, sino que además reciba información de la canción que se está reproduciendo en cada momento; Una forma de plasmar el estado del reproductor en el dispositivo que hace de “mando a distancia”.

Mandos

Al entrar en el programa, disponemos de un botón que nos permitirá conectar con nuestro reproductor, si este está presente. Para ello se utiliza el método DiscoverManagers. Si encontramos alguna aplicación a la que poder unirnos, se realiza un PairManager.

Durante este proceso, como ya he comentado antes, “saltan” eventos en ambas aplicaciones, que nos permiten comprobar el estado de cada operación. Algunos de ellos son OnNewManager, OnPairedFromLocal, OnPairedToRemote,…

Una vez completado el “emparejamiento” lo que hago desde la aplicación móvil es subscribirme a los recursos publicados por la aplicación de escritorio. De esta forma, una vez que la suscripción se ha completado, cuando esos recursos se modifiquen, el control remoto recibirá automáticamente una notificación.

// Suscribirse a los recursos remotos 
b := ttProfiler.SubscribeToRemoteItem(RemoteProfiles[0], 'rArtista'); 
b := ttProfiler.SubscribeToRemoteItem(RemoteProfiles[0], 'rAlbum'); 
b := ttProfiler.SubscribeToRemoteItem(RemoteProfiles[0], 'rCancion'); 
b := ttProfiler.SubscribeToRemoteItem(RemoteProfiles[0], 'rGenero'); 
b := ttProfiler.SubscribeToRemoteItem(RemoteProfiles[0], 'rAnyo'); 
b := ttProfiler.SubscribeToRemoteItem(RemoteProfiles[0], 'rVolumen'); 
b := ttProfiler.SubscribeToRemoteItem(RemoteProfiles[0], 'rTiempoTotal'); 
b := ttProfiler.SubscribeToRemoteItem(RemoteProfiles[0], 'rTiempoCancion');
 
…
 
b := ttProfiler.SubscribeToRemoteItem(RemoteProfiles[0], 'ListaCanciones');

Además la aplicación “solicita” mediante una acción remota, que se le envíen los datos iniciales. De esta forma justo después de conectarse recibe toda la información del estado del reproductor. A partir de ese momento, sólo se irán recibiendo aquellos recursos que se van modificando. Esta llamada a una acción remota se realiza con un código como este:

// Perfiles remotos 
i := RemoteProfiles.Count;
 
// Hay alguno? 
  if (i > 0) then begin 
    // Accedemos al primero 
    pInfo := RemoteProfiles[0]; 
    // Ejecutamos la acción remota 
    ttProfiler.RunRemoteAction(pInfo, 'ActionSendInfoToRemote');
 
…

Más o menos con lo explicado hasta ahora ya podemos ver cómo el control remoto se mantiene “actualizado” con el estado que en cada momento tiene el reproductor.

La otra parte del trabajo es la de que el control remoto se comporte realmente como un “control remoto” de nuestra aplicación. Para ello basta con utilizar las acciones publicadas desde la aplicación del reproductor que vimos anteriormente.

Acciones_publicadas

El el componente AppProfiler hemos creado acciones que “mapean” las acciones del formulario (las de siempre).

De esta forma, creamos una acción pública (IsPublic=True) compartida (Shared) llamada rPlay (nombre único) y que ejecutará la acción ActionPlay. Igual para el Pause, Stop,…

Desde nuestro mando a distancia sólo nos queda ejecutar la acción remota rPlay, para que en el reproductor se ejecute la acción ActionPlay.

// Alguien conectado?
if (ttManager.RemoteProfiles.Count > 0) then begin
pInfo := ttManager.RemoteProfiles[0];
 
// Ejecutamos acciones remotas…
ttProfiler.RunRemoteAction(pInfo, 'rPlay');
 
…

 

TIPOS DE CONEXIÓN

Una de las mejoras más importantes en la última versión de Delphi con respecto a Tethering, es la inclusión de un nuevo protocolo de comunicación. A partir de RAD Studio XE7 en el componente TTetheringManager encontramos la propiedad AllowedAdapters, que nos permite seleccionar entre Network (wifi) o Bluetooth. No sólo eso, sino que podemos seleccionar ambos, utilizando “;” o “pipes” (|), de forma que se inicializarán ambos.

Debería bastar con modificar esta propiedad para hacer funcionar nuestro programa con uno u otro protocolo (**).

(**) NOTA: Mi intención, era probar esta nueva característica y poder enseñarla en esta entrada, pero desgraciadamente no he conseguido conectar mi ordenador con la aplicación móvil utilizando bluetooth. Creo que los problemas de conexión me los está generando el ordenador portátil (que ya es algo viejo) y cuyos drivers bluetooth no están correctamente instalados, así que por ahora me he quedado sin poder probar/enseñar esta opción. Sigo intentándolo, pero no quería demorar más la publicación de esta entrada por este tema.

A medida que descubra más sobre el problema lo añadiré a la entrada (prometido).

CONCLUSIÓN

La sensación que deja esta característica una vez que la usas en una aplicación y ves el funcionamiento, es que realmente es algo muy “simple”, pero a la vez muy “útil”. De aquellas cosas que piensas cuando las ves, que ”porqué no se le habrán ocurrido a alguien antes”…

;-D

Salvando las primeras pruebas, cuando ya se dominan un poco los métodos y propiedades de ambos componentes y se entiende el funcionamiento básico de “Acciones” y “Recursos”, la utilización se vuelve bastante simple.

He grabado un vídeo para que se pueda ver funcionando ambas aplicaciones, espero que sea útil junto con el resto de la entrada.

Os adjunto los links para descargar tanto los ejecutables como los fuentes de ambos aplicaciones:

Código fuente del reproduvctor

Código fuente del mando a distancia

Ejecutable del reproductor

Ejecutable del mando a distancia

Apliación APK del mando a distancia (actualizado 25/02/2015)

Código fuente del package PBass.BPL

DLLs y BPLs extras de delphi 6 (PBass.bpl)

NOTA: Hay diferentes versiones de la librería BASS.DLL; en mi caso estoy utilizando una compatible con 64 bits. Si os da problemas en otras versiones/siatemas avisadme por favor.

NOTA2: Gracias a Cocce, que me ha comentado que le daba un error al ejecutar si no está delphi 6 instalado (debido a la compilación del package PBass.bpl -que os dije que había compilado con D6-). He subido el código fuente del package y un par de BPL’s que necesita para ejecutar.


AÑADIDO (25/02/2015): (Gracias iretai).

El usuario Iretai me comenta que la aplicación ha “salido” con demasiados permisos y tiene razón. Por defecto Delphi tiene una serie de permisos “predefinidos” activados, que si no nos acordamos de desactivar quedan activados en su publicación. No es algo sin importancia y menos si la aplicación va a ser una aplicación que posteriormente se distribuya en la tienda de Google.  En este caso, como estábamos hablando de un ejemplo no le di más importancia, pero si la tiene.

Además los permisos activados no son “triviales”, pues hablamos de acceso a las llamadas, a la cámara o al calendario. En concreto las siguientes líneas están en el XML:

  • <uses-permission android:name=”android.permission.CALL_PHONE” />
  • <uses-permission android:name=”android.permission.CAMERA” />
  • <uses-permission android:name=”android.permission.INTERNET” />
  • <uses-permission android:name=”android.permission.READ_CALENDAR” />
  • <uses-permission android:name=”android.permission.READ_EXTERNAL_STORAGE” />
  • <uses-permission android:name=”android.permission.READ_PHONE_STATE” />
  • <uses-permission android:name=”android.permission.WRITE_CALENDAR” />
  • <uses-permission android:name=”android.permission.WRITE_EXTERNAL_STORAGE” />

El código fuente de la aplicación ha estado disponible desde que publiqué la entrada, así que basta con cambiarlos y recompilar, pero ya que también he publicado el APK compilado, los he modificado yo y lo he vuelto a subir actualizado.

También he subido la aplicación a Google Play (donde al descargar nos avisa de los permisos activados) para que también se pueda descargar desde allí.

Os adjunto la captura donde aparecen los cambios de permisos al subir la actualización a Google Play (cosa que está muy bien).

Nuevos_permisos

Como siempre los comentarios, sugerencias, críticas,…  serán bien recibidas.

Un saludo y hasta la próxima.

Share Button
Categories: Delphi, Tethering, XE7 Tags: , ,
  1. casimiro
    sábado, 18 de octubre de 2014 a las 09:10 | #1

    Impresionante :)

  2. Cocce
    sábado, 18 de octubre de 2014 a las 10:16 | #2

    I tried your project but i have an error executing the line
    H := LoadPackage(‘PBass.BPL’);

    PBass.BPL requires “rtl60.bpl” and then the program aborts.

    Would you please provide the PBass.bpl source project?

    Thanks in advance
    Cocce

  3. Neftalí
    domingo, 19 de octubre de 2014 a las 13:05 | #3

    @Cocce
    Tranks Cocce.
    I have upload the source of the project (originally in D6).

    I have uploaded the BPL’s in a separate file.
    Thanks for the advice.

  4. Eliseo GN
    lunes, 20 de octubre de 2014 a las 03:49 | #4

    Que decir, una entrada excelente amigo Germán, si ésto no convence a los que aún tienen dudas que Delphi está haciendo cosas geniales, entonces nada los convencerá.

    Impresionante la forma como abordaste el ejercicio amigo, muchas felicidades.

    Saludos

  5. Neftalí
    lunes, 20 de octubre de 2014 a las 10:47 | #5

    @Eliseo GN
    Gracias Eliseo.
    La verdad es que he intentado que al menos fuera algo atractivo y se saliera de lo que es un ejemplo convencional. Espero que al menos eso aumente el interés.

    Un saludo.

  6. dec
    lunes, 20 de octubre de 2014 a las 15:10 | #6

    Muy interesante el artículo así como la simplicidad y potencia que el invento proporciona. ¿Qué hay debajo de los componentes que se mencionan? ¿Cómo funcionan “realmente”? Sea como sea muchas gracias por la aportación Germán. :)

  7. Neftalí
    lunes, 20 de octubre de 2014 a las 18:21 | #7

    @dec
    Hola David. Gracias por los comentarios.
    Los dos protocolos están definidos en dos unidades creadas expresamente para ello, System.Tether.NetworkAdapter y System.Tether.BluetoothAdapter. A partir de ahí cada uno de ellos se basa en una tecnología diferente que se ha encapsulado para hacerlos más sencillos de utilizar y para que ambas conexiones funcionen, a vista del usuario, de la misma manera (modificando la propiedad AllowedAdapters).

    En el caso de conexión wifi (network) se usa TCPIP ; Revisar la unit System.Tether.Comm; para más detalles la clase TTetheringNetworkComm. Veremos que el cliente y el server, tienen definidas estas dos variables:
    * FTCPClient: IIPTCPClient;
    * FTCPServer: IIPTCPServer;

    En el caso de bluetooth, se usa la unit System.Bluetooth que proporciona todas las clases para conexión entre programas utilizando bluetooth (que se puede hacer sin necesidad de utilizar tethering -pero resulta un poco más complejo-) y otras clases relacionadas, como por ejemplo, las que implementan el protocolo BluetoothLE (Low energy).

    Un saludo.

  8. dec
    martes, 21 de octubre de 2014 a las 13:20 | #8

    Muchas gracias por la información Germán.

  9. Javier
    martes, 4 de noviembre de 2014 a las 10:26 | #9

    Hola.-

    Gran artículo y muy bien explicado.

    Solo tengo una cuestión, si quisiera enviar desde el reproductor al mando además de los datos propios de la canción (titulo, autor, etc.) la canción misma (supongo que mediante un TStream), tienes idea de como deberia enfocar este tema?

    Saludos y muchas gracias por tus articulos.

  10. Neftalí
    jueves, 6 de noviembre de 2014 a las 10:43 | #10

    @Javier
    Hola Javier.
    Lo deberías hacer enviando streams. De la misma forma como envío la carátula (que se hace así -con streams-) puedes enviar la canción.

    No deberías tener problemas en hacerlo.

    Un saludo.

  11. Javier
    jueves, 6 de noviembre de 2014 a las 11:06 | #11

    @Neftalí

    Hola. Gracias por tu respuesta. Actualmente utilizo esto como metodo de envío:

    procedure TForm1.PlayerRemoto(Sender: TObject);
    var
    newDish: TStream;
    begin
    newDish.Create;
    newDish := StringToStream(LoadFile(‘/sdcard2/musica/EMANUELLE.mp3’));
    AppProfile.SendStream(Manager.RemoteProfiles[0], ‘Musica’, newDish); <——Error
    end;

    function TForm1.LoadFile(const FileName: TFileName): string;
    begin
    with TFileStream.Create(FileName,
    fmOpenRead or fmShareDenyWrite) do begin
    try
    SetLength(Result, Size);
    Read(Pointer(Result)^, Size);
    except
    Result := ''; // Deallocates memory
    Free;
    raise;
    end;
    Free;
    end;
    end;

    Desgraciadamente, me devuelve un error cuando envío el stream en la línea indicada más arriba. Se que con imagenes es facil ya que en la propiedad bitmap tenemos la funcion SaveToStream, sin embargo, en audio, no existe esto.

    Saludos.

  12. Javier
    jueves, 6 de noviembre de 2014 a las 13:10 | #12

    Javier :
    @Neftalí
    Hola. Gracias por tu respuesta. Actualmente utilizo esto como metodo de envío:
    procedure TForm1.PlayerRemoto(Sender: TObject);
    var
    newDish: TStream;
    begin
    newDish.Create;
    newDish := StringToStream(LoadFile(‘/sdcard2/musica/EMANUELLE.mp3?));
    AppProfile.SendStream(Manager.RemoteProfiles[0], ‘Musica’, newDish); <——Error
    end;
    function TForm1.LoadFile(const FileName: TFileName): string;
    begin
    with TFileStream.Create(FileName,
    fmOpenRead or fmShareDenyWrite) do begin
    try
    SetLength(Result, Size);
    Read(Pointer(Result)^, Size);
    except
    Result := ”; // Deallocates memory
    Free;
    raise;
    end;
    Free;
    end;
    end;
    Desgraciadamente, me devuelve un error cuando envío el stream en la línea indicada más arriba. Se que con imagenes es facil ya que en la propiedad bitmap tenemos la funcion SaveToStream, sin embargo, en audio, no existe esto.
    Saludos.

    Por cierto, el error que me devuelve es error en parámetros (en newDish). Mi problema es que newDish posiblemente aunque es del tipo correcto, no contenga el audio correctamente.

    Saludos.

  13. Neftalí
    jueves, 6 de noviembre de 2014 a las 13:54 | #13

    @Javier
    Hola Javier.

    Yo probaría a generar utiliando un TMemoryStream un Stream en memoria a partir del MP3 de la canción. Enviarlo, y una vez recibido volver a volcarlo a disco (o a la tarjeta).
    A partir de ahí ya deberías poder reproducirlo.

    Un saludo.

  14. Javier
    viernes, 7 de noviembre de 2014 a las 10:13 | #14

    Gracias. Probaré eso que me comentas. Quería hacerlo de la misma forma como se reproducen los wav, pero según parece, los mp3 no se pueden reproducir directamente desde memoria, sino como tu bien dices, grabandolo primero a disco (o a la tarjeta).

    Saludos.

  15. Neftalí
    viernes, 7 de noviembre de 2014 a las 10:40 | #15

    Javier :

    Quería hacerlo de la misma forma como se reproducen los wav, pero según parece, los mp3 no se pueden reproducir directamente desde memoria, sino como tu bien dices, grabandolo primero a disco (o a la tarjeta).

    Hola Javier.
    Pensé que querías hacer justo lo contrario. Grabar el MP3 a disco para luego reproducirlo.
    Hay poca información para realizar streaming, pero algo hay.
    En la propia página de la librería BASS hay algo. Es posible que utilizando lo librería que he utilizado en el reproductor puedas conseguirlo (http://www.un4seen.com/forum/?topic=15197.0).

    Y en esta página (http://codes-sources.commentcamarche.net/source/100709-android-stream-mp3-implementation-listener-java-depuis-delphi) también explican algo. A ver si te sirve.

    Un saludo.

  16. Javier
    miércoles, 12 de noviembre de 2014 a las 07:43 | #16

    Gracias. He escrito el siguiente código:

    procedure TForm1.PlayerRemoto(Sender: TObject);
    var
    newDish: TMemoryStream;
    //sound: TSoundItem;
    begin
    newDish:=TMemoryStream.Create;
    try
    newDish.LoadFromFile(‘/sdcard2/musica/EMANUELLE.mp3’);
    newDish.Position:=0;
    AppProfile.SendStream(Manager.RemoteProfiles.Items[0], ‘Musica’, newDish);
    finally
    newDish.Free
    end;
    end;

    Pero desafortunadamente me da error de “Parameter out of range” en el paso de parámetros en el appProfile. Me temo que no carga correctamente el archivo mp3 en el memorystream o bien al enviar newDish no es la forma correcta de enviarlo.

  17. mike
    viernes, 14 de noviembre de 2014 a las 11:57 | #17

    gran artículo. No pienso lo mismo de tu gusto musical :D

  18. Neftalí
    viernes, 14 de noviembre de 2014 a las 14:13 | #18

    @mike
    Gracias.

    Ya sabes, como se suele decir, “sobre gustos, no hay nada escrito…”
    ;-DD

  19. iretai
    miércoles, 25 de febrero de 2015 a las 00:31 | #19

    Muy interesante, gracias por comparti.

    el apk bajado, solicita permisos com hacer llamadas telefonicas, localizarme a traves del GPS, Leer informacion como ID e IMEI, Acceder a mi camara y finalmente permisos para manipular mi disco. Creo que ninguna de estas solicitudes es necesaria, claro que son interesantes de utilizar si uno quiere usmear al que bajo la apk.

  20. Neftalí
    miércoles, 25 de febrero de 2015 a las 09:51 | #20

    @iretai
    Hola iretai.
    Seguramente tienes razón. Deben ser los permisos que Delphi tiene activos por defecto al generar una aplicación que en este caso debí dejarlos activados.
    Si tienes algún reparo en descargarla, no hay problema (tiene lógica), para eso está subido el código fuente.
    Basta con que lo compiles y elimines los permisos innecesarios. El APK puedes copiarlo al teléfono e instalarlo desde allí (sin pasar por la tienda).
    Si puedo cuando tenga un hueco yo mismo lo haré y volveré a subir la aplicación.

    CORRIJO: Esta no la había subido a la tienda de google. Recompilo y lo vuelvo a poner. Cuando lo tenga actualizado os aviso con un añadido en la entrada.

    Gracias.

  21. Neftalí
    miércoles, 25 de febrero de 2015 a las 12:58 | #21

    @iretai
    Corregido.
    He subido el APK con los nuevos permisos y también lo he subido a Google pLay.
    Arriba (al final del artículo) está el link.

    Un saludo.

  22. MiguelC-II
    viernes, 1 de mayo de 2015 a las 19:00 | #22

    Saludos a Todos.

    He estado indagando sobre como hacer un APP buscador de musica (vía Streaming/Internet), utilizando Firemonkey/Delphi(preferiblemente FireMonkey por lo de los iOS/Android); es decir, poder escribir el nombre de una canción o artista, y acto seguido me muestre el/las canciones encontrada(s); Poder reproducirla o hacer el download de la canción. Tengo la idea, de que esto amerita esta inscrito en algun servicio de musica (Spotify, etc) y usar algun API; pero la verdad que encuentro muy poca documentación o ejemplos claros de como hacerlo.
    Gracias de Antemano por cualquier orientación.

  23. Neftalí
    lunes, 4 de mayo de 2015 a las 09:13 | #23

    @MiguelC-II
    Hola Miguel.
    Tal y como comentas, hay varios temas a los que debes atender.
    Lo primero es tener algún servicio donde buscar canciones. En este mismo ejemplo o en este otro de la web (http://neftali.clubdelphi.com/?p=3572) puedes ver cómo acceder. Las API’s que te puedas encontrar seguramente son similares (REST) y el acceso concreto dependerá del servicio.

    Lo segundo es el tema del Streaming una vez seleccionada la canción. Para esto deberás buscar algún componente que te permita la descarga y reproducción. La descarga es fácil que puedas hacerla con las Indy (por ejemplo) la reproducción vía streaming, tendrás que ser creo con un componente específico.

    Un saludo.

  24. victor
    viernes, 20 de noviembre de 2015 a las 17:34 | #24

    Hola Neftalí, por que será que siempre que estoy por empezar un proyecto termino en tu blog??

    Tengo en mente realizar un programa que se comunique vía bluetooth con un arduino, con fines didácticos, se trata de controlar dirección y velocidad de un auto de juguete, el líneas generales.

    No se realizar la comunicación bluetooth desde delphi -este será mi primer intento- y este trabajo tuyo es lo primero que leo, pero desde ya intuyo que lo mio es mucho más modesto que lo que aquí encuentro. Alguna recomendación que puedas darme??? todo comentario se agradece y ayuda.

  25. Neftalí
    viernes, 20 de noviembre de 2015 a las 17:58 | #25

    @victor
    Hola Víctor.
    Al contrario, este sistema usando Tethering es muy sencillo.
    Tethering es una capa que simplifica mucho las cosas y utiliza como vía de comunicación bluetooth o wifi de forma transparente para el programador. Es este caso ideal para programar comunicación entre varias aplicaciones que utilicen delphi.
    En tu caso tendrás que utilizar directamente bluetooth (que se puede hacer sin utilizar tethering).

    Para ello puedes revisar la documentación de la docwiki de embarcadero referente a bluetooth.

    Tendrás que utilizar las units destinadas a ello. Te dejo un par de enlaces:

    http://docwiki.embarcadero.com/RADStudio/XE8/en/Using_Bluetooth_Low_Energy
    http://docwiki.embarcadero.com/RADStudio/XE8/en/Using_Bluetooth

    Un saludo.

  26. victor
    sábado, 21 de noviembre de 2015 a las 14:58 | #26

    Muchas gracias por tu respuesta, ya me pongo a estudiarlo.

    Saludos!!

  27. Vinicius
    lunes, 18 de enero de 2016 a las 20:42 | #27

    Hola Neftalí,

    En primer lugar gracias por compartir sus conocimientos, fue muy útil a sus enseñanzas sobre ” Tethering Delphi XE7″ desarrollaron un sistema muy similar, pero tengo un problema que no he encontrado una solución, tal vez usted me puede ayudar, tengo un servidor con un programa principal y una aplicación de Android, todo funciona muy bien, conectado a través de wifi, pero la conexión en adroid se perdió al quedarse fuera del alcance de wifi tiene un error: SOCKET ERROR…CONNECTION RESET BY PEER, y volver al trabajo tienen que cerrar la aplicación en Android y cerca también en el servidor, ya sabes alguna manera de evitar que el programa se bloquea el servidor y que no es necesario cerrar y abrir de nuevo?

    muchas gracias.

  28. Neftalí
    martes, 19 de enero de 2016 a las 08:32 | #28

    @Vinicius
    Hola Vinicius.
    Tendríais que revisar los métodos que vienen con los componenntes de tethering para controlar la conexión.
    Tal vez podaís cerrar la conexión y volver a abrirla cuando los dispositivos vuelvan a estar dentro de la red wifi.

    Un saludo.

  29. Arturo Serrallés
    viernes, 4 de noviembre de 2016 a las 18:34 | #29

    Interesante artículo!, lamentablemente los fuentes no están disponibles, ¿es posibles resubirlos?.

  30. lunes, 7 de noviembre de 2016 a las 09:41 | #30

    @Arturo Serrallés
    Los reviso.
    Por un cambio en el blog, hay muchos enlaces que han quedado incorrectos.

  31. lunes, 7 de noviembre de 2016 a las 09:44 | #31

    @Arturo Serrallés
    Ya están modificados los links.
    Debería poder bajar los ficheros sin problemas.

    Un saludo.

  32. Arturo Serrallés
    miércoles, 9 de noviembre de 2016 a las 16:53 | #32

    Muchas gracias Neftalí, ya pude descargarlos.

  1. Sin trackbacks aún.
What is 6 + 23 ?
Please leave these two fields as-is:
IMPORTANTE! Para continuar, debes contestar la pregunta anterior (para evitar SPAM) :-)