Introducción

Esta mañana justamente estaba planeando un tutorial sobre cómo extraer la información de una canción MP3 que usa el formato Id3 o algo así. Para ello estaba usando la magnífica librería llamada Mp3Info y el lenguaje de programación PHP.

Todo iba bien, hasta que noté algo extraño. Extraje la información de una canción pero regresaba los datos con un carácter de menos. Por ejemplo, si el artista era León Larregui, mostraba León Larregu. O si el nombre de la canción era Locos, mostraba Loco.

Aquí una imagen que lo comprueba:

Información incorrecta, último carácter perdido

Entonces me puse a depurar y depurar por un montón de tiempo hasta que di con la solución que justo ahora vengo a exponer

Resumen nivel 5

Bueno, al final sólo fue agregar el carácter NUL al final de la cadena.

Hice un fork del repositorio aquí, el cual soluciona este bug citado en el repositorio original.

Solución a bug de Mp3info

Arreglar bug de Mp3Info que remueve el último carácter

Probé de todo, primero imprimí la cadena y la analicé. Vi que decía algo como “V o l u m a”, así con espacios. Supuse que es porque usa dos bytes para representar los caracteres unicode o esas cosas, pero tenían un espacio porque el segundo carácter no era usado, ya que todo cabía en un byte.

Luego intenté decodificar la cadena con todos los formatos que devolviera mb_list_encodings usando mb_convert_encoding, pero todo salía feo; con caracteres raros, signos de interrogación y esas cosas.

Más tarde recorrí la cadena y fui imprimiendo su representación ASCII usando la función ord con este código, (no está muy bonito pero estaba depurando, no programando):

<?php
echo "Vamos con '" . $data["information"] . "'\n";
$l = strlen($data["information"]);
for($x = 0; $x < $l; $x++){
    echo ord($data["information"][$x]) . "\n";
}

Comenzaba mostrando el 255 y 254, supongo que son unos bytes que indican otra cosa que desconozco (tal vez algo como un encabezado).

Lo que realmente importaba era que después imprimía el 86 y más tarde el 0; el 86 representaba la V de Voluma. ¿Y ese cero? Así que (y me vengo a dar cuenta de la posible explicación al escribir este post) usaba dos caracteres para representar una letra, por cosas del unicode como lo mencioné anteriormente.

Dejaba ese carácter nulo porque no lo necesitaba, ya que la V se puede representar con un byte. La salida era así:

Depurando con ord

Y fue ahí cuando me di cuenta de que faltaba un cero al final, ya que cada letra llevaba su representación y un 0, o el carácter nulo si hablamos del código ASCII.

Yo creo que PHP, al convertir la codificación, detectaba el 97 como un carácter que estorbaba, porque no usaba 2 bytes, sólo uno; era el sobrante que no tenía pareja.

Así que lo que hice fue agregar al final de la cadena el carácter nulo:

$data["information"] .= chr(0);

La función chr complementa a ord, dado un número devuelve su representación ASCII. Y entonces la salida ahora era distinta:

Agregar NUL al final

Después del 97 ahora sí salía un cero. Si borraba todo mi código de depuración y dejaba todo limpio, la función ahora se veía así:

<?php
private function handleTextFrame($frameSize, $raw)
{
    $data = unpack('C1encoding/A' . ($frameSize - 1) . 'information', $raw);
 
    if ($data['encoding'] == 0x00) # ISO-8859-1
        return mb_convert_encoding($data['information'], 'utf-8', 'iso-8859-1');
    else{ # utf-16
        # Fix the missing last char of the info bug
        # Add NUL character at the end of the string. 
        # Don't ask, just enjoy. Idk why it works, but it works!
        $data["information"] .=  chr(0);
        return mb_convert_encoding($data['information'], 'utf-8', 'utf-16');
    }
}

Simplemente agregué una concatenación. Se ve simple, pero llevó horas escribir esa maldita línea. Y si probaba…

Bug arreglado

Ahora ya devolvía toda la información. Me pregunto qué pasa si el último carácter de la cadena es multibyte, supongo que habría que comprobar si su último byte es NUL y si no entonces concatenar.

El punto es que quedó arreglado y ahora sí continuaré escribiendo el tutorial.

Si el post ha sido de tu agrado te invito a que me sigas para saber cuando haya escrito un nuevo post, haya actualizado algún sistema o publicado un nuevo software. Facebook | X | Instagram | Telegram | También estoy a tus órdenes para cualquier contratación en mi página de contacto