Hasta las narices de que me quieran sajar por un minuto de canción más o menos lo mismo que por una canción completa (con una calidad ridícula), aquí tenéis un manual para reducir el tamaño y la calidad de vuestras canciones favoritas. Eso sí: siempre y cuando tengáis un “título de propiedad” de la canción…
Paso 0: A WAV
Lo primero que hago es obtener el wav. Como lo tengo en CD uso grip (paquetito debian y tal). Si lo tenéis ya en mp3, saltaros este paso.
Paso 1: Recortando
Pues yo uso mhwaveedit. Abre tanto el mp3 como el wav. Recortas, pruebas, recortas, pruebas, pruebas, recortas, pruebas, deshaces, recortas, pruebas… Ale, ya tienes el trocito que te gusta.
Paso 2: Calidad paupérrima
¿Vas a difrutar de la música? La respuesta es: NO. Un montón de investigación para obtener mejor calidad y al final lo que queremos es basura. ¿Qué se le va a hacer? Pero… ¡No hay problema!
Con esto conseguiréis una calidad regulera, pero un tamaño enano. Para un wav de 5Mb he conseguido un mp3 de 200Kb. Podéis jugar con el parámetro de -b hasta encontrar lo que queréis (si ponéis un valor inválido, os avisa de cuáles son los buenos).
Ironic mode: ON
Ale… a difrutar de la música
Ironic mode: OFF
—
20070522 – Añadida opción “-m m” para transformar a Mono. Gracias, Paco; se me había pasado al transcribirlo.
Hola! supongo que ya os habréis enterado del DSA-1571 (aviso de seguridad de Debian), que viene a ser esto: el generador de números aleatorios de openssl que se emplea para generar las claves privadas y públicas de SSH es predecible, lo cual es un grave problema porque ahora, conocida una cable pública, se puede obtener su clave privada. Esto hace que desde el 17 de septiembre de 2006 las claves generadas con Debian (y similares) sean inseguras. Así que ya sabéis: revocad vuestras antiguas claves y generaros unas nuevas!.
Mercurial es otro sistema de control de versiones distribuido (otro más) que no tiene nada (o poco) que envidiar a git (en lo referente a prestaciones) pero casi tan fácil de usar como subversion. Esta recetilla incluye unas nociones de su uso básico y algunas buenas referencias.
Pues la verdad es que viendo este articulo que me he encontrado buscando algunas cosas, me he sentido muy identificado (yo creo que hasta he sentido algo de nostalgia de cuando programaba con Visual Basic allá por los noventa…)
Cómo crear fácilmente bindings de Python para una librería C++ usando SIP
Introducción
¿Tienes algunas librerías en C++ hechas y no sabes cómo utilizarlas en Python? ¿Estás "decepcionado" porque tu programa construído totalmente en Python no es tan rápido como esperabas?. La solución es "bien sencilla": te programas una librería en C/C++ (si no la tienes) que haga el trabajo, construyes un wrapper para utilizarla en Python y... ¡listo!
Para el primer paso no hay, de momento, solución automática. Pero para el segundo existen diversas herramientas que ayudan a la construcción del wrapper, su compilación e instalación. Una de estas herramientas, la que vamos a utilizar en esta receta, se llama python-sip. Asiste a la creación de los wrappers para librerías C/C++, aunque los ejemplos que se mostrarán serán de C++. Para más información, consulta la sección de Enlaces.
Ingredientes
Ejemplo
Librería C++
Para ilustrar el funcionamiento de python-sip, ¡qué mejor que un ejemplo!. Supón que la librería consta de una clase construída en C++ y cuyo fichero de cabecera (Impresora.h) es el siguiente:
Y cuya implementación es la siguiente (Impresora.cpp):
Como se puede apreciar, la clase tiene poca historia: tiene un atributo privado ("nombre") y un método para imprimir una lista (conjunto) de strings ordenados alfabéticamente. Para compilar esta librería tenemos el siguiente Makefile:
En definitiva, partimos de un archivo "libImpresora.so" que contiene la librería en C++ ya compilada. Ahora comienza la construcción del wrapper con ayuda de python-sip4.
Construcción del wrapper
Comenzamos creando un archivo que, por convenio, llamaremos "Impresora.sip". En este fichero especificaremos la estructura del wrapper con sintaxis sip, muy parecida a C++. Una primera aproximación sería la siguiente:
%Module especifica el módulo en el que va estar incluída la librería, junto con un número de versión. En el ejemplo, el módulo es "Ejemplo" en su versión "0".
%TypeHeaderCode es necesario para que sip pueda leer la información de la clase que se va a "wrappear". Todo lo encerrado en ese bloque, sip lo utilizará para construir el wrapper con los tipos adecuados.
Nótese que se ha modificado los tipos de los argumentos del constructor y del método "imprimirLista". Sip no soporta tipos como string (y no digamos sets de la STL de C++). Sin embargo, en el caso de string, basta con especificar de que se trata de un "char*" y C++ se encargará del cambio de tipo. El argumento de tipo set se ha sustituído por una constante de sip que representa una lista de Python. Existe una constante de este tipo para cada uno de los tipos de Python (SIP_PYDICT...). Consúltese las referencias para más información.
Como se puede apreciar, no está especificado el atributo privado "nombre". Sip no soporta atributos ni métodos privados para la construcción del wrapper.
SIP_PYLIST representa a una lista de Python pero, ¿cómo se hará la correspondecia set->PythonList?. ¿Sip se encargará de ello?. Pues no. La correspondencia entre tipos complejos (objetos propios, clases auxiliares...) debe hacerse de forma explícita. Para especificar la conversión de uno a otro utilizaremos la directiva %MethodCode:
Declaramos una variable del mismo tipo que el argumento que espera el método, en nuestro caso un set de strings.
a0 es el argumento (SIP_PYLIST) de tipo PyObject* de la librería CPython. Por tanto, el bucle no sirve nada más que para guardar cada valor de la lista Python en el set.
sipCpp es un puntero al objeto de entorno, en nuestro caso, "Impresora*". Cuando tenemos el set completamente construído llamamos a "imprimirLista" con el parámetro convertido.
Construcción del Wrapper
Una vez tenemos la especificación del fichero .sip, es hora de construir el wrapper de forma automática. Para ello, es aconsejable utilizar un pequeño script en python que genera los archivos .cpp y el Makefile final. Un fichero básico sería el siguiente:
El código de este script es "autocomentado". Supongamos que el fichero que contiene el código anterior se llama "configure.py":
El comando anterior genera el wrapper en C++ y el Makefile que construirá todo.
La compilación del wrapper generará un archivo "Ejemplos.so", que es la librería compartida que será utilizada por Python.
Probando
Tenemos 2 opciones:
Ejecutar "make install" y, por tanto, instalaremos el archivo "Ejemplos.so" en /usr/lib/pythonX.X/site-packages donde será accesible para cualquier programa Python que utilize el módulo Ejemplos.
Probar la librería con iPython, ejecutándolo en el directorio donde se encuentra el nuevo módulo
Cualquiera que sea el modo de prueba se debe modificar la variable de entorno LD_LIBRARY_PATH para que el intérprete de Python pueda localizar el "libImpresora.so". A continuación, un ejemplo utilizando iPython:
Referencias
Documentación de sip4 en /usr/share/doc/sip4/reference/sipref.html, incluída en el paquete sip4
Cómo parsear un archivo Slice (el lenguaje de especificación de interfaces de ICE) para obtener toda la información semántica que contiene utilizando libSlice (el Parser proporcionado por ZeroC)
Introducción
Los archivos slice todos sabemos lo que son: Archivos que proporcionan una descripción de las clases, módulos, interfaces y operaciones que implementará un sistema distribuido basado en ICE.
La gente de ZeroC proporciona el código fuente de su middleware, pero éste no está muy bien documentado (siendo generosos), así que la manera de parsear un archivo slice y obtener toda la semántica que se necesitaba, ha pasado por hacer un poco de ingenería inversa del código de una de las utilidades que da ZeroC (en concreto slice2cpp). Así que para evitar tener que repetir el trabajo, aquí dejo un poco documentado el funcionamiento del parser para Slice que la gente de ZeroC montó.
Prerequisitos
Antes de empezar, no estaría de mas conocer un poco el patrón de programación visitor, ya que se utilizará para acceder a la información proporcionada por el parser. También se supone que se tienen nociones básicas de C++.
Yo he utilizado ice-3.3-beta así que tampoco estará de mas que además de tener ice instalado, se instalen los fuentes:
Parseando que es gerundio…
Para parsear un archivo slice, necesitaremos utilizar la librería Slice/Parser proporcionada por ZeroC. Un ejemplo de función que llama al parser sería la siguiente:
¿y como funciona esto? Bueno, la clase OurParser será quien realice todo el trabajo. Básicamente, su cometido consiste en crear una instancia del preprocesador pasándole el archivo a parsear y un conjunto de opciones (en nuestro caso, como no queremos pasarle nada, se le pasa un vector de cadenas vacío, dummyArgs). Después de instanciar el preprocesador, se preprocesa el archivo:
Después se instancia el parser propiamente dicho, y se parsea el Slice
u es quien realiza todo el trabajo de parseado. Las opciones que se pasan cuando se crea se utilizan entre otras cosas para propósitos de depuración y las que se dan en el ejemplo son válidas en general. Si se quiere, se puede jugar con los valores a ver qué pasa o directamente echar un ojo al código de slice2cpp donde están un poco mas comentadas.
la variable parseStatus almacena el resultado del parseo.
Una vez que se ha terminado de parsear el archivo, ya se cuenta con toda la información semántica, así que solo nos resta obtener dicha información y procesarla a nuestro antojo. Como se ha intentando que el parser sea lo mas general posible, de modo que pueda ser reutilizado para lo que sea necesario (vamos, que haya buena separación entre frontend y backend), se ha hecho uso del patron de diseño visitor que se comentó anteriormente. En nuestro caso creamos una clase que contendrá una serie de métodos que serán llamados en determinados momentos, como por ejemplo cuando se inicie la declaración de un módulo, su finalización, cuando se declare una función, sus parámetros, metadatos, se defina una estructura de datos o una clase, etc….
En nuestro caso, después del parseo, “visitamos” al parser para pedirle toda la información:
visitor es una instancia de nuestra clase visitante, que implementará los métodos necesarios para procesar la información.
Ésta clase viene a ser algo como ésto:
Bueno, como se puede ver, nuestro “visitante” tiene que heredar de la clase ParserVisitor, además, debe implementar todos los métodos que se ven. Éstos métodos serán llamados por el parser y será en ellos en los que nosotros definamos el procesado que queremos hacer de la información.
“Extrayendo” información
Bueno, aquí voy a comentar qué he hecho yo para obtener cierta información que necesitaba del Slice. Si se necesitase otra información, pues nada, a mirar el código de los conversores de código que da ZeroC, que es muy entrentenido :-P …
Modulos
Cada vez que se inicia el parseado de un módulo, se invoca la función visitModuleStart de nuestro visitor. Entre otras cosas, la información que puede ser necesaria en este punto es el nombre del módulo y su ámbito de declaración. Para acceder a esa información, algo como esto:
Cuando se abandona la declaración de un módulo, se invoca a visitModuleEnd, pasandole exactamente la misma información que a su homólogo de inicio de módulo.
Clases e Interfaces
Cuando se declara una clase o una interfaz en el Slice, el parser visita la misma función: visitClassDefStart para el inicio de la declaración y visitClassDefEnd para finalizar la declaración. Si la declaración se encuentra dentro de un módulo, estaremos en la declaración de una interfaz. En caso contrario, se tratará de una clase.
Aquí la información que leí fue la misma que en el módulo, así que el código es casi idéntico:
Operaciones
Para el caso de las operaciones necesitaremos leer mas información aparte del nombre y el ámbito. Ésta información se refiere al tipo de retorno, tipo de la función (por ejemplo puede ser idempotent), y los argumentos (nombre, tipo y dirección).
El procedimiento encargado de obtener la información de las operaciones es VisitOperation y un ejemplo de implementación que simplemente imprime información sería el siguiente:
Vayamos por partes: Lo primero que hemos obtenido, ha sido el nombre y el ámbito de declaración de la operación. Después de ésto, se obtiene el tipo de retorno. Éste tipo es un enumerado, por lo que deberemos transformarlo a una cadena con el nombre adecuado para poderlo imprimir (o hacer cualquier otro tipo de proceso que nos interese)
Después se consulta el modo de la operación, que nos dirá si es o no idempotente.
A continuación yo necesitaba los metadatos de las operaciones. getMetaData() devuelve una lista de cadenas, cada una de las cuales es un metadato, así que con un iterador recorremos dicha lista y vamos imprimiendo todos los metadados de cada operación. Ésto también se puede hacer con módulos, clases, interfaces, etc….
Finalmente, se consultan los parámetros: parameters() devuelve una lista de parámetros. Una vez que se tiene esa lista, hay que iterar sobre ella e ir obteniendo toda la información sobre cada uno de los parámetros:
Nombre: (q)→name()
Dirección: (q)→isOutParam(), devuelve verdadero si el parametro es de salida.
Tipo: (*q)→type(), enumerado con el tipo del parametro.
Sobre los tipos de retorno y de los parámetros
returnTypeToString, inputTypeToString y outputTypeToString son funciones que son llamadas para convertir los enumerados que determinan el tipo de una función o parámetro en la cadena que los representa textualmente. En la implementación que tenemos, éstas funciones se encuentran dentro de un módulo llamado Slice/CPlusPlusUtil que hay que incluir. Cuando se detecte un string, int o bool por ejemplo, nuestro programita responderá con algo como ::ICE::string o ::ICE::int pero ¿y si queremos que, por ejemplo, cuando detecte un entero nos diga “cacho entero” (por ejemplo ;-))? Bueno, pues tendremos que sobreescribir las funciones de las que hablé antes para que nos devuelva lo que nosotros queramos. Yo en mi caso, modifiqué las tres. Aquí muestro mi outputTypeToString:
Lo único que modifiqué fue la tabla de nombres del principio, dándole los que yo quise. Si los nombres que vamos a dar a los parámetros de entrada, de salida y al tipo de retorno van a ser los mismos (es decir, no vamos a distinguir por ejemplo un entero de salida, un entero de entrada y un tipo de retorno entero, llamandolos por el mismo nombre de forma interna en los tres casos) podemos utilizar solo una de éstas tres funciones y llamarla en todos los casos. En caso contrario, habrá que utilizar una función distinta para cada caso y llamarla cuando sea necesaria.
Con motivo de la apertura del canal en YouTube del CESLCAM y para celebrar el día de Internet el próximo 17 de mayo, el Centro ha puesto en marcha un concurso referido a la creación de videotutoriales en Molinux para premiar al mejor video formativo.
Aquí tenéis el material para el taller de videojuegos, aunque el material está disponible en repositorio con sus distintas trunks, para facilitar el uso y la comprensión, se han creado el “juego final” en 16 pasos.