Xwork's Blog

The lord is waiting to take your hand.

Mostrando las entradas con la etiqueta mp3. Mostrar todas las entradas
Mostrando las entradas con la etiqueta mp3. Mostrar todas las entradas

El Video (AUDIO) Correcto #7 - Un poco mas sobre el audio.

 Hola de nuevo a todos, el día de hoy vamos a ver un poco mas sobre los archivos FLAC.

imagen tomada de: https://www.revistagadgets.com/2017/04/04/audio-hi-res-la-nueva-linea-audifonos-pioneer/


Cómo muchos de ustedes sabrán, me gusta mucho oír música (música de verdad y no "regeton"), pero siempre había usado equipo de "baja calidad" (y solo por nombrarlo de alguna manera). Pero no hace mucho tiempo, visité un lugar donde se exponían diferente sistemas de audio, desde los mas sencillos; cómo bocina bluetooth; hasta los mas complejos; llámese audio de estudio.


Dentro del mismo lugar había un apartado para los audífonos, de inmediato fui a revisar, y había varios para probar. Cada uno tenia la ficha que describía cuales eran sus características, pero hacían un fuerte énfasis en la frecuencia de respuesta de cada uno.


Primero probé los que ofrecían una respuesta de 20 hasta 20000Hz, justo cómo la mayor parte de audífonos son, la calidad era aceptable, mucho de los audífonos que he tenido cumplían con esa frecuencia y todo parecía aceptable. Los siguientes tenían una respuesta de 20 hasta 32000Hz, la misma calidad que los audífonos que siempre usaba, y justo cómo los anteriores, todo funcionaba bien, y a mi gusto, nada podía superar la calidad. En la siguiente mesa había unos audífonos con una respuesta de 20 hasta 40000Hz... había vivido engañado todo el tiempo, siempre me habían dicho que el oído humano no era capaz de oír frecuencias mas allá de los 32000Hz, pero al momento de probar esos, de inmediato me di cuenta que, si bien es cierto que el oído humano está limitado a ciertas frecuencias, el hecho es que cada persona es diferente y hay quienes alcanzan un rango auditivo mayor al de otros. En mi caso es muy difícil decir hasta "donde puedo escuchar", ya que nunca he hecho un estudio que sirva para eso, pero no había duda de que esos audífonos "sonaban mejor" que los otros, por lo que decidí conseguir unos.


Finalmente cosegui unos audifonos edifier w820nb plus que cuestan poco y tienen la calidad superior a los que ya tenía. 

Pero para poder disfrutar de un buen audio, no solo basta con tener los audífonos, también hay que tener un dispositivo compatible y los archivos de audio adecuados. Por fortuna para mi, aun soy de la vieja escuela y tengo mi colección de discos guardados. Normalmente lo que hacia era utilizar mi reproductor de CD (si, en 2023 aun sigo usándolo), pero el inconveniente que encontré, es que la única salida que tiene, es el plug tradicional de 3.5 mm, mientras que los audífonos soportan bluetooth y USB-C.

Para poder escuchar mis discos decidí crear una copia  de ellos, pero no en mp3, porque ya en ocasiones anteriores he hablado de ciertas desventajas que este tiene con respecto a FLAC, detalles que puedes encontrar en un post que hice hace un buen rato.




 Y bien, por ahora es todo, en los siguientes post veremos detalles de cómo importar la música en FLAC, pero además entraremos en detalles técnicos para poder leer y editar los metadatos de los archivos; para mantener todo mas o menos organizados (al igual que lo hicimos con MP3).

Los leo luego.

Vamos a programar #78 - Un vistazo a Python.

Hola de nuevo a todos, el día de hoy vamos a ver un vistazo al lenguaje de programación Python y cómo es posible portar alguno de los programas que previamente ya teníamos hechos a este lenguaje.

Hace poco mientras revisaba twitter, alguien me pregunto la razón por la que no había portado los programas que ya tenia a Python y tras analizar, llegué a la conclusión que simplemente lo olvide (por mucho tiempo al parecer).



Entonces para echarle un vistazo rápido al lenguaje, el día de hoy vamos portar uno de los programas que servia para extraer las imágenes en los archivos mp3.

Primero que nada, vamos a tomar un par de cosas en cuenta. Primero: vamos a hacer una portación de la version que hicimos en LUA, asi que puedes (y recomiendo mucho) visitar el post. Y esto se debe principalmente a que LUA y Python son lenguajes interpretados.

Segundo: No te olvides de revisar la documentación en la página de ID3.org y leer la documentación además de los post que hay aquí en el blog.

Tercero: este post será una versión reducida y el programa solo extraerá la imagen del archivo mp3 si la versión de los TAG's es la 2.3, si quieres hacerlo para las otras versiones o extraer más información, puedes usar cómo ejemplo la versión en LUA.

Con lo anterior dicho, pasemos a ver el código en Python que sirve para extraer una imagen embebida en un archivo MP3.

# ---------De la documentación disponible en http://www.id3.org para la versión 2---------------
# -- Para la version 2.2
    # -- Header for 'Attached picture', ID: "APIC">
    # -- Text encoding   $xx
    # -- MIME type       <text string> $00
    # -- Picture type    $xx
    # -- Description     <text string according to encoding> $00 (00)
    # -- Picture data    <binary data>

