Problemas para reproducir sonidos en Processing con Minim bajo Ubuntu 10.10

Introducción.

Minim parece ser una muy buena librería para la reproducción y manipulación de archivos de audio utilizando a Processing como lenguaje de desarrollo.

Experimentando con esta librería intentaba reproducir dos sonidos provenientes de dos archivos WAV diferentes, sin embargo siempre obtenía el siguiente mensaje de error a pesar de que cuando lo intentaba con uno sólo funcionaba correctamente.

JavaSound Minim error
==== Couldn’t open the line: line with format PCM_SIGNED 44100.0 Hz, 16 bit, mono, 2 bytes/frame, little-endian not supported. ====

Este problema me sucedía tanto al utilizar AudioSample como con AudioSnippet.

Solución.

Aparentemente el problema está relacionado con la plataforma Ubuntu ya que esta apuesta por OpenJDK como máquina virtual de Java por encima de la distribución oficial de Oracle (sun-java6) y por este motivo desafortunadamente olvidaron agregarle el soporte para el sistema PulseAudio que utiliza por defecto esta distribución.

La solución propuesta en los foros es copiar los archivos necesarios desde los directorios de OpenJDK a sus equivalentes de SunJava6.  En mi caso que utilizo exclusivamente la última versión fue necesario que instalara temporalmente el paquete openjdk-6-jre-lib, copiara los siguientes archivos y lo desinstalara nuevamente.

$ sudo cp /usr/lib/jvm/java-6-openjdk/jre/lib/[ARCH]/libpulse-java.so /usr/lib/jvm/java-6-sun/jre/lib/[ARCH]/

$ sudo cp /usr/lib/jvm/java-6-openjdk/jre/lib/ext/pulse-java.jar /usr/lib/jvm/java-6-sun/jre/lib/ext/

Se debe tener cuidado al reemplazar la subcadena [ARCH] contenida en la primera instrucción a ejecutar con la correspondiente plataforma instalada realmente, ya sea de 32 bits (será entonces i386) o de 64 bits (amd64).  La ejecución del comando uname -m debería brindar información suficiente al respecto.

Enlaces.

Problemas de permisos para ejecutar las máquinas KVM después actualizar a Ubuntu 10.10 Server

Introducción.

Después de actualizar mi servidor de desarrollo a Ubuntu 10.10 server empecé a tener problemas para iniciar las máquinas virtuales basadas en KVM.  Básicamente se quejaba por problemas de permisos en /u/vms donde se almacenan las imágenes de los discos duros virtuales (por defecto es /var/lib/libvirt/images -global- o ~/.libvirt/storage -usuario-).

En los logs de las máquinas virtuales ubicados bajo /var/log/libvirt/qemu se pueden apreciar mensajes como el siguiente.

char device redirected to /dev/pts/1
qemu: could not open disk image /u/vms/sandbox_ubuntuserver-10.10_x64.img: Permission denied

Las imágenes de los discos duros pertenecen a root:root y esta relación es restaurada cada vez que se intenta ejecutar cada una de las máquinas virtuales así que modificarlas manualmente es infructuoso.

$ ls -l /u/vms

-rw——- 1 root root 7516192768 2010-08-11 23:32 c-head.img
-rw——- 1 root root 7516192768 2010-08-04 18:21 c-wn1.img
-rw——- 1 root root 7516192768 2010-07-24 16:42 c-wn2.img
-rw——- 1 root root 7516192768 2010-07-15 00:34 c-wn3.img

Solución.

Indicar explícitamente que root será el grupo y el usuario que ejecutará los procesos de QEMU.  Para hacer esto es necesario realizar el siguiente ajuste de configuración.

$ sudo vi /etc/libvirt/qemu.conf

# The user ID for QEMU processes run by the system instance
user = “root”

# The group ID for QEMU processes run by the system instance
group = “root”

Reiniciar el servicio para tener en cuenta los cambios recién realizados.

$ sudo service libvirt-bin restart

Conclusiones.

