Xwork's Blog

The lord is waiting to take your hand.

Vamos a programar #50 - ¿Debo usar las interrupciones de arduino?

Hola de nuevo a todos el día de hoy vamos a continuar con la construcción del velocímetro para bicicleta, pero antes de continuar, vamos a tomar una decisión importante.
En algunos de los post anteriores, vimos código que se encargaba de contar las vueltas que llevaba un motor (usando un sensor de efecto hall y un par de imanes), pero poco después, alguien me contacto en twitter y me dijo que esa no era la manera correcta de hacer la medición, que arduino posee las herramientas necesarias para medir de una manera más precisa. En arduino, es posible asignarle prioridad a uno de los pines, dependiendo de modelo de la placa, al menos dos pines tendrán esta capacidad.

Las interrupciones, se dan cuando se detecta un valor en un pin, siempre y cuando este se haya inicializado con attachinterrupt(), esta es una manera de indicar que uno de los pines tendrá prioridad y cuando su valor cambie (ahorita veremos de que modo), se llevará a cabo cierta acción, esto es independiente a lo que arduino este haciendo.

La función attachinterrupt(), toma 3 argumentos. El primero; es el pin en el cual arduino "vigilará". El segundo; es la función que se ejecutará cuando se detecte un cambio en el pin designado. El tercero, será la forma en que arduino se dará cuenta del cambio.

Para designar el pin, debemos de tomar en cuenta el modelo de arduino, en mi caso, el arduino uno dispone de los pines 2 y 3 (para los modelos Mega y Micro tambien aplica).

La función que se usará cómo segundo parametro, no debe de requerir de ningun parametro y tampoco debería de devolver nigun valor.

Para el modo en que arduino detectará el cambio, disponemos de los siguientes:

  • LOW
  • CHANGE
  • RISING
  • FALLING
  • HIGH
 LOW ocurre cuando el pin esta en ese estado (LOW), CHANGE ocurre cuando el pin pasa de HIGH a LOW o viceversa, RISING ocurre cuando el pin pasa de LOW a HIGH, FALLING ocurre cuando el pin pasa de HIGH a LOW y finalmente HIGH, este ocurre cuando el pin esta en estado HIGH (valga la redundancia).

Para apreciar mejor, veamos el siguiente fragmento de codigo:

#include <LiquidCrystal.h>
const int rs = 12, en = 11, d4 = 9, d5 = 8, d6 = 7, d7 = 6;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
const float Llanta = 1.9572;
//Algunas variables
const int HallSensor = 3;
const int LED =  13;
int HallState = 0;
int Vuelta = 0;
int OldState = 0;
unsigned long OldTime = 0;
float Distancia = 0;
float Speed = 0;
//Incializar todo
void setup() {
 Serial.begin(9600);
 lcd.begin(16, 2);
 lcd.setCursor(0, 0);
 lcd.print("@XWork");
 pinMode(HallSensor, INPUT);
 attachInterrupt(digitalPinToInterrupt(HallSensor), GetVelocity, LOW);
 pinMode(LED, OUTPUT);
}
void GetVelocity() {
 if (millis()-OldTime > 70) {
  Speed=Llanta/((float)(millis()-OldTime)/1000)*3.6;
  OldTime=millis();
  Distancia=Distancia+Llanta/1000; 
 }
}
//Bucle principal
void loop() {
 Serial.println("Velocidad en m/s");
 Serial.println(Speed);
 lcd.setCursor(0, 0);
 lcd.print("v= Km/h D = Km");
 lcd.setCursor(0, 1);
 lcd.print(Speed);
 lcd.setCursor(8, 1);
 lcd.print(Distancia);
  if ((millis()-OldTime)>2000){
  Speed=0.0; 
  }
}
Podemos observar que además de la declaración normal del pin (3), justo debajo hacemos la inicialización del pin que se usara para las interrupciones y hacemos uso de la función digitalPinToInterrupt() y le asignamos cómo parámetro el pin que antes habíamos iniciado (el pin 3), luego pasamos cómo parámetro la función GetVelocity(), esta es la que se ejecutará cada vez que se de la interrupción. Finalmente en el ultimo parámetro, le decimos que la interrupción se dará cuando el pin este en LOW

El código anterior es la mejor forma de hacer la cuenta, el código de post anterior cumplía y funcionaba, pero si por alguna razón la rueda girara muy rápido, llegaría el punto en que algunas de las lecturas del sensor del lector serian omitidas. Y bien, por ahora es todo en el próximo post terminaremos con este preyecto.

Los leo luego.

Vamos a programar #49 - Inútil apps #3 - Cómo calcular la velocidad.

Hola de nuevo a todos, el día de hoy vamos a continuar con la construcción de un velocimetro para bicicleta, en el post anterior vimos cómo hacer uso del sensor de efecto hall para poder detectar cuando es que la rueda da una vuelta completa (o cuando un imán es detectado por el sensor). Asi que ahora vamos a ver una parte importante.

Para que el velocimetro quede bien, pienso que al menos debe de medir lo siguiente:

  1. Velocidad (obviamente).
  2. Distancia.
  3. RPM (revoluciones por minuto).
Bien, con lo anterior solo necesitaremos saber cual es la forma de calcular la velocidad. La forma más simple de hacerlo, es usando la formula fisica: v= d/t; donde v es la velocidad en Km/h, d es la distancia en Km y t es el tiempo en horas.

Al mirar el enunciado anterior, podemos determinar que es lo que ya tenemos; la velocidad, es lo que queremos saber, distancia, esta la podemos obtener y el tiempo; para este, arduino cuenta con varias formas de "contar el tiempo".