#Abrimos el archivo
InputFile = input('Ingresa la ruta de un MP3\n')
Rdata = open(InputFile, 'rb+')
#Buscamos que version ID3 es
Rdata.seek(3)
Version = ord(Rdata.read(1))
#escribimos que version es
print(Version)

if Version == 3:

	print('Tag Version 2.3')
	Rdata.seek(0)
	Idata = Rdata.read(1024)
	Found = Idata.find(b'APIC')
	if Found > -1:
		GotPNG = Idata.find(b"\x89\x50\x4e\x47")
		Rdata.seek(Found + 4);
		b1 = ord(Rdata.read(1))
		b1 = b1 * 256 * 256 *256
		b2 = ord(Rdata.read(1))
		b2 = b2 * 256 *256
		b3 = ord(Rdata.read(1))
		b3 = b3 * 256
		b4 = ord(Rdata.read(1))
		bt = b1 + b2 + b3 + b4 + 10
		print(bt)
		
		if GotPNG > -1:
			PPicInit = Idata.find(b'\x89PNG')
			Rdata.seek(PPicInit)
			IMGData =  Rdata.read(bt - (PPicInit - Found))
			OutPath = input('En que carpeta guardar la imagen\n')
			ResultImage = open(OutPath + '/TestImage.png','wb')
			ResultImage.write(IMGData)
			ResultImage.close()
		else:
			JPicInit = Idata.find(b'\xFF\xD8\xFF')
			Rdata.seek(JPicInit)
			IMGData = Rdata.read(bt - (JPicInit - Found))
			OutPath = input('En que carpeta guardar la imagen\n')
			ResultImage = open(OutPath + '/TestImage.png','wb')
			ResultImage.write(IMGData)
			ResultImage.close()
	else:

		print('Not Found')

Rdata.close()
input() 


Cómo podrás ver el código es realmente similar al de LUA, primero preguntamos por la ubicación de un archivo MP3, para un ejemplo insertamos "X:/Dropbox/MUSIC/American Idiot.mp3" y lo abrimos para su lectura en modo binario usando la función "open()"; la función "open()" recibe dos parámetros, el primero es una cadena de texto con la ruta del archivo a abrir y el segundo parámetro es una cadena de texto que indica el modo en el cual el archivo se abrirá, la cadena de texto "rb+" indica que abriremos el archivo en modo de lectura binario. Todo el "stream" lo asignaremos a la variable "Rdata". Al igual que en lua podemos desplazarnos en el archivo con la función "seek()" que recibe un parámetro que será un número que indicará a que lugar del archivo queremos desplazarnos, este puede ir de 0 a la longitud del archivo. Cómo la versión de los TAG's se indica en el byte 3, nos moveremos ahi y lo leeremos (e imprimimos en pantalla que versión es, no es necesario pero ayuda a tener una idea de lo que está sucediendo).

Luego si la versión de los TAG's es la versión 3, nos desplazamos de vuelta al byte 0 (o inicio del archivo) y leemos  un KByte de datos que asignaremos a la variable "Idata". Con los datos asignados, procedemos a buscar el frame que nos interesa (APIC en este caso) con la función "bytes.find()", está función regresa -1 si la cadena de texto a buscar no se encuentra, en caso contrario nos devuelve el lugar en donde se encontró el valor.

Luego buscamos el encabezado de las imágenes PNG y leemos los bytes que nos indican la longitud del frame (4 bytes). Si NO se encontró el encabezado que previamente buscamos, asumimos que la imagen es jpg (asumir es malo ;) ) y buscamos el encabezado de los archivo JPG. Luego leemos los datos de la imagen y preguntamos en que carpeta se quiere guardar la imagen resultante, por ahora el nombre es TESTImage.???. Hacemos lo mismo para la imagen PNG

Y el resultado:

Y bien, por ahora es todo. Cómo de costumbre puedes bajar el programa completo de mi dropbox para probarlo o modificarlo. Además puedes bajar el interprete de la página oficial de Python. En la descarga se incluye un manual bastante extenso del lenguaje con ejemplos y lo necesario para portar lo programas que hemos hecho en el blog.

Los leo luego


Vamos a programar #54 - ID3 y LUA.

Hola de nuevo a todos, el día de hoy vamos a ver código en LUA que sirve para extraer las imágenes embebidas en un MP3. No es lo lo más optimizado, pero a veces lo que cuenta es conseguir nuestro objetivo.


Antes de continuar, es recomendable que leas uno de los post antiguos del blog en cual se explica cómo hacer de manera "artesanal" todo el procedimiento, a veces ayuda un poco saber exactamente que es lo que vamos a hacer para comprender.

Para poder probar el código vamos a necesitar un interprete de LUA para windows (o para PC con cualquier sistema operativo). Si usas windows al igual que yo, recomiendo usar ZeroBrane Studio, es gratuito para todos pero el autor muestra links para donaciones (si puedes apoyarlo hazlo, siempre le cae bien una bebida a cualquier programador independiente). ZeroBrane además de contener el interprete de lua, es un IDE que sirve para depurar código. Es muy importante instalar localmente una copia del interprete para poder abrir los archivos desde nuestro disco duro.

El código.

Con el interprete ya instalado veamos el código.