Aparentemente la nueva versión de KVM en Ubuntu trae consigo cambios en la forma como este se ejecuta, siendo ahora bajo usuarios sin privilegios y antes como root.  El proceso de actualización, al menos en mi caso, parece que no consiguió realizar todas las modificaciones necesarias para alcanzar este objetivo y por ello fue necesario indicar explícitamente que se utilizaba a root para ejecutar los procesos (método antíguo).

Otra posible solución a este problema podría haber sido indicarle a QEMU/libvirt (o a quien corresponda) que efectivamente se desea ejecutar los procesos con usuarios sin privilegios (método nuevo) sin embargo por el momento no he logrado encontrar quien es el responsable de los cambios de estos cambios de propiedad de los archivos.

Enlaces.

Actualizar GNU/Linux Ubuntu Server 10.04 a 10.10

Introducción.

El día de hoy me animé a actualizar mi servidor de desarrollo de GNU/Linux Ubuntu 10.04 a 10.10.

El procedimiento para hacerlo es muy sencillo aunque es diferente del ya conocido de la versión de escritorio porque se realiza enteramente desde la línea de comando.

A continuación se explican los pasos necesarios para actualizar un servidor con Ubuntu a la versión mas recientemente disponible.

Procedimiento.

Instalar el paquete update-manager-core responsable de la actualización de paquetes y de versiones del sistema operativo, si aún no se cuenta con él.

$ sudo aptitude install update-manager-core

Opcionalmente modificar el comportamiento del administrador de actualizaciones para que tenga en cuenta todas las versiones liberadas del sistema operativo ya que por defecto sólo tiene en cuenta las LTS (Long Term Support).

$ sudo vi /etc/update-manager/release-upgrades

Prompt=normal

Iniciar el proceso de actualización del sistema operativo desde la línea de comando.

$ sudo do-release-upgrade

Enlaces.

Interferencia entre wrj4P5 y los eventos de mouseMoved con Processing en GNU/Linux Ubuntu 10.10

Introducción.

En días anteriores escribí acerca de como utilizar el wiimote en sketches de Processing bajo GNU/Linux Ubuntu 10.10.  El método detallado funciona bastante bien al menos para el manejo del evento de la presión de los botones del dispositivo, sin embargo he encontrado un problema molesto con el uso de esta librería y del cual no he podido encontrar mayor información.

Si en un sketch en el cual se está manejando el evento de movimiento del ratón (mouse moved) se empareja un wiimote utilizando la librería wrj4P5 se empiezan a generar múltiples eventos sugiriendo que el ratón se está moviendo aunque sus coordenadas no varian!

Aplicación de demostración.

Aplicación de demostración uso de Wiimote con Processing
Aplicación de demostración uso de Wiimote con Processing

Para experimentar con el problema sugerido he creado un sketch muy simple que utiliza la librería mencionada y además maneja el evento de movimiento del ratón.

Este problema ha sido probado en tres equipos, dos portátiles y uno de escritorio, con GNU/Linux Ubuntu 10.10 y 9.10 respectivamente.  Aún no he tenido la oportunidad de experimentar con la aplicación de demostración en Windows.

Una solución temporal.

Por ahora la única solución, para nada elegante, que he encontrado es el verificar si efectivamente el puntero del ratón ha sido movido para garantizar que el supuesto evento si sucedió en realidad.

int lastX;
int lastY;

void mouseMoved()
{
  if(mouseX == lastX && mouseY == lastY)    // There was no movement: avoid this "event"!
  {
    return;
  }
  else    // There WAS movement, update "last" positions.
  {
    lastX = mouseX;
    lastY = mouseY;
  }

  // ... do some stuff ...
}

El sketch puede ser descargado del sitio de demostraciones con la modificación para evitar el problema desactivada.  Para activarla remueva los comentarios al rededor del condicional if(mouseX == lastX && mouseY == lastY) aproximadamente en la línea 66.

Enlaces.

Descubrir equipos cercanos con Processing y bluetoothDesktop

Introducción.

En el presente artículo se describe el proceso de instalación de la librería bluetoothDesktop en GNU/Linux (para Windows es similar) que permite manipular el módulo de Bluetooth del dispositivo donde se ejecuta el sketch y con ella se implementa el procedimiento para detectar dispositivos cercanos que utilicen este protocolo de comunicaciones.