Obteniendo la distancia.

Para poder obtener la distancia, tenemos que medir cual es la circunferencia de la rueda, hay varias forma de obtenerla.

La primera es mirando la siguiente tabla:

Diámetro Medida rueda (mm)
16x1.75x2 1272
20x1.75x2 1590
24x1 3/8 1948
24x1.75x2 1907
26x1 1973
26x1.5 2026
26x1.6 2051
26x1.75x2 2070
26x1.90 2089
26x1.95 2050
26x2.00 2114
26x2.125 2133
26x1 3/8 2105
26x1 3/8x11/2 2086
26x3/4 1954
27x1 1/4 2199
27x1 1/4 Fifty 2174
27.5x2.10 2150
27.5x2.25 2195
28x1.5 2224
28x1.75 2268
28x1 1/2 2265
28x1 3/8x1 5/8 2205
29 x 2.10 2300
Para saber cual es tu llanta, bastará con revisar un costado de la misma, en mi caso es 24x2.125 (que no esta en la tabla), al revisar, no hay una medida para esa, por lo que pasaremos al siguiente metodo.

Si al revisar la tabla no viste un valor igual al tuyo, podemos obtener la circunferencia si usamos la siguiente formula: C = D * π, Donde C = a la circunferencia en centimetros, D = al diametro en centimetros y π = al numero pi (3.141592654), el diametro lo podemos obtener si usamos una regla para medir. En mi caso, la llanta tiene un diámetro de 62.3 cm, al aplicarlo a la formula anterior, tenemos que la rueda tiene una circunferencia de 195.72 cm.

Otra opción y la mas viable, es medir la circunferencia dando una vuelta, es decir poner una marca en la rueda y en el suelo; pedalear hasta dar una vuelta completa y medir la longitud de la linea.En mi caso la mediad resultante fue de 194.6 cm y esta medida es la mejor, porque ya cuenta con mi peso y si vemos no es muy diferente a la del calculo anterior.

Ahora ya podemos tener la distancia, sabemos, que en mi caso, cada vuelta representan 195.72 cm, la manera simple, es sumar cada medición, por lo que si al dar una vuelta sumamos 195.72 cm, al dar la segunda tendremos 391.44 cm y asi sucesivamente, por lo que la distancia la podemos definir de la siguiente manera: d=195.72 * N° de vuelta, para hacer más fácil la visualización, vamos a convertir los 195.72 cm a m lo que resulta que cada vuelta son 1.9572 m.


Obteniendo la velocidad.

Ahora que tenemos la distancia, podemos obtener la velocidad, de la formula v=d/t, solo nos queda determinar el tiempo. al inicio pensaba usar el modulo de reloj ds1302, pero no se me hacía rentable, dado que solo podria medir tiempo en segundos, cómo programador, se que cada procesador es capaz de llevar la cuenta del tiempo (relativamente), al investigar un poco, vi que arduino cuenta con las funciones millis() y micros() que; respectivamente, llevan la cuenta de los milisegundos y los microsegundos desde que se empezo a ejecutar el programa (desde que se incendio?), para nuestros fines, podemos usar la función millis().

Con un temporizador, podemos tomar el tiempo de cada cuanto el sensor se activo, para eso, primero haremos la medicion de "en que momento estamos" y cuando el sensor detecte un cambio, anotar el tiempo, despues restaremos el tiempo de inicio con el del final y ese lapso de tiempo nos indicará, cuanto tiempo le toma avanzar 1.9572 m (en mi caso).

Con esa información, podemos crear una función en arduino cómo la que sigue:

// {...}
//obtener la velocidad
float GetVelocity(){
	float Velocity = Distancia/((float)(millis()-OldTime)/1000);
	OldTime=millis();
	return Velocity;
}
// {...}

Con esta estaremos midiendo la velocidad en metros por segundo. para hacer la conversion a Km/h, debemos de multiplicar por 3600 (segundos en una hora) y dividir entre 1000 (metros por cada kilometro).

Hay que tener en cuenta algunas consideraciones, ¿cuando es prudente decir que la bicicleta se detuvo? en mi experiencia, el usuario normal, no puede ir muy lento asi que haremos las preparaciones para que cuando el sensor no se active en al menos tres segundos, asumamos que la bicicleta se ha detenido.

Y bien, por ahora es todo, en el siguiente post implementaremos todo el código neceario.

Los leo luego.

Vamos a programar #48 - Inútil apps #3 -Velocímetro para bicicleta, sensor de efecto Hall.

Hola de nuevo a todos. El día de hoy vamos a ver una pequeña introducción a la construcción de un velocímetro.

Cómo algunos sabrán, tengo cierta afición por hacer viajes en bicicleta, pero cómo soy un hombre de números, siempre me ha gustado cuantificar lo que hago (en medida de lo posible), así que decidí llevar la cuenta de la actividad que hacía. Por fortuna, muchos de los teléfonos actuales, tienen aplicaciones que ayudan a llevar el registro (gracias a sensores y al GPS).
Hasta hace poco tenía la idea de hacer una versión propia de un programa que lleve todos esos datos, pero un par de eventos bastante desafortunados, bastaron para pensarlo dos veces. Entonces opté por comprar un velocímetro, lo encargue desde China y tenía un tiempo estimado de entrega de dos meses, pero por sismos y varias cosas, no llegaron (ya va medio año prácticamente, pero aun espero su llegada junto con la de algunos paquetes mas grandes).
Al revisar cómo es que funcionan, noté que la gran mayoría funcionan de la misma manera; se pone un sensor en la tijera que sostiene una llanta y cada vez que ésta da una vuelta, de alguna manera se notifica. En el caso del velocímetro que encargue, en el manual (de internet) decía que se debía de pegar un imán a un rayo de la llanta y el sensor ponerse fijo en la tijera, al revisar más a detalle, averigüe que se trataba de un sensor de efecto Hall.

El sensor de efecto Hall.

El sensor de efecto Hall o simplemente sensor Hall o sonda Hall (denominado según Edwin Herbert Hall) se sirve del efecto Hall para la medición de campos magnéticos o corrientes o para la determinación de la posición en la que está.
Si fluye corriente por un sensor Hall y se aproxima a un campo magnético que fluye en dirección vertical al sensor, entonces el sensor crea un voltaje saliente proporcional al producto de la fuerza del campo magnético y de la corriente. Si se conoce el valor de la corriente, entonces se puede calcular la fuerza del campo magnético; si se crea el campo magnético por medio de corriente que circula por una bobina o un conductor, entonces se puede medir el valor de la corriente en el conductor o bobina.
Si tanto la fuerza del campo magnético como la corriente son conocidos, entonces se puede usar el sensor Hall como detector de metales. Wikipedia/SensorHall
Ahora veremos cómo conectar el sensor de efecto hall, para hacerlo, necesitas lo siguiente:

  1. Sensor efecto hall.
  2. Resistencia 1KΩ  ~10KΩ. 
  3. Arduino

En mi caso, el sensor es el 44e938, cuyos pines son:


  1. Supply (4.5~24V).
  2. Ground.
  3. OutPut.
Para conectarlo, simplemente debemos de conectar la pata uno a V+, en este  caso 5V, Ground a tierra y output a un pin del arduino. Para poder sensar correctamente, debemos de poner una resistencia entre la pata 3 (output) y la pata 1 (supply), al inicio veía esquemas en los cuales siempre usaban resistencias de 10K, pero en mi caso y por el tamaño de los imanes que uso, con una resistencia de 1K fue más que suficiente. Si la resistencia no se pone, el sensor queda muy sensible (valga la redundancia) y detectara cambios tan pequeños en el flujo magnético que incluso acercar tu dedo afecta. OutPut lo conectaremos a un pin del arduino, si quieres probar el código, puedes conectarlo en el pin 3.

Con lo anterior pasemos al código.
//Pin al que coenctaremos el sensor
const int HallSensor = 3;
//LED para mostrar el cambio en caso de no disponer del monitor serie
const int LED =  13;
//Estado del sensor
int HallSensorVal = 0;
//Número de vuelta
int Lap = 0;
//Estado anterior del sensor
int OldState = 0;
//Inicializar lo necesario
void setup() {
	Serial.begin(9600);
	pinMode(LED, OUTPUT);
	pinMode(HallSensor, INPUT);
}
//Bucle princial
void loop() {
	HallSensorVal = digitalRead(HallSensor);
	if (OldState != HallSensorVal) {
		if (HallSensorVal == LOW) {
			digitalWrite(LED, HIGH);
			Lap += 1;
			Serial.println("Vuelta:");
			Serial.println(Lap);
			delay(1);
		}
		else {
		digitalWrite(LED, LOW);
		}
	}
	if (Lap > 99)
		Lap = 1;
	OldState = HallSensorVal;
}

El código anterior, lo único que hace, es verificar cual es el estado del sensor, cuando esta en modo "LOW", prende el LED que viene por default en el pin 13 y a la variable "Lap" le suma 1 (indicando una vuelta). Para probar el código anterior, necesitaras también un par de imanes, en mi caso, los extraje de un quemador de CD viejo, y los pegué en un motor (también extraído del lector de CD).
El resultado:

Con esto podemos probar que el sensor funciona de manera correcta.

Y bien, por ahora es todo, en el siguiente post, veremos cómo medir distancia y velocidad con el mínimo hardware posible. Aun no he decidido si usar las matrices para mostrar los datos o usar una pantalla LCD (cómo la del nokia) o incluso pantalla de siete segmentos. El código lo puedes copiar y pegar para usarlo, en cuanto este completo lo subire a mi dropbox.

Los leo luego.

Vamos a programar #47 - Co-Rutinas en LUA.

Hola de nuevo a todos, el día de hoy vamos a ver un poco sobre el lenguaje de programación LUA.

Antes que nada, debo de aclarar que este no es un tutorial para aprender a programar en Lua, en este post solo explicaré cómo hacer uso de las co-rutinas. Probablemente en el futuro, habrá un ecuatorial completo en las series "Learning machine", pero por ahora y por petición de un amigo mio, veremos cómo es que se hace uso de ellas. Eso no nos impedirá ver lo básico, ¿Que es Lua?; bueno, lua es un lenguaje de programación interpretado; es decir, a diferencia de lenguajes cómo C o C#, este, no es necesario compilarlo para usarlo, una vez escrito el código fuente, se puede pasar al interprete (que será el ejecutable) y este interpretará el código y producirá un resultado, si es necesario cambiar alguna parte del programa, en lugar de volver a compilar el ejecutable, bastará con modificar el código fuente.

Co-rutinas.

Ahora veamos lo que nos trajo a este post, hace un tiempo, mientras un escribía código para "Play List M" (una aplicación para PSVita), un amigo vio lo que hacía y pregunto que si sabía usar lua, le dije que si, toda la aplicación esta hecha en ese lenguaje, entonces me pregunto si sabia usar las co-rutinas y si podía explicarserlas.

Las co-rutinas en lua, sirven para ejecutar código en una sección aparte, es muy similar un "thread" en c#, pero de una forma un tanto distinta. Veamos cómo es que se ejecuta un programa con un "thread" (hilo desde ahora) separado. Ahora observemos las siguientes imágenes.
Esta es la representación más simple de un programa

Esta es la representación más simple de un programa con un hilo adicional.

La ventaja de tener un hilo secundario, es que la aplicación no se bloqueara mientras haga un trabajo difícil (o haga un ciclo o un bucle), si los resultados no son necesarios para continuar con la ejecución del programa, podemos hacer otra tarea mientras el código del otro hilo termina.

En el caso de lua, cuando hablamos de co-rutinas, no significa que creemos otro hilo, para observar a detalle lo que ocurre, tomemos el siguiente código:


local counter = 1; local function OneToHoundred() while counter < 50 do io.write("Contador:"..counter.."\n"); coroutine.yield(); counter = counter + 1; end end CoProc = coroutine.create(OneToHoundred); local counter2 = 0; while counter2 < 101 do coroutine.resume(CoProc); io.write("Contador2:"..counter2.."\n"); counter2= counter2 + 1; end

En lua, la manera sencilla de crear una co-rutina, es haciendo uso de "coroutine.create()" y en su forma más sencilla, debemos de pasar una función cómo parámetro. para poder interactuar con la función, dentro de esta, debe de haber algo que diga "oye, tengo que decir algo", la función "coroutine.yield()" se encarga de hacer eso, finalmente, debemos de tener una parte que escuche y para eso existe "coroutine.resume()". Si tomamos cómo ejemplo el código anterior, vemos que hay que crear la funcion y esta debe de contener la "interrupción", en este caso "coroutine.yield()" la manera en que lua trabaja con las co-rutinas, es interrumpiendo, es decir si estamos ejecutando un trozo de código, este normalmente se ejecutará de forma lineal hasta el final, pero si usamos las co-rutinas, cuando observamos en el segundo bucle, y en general cuando se encuentre con la instrucción "coroutine.resume()", la ejecución pasará a la co-rutina y esta se ejecutará hasta que se encuentre con "coroutine.yield()", cuando se encuentra con esta instrucción, la ejecución continuará con la linea inmediata después de "coroutine.resume()".

Para poder apreciar mejor el resultado, puedes ir a la página de lua, y probar el código anterior.
Cómo lua es un lenguaje de "scripting" es usado en varias plataformas y las funciones disponibles pueden variar, pero todas las que se usan aquí, deben de estar disponibles en la mayoría, ahora por ejemplo, si queremos cargar archivos sin bloquear la pantalla principal, podemos hacerlo usando co-rutinas. Esa seria la introducción, pero esto se puede ampliar un poco mas, en otro post.

Los leo luego.

Vamos a platicar #4 - Luchando contra la estupidez.

Hola de nuevo a todos, el día de hoy vamos a platicar un poco sobre la estupidez.

Cómo muchos se habrán dado cuenta, el día 16 de febrero del 2018, en México, ocurrió un sismo de una magnitud considerable.


Aquí lo rescatable, es que todo estuvo en orden, la preocupación por las edificaciones que resultaron dañadas el 19 de septiembre, solo quedó en eso; preocupación. Todo quedo en la sacudida y ya. Pero la gente es pesimista por naturaleza, y de inmediato empezaron a cuestionarse; ¿Si este fue más fuerte que el otro, por que no pasó nada (al menos en la zona centro de México)? La respuesta es simple, el sismo se genró en una zona alejada, por eso a comparación del 19 de septiembre, ahora si sonó la alerta sísmica con 60 segundos (+-), suficiente para salir o resguardarte en una zona segura.

Desde el sismo anterior, a muchas personas les dio por adivinar cuando es que ocurriría el próximo sismo, muchos prácticamente todos los días publicaron en su twitter "hoy va a temblar"; así lo hicieron todos los días hasta que eventualmente ocurrió una coincidencia. Es importante resaltar la palabra "coincidencia", si has andado por estos lares, sabrás que prácticamente todos los días tiembla, lo hace en magnitudes "pequeñas" (2.0~5.0 grados) y por eso no son perceptibles para la mayoría de la población. De todos los temblores que ocurren, un día habrá uno de una magnitud considerable y esta gente cómo se la pasó todos los días haciendo lo mismo, lamentablemente acierta.

Mientras navegaba por internet, vi una cantidad de vídeos impresionante en las que se aseguraba predijeron tal evento. Solo para tratar de evidenciar lo charlatanes que eran las personas que hicieron esos videos, entré y comenté algo sensato. La respuesta de la gente fue realmente estúpida, de 10 personas que respondieron mi comentario, ninguna se tomo la molestia de revisar alguno de los enlaces que puse a fuentes confiables (cómo la universidad nacional autónoma de México), lejos de eso, me atacaron, que dejara de desvirtuar el video, que solo era un troll de internet tratando de hacer fama insultando al video e incluso alguien que aseguraba ser geólogo y además decía que si era posible predecir los temblores. Todo esto era bastante frustrante, tan solo por la primicia; "predecir temblores en base a la actividad del sol".

Cualquiera creería que no hay gente tan tonta cómo para creer en cosas tan descabelladas, y si, es muy probable que algunos de los comentarios fueran solo con la intensión de molestar, pero cuando hablé con alguien en la vida real, me dijo exactamente lo mismo que el video y me trato de idiota por decir que estaba mal.

Alguna vez, uno de mis profesores en la facultad, dijo que la estupidez debía de combatirse, pero cómo rayos se hace cuando la persona es necia? Tratar de insistir me volveria en alguien molesto que solo trata de mostrar que tiene un poco de razón. Finalmente desistí, algunas veces hay que dejar a los necios morir en su necedad.

Y bien por ahora es todo, solo me queda recordarles lo básico: los temblores no son predecibles, diario tiembla y en el caso de la zona centro de México; si suena la alerta sísmica, es para resguardarse (y no para ver si tiembla). La semana que viene continuaremos con más programación, hace poco alguien me pidió ayuda en lua y creo que serviría para aprender y cómo introducción para hacer lo mismo en otros lenguajes.

Los leo luego.

El Video Correcto #5 -Convertir archivos FLAC a MP3..

Hola de nuievo a todos, el día de hoy vamos a ver cómo convertir archivos FLAC (y realmente casi cualquier archivo multimedia) a MP3 usando FFMPEG.

La forma básica de hacerlo. Utilizando un bloc de notas cómo notepad++ vamos a escribir el nombre del programa; en este caso FFMPEG.exe, luego agregaremos el parámetro "-i" que sirve para indicar que queremos "ingresar" un archivo. FFMPEG acepta múltiples archivos de entrada, pero en este caso solo usaremos uno.
Luego debemos de indicar cual es el codec que vamos a usar; cómo queremos convertir a MP3, usaremos el codec que FFMPEG nos ofrece; el codec libmp3 lame.
Luego estableceremos la velocidad de bits que se usarán, para mayor calidad, usaremos el máximo disponible para MP3, que es 320Kb/s y finalmente pondremos el nombre al archivo de salida.
Suponiendo que tenemos el archivo "Infile.flac" y lo queremos convertir a "Output.mp3", podemos crear la sigueinte liena de comando:

ffmpeg.exe -i "InFile.flac" -codec:a libmp3lame -b:a 320k output.mp3
pause

Con lo anterior, podemos crear un archivo MP3 de la mejor calidad (dependiendo de la calidad del archivo FLAC mismo).


A tomar en consideración.

Hay varios factores a considerar para obtener el mejor resultado al momento de crear una versión MP3. Además de la velocidad de bits, cosas cómo la frecuencia y la cantidad de canales, afectaran el tamaño del archivo.
Ahora tomemos un ejemplo, tenemos una canción en FLAC que tiene una frecuencia de 48000Hz y queremos que nuestro archivo MP3 también tenga esa frecuencia, para indicar a FFMPEG que queremos usar esa frecuencia, la linea de comando anterior la podemos cambiar a algo cómo lo que sigue:

ffmpeg.exe -i "InFile.flac" -codec:a libmp3lame -b:a 320k -ar 48000 output.mp3
pause

Con eso se habrá creado una versión MP3 con una frecuencia de 48000 Hz, pero cómo cualquier cosa; "no podemos sacar más de lo que hay" por lo que si el archivo FLAC está a una frecuencia de 44100Hz, es un malgasto crear un MP3 a 48000Hz.

Otra de las cosas importantes, es la cantidad de canales de audio que el MP3 poseerá, normalmente todas las pistas de audio vienen en "estéreo" por lo que hay dos pistas de audio una para el canal derecho y otra para el canal izquierdo. En ocasiones solamente queremos oír todo de "un solo lado" por lo que habrá algunos que querrán convertir sus canciones en "mono", para poder cambiar los canales de audio que la canción tendrá, podemos modificar la linea anterior para  tener algo cómo lo que sigue:

ffmpeg.exe -i "InFile.flac" -codec:a libmp3lame -b:a 320k -ar 48000 -ac 1 output.mp3
pause

Al usar el parámetro "-ac" le vamos a indicar que queremos usar X número de canales de audio, para los archivos MP3, el mayor número de canales es dos. En este caso es posible crear una pista estéreo deesde una pista mono, pero al hacer esto, ambos canales de audio sonarán exactamente igual.

Para que todo lo anterior funcione, debemos de guardar el archivo de texto junto al ejecutable de FFMPEG, pero además, debemos de cambiar la extensión del archivo de "TXT" a "BAT", si no lo hacemos, al momento de abrirlo, solo mostrará su contenido en lugar de ejecutarlo. En notepad++ en el cuadro de dialogo para guardar, bastará con poner el nombre del archivo más la extensión "BAT".

Y bien, por ahora es todo, aun quedan unas cuantas cosas quedemos de saber sobre el formato MP3, pero si no te quieres complicar, puedes usar una linea de comandos cómo la primera que viste en este post, con eso FFMPEG ajustará los demás parámetros a los más indicados dependiendo del archivo FLAC que se ingrese. Probablemente haga una GUI para hacer conversiones en lotes, pero cómo aun quedan muchas cosas pendientes, a lo mejor primero actualizo VEncoder y después hago la GUI par convertir los archivos MP3.

Los leo luego.

El Video Correcto #4 - Flac Vs MP3 #2.

Hola de nuevo a todos, el día de hoy vamos a continuar con la revisión del "por qué" el formato FLAC es mejor al formato MP3.

En el post anterior, dije que el formato FLAC es mejor a el MP3, ya que este comprime en lugar de eliminar. Vamos a ver una pequeña muestra de que tanto es lo que se elimina cuando pasamos de un formato a otro.

Para poder realizar la prueba, necesitas lo siguiente:


  1. Un archivo FLAC.
  2. Un archivo MP3.
  3. Un editor de audio (Yo uso Audacity).

Una vez que tenemos lo anterior, abriremos nuestro editor de audio e importaremos las pistas de audio.

Lo primero que haremos, es alinear las pistas, de tal modo que ambas empiecen en exactamente el mismo momento, si descargaste las canciones de muestra, en el archivo MP3 hay que cortar un poco y con ayuda del zoom, buscar que ambas inicien en el mismo instante.
Una vez alineadas ambas pistas, seleccionaremos la pista que corresponde al archivo mp3 y le aplicaremos el efecto de "inversión", no hay que confundirlo con "revertir", ya que uno lo que hace, es invertir "de arriba a abajo", mientras que el otro efecto lo hace "de izquierda a derecha", o mas técnicamente, uno invierte las onda por lo que los valles pasan a ser crestas (y viceversa).
En las ondas cuando sumamos dos ondas que son iguales pero invertidas, tenemos que se cancelan entres si, si aplicamos eso a nuestro audio, al mezclar las pistas, obtendremos, que los "sonidos" existentes en ambas se cancelaran y solo quedaran aquellos que estén ausentes en cualquiera de las pistas. En el caso de audacity, bastará reproducir las dos canciones al mismo tiempo y notarás de inmediato que solo se escucha una versión reducida y aguda de la canción original. Esto que escuchas, es todo lo que se pierde al momento de convertir a mp3.

Las siguientes imágenes muestran cómo se "ve" cada formato:
Formato MP3

Formato FLAC
Residuo al "sumar" las dos canciones.
El resultado lo puedes descargar para escucharlo, es importante que antes hayas escuchado los otros 2 archivos (eso si, recuerda eliminarlos a terminar de usarlos). Cómo notarás, el sonido es bajo y agudo, esa es una de las principales características del MP3, eliminar las frecuencias que la mayoría de las personas (en teoría) no escuchan más allá de los 20KHz, pero también es un hecho que hay personas (ademas de los niños), que pueden oír sonidos de hasta 32KHz.

Y bien, por ahora es todo, en el próximo post, veremos cómo convertir de FLAC a MP3 usando FFMPEG.

Los leo luego.

El video correcto #3 -FLAC Vs MP3

Hola de nuevo a todos, el día de hoy vamos a ver cual es la diferencia entre un archivo MP3 y un archivo FLAC


Hace poco, mi computadora murió y junto con ella, todos los archivos que se encontraban en el disco duro, una perdida considerable, pero no tan grave, puesto que tengo un respaldo de las cosas importantes en un disco duro externo. Cómo no tenia el presupuesto necesario para poder comprar un disco duro de 8TB, opté por comprar lo primero que encontrará, lo que conseguí, fue un disco duro de 80GB. Podría parecer bastante, pero suponiendo que todos los IDE's de desarrollo que uso, ocupan en conjunto 20 GB + 10GB del sistema operativo + los archivos que requiero utilizar, el espacio restante, fue realmente pequeño.
Actualmente no poseo mucha música, pero la poca que tengo, al estar en formato FLAC, ocupa un espacio considerable (en comparación al tradicional y compacto MP3).

Antes de seguir, el formato FLAC es el siguiente:
FLAC (son las siglas de Free Lossless Audio Codec) es un códec de audio que permite que el audio digital sea comprimido sin pérdidas de tal manera que el tamaño del archivo de audio se reduce sin que se pierda ningún tipo de información. El audio digital comprimido por el algoritmo de FLAC típicamente se puede reducir de 50 a 60% de su tamaño original,3​ y se descomprime en una copia idéntica de los datos de audio originales.
Por la calidad superior que estos poseen, es que los prefiero, pero ante la limitada capacidad del disco duro, decidí hacer una versión en MP3 de los archivos, para ver un ejemplo de cuantas es la diferencia entre uno y otro, veamos la información de la siguiente canción:

Archivo MP3 de la mejor calidad, 8 MB

Archivo FLAC con calidad buena (no superior) 26MB
Muchos no notan la diferencia entre un MP3 de máxima calidad y un FLAC promedio, pero por desgracia, yo si la notaba (antes porque ahora mi oído se ha gastado) y por eso trataba de "conseguir" la música en la mejor calidad posible.
En archivo MP3 se mutilan algunas frecuencias, que por lo general el oído no logra captar, pero en el archivo FLAC, todas las frecuencias se mantienen, y se comprimen, logrando que el sonido sea mucho más fiel al original.
Al deteriorarse mi oído, debo admitir que realmente, en el 60% de los casos ya no escucho ninguna diferencia entre un MP3 de alta calidad y un FLAC por lo que decidí crear una versión liviana. Lo importante de esto, es que podemos hacer una versión MP3 de un archivo FLAC; pero no el reves, crear un archivo FLAC de un MP3, es un desperdicio, cómo el MP3 literalmente fue "cortado", esas frecuencias faltantes ya no se pueden recuperar y estariamos haciendo un archivo pesado que realmente no valdría la pena.

Si quieres oír la diferencia entre FLAC y MP3, dejo una canción de muestra (que deberás de eliminar una vez que termines de comparar).

El archivo flac, lo puedes descargar de aqui.
El archivo mp3, lo puedes descargar de aqui.

Por ahora es todo, en el próximo post, veremos cómo crear un MP3 desde un archivo FLAC usando FFMPEG.

Los leo luego.

Vamos a programar #46 - Código Bacon (ver Java android)

Hola de nuevo a todos, el día de hoy vamos a ver un poco mas sobre el código Bacon y cómo implementar una aplicación en android que haga todo el trabajo sucio por nosotros.
En post anteriores vimos cómo hacer la misma aplicación para C# (escritorio) y Javascript (Web), en esta ocasión, todo el post será solo para mostrar la app, puesto que las funciones que se emplean son exactamente las mismas que la versión de C#. El código completo es el siguiente:

package com.mdev.baconiancipher;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

public class MainActivity extends Activity {
	EditText ETInput, ETInput2, ETPreResult, ETResult;
	Button BtnPreCipher, BtnCipher;
	String[] Values = new String[]{"AAAAA", "AAAAB", "AAABA",
				"AAABB", "AABAA", "AABAB", "AABBA", "AABBB", "ABAAA", "ABAAA",
				"ABAAB", "ABABA", "ABABB", "ABBAA", "ABBAB", "ABBBA", "ABBBB",
				"BAAAA", "BAAAB", "BAABA", "BAABB", "BAABB", "BABAA", "BABAB",
				"BABBA", "BABBB"};


	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		ETInput = findViewById(R.id.ETInput);
		ETInput2 = findViewById(R.id.ETInput2);
		ETPreResult = findViewById(R.id.ETPreResult);
		ETResult = findViewById(R.id.ETResult);
		BtnPreCipher = findViewById(R.id.BtnPreCipher);
		BtnCipher = findViewById(R.id.BtnCipher);

		BtnPreCipher.setOnClickListener(new View.OnClickListener() {
				@Override
				public void onClick(View v) {
					ETPreResult.setText(CharToBaconianCode(ETInput.getText().toString()));
				}
			}
		);
		BtnCipher.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				ETResult.setText(FinalCipher(ETPreResult.getText().toString(),ETInput2.getText().toString()));
			}
		});
	}

	private String CharToBaconianCode(String Input)
	{
		Input = Input.toUpperCase();
		char[] Letras = Input.toCharArray();
		String OutValue = "";
		for (int X = 0; X < Letras.length; X++)
		{
			if ((byte)Letras[X] < 91 && (byte)Letras[X] > 64)
			{
				OutValue += Values[(byte)Letras[X] - 65];// + " ";
			}
		}
		return OutValue;
	}
	private boolean IsLower(byte Code)
	{
		if (Code > 96 && Code < 123)
			return true;
		else
			return false;
	}
	private String FinalCipher(String PreCipher, String Phrase)
	{
		if (PreCipher.length() != Phrase.length())
		{
			ShowDialog("Error","Las longitudes del pre-cifrado y la frase para insertarlo no coinciden");
			return "Error";
		}
		char[] PreCipherChars = PreCipher.toCharArray();
		char[] PhraseChars = Phrase.toCharArray();
		String FinalResult = "";
		for (int Y = 0; Y < PhraseChars.length; Y++)
		{
			if ((byte)PreCipherChars[Y] == 65)
			{
				if (IsLower((byte)PhraseChars[Y]))
					FinalResult += PhraseChars[Y];
				else
					FinalResult += (char)(PhraseChars[Y] + 32);
			}
			else
			{
				if (!IsLower((byte)PhraseChars[Y]))
					FinalResult += PhraseChars[Y];
				else
					FinalResult += (char)(PhraseChars[Y] - 32);
			}
		}
		return FinalResult;
	}
	//Mostrar dialog de manera sencilla
	private void ShowDialog(String Title, String Caption)
	{
		new AlertDialog.Builder(this)
				.setTitle(Title)
				.setMessage(Caption)
				.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
					public void onClick(DialogInterface dialog, int which) {
						// algo
					}
				})
				.setIcon(android.R.drawable.ic_dialog_alert)
				.show();
	}
}


Cómo notarás, no hay ningún cambio significativo (y no lo habrá en unos días debido a que mi PC murió y no cuento con el tiempo cómo para arreglarlo). Por ahora dejo el código fuente y el APK ya compilado para que los descargues de mi dropbox. Las actualizaciones se harán juntas para las 3 versiones en alguno de los próximos post

Por ahora es todo, los leo luego.

Vamos a programar #45 - Código Bacon (ver. Javascript).

Hola de nuevo a todos, el día de hoy vamos a ver cómo crear una aplicación en javascript que sirva para aplicar el código Bacon en un texto.

En el post anterior hicimos una versión en C# para escritorio, el día de hoy vamos a realizar una versión para la Web.

El código.

El codigo en javascript que sirve para cifrar una frase usando el código Bacon es el siguiente:

<html>
	<head>
	<title>Bacon Cipher</title>
	<style type="text/css">
		.FormBacon{
			font: 95% Arial, Helvetica, sans-serif;
			max-width: 600px;
			margin: 10px auto;
			padding: 16px;
			background: #F9F9F9;
		}
		.FormBacon h1{
			font-size: 140%;
			text-align: center;
		}
		.h1c
		{
			font-size: 16px;
		}
		.textareaV
		{
			width: 100%;
			height: 150px;
			padding: 2px 5px;
			box-sizing: border-box;
			border: 2px solid #999;
			border-radius: 4px;
			resize: none;
			-webkit-transition-duration: 0.4s; /* Safari */
			transition-duration: 0.4s;
		}

		.textareaV:focus
		{
			width: 100%;
			height: 150px;
			padding: 2px 5px;
			box-sizing: border-box;
			border: 2px solid #26c6da;
			border-radius: 4px;
			resize: none;
		}

		.TextV
		{
			width: 100%;
			box-sizing: border-box;
			border: 2px solid #999;
			border-radius: 4px;
		}

		.ButtonV
		{
		width: 100%;
			background-color: #26c6da;
			border: none;
			color: white;
			padding: 15px 32px;
			text-align: center;
			text-decoration: none;
			display: inline-block;
			-webkit-transition-duration: 0.4s; /* Safari */
			transition-duration: 0.4s;
		}

		.ButtonV:hover
		{
		width: 100%;
			background-color: #30e0e0;
			border: none;
			color: white;
			padding: 15px 32px;
			text-align: center;
			text-decoration: none;
			display: inline-block;
		}
		</style>
		<script type="text/javascript">
		var Values =["AAAAA", "AAAAB", "AAABA",
			"AAABB", "AABAA", "AABAB", "AABBA", "AABBB", "ABAAA", "ABAAA",
			"ABAAB", "ABABA", "ABABB", "ABBAA", "ABBAB", "ABBBA", "ABBBB",
			"BAAAA", "BAAAB", "BAABA", "BAABB", "BAABB", "BABAA", "BABAB",
			"BABBA", "BABBB"];
		function PhraseToArray(Text){
			var Out = [];
			for(var i = 0; i < Text.length; i++){
				var CodeChar = Text.charCodeAt(i);
				Out.push(CodeChar);
			}
			return Out;
		}
		function CharToBaconianCode(){
		//debugger;
			Input = document.getElementById('InputText').value;
			Input = Input.toUpperCase();
			var Letras = PhraseToArray(Input);
			var OutValue = "";
			for (var X = 0; X < Letras.length; X++)
			{
				if (Letras[X] < 91 && Letras[X] > 64)
				{
					OutValue += Values[Letras[X] - 65];// + " ";
				}
			}
			document.getElementById('PreResult').value = OutValue;
			return OutValue;
		}
		function IsLower(Code)
		{
			if (Code > 96 && Code < 123)
				return true;
			else
				return false;
		}
		function FinalCipher()
		{
		//debugger;
			var PreCipher = document.getElementById('PreResult').value;
			var Phrase = document.getElementById('Phrase2').value;
			if (PreCipher.length != Phrase.length)
			{
				alert("Las longitudes del pre-cifrado y la frase para insertarlo no coinciden");
				return "Error";
			}
			var PreCipherChars = PhraseToArray(PreCipher);
			var PhraseChars = PhraseToArray(Phrase);
			var FinalResult = "";//string
			for (var Y = 0; Y < PhraseChars.length; Y++)
			{
				if (PreCipherChars[Y] == 65)
				{
					if (IsLower(PhraseChars[Y]))
						FinalResult += String.fromCharCode(PhraseChars[Y]);
					else
						FinalResult += String.fromCharCode(PhraseChars[Y] + 32);
				}
				else
				{
					if (!IsLower(PhraseChars[Y]))
						FinalResult += String.fromCharCode(PhraseChars[Y]);
					else
						FinalResult += String.fromCharCode(PhraseChars[Y] - 32);
				}
			}
			document.getElementById('Result').value = FinalResult;
		}
		</script>
	</head>
	<body>
		<div class="FormBacon">
			<br />
			<h1>Código Bacon</h1><br />
			<br />
			<form>
				<span class="h1c">Frase a encriptar:</span>
				<br />
				<input class="TextV" id="InputText" type="text"></input><br />
				<br />
				<input class="ButtonV" id="Encrypt" onclick="CharToBaconianCode()" type="button" value="Pre-cifrar" /><br />
				<br/>
				<span class"h1c">Resultado del Pre-cifrado</span><br/>
				<br/>
				<textarea class="textareaV" id="PreResult" rows="5"></textarea><br/>
				<br/>
				<span class="h1c">Frase para insertar</span><br /><br/>
				<input class="TextV" id="Phrase2" type="text" /><br />
				<br />
				<input class="ButtonV" id="UnEncrypt" onclick="FinalCipher()" type="button" value="Cifrar" /><br />
				<br />
				<span class="h1c">Resultado</span>
				<textarea class="textareaV" id="Result" rows="5"></textarea><br/><br/>
			</form>
		</div>
	</body>
</html>
 
La aplicación consiste de cinco partes; de las cuales cuatro son funciones y una más, es una variable que se usará para almacenar las constantes del cifrado
Primero definimos una nueva variable, será una matriz (que ya dije para que se usa). Luego creamos una funcion que reemplazará a la función de C# "string.toArray()", esta función es "PhraseToArray()"; recibe un parámetro que será el texto que queremos convertir a una matriz de números, hay que recordar que en el caso de la función en C#, cuando la llamamos, esta devuelve una matriz del tipo "char", en este caso, cómo vamos a usar el valor ASCII de esa letra, directamente guardamos en la matriz el valor resultante de llamar a la función "charCodeAt()", si hacemos memoria, está función, lo que hace es obtener el código ASCII de la letra (o símbolo) que se le pasa cómo argumento, entonces, solamente hacemos un ciclo "for" que recorra cada letra desde el inicio al final del texto que se ingreso cómo parámetro en la función "PhraseToArray()". Finalmente, está función devuelve una matriz con números que representa el código ASCII de cada letra.

La siguiente función es "CharToBaconianCode()", cómo su nombre lo dice, está función lo que hace, es convertir cada letra en su equivalente en el código Bacon. La única diferencia con la función de C#, es que esta no recibe parámetros, lo que hace para obtener el texto sobre el cual trabajara, es usar la función "document.getElementById('')" y obtener el valor de la caja de texto. El resto del proceso es similar, solamente que no hace conversiones de "char" a "byte", porque hay que recordar que los valores de la función "PhraseToArray()" ya están en un valor numérico.
La función "IsLower()" no sufre ningún cambio.
Finalmente la función  FinalCipher() al igual que la función "CharToBaconianCode()" no sufre mayor cambio con respecto a la función de C#.
Y bien, por ahora es todo, cómo te habrás dado cuenta, todo funciona perfecto cuando vamos al derecho, pero no al revés, por absurdo que parezca, olvide escribir el código para realizar el proceso contrario, pero no te preocupes, en breve agregaré las partes faltantes en esta versión y en la versión de C#, además, la siguiente semana, la versión para android vendrá con la función para hacer el proceso inverso. Cómo siempre, la descarga del código la puedes hacer desde mi dropbox.

Por ahora es todo, los leo luego.