-----------De la documentación disponible en http://www.id3.org para la versión 2---------------
--[[
Para la version 2.2
	Header for 'Attached picture', ID: "APIC">
	Text encoding   $xx
	MIME type       <text string> $00
	Picture type    $xx
	Description     <text string according to encoding> $00 (00)
	Picture data    <binary data>
Para la version 2.0
	Attached picture   "PIC"
	Frame size         $xx xx xx
	Text encoding      $xx
	Image format       $xx xx xx
	Picture type       $xx
	Description        <textstring> $00 (00)
	Picture data       <binary data>
]]
function WriteImage(NombreDeLaCancion,NombreDeLaImagen)
--Abrimos el archivo en modo de lectura binario
	local Rdata = io.open(NombreDeLaCancion,"rb")
--Ponemos el puntero al inicio del archivo
	Rdata:seek("set")
--Ahora leemos los primeros 1024b del archivo, si no encontramos aquí la etiqueta "APIC" o "PIC" asumimos que el archivo no tiene imagen embebida
	local Sdata = Rdata:read(1024)
--En esta variable Guardaremos la ubicación del La etiqueta "APIC"
	local GotIt = string.find(Sdata,"APIC")
--Si GotIt es nil en este punto, buscamos la etiqueta PIC
	if GotIt == nil then
		GotIt = string.find(Sdata,"PIC")
	end
--
--Si la variable GotIt tiene cualquier valor diferente de nil; procedemos a buscar el tipo de imagen que este embebida.
	if GotIt ~= nil then
		local GotPNG =string.find(Sdata,string.char(137)..string.char(80)..string.char(78)..string.char(71))
	--Obtenemos la versión
		local GetVer = string.find(Sdata,"APIC")
--La comprobación es simple ya llegados a este punto; si uno es falso el otro debe de ser verdadero.		
--Regresamos el puntero a la posición en donde se encontró la etiqueta APIC y leemos la longitud de la etiqueta
		local b1,b2,b3,b4
		local TagSize	
		if GetVer ~= nil then
			Rdata:seek("set",GotIt+3)
			b1=string.byte(Rdata:read(1))*256*256*256
			b2=string.byte(Rdata:read(1))*256*256
			b3=string.byte(Rdata:read(1))*256
			b4=string.byte(Rdata:read(1))
			TagSize = b1+b2+b3+b4+10
		else
			Rdata:seek("set",GotIt+2)
			b1=string.byte(Rdata:read(1))*256*256
			b2=string.byte(Rdata:read(1))*256
			b3=string.byte(Rdata:read(1))
			TagSize = b1+b2+b3+5
		end
--Si los datos dicen que la imagen es JPG
		if GotPNG  == nil then
			local JPicInit = string.find(Sdata,string.char(255)..string.char(216)..string.char(255))
			Rdata:seek("set",JPicInit-1)
			IMGData = Rdata:read(TagSize-(JPicInit-GotIt))
			local MPImg = io.open(NombreDeLaImagen..".jpg","wb")
			MPImg:write(IMGData)
			MPImg:flush()
			MPImg:close()
			MPImg=nil
		else
			local PPicInit = string.find(Sdata,string.char(137).."PNG")
			local TagDMP
			if GetVer then
				TagDMP=8
			else
				TagDMP=6
			end
			Rdata:seek("set",PPicInit-1)
			local IMGData = Rdata:read(TagSize-(PPicInit-GotIt))
			MPImg = io.open(NombreDeLaImagen..".png","wb")
			MPImg:write(IMGData)
			MPImg:flush()
			MPImg:close()
			MPImg=nil
		end
	end
	Rdata:close()
	Rdata = nil
end

print("Inserta la ubicacion de un mp3")
local FILE = io.read();
print("Inserta un nombre de archivo")
local SavePlace = io.read();
WriteImage(FILE, SavePlace);

Primero que nada, LUA dispone de funciones de lectura y escritura bastante similares a los de C. Para usar un lector, debemos de iniciarlo pasando dos parámetros, uno de ellos es el archivo que queremos leer (o nuestra canción) y el segundo parámetro es el modo en cual se abrirá; en este caso, usamos "rb" que indicará que leeremos el archivo de forma "binaria".

Al igual que en C, disponemos de una función que sirve para desplazarnos en el archivo que tenemos abierto. Antes de continuar, hay que aclarar algo; LUA no es un lenguaje orientado a objetos pero tiene una sintaxis que ayuda a simularlo y para el código anterior es lo mismo "Rdata:seek("set");" que "Rdata = io.seek("set");" y lo mismo para todos los procedimiento provenientes de cualquier "clase" así que una vez que se inicializa una variable del tipo "io", podemos usar el nombre que le dimos seguido de dos puntos para acceder a los sub-métodos. Entonces, usando "seek" sin ningún parámetro adicional a "set", le indicamos que queremos mover el puntero al inicio del archivo (byte cero)

Cuando ya iniciamos todo, leemos los primeros 1024 bytes del archivo y al igual que en C#, vamos a buscar cualquiera de las etiquetas que se usan para indicar una imagen; es decir: "APIC" o "PIC". Algo que probablemente olvide decir en el primer post sobre imágenes en los MP3's es la importancia de buscar primero "APIC" a algunos les resultará obvio cual es la razón pero a otros no. La razón es simple si buscamos PIC (que corresponde a la versión 2), el resultado de la búsqueda sera verdadero si existe "APIC" porque esta contiene toda la cadena de caracteres que buscamos, en cambio si buscamos "APIC" primero, si no se encuentra, eso no significa que necesariamente no exista "PIC".

Para realizar la búsqueda de la etiqueta, una vez que leímos todos los bytes en los cuales podría estar, creamos un flag llamado "GotIt" del tipo "boolean" y a esta le asignamos el resultado de la función "string.find()" usando cómo parámetros "Sdata" que es una copia de "Rdata" (los 1024 bytes leídos) y es en donde buscará la cadena de texto que le pasamos en el segundo parámetro.

Repetimos lo anterior 4 veces, la primera es para buscar "APIC", la segunda para buscar "PIC" luego si la variable "GotIt" resulta "true" en cualquiera de las dos funciones, hacemos una nueva búsqueda pero ahora en lugar de las etiquetas de imagen, buscamos el tipo de imagen; ya sea PNG o JPG, pero para hacerlo, hacemos uso de de los caracteres que sirven de "encabezado" para los archivos PNG, cómo ya estamos dentro de la condición en la cual si se encontró una etiqueta de imagen, solo hacemos una comprobación y si no encontramos al "encabezado" del archivo PNG entonces la imagen es JPG

Para calcular el tamaño de las imágenes, hay que recordar que seguido de la etiqueta, los cuatro bytes siguientes  para la versión tres y los tres bytes siguiente en la versión dos, indican cual es el tamaño de total de la etiqueta, para poder saber cual es el valor, bastará con multiplicar cada byte por potencias de 256, empezando de izquierda a derecha por 256^3 para la versión 3 y 256^2 para la versión dos.

Finalmente  guardamos la imagen haciendo uso de un escritor de archivos que se usa igual al de C, es decir creamos el archivo e indicamos cual es el modo en el cual se trabajara, cuando usamos "wb" indicamos que abriremos un archivo pra escribir de forma binaria, si el archivo no existe, se creara uno nuevo.

Para poder usar la función, en ZeroBrane presionaremos F6 y en la parte inferior, el programa no preguntará por la ubicación de una canción seguido de la ubicación que usaremos para guardar la imagen, para este ultimo es importante solo indicar la ruta y el nombre del archivo sin extensión, dentro del programa al determinar si la imagen es jpg o png automáticamente agrega la extensión adecuada.

Si observas bien, el archivo de imagen solo tiene la ruta y el nombre, pero no la extensión.


Y bien por ahora es todo, el código lo puedes copiar y pegar para probarlo.

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 #19 - Extraer una imagen embebida en un MP3 - ArtView

El día de hoy vamos a terminar (ahora si no es broma) el código para extraer las imágenes que se encuentran embebidas en los archivos MP3.


En el post anterior implementamos algunas partes del código y lo pasamos a funciones, antes de avanzar hay algo que quiero decir de una de las funciones anteriores. Una persona que visitó el blog me dijo que la función DataToAlbum no funcionaba bien, a pesar de que yo mostré imágenes en la que parecía que todo funcionaba bien, pero no era asi, al analizar todos los bytes que componían al string (solo la versión unicode), había un carácter no visible. El carácter es 0xFF, eso se debia a que también le pasábamos a la función GetString los bytes adicionales que se usan para indicar que se trata de un string unicote, para agregarlo, solo hay que movernos hasta el tercer byte. Recordemos que la función que hicimos recibía cómo parámetro una matriz de bytes donde el primero, era el byte que nos indicaba cual era el tipo de codificación; uno para unicode y cero para ASCII. En el caso de la codificacion ASCII no existía mayor problema porque seguido de ese byte, inmediatamente va lo que vendría siendo el string en sí, pero en el caso del string unicode, inmediatamente después del primer byte, siguen dos bytes que sirven para indicar que tipo de codificación se usa (en lectores de texto por ejemplo,0xFF 0xFE para unicode).

private string DataToAlbum(byte[] Data)
{
 if (Data[0] == 0)
  return Encoding.ASCII.GetString(Data,1,Data.Length-1).TrimEnd((char)0).TrimStart((char)0);
 if (Data[0] == 1)
  return Encoding.Unicode.GetString(Data,3,Data.Length-3).TrimEnd((char)0).TrimStart((char)0);
 else
  return "Desconocido";
}

Procesando por lotes.

El chiste de todo esto era extraer las imágenes debido a que cuando se convertían entre una versión a otra, por lo que me dijeron, algunos programas omiten la imagen, entonces lo ideal es extraer primero la imagen y después insertarla de nuevo, eso si, solo después de haber realizado la conversión a la versión de destino.

Para eso creamos la función BatchProcess cuyo código es el siguiente:

private void BatchProccess(List<string> Files, string SaveFolder)
{
 int Current = 0;
 PBProgress.Maximum = Files.Count;
 Thread DoWork = new Thread (new ThreadStart(() =>
 {
  foreach (string Item in Files)
  {
   ImageContent(Item,SaveFolder);
   Current = Current + 1;
   UpdateProgress(Current);
   if (Current == Files.Count) {
    IsWorking =false;
   }
  }
 }
 ));
 DoWork.Start();
}


Está función recibe cómo parámetros una lista del tipo string que contiene todos los archivo de los cuales queremos extraerles la imágenes, el otro parámetro es un string que contiene la ruta (fólder) en donde guardáremos las imágenes. Después creamos un thread para no bloquear la interfaz principal. De hecho cuando pruebes el código, al agregar los archivos, si agregas muchos, notarás que la aplicación se congela un poco (dependiendo de cuantos agregues y de que tan potente sea tu equipo), esto lo deje así para que notes la diferenta entre usar un thread aparte y cuando no.
El uso del thread (hilo a partir de este momento). Para esta aplicación es realmente sencillo, ya que no hacemos uso de las funciones para controlarla como es debido, de hecho para saber si aun sigue ocupado ese hilo usamos el flag IsWorking. Para poder saber el progreso del proceso usamos un delegado que se encargara de recibir el valor actual del progreso, después llamará a la funcion UpdateProgress que se encargará de actualizar la propiedad Value del ProgressBar.

El código de la funcion UpdateProgress (incluido el delegado) es el siguiente:

private delegate void UpdateProgressDelegate(int Value);

private void UpdateProgress(int Value)
{
 if (PBProgress.InvokeRequired)
 {
  PBProgress.Invoke(new UpdateProgressDelegate(UpdateProgress), Value);
 }
 else
 {
  PBProgress.Value = Value;
 }
}

El uso un poco más avanzado de los hilos lo veremos en el siguiente Post.
Y ya solo que da el código que se encargará de hacer el trabajo. El uso de un flag para determinar si hay o no un proceso es importante, ya que podemos cerrar la ventana principal y algun proceso todavía se puede estar ejecutando, suponiendo que eres cómo yo y rara vez le pide a Windows que te dé permiso para sacar tu dispositivo, si lo extraes en medio de un proceso de lectura/escritura, puede ser que se dañe tu USB.

El código completo lo puedes descargar aquí, Dudas o comentarios no dudes en decirlo. Antes de terminar quiero agradecer al que reportó el error, preferiría que no usaran perfiles anónimos para poder dar el reconocimiento que se debe.

Por ahora es todo, los leo luego.
P.D, “Arceus existe ;)”

Vamos a programar #18 - Extraer una imagen embebida en un MP3.

Hola de nuevo a todos, el día de hoy vamos terminar con el programa encargado de extraer la imágenes que están embebidas en los archivos MP3.
El día de hoy vamos a implementar todo el programa y ademas haremos una pequeñas optimizaciones al código que ya teníamos previamente hecho.

Mejorando el código previo.

Primero vamos crear una función para calcular el tamaño de la etiqueta, en el código, lo que hacíamos primero, era buscar la etiqueta, luego leer la longitud, pero hay que recordar que dependiendo de la versión, se usan tres o cuatro bytes para esto.
Entoces basados en la informacion anterior, podemos hacer una función similar a la siguiente:



private int ByteArrToSize(byte[] Data)
{
 int Result = 0;
 if (Data.Length == 4)
 {
  Result = Data[0] * 256 * 256 * 256 + Data[1] * 256 * 256 + Data[2] * 256 + Data[3];
  return Result;
 }
 if (Data.Length == 3)
 {
  Result = Data[0] * 256 * 256 + Data[1] * 256 + Data[2];
  return Result;
 }
 else return 0;
}

Si recordamos en otro código, es exactamente lo mismo, solo que lo separamos para poder usarlo de manera rápida. Esta función recibe una matriz de bytes que contiene los 4 o 3 bytes después de la etiqueta "PIC" o "APIC". cómo resultado, devuelve un numero del tipo int que contiene la longitud de la etiqueta. Para usarla solo hay que sustituir la parte de los calculos por la llamada a la funcion.

Código anterior:

//Size
TotalLenght = TotalLenght + BinRead.ReadByte() * 256 * 256 * 256;
TotalLenght = TotalLenght + BinRead.ReadByte() * 256 * 256;
TotalLenght = TotalLenght + BinRead.ReadByte() * 256;
TotalLenght = TotalLenght + BinRead.ReadByte();
txtInfo.Text = txtInfo.Text + "\n La etiqueta APIC esta en la dirección: " + PicTagPos.ToString() + "\n";
txtInfo.Text = txtInfo.Text + "con longitud de" + TotalLenght.ToString() + " bytes\n";

Código nuevo implementando la función ByteArrToSize:


TotalLenght = ByteArrToSize(BinRead.ReadBytes(4));
txtInfo.Text = txtInfo.Text + "\n La etiqueta APIC esta en la dirección: " + PicTagPos.ToString() + "\n";
txtInfo.Text = txtInfo.Text + "con longitud de" + TotalLenght.ToString() + " bytes\n";


y cómo es un cálculo que lo vamos a hacer más de una vez, lo mejor es usar la función, originalmente la hice de la primer forma debido a que quería mostrar lo que estábamos haciendo a mano, pero para fines más prácticos, usamos la función, no hace falta ser experto para ver que en ambos casos se hace exactamente lo mismo, solo que la función determina; hasta cierto punto; que es lo que va a hacer, si usáramos el código original, debido a que es diferente como esta descrito el tamaño de la etiqueta. Primero había que comprobar para que caso íbamos a hacer el cálculo, y de leer un byte, multiplicar y sumar 3 o 4veces dependiendo del caso. En la función solo pedimos los datos y dependiendo de el numero de bytes que contenga el argumento, seguirá las condiciones pertinentes para devolver el valor correcto.

Otro de los procesos que decidí volver en una función; fue el de obtener la imagen, si recordamos, en el código original, una vez que ya teníamos definidos todos los datos de la imagen (tipo y longitud), en cada una de las versiones de las versiones, leíamos los datos, creábamos un MemoryStream y creábamos una imagen a partir de el.

El codigo es el siguiente:

using (MemoryStream MS = new MemoryStream(BinRead.ReadBytes(ImageLenght)))
{
 PicArt.Image = Image.FromStream(MS);
 using (FileStream file = new FileStream(SaveLocation + "." + ImageType(MType), FileMode.Create, FileAccess.Write))
 {
  MS.WriteTo(file);
  file.Flush();
  file.Close();
 }
 MS.Close();
}

Entonces para pasar todo el fragmento de código anterior, solo necesitamos crear una función que devuelva una imagen cómo resultado.

El siguiente código es la versión en función del primer código.

private Image StreamToImage(byte[] Data, string SaveLocation)
{
 using (MemoryStream MS = new MemoryStream(Data))
 {
  Image TempImage;
  using (FileStream file = new FileStream(SaveLocation, FileMode.Create, FileAccess.Write))
  {
   MS.WriteTo(file);
   file.Flush();
   file.Close();
   TempImage = Image.FromStream(MS);
  }
  MS.Close();
  return TempImage;
 }
}


Al igual que en el caso anterior, tomamos todo el código que ya teníamos y lo usamos tal cual estaba, solo que este caso, devolvemos un valor del tipo Image que contiene la imagen que extrajimos. La función recibe dos parámetros: uno del tipo byte[] que es el que contiene todos los datos leídos previamente desde el Stream donde se manda a llamar esta función y un valor del tipo string que contiene la ruta en donde se guardará la imagen (en adición a la imagen que devuelve la función).

El código nuevo.

Todos estos post comenzaron por el hecho de que cuando se realiza una conversión entre una versión de las etiquetas ID3 a otra, en muchos casos se pierde la imagen (o eso fue lo que me dijeron), entonces nuestro objetivo es crear un Back up de estás. Pero ahora que ya sabemos cómo extraerlas, sigue almacenarlas, pero creo que surgiría un pequeño problema.
Cuando guardábamos las imágenes, les poníamos como nombre de archivo el nombre de la canción, solo que en lugar de extensión MP3 le poníamos JPG o PNG (dependiendo el caso), pero imaginemos que tenemos el álbum completo, no tendría caso extraer la imagen para cada una, porque seria la misma para cada canción del mismo álbum, supongamos que tenemos un disco completo ya en MP3 y este contiene 20 canciones, si le extraemos la imagen a cada uno, tendríamos 20 archivos de imagen iguales con nombres distintos.
La solucion es un tanto sencilla, solo debemos de tomar cómo nombre de archivo algo que tengan en comun los archivos que no sea el artista. El album es un buen identificador, porque bastará que extraigamos una sola imagen para cubrir los 20 archivos.
Para poder usar el nombre del album cómo nombre de archivo para la imagen, primero debemos de saber cual es el nombre del album. Igual que con la imagen, dentro del tag ID3 hay un frame que describe la información para el album. En el caso de la version 2.3 es la siguiente:

Text information frames The text information frames are the most important frames, containing information like artist, album and more. There may only be one text information frame of its kind in an tag. If the textstring is followed by a termination ($00 (00)) all the following information should be ignored and not be displayed. All text frame identifiers begin with "T". Only text frame identifiers begin with "T", with the exception of the "TXXX" frame. All the text information frames have the following format:
<Header for 'Text information frame', ID: "T000" - "TZZZ", excluding "TXXX" described in 4.2.2.>
Text encoding $xx Information
text string according to encoding
Y para la version 2 es la siguiente:
The text information frames are the most important frames, containing information like artist, album and more. There may only be one text information frame of its kind in an tag. If the textstring is followed by a termination ($00 (00)) all the following information should be ignored and not be displayed. All the text information frames have the following format:
Text information identifier "T00" - "TZZ" , excluding "TXX", described in 4.2.2.
Frame size $xx xx xx
Text encoding $xx
Information <textstring>
Entonces sabiendo lo anterior podemos crear una código cómo el que sigue:


       private string DataToAlbum(byte[] Data)
      {
            if (Data[0] == 0)
                return Encoding.ASCII.GetString(Data, 1, Data.Length - 1);
            if (Data[0] == 1)
                return Encoding.Unicode.GetString(Data, 1, Data.Length - 1);
            else
                return null;
        }

El código anterior sirve para leer cualquier frame de texto (exceptuando los que dice la documentación), pero ademas incluye casos definidos para cuando el texto es ASCII o unicode. La función recibe como argumento una matriz de bytes que incluye todos los bytes que le siguen a la longitud del frame, es decir, el primer byte deberá de ser el que indica que tipo de codificación se va a usar; inmediatamente seguido de el string (que es el resto de la etiqueta)esto para el caso de la versión 2. Para la versión 2.3 se deben de pasar los bytes empezando desde el byte que indica la codificación del texto, omitimos los 2 bytes que se usan cómo flags.
La función se debe de mandar a llamar cuando ya tenemos separados los datos de este frame, es decir, primero hay que determinar que tipo de tag ID3 es, luego buscar "TALB" o "TAL" dependiendo de la versión y leer los datos de la misma forma que lo hicimos para el frame "APIC" o" PIC", esto se puede hacer dentro del mismo código que lee los datos para buscar la imagen y ya una vez que se encuentran, simplemente le pasamos la información a la función.

Es importante usar la codificación correcta. Si no lo hiciéramos, no podríamos leer "ドリドリ" que es el álbum de esta canción.


Es importante leer la codificación, porque para este caso, los caracteres del japones, no están disponibles en el estandar ASCII, Cuando el byte que nos indica que codificación se usa es 1, hay que recordar que la codificación es UNICODE, cuando es 0 es ASCII.


Y bien, por ahora solo queda pendiente la extracción por lotes, pero será en el siguiente post.
Los leo luego.

Vamos a programar #17 - Extraer una imagen embebida en un MP3

Hola a todos de nuevo. El día de hoy vamos a terminar con el temas de las imágenes embebidas en los archivos MP3.
En el post anterior, hicimos una parte del código de C# encargado de hacer la extracción. Había quedado pendiente el trabajo para la versión 2 de las etiquetas ID3.

Hay que recordar que la esctructura del la etiqueta "PIC" es la siguiente:

Attached picture "PIC"
Frame size $xx xx xx
Text encoding $xx
Image format $xx xx xx
Picture type $xx
Description <textstring> $00 (00)
Picture data <binary data>
Basados en la información anterior crearemos el código necesario.

El código para la versión 2.

Retomando el codigo anterior, solo hay que agregar lo que nos hace falta. La función ImageContent constaba de 3 condiciones if importantes (ademas de algunos secundarios), el primero y principal consiste en verficar si hay una etiqueta ID3 valida, si la había, el siguiente if comprobaba que la version fuera la 2.3, el siguiente comprobaba para la version 2. Está fue la que quedo pendiente, el siguiente codigo va inmediatamente despues del if que comprobaba para la version 2.3.


 if (MyTagType == TagVersion.TagVer22)
                    {
                        {
                            //PIC
                            BinRead.BaseStream.Position = PicTagPos + 3;
                            //Tamaño
                            TotalLenght = TotalLenght + BinRead.ReadByte() * 256 * 256;
                            TotalLenght = TotalLenght + BinRead.ReadByte() * 256;
                            TotalLenght = TotalLenght + BinRead.ReadByte();
                            txtInfo.Text = txtInfo.Text + "\n La etiqueta PIC esta en la dirección: " + PicTagPos.ToString() + "\n";
                            txtInfo.Text = txtInfo.Text + "con longitud de" + TotalLenght.ToString() + " bytes\n";
                            //TextEncoding
                            BinRead.ReadByte();
                            //Type JPG o PNG
                            string MType = "";
                            MType = MType + string.Concat((char)BinRead.ReadByte());
                            MType = MType + string.Concat((char)BinRead.ReadByte());
                            MType = MType + string.Concat((char)BinRead.ReadByte());
                            txtInfo.Text = txtInfo.Text + "\nDel tipo " + MType;
                            //Tipo de imagen
                            BinRead.ReadByte();
                            //Reciclamos el flag anterior
                            IsNotZero = true;
                            string ImgDescription = "";
                            //Descripcion de la imagen
                            while (IsNotZero == false)
                            {
                                byte Val = BinRead.ReadByte();
                                if (Val != 0x00)
                                {
                                    ImgDescription = ImgDescription + string.Concat((char)Val);
                                    IsNotZero = false;
                                }
                                else
                                {
                                    IsNotZero = true;
                                }
                            }
                            long ImageFileBegin = BinRead.BaseStream.Position;
                            int ImageLenght = (TotalLenght - 1 - ((int)ImageFileBegin - PicTagPos - 6));
                            txtInfo.Text = txtInfo.Text + " La imagen empieza en " + ImageFileBegin.ToString();
txtInfo.Text = txtInfo.Text + "Y mide " + ImageLenght.ToString();
                            //Creamos una imagen desde el stream y la asignamos al picbox
                            //Ponemos el puntero al inicio de la imagen
                            BinRead.BaseStream.Position = ImageFileBegin + 1;
                            using (MemoryStream MS = new MemoryStream(BinRead.ReadBytes(ImageLenght)))
                            {
                                PicArt.Image = Image.FromStream(MS);
                                using (FileStream file = new FileStream(SaveLocation + "." + ImageType(MType), FileMode.Create, FileAccess.Write))
                                {
                                    MS.WriteTo(file);
                                    file.Flush();
                                    file.Close();
                                }
                                MS.Close();
                            }
                        }
                    }
}

Si revisamos el código de forma detenida veremos que es mu similar al código anterior. Entonces te preguntarás ¿No es un desperdicio? Si y no, el código está pensado para hacer una demostración de lo que hicimos en el primer post? Y la respuesta es: NO, lo que trate de hacer con estos post es demostrar como es que se puede portar una situación al código así "tal cual", porque de hecho lo que hicimos aquí, fue lo mismo que hicimos con el programa XVI32. Es decir lo que primero hicimos a mano, luego lo plasmamos en el código.

Información para una canción con etiqueta PIC (Ver 2)

Información para una canción con etiqueta APIC (Ver 2.3 {Lo admito soy un Pokefan!!!})
 En el próximo post crearemos una función para procesar por lotes y liberaré la aplicación completa, por ahora es todo.

Los leo luego.

Vamos a programar #15 - Extraer una imagen embebida en un MP3

Hace algunos días estaba hablando con un amigo y me comento un problema que le pasó. Hace algunos días quiso organizar su colección de música en formato MP3. Todas las canciones poseen los meta-datos o "TAGS" que sirven para identificar a una canción. La información básica que suele incluirse es:

  1. Nombre de la canción.
  2. Álbum.
  3. Artista.
  4. Año.
  5. Genero.
Adicionalmente los archivos MP3's suelen tener una imagen que representa la portada del disco (algunos incluso incluyen varias imágenes). El problema que me comento mi amigo fue que al convertir todas las canciones para que la información esté en la misma versión en todas las canciones, la imagen se perdía. cuando se hacia una conversion de la version ID3 2.2 a la versión 2.3.

Etiquetas ID3.

Antes que nada veamos que es una etiqueta ID3.
ID3 es un estándar de facto para incluir meta-datos (etiquetas) en un archivo contenedor audiovisual, tales como álbum, título o artista. Se utiliza principalmente en ficheros sonoros como MP3. Wikipedia/ID3.
Básicamente es eso. Para identificar cada una de las partes de la información se usan etiquetas ya predefinidas, es decir: si se quiere saber el autor se deberá de buscar una etiqueta en especifico.
La lista completa de etiquetas se puede encontrar en ID3/V2 (para la version 2.2.0) y en ID3V2.3(para la version 2.3).
Por ahora solo vamos a revisar la etiquetas que nos interesan:

Para la version 2.3
Header for 'Attached picture', ID: "APIC"
MIME type <text string> $00
Picture type $xx
Description <text string according to encoding> $00 (00)
Picture data <binary data>
Para la version 2.2
Attached picture "PIC"
Frame size $xx xx xx
Text encoding $xx
Image format $xx xx xx
Picture type $xx
Description <textstring> $00 (00)
Picture data <binary data>
Lo que haremos es buscar "PIC" o "APIC" para determinar si hay imagen o no, si la hay, después leeremos la longitud, luego el tipo (usualmente solo se usa PNG o JPG) y finalmente leeremos los datos correspondientes a la imagen en sí.

Vista previa.

Antes que nada, primero vamos a hacer una pequeña prueba "en crudo" y trataremos de seguir las instrucciones a mano, es decir solo usando un lector hexadecimal, extraeremos la imagen. Para eso necesitarás un editor hexadecimal, hay muchos disponibles actualmente, pero yo uso XVI32, puedes usar el de tu preferencia, pero el programa que escojas deberá de tener las funciones de lectura y escritura.
También recomiendo que leas las definiciones para las etiquetas antes de continuar.

Primero abriremos el archivo del cual queremos obtener la imagen. Una vez abierto procederemos a buscar directamente los datos que nos interesan. Para nuestro caso solo requerimos saber si hay una etiqueta "PIC" o "APIC". Entonces, con nuestro archivo ya abierto buscaremos eso.

Al terminar la búsqueda, si existe, el cursor se moverá a la posición en donde se encuentra el inicio del lo que buscamos.
Si hacemos memoria recordaremos que después de la etiqueta "APIC", los siguientes 4 bytes indican el tamaño total de la etiqueta y dos bytes más son usados cómo flags (no nos fijaremos en esto), entonces los datos que nos interesan son lo que estan en el recuadro rojo de la siguiente imagen:
Para este caso, la longitud de esta etiqueta es 0x00027225, hay que recordar que esta longitud no incluye los 10 bytes que se usan para definir la etiqueta, el tamaño y los flag. Si seleccionamos los 0x00027225 bytes, estaríamos seleccionando los datos que describen la imagen, el tipo de imagen (icono, cubierta frontal, etc) y formato de imagen (jpg o png).
Para poder guardar la imagen, seleccionaremos tantos bytes como nos indica, pero le restaremos 14 bytes que corresponden a los frames que describen para el tag APIC.
Si convertimos el numero 0x00027225 a decimal,obtenemos el numero decimal 160,293 restandole 14 obtenemos 160,279.
En nuestro editor primero pondremos el cursor al final de todos los descriptores de la etiqueta. y si tiene disponible la opcion de seleccionar una cantidad "n" de caracteres, usaremos esa indicando la cantidad de bytes que obtuvimos al hacer la resta.
Finalmente solo guardaremos la parte seleccionada (si el editor lo soporta).


 Hay algo importante por remarcar, según la descripción en ID3.org es preferible usar solo dos tipos de imágenes, puede ser Jpeg o PNG, dependiendo de cual se use, el descriptor que se usa es diferente. En este ejemplo al ser una imagen jpg la que está incrustada en el mp3, al ver la descripción tenemos: "image/jpeg" pero si la imagen esta en png tendremos algo como: image/png. Eso causaría que si solo restamos a la longitud 14 bytes, la imagen no la obtendríamos completa, por eso es necesario revisar el tipo de imagen, ademas de que hay información adicional que en este caso no la hay.


Y bien es todo por hoy, en el siguiente post continuaremos ahora si con el código de C# para extraer la imágenes dentro de los mp3. Este post solo trata de exponer como es que funciona y de hecho ya hiciste a mano lo que C# haría internamente.
Si solo quieres seguir los pasos tal  como están en el post. La canción que use como prueba está aquí.

Los leo luego.