Obtener la librería.

Descargar la versión disponible mas reciente en el sitio web del proveedor.

http://www.extrapixel.ch/processing/bluetoothDesktop/download.html

Instalación de la librería.

Descomprimir el paquete recién descargado.

Mover el directorio bluetoothDesktop/ al directorio de librerías de Processing en ~/sketchbook/libraries/.  El resultado se deberá apreciar de la siguiente manera.
$ ls -l ~/sketchbook/libraries/bluetoothDesktop/

drwxr-xr-x 5 jimezam jimezam 4096 2010-12-08 22:47 examples
-rw-r–r– 1 jimezam jimezam  910 2007-12-17 21:25 howto.txt
drwxr-xr-x 3 jimezam jimezam 4096 2010-12-08 23:09 library
drwxr-xr-x 2 jimezam jimezam 4096 2010-12-08 22:47 src

Remover los siguientes archivos ubicados bajo el directorio ~/sketchbook/libraries/library/.

  • libavetanaBT.so
  • export.txt
  • bluecove-2.0.2-snapshot.jar
  • avetanaBT_readme.txt
  • avetanaBT.jar

Copiar en es mismo directorio los siguientes archivos que pueden obtenerse del sitio web de Bluecove.

  • bluecove-x.x.x.jar
  • bluecove-gpl-x.x.x.jar (requerido para Linux).

El contenido final de ese directorio se aprecia de la siguiente manera.

$ ls -l ~/sketchbook/libraries/bluetoothDesktop/library/

-rw-r–r– 1 jimezam jimezam 547156 2010-12-08 21:27 bluecove-2.1.0.jar
-rw-r–r– 1 jimezam jimezam  89022 2010-12-08 21:26 bluecove-gpl-2.1.0.jar
-rw-r–r– 1 jimezam jimezam   9409 2007-11-14 19:01 bluetoothDesktop.jar

Implementación.

Importar el paquete principal de la librería.

import bluetoothDesktop.*;

Crear un objeto para hacer referencia al dispositivo de Bluetooth.

Bluetooth bluetoothInstance = new Bluetooth(this);

Iniciar el proceso de descubrimiento de dispositivos cercanos.

bluetoothInstance.discover();

Implementar el método deviceDiscoverEvent el cual es llamado cada vez que un nuevo dispositivo Bluetooth es detectado.

void deviceDiscoverEvent(Device dev)
{
    println("Discovering ... name=" + dev.name + "; address=" + dev.address);
}

Implementar el método deviceDiscoveryCompleteEvent el cual es llamado una única vez al terminarse el proceso de descubrimiento.

void deviceDiscoveryCompleteEvent(Device[] devices)
{
  println("I Found " + devices.length+ " devices.");

  for(int i=0; i<devices.length; i++)
  {
    println(i + "# name=" + devices[i].name + "; address=" + devices[i].address);
  }
}

Aplicación de demostración.

Aplicación de demostración - Descubrir dispositivos Bluetooth cercanos
Aplicación de demostración - Descubrir dispositivos Bluetooth cercanos

Enlaces.

Laberinto 0.2 controlado por wiimote

Introducción.

Preparando un mejor ejemplo para el uso del wiimote con Processing decidí actualizar la versión procedimiental del Laberinto que utilicé como demostración en una charla hace un par de meses.

Con esta mejora es posible conectar el wiimote al sketch y controlar con él al caballero en busca de su tesoro mas preciado: la princesa 🙂  Adicionalmente el wiimote vibrará cuando el caballero se encuentre con ella.

El movimiento del caballero es controlado por el d-pad mientras que el botón de inicio (home) lo teletransporta y el botón #1 reinicia el juego.

Implementación.

Como primera instancia es necesario tener instaladas las librerías requeridas para el uso del wiimote con Processing.

A continuación se realizan las siguientes modificaciones al código base del sketch.

Se importa el paquete de las clases relacionadas con el wiimote.

import lll.wrj4P5.*;

Se establece una referencia global al control.

Wrj4P5 wii;

En el setup se establece la conexión con el wiimote.

void setup()
{
    // ...
    wii = new Wrj4P5(this);
    wii.connect();
}
Se implementa el método buttonPressed para manejar los eventos provenientes del control con la lógica que se mencionó anteriormente: d-pad controla la dirección, home teletransporta al jugador y el botón #1 reinicia el juego.
void buttonPressed(RimokonEvent evt, int rid)
{
  if (evt.wasPressed(RimokonEvent.LEFT))
    moverJugador(LEFT);

  if (evt.wasPressed(RimokonEvent.RIGHT))
    moverJugador(RIGHT);

  if (evt.wasPressed(RimokonEvent.DOWN))
    moverJugador(DOWN);

  if (evt.wasPressed(RimokonEvent.UP))
    moverJugador(UP);

  if (evt.wasPressed(RimokonEvent.ONE))
    reset();

  if (evt.wasPressed(RimokonEvent.HOME))
    teletransportarJugador();
}

La verificación del encuentro entre la princesa y el caballero se realiza en el método moverJugador y sucede cuando la nueva ubicación del caballero no es una pared && es un tesoro, en ese momento se ejecuta la siguiente instrucción para hacer vibrar el control.

wii.rimokon.vibrateFor(400);

Enlaces.

Utilizar el wiimote con Processing en Ubuntu 10.10

Introducción.

En el presente artículo se describen las librerías necesarias para acceder a la información proveniente de un wiimote desde sketches desarrollados en Processing y se implementa un sketch simple para verificar su funcionamiento.

Obtener e instalar las librerías requeridas.

Obtener los siguientes archivos.

wrj4P5 (alpha-011) http://sourceforge.jp/projects/wrj4p5/releases/ wrj4P5.jar
Loc (beta-005) http://sourceforge.jp/projects/wrj4p5/releases/ Loc.jar
WiiRemoteJ (1.6) http://www.world-of-cha0s.hostrocket.com/WiiRemoteJ/ WiiRemoteJ v1.6.zip.gz
BlueCove (2.1.0) http://code.google.com/p/bluecove/downloads/list bluecove-gpl-2.1.0.jar
bluecove-2.1.0.jar

Crear el directorio libraries/wrj4P5/library bajo el directorio de los sketches del usuario.  En mi caso, bajo ~/sketchbook.

$ mkdir ~/sketchbook/libraries/wrj4P5/library

Copiar los siguientes archivos a la ruta recién creada.

  1. bluecove-2.1.0.jar
  2. bluecove-gpl-2.1.0.jar (requerido para Linux)
  3. Loc.jar
  4. WiiRemoteJ.jar
  5. wrj4P5.jar

Con las versiones utilizadas el contenido del directorio queda de la siguiente manera.

$ ls -l ~/sketchbook/libraries/wrj4P5/library

-rw-r–r– 1 jimezam jimezam 547156 2010-12-08 21:27 bluecove-2.1.0.jar
-rw-r–r– 1 jimezam jimezam  89022 2010-12-08 21:26 bluecove-gpl-2.1.0.jar
-rw-r–r– 1 jimezam jimezam  34765 2010-12-08 21:44 Loc.jar
-rw-r–r– 1 jimezam jimezam  91091 2009-03-12 12:00 WiiRemoteJ.jar
-rw-r–r– 1 jimezam jimezam  39825 2010-12-08 21:40 wrj4P5.jar

Además es necesario contar (en mi caso) con las siguientes librerías del repositorio de Ubuntu.

  • bluez
  • libbluetooth3
  • libbluetooth3-dev

Crear un sketch de demostración.

Para esto se utiliza el código fuente del sketch MyFirstWii.

Sketch de demostración
Sketch de demostración

Crear un nuevo sketch en el PDE (File > New) y copie el código fuente de demostración.  Ejecute el sketch para probar el código (Sketch > Run).

Para conectar el wiimote al sketch presione al tiempo los botones 1 y 2 del wiimote por un momento.

Conclusiones iniciales.

  • Sería interesante tener mas control e información acerca del proceso de conexión con el wiimote.
  • La lectura de los botones del wiimote funciona muy bien.
  • La lectura del acelerómetro parece ser incorrecta y el cubo de la demostración gira sin control.
  • Será necesario hacer mas pruebas con estas librerías, incluyendo otras versiones de las mismas.

Enlaces.

Permitir el acceso de los nodos del cluster a Internet a través del nodo principal

Introducción.

Es muy común que debido al alto tráfico que se genera entre los nodos trabajadores de un clúster y su nodo principal, estos se separen en una red independiente y el nodo principal, con dos interfaces de red, actúe como puente entre los clientes en la red LAN y los nodos trabajadores en la red finalmente oculta.

La configuración de esto es tan simple como la simple conexión física de los dispositivos y la configuración de direcciones IP estáticas para los nodos trabajadores si es que no se desea instalar un servidor DHCP en el nodo principal para que realice esta tarea a través de la interfaz de red específica.  El problema surge cuando es necesario actualizar los nodos trabajadores o en general, que estos accedan a Internet.  Como estos se encuentran en un red diferente de la LAN, no hay nada que los enrrute hacia la WAN, quedando completamente aislados.

La solución a este problema es muy fácil de implementar gracias a las características intrínsecas de enrrutamiento de Linux (y en general de los sistemas operativos *nix).  Simplemente es necesario indicarle al nodo principal que actúe como enrrutador entre sus interfaces de red: red LAN y red de nodos trabajadores, pudiéndose aplicar los controles que se consideren necesarios a través del uso de iptables.

El panorama.

Estructura general del cluster
Estructura general del cluster

El nodo principal del cluster cuenta con dos interfaces de red: eth0 que se comunica con la red LAN de la organización y eth1 que se conecta con los nodos trabajadores, los cuales sólo cuentan con una única interfaz de red eth0.  Estos últimos se encuentran aislados del exterior ya que no son enrrutados inicialmente hacía la red LAN y eventualmente hacia Internet.

Implementación del enrrutamiento.

Configuración en el nodo principal del cluster.

Este actuará como enrrutador para la información enviada desde los nodos trabajadores y permitirles entonces acceder a las redes exteriores.  Para es necesario implementar en él las siguientes modificaciones de configuración.

Activar el envío de paquetes del kernel.

Esto se puede implementar de manera temporal, es decir, esta modificación no perdurará al reinicio del sistema, mediante la ejecución del siguiente comando.

# echo 1 > /proc/sys/net/ipv4/ip_forward

O puede implementarse de manera permanente mediante la edición de los parámetros del kernel de la siguiente manera.

# vi /etc/sysctl.conf

net.ipv4.ip_forward = 1

Esta último método puede verifcarse mediante la ejecución del siguiente comando en el shell.

# sysctl -p | grep ipv4

net.ipv4.ip_forward = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.default.accept_source_route = 0
net.ipv4.tcp_syncookies = 1

Establecer la regla de filtrado.

# iptables -t nat -A POSTROUTING -o eth0 -s 192.168.56.0/24 -j MASQUERADE

Esta instrucción le indica al nodo principal que permita el paso de los paquetes que vienen desde la red de los nodos trabajadores (192.168.56.0/24) hacía la otra red, en este caso la LAN (192.168.1.0), que se accede a través de la interfaz de red eth0.

Esta modificación desde la línea de comando no es persistente, por lo cual se deberá configurar en alguno de los archivos que se ejecutan al inicio.  Probablemente el mejor sitio sea en /etc/sysconfig/iptables, sin embargo debe tenerse cuidado si se utiliza system-config-securitylevel para administrar el firewall ya que este puede sobreescribir el archivo y olvidar los cambios.

Permitir el acceso al servicio DNS.

Este paso es opcional, pero probablemente si no se lleva a cabo el enrrutamiento sea exitoso si se utilizan direcciones IP directamente mas no si se intenta acceder utilizando nombres FQDN ya que los intentos de acceso al DNS son filtrados por el nodo principal.

Para permitir el acceso al servicio DNS es necesario permitir el paso de paquetes UDP cuyo destino sea el puerto 53 de la siguiente manera.

# iptables -A INPUT -m state –state NEW -m udp -p udp –dport 53 -j ACCEPT

Igual que en el paso anterior, para hacer persistente esta modificación es necesario incluírla en un archivo como /etc/sysconfig/iptables o realizarla con el system-config-securitylevel si se utiliza.

Configuración de los nodos trabajadores.

Es necesario finalmente indicarle a los nodos trabajadores que el nodo principal del cluster será a partir de ahora su nueva puerta de enlace.  Para hacer esto es necesario modificar el siguiente parámetro de su configuración con la dirección IP del servidor principal configurado anteriormente.

GATEWAY = 192.168.56.100

Esta modificación puede realizarse uno de los siguientes puntos de configuración de los nodos trabajadores.

  1. El archivo /etc/sysconfig/network.
  2. El archivo /etc/sysconfig/network-scripts/ifcfg-eth0.

Si el cambio se aplica en el primero de ellos, la modificación tiene un alcance global sobre todas las interfaces de red.  Si se aplica sobre el segundo archivo, la modificación será específica para la interfaz de red eth0.  En el caso de los nodos trabajadores, estos sólo tienen una única interfaz de red así que ambas opciones tienen igual implicación.

Finalmente es necesario reiniciar el servicio de red para garantizar que los cambios realizados tengan efecto.

# /etc/init.d/network restart

echo 1 > /proc/sys/net/ipv4/ip_forward

Actualizar DokuWiki

Procedimiento.

Verificar los cambios introducidos en la nueva versión para determinar los pasos adicionales a este estándar que se deban llevar a cabo.

Obtener la última versión disponible en el sitio de descargas de DokuWiki.

$ wget http://www.splitbrain.org/_media/projects/dokuwiki/dokuwiki-xxxx-xx-xx.tgz

Realizar una copia de seguridad de la instalación actual.

$ cp -rf wiki wiki.old

Descomprimir la nueva versión recién descargada.

$ tar zxvf dokuwiki-xxxx-xx-xx.tgz

Copiar los archivos de la nueva versión a la instalación actual.

$ cp -rf dokuwiki-xxxx-xx-xx/{*,.??*} wiki/

Remover el directorio temporal donde se desempaquetó la nueva versión.

$ rm -rf dokuwiki-xxxx-xx-xx

Remover la aplicación de instalación de DokuWiki (requerido).

$ rm wiki/install.php

Remover el paquete comprimido de la nueva versión (opcional).

$ rm dokuwiki-xxxx-xx-xx.tgz

Enlaces.

Manejar los errores fatales en PHP

Introducción.

Los errores fatales son problemas críticos de los cuales no es posible recuperar la ejecución del programa, por esta misma razón no pueden ser manejados como excepciones con bloques try/catch.

A pesar de que no es posible recuperar un programa después de un error fatal, si es posible realizar ciertas acciones de cierre que pueden ser útiles para mostrar un mensaje de error mas amigable y realizar el registro del error para su futura revisión, evitando que el lenguaje o el framework que se esté utilizando muestre un error difícil de comprender para el usuario final.

Ejemplo.

Un caso de error fatal es el producido por la recursión infinita.

function f($i)
{
 return f($i+1);
}

f(1);

El código anterior generará el siguiente mensaje de error de PHP.

Fatal error: Maximum function nesting level of '100' reached, aborting! in php shell code on line 3

Call Stack:
 12.0763      62388   1. {main}() php shell code:0
 12.0764      62548   2. f() php shell code:1
 12.0904      62772   3. f() php shell code:3
 12.0905      62996   4. f() php shell code:3
 12.0905      63220   5. f() php shell code:3
 12.0905      63444   6. f() php shell code:3
 12.0905      63668   7. f() php shell code:3
 ...

Implementación del manejo.

Desactivar la funcionalidad de PHP para mostrar los errores al usuario.

ini_set('display_errors', 0);

Registrar la función que se va a ejecutar durante el cierre de la ejecución de la aplicación.

register_shutdown_function('shutdown');

Realizar la implementación de la función de cierre.  En ella se filtran los diferentes tipos de error y se implementan, en este caso, únicamente los errores fatales.

function shutdown()
{
 if(!is_null($e = error_get_last()))
 {
  if($e['type'] == E_ERROR)
  {
    // ... implementación ...
  }
 }
}

Enlaces.