miércoles, 3 de noviembre de 2021

Grabación de Archivos en Casetes

Después de escribir el artículo Casete a PC con Audacity pensé en realizar el experimento de utilizar la grabadora para guardar archivos del PC en casetes, igual que se hacía en ordenadores como los ZX Spectrum o MSX. Las casetes son un sistema pensado para grabar ondas sonoras, por lo que los datos a grabar se codifican como ondas sonoras de diferentes frecuencias para ceros y unos. Para todo tipo de ordenadores que usaban casetes hay programas que permiten pasar de casete a archivo de PC y viceversa. Muchos de estos programas utilizan archivos WAV como formato intermedio. Yo he creado un programa en Java que usa este sistema.

Como expliqué en el artículo Casete a PC con Audacity la digitalización del audio consiste en tomar una serie de muestras por segundo y registrar su volumen. El programa que he desarrollado crea un archivo WAV con codificación LPCM, frecuencia de muestreo de 44100 Hz, un canal de audio y un número de 8 bits sin signo para registrar el volumen.

protected static int FREQUENCY = 44100;
protected static int SAMPLE_SIZE = 8;
protected static int CHANNELS = 1;
protected static boolean SIGNED = false;

AudioFormat format = new AudioFormat(FREQUENCY, SAMPLE_SIZE, CHANNELS, SIGNED, true);

La frecuencia de 44100 Hz permite guardar 44100 muestras por segundo y con un número de 8 bits sin signo se pueden registrar 256 niveles de volumen entre 0 y 255. El programa utiliza dos tipos de onda cuadrada para los ceros y unos. Para los ceros utiliza ondas compuestas por 20 muestras en nivel alto y 20 muestras en nivel bajo. Para los unos se utiliza la mitad, 10 muestras en nivel alto y 10 muestras en nivel bajo. El nivel alto es un volumen de 228 y el nivel bajo de 28. Por tanto la frecuencia de las ondas de los ceros es de 44100 Hz / 40 = 1102,5 Hz y la de los unos el doble, 44100 Hz / 20 = 2205 Hz. Estas frecuencias permiten velocidades de grabación entre 1102,5 y 2205 b/s = 1,1025 y 2,205 Kb/s dependiendo del número de ceros y unos.

int samples;
		
if (input == 0) {
   samples = 20;
} else if(input == 1) {
   samples = 10;
} else {
   throw new Exception("Invalid bit value: " + input);
}
		
for (int x = 0; x < samples; x++) {
   wavOutput.write(228);
}
		
for (int x = 0; x < samples; x++) {
   wavOutput.write(28);
}

Los bits de cada byte del archivo se leen utilizando los operadores a nivel de bits >> y &. Con el operador >> se desplazan los bits hacía la derecha entre 0 y 7 posiciones. Con el operador & se realiza una operación AND entre los bits desplazados y 00000001. El resultado es el valor del bit de la derecha. De esta forma todos los bits pasan por la posición de la derecha y pueden ser leídos. El desplazamiento de 0 posiciones es innecesario ya que no desplaza los bits pero simplifica el código fuente.

for (int x = 0; x <= 7; x++) {
   writeBit((input >> x) & 1);
}

A continuación se muestra como ejemplo la lectura de un byte cuyo valor es 00101001. El resultado son los bits por separado de derecha a izquierda, en formato little-endian, primero el dígito menos relevante: 1 0 0 1 0 1 0 0. Este formato es conveniente para ser usado con la clase BitSet, que utiliza el mismo formato. Para otros usos puede ser necesario leer los bits en el sentido contrario, en formato big-endian.

00101001 >> 0 = 00101001 & 00000001 = 1
00101001 >> 1 = 00010100 & 00000001 = 0
00101001 >> 2 = 00001010 & 00000001 = 0
00101001 >> 3 = 00000101 & 00000001 = 1
00101001 >> 4 = 00000010 & 00000001 = 0
00101001 >> 5 = 00000001 & 00000001 = 1
00101001 >> 6 = 00000000 & 00000001 = 0
00101001 >> 7 = 00000000 & 00000001 = 0

El archivo WAV resultante se puede visualizar con un programa de edición de audio como Audacity. Ampliando el audio podremos ver las ondas y ampliando un poco más las muestras que componen las ondas.


Al reproducir el audio en Audacity para grabarlo en la casete me di cuenta de que por alguna razón que no he podido averiguar el final del archivo de audio no llegaba al grabador de casetes. Para subsanar este problema el programa añade un segundo sin ondas, 44100 muestras más, al final del archivo. De esta forma es este segundo de audio el que no llega al grabador y el resto del archivo se graba correctamente.

Si utilizamos casetes que tengan muchos años es probable que estén deterioradas, en algunos de sus tramos no se realice bien la grabación y tengamos que probar a grabar en otros tramos.

Para conectar el ordenador al reproductor/grabador de casetes utilicé dos cables con dos conectores RCA macho en un extremo y un conector Jack estéreo en el otro. Los conectores RCA macho se conectan a los canales izquierdo y derecho de la entrada y salida de audio del reproductor/grabador. Los conectores Jack se conectan a la entrada de micrófono y la salida de auriculares del ordenador. Dado que el archivo WAV solo tiene un canal se podrían utilizar cables con un solo conector RCA y un conector Jack monofónico.


La grabación del audio de la casete al ordenador también se puede realizar con Audacity. Debemos realizarla a 44100 Hz y en un solo canal. Ampliando el audio podremos ver como la grabación mantiene las ondas pero con algunas imperfecciones causadas por las grabaciones analógicas entre el ordenador y la casete en ambas direcciones.


Además las ondas pueden estar invertidas y un poco descentradas. Para ponerlas igual que el archivo original podemos seleccionar todo el audio y ejecutar Menú Efecto -> Inversión y Menú Efecto -> Normalizar.



Si ampliamos más el audio veremos las muestras que forman las ondas. Los niveles de volumen de las muestras de una onda no son todos exactamente iguales ni los mismos del archivo original. También puede haber alguna variación en el número de muestras de una onda. Por estas inexactitudes el programa utiliza valores mínimos y máximos con un cierto margen respecto a los valores originales para analizar el archivo. El nivel de volumen por encima de 200 se toma como nivel alto y por debajo de 56 como nivel bajo. El final de una onda y el comienzo de otra onda lo marca el paso de un nivel bajo a un nivel alto. Las ondas que tienen más de 30 muestras se toman como un cero y las que tienen 30 o menos como un uno. Si el programa utilizara ondas con frecuencias más altas y menos muestras la probabilidad de error sería mayor.

Una vez preparado el audio debemos exportarlo ejecutando Menú Archivo -> Exportar -> Exportar como WAV. El archivo se debe guardar con la codificación "Unsigned 8-bit PCM", que es la utilizada por el programa tanto para escribir como leer archivos.

El programa se puede descargar en un archivo JAR ejecutable con tres parámetros: acción, archivo origen y archivo destino. Las acciones son "file2wav" para codificar un archivo en formato WAV para grabarlo en una casete y "wav2file" para realizar la operación inversa.

java -jar wavio.jar file2wav archivo archivo.wav

java -jar wavio.jar wav2file archivo.wav archivo

El software podría incluir otras funciones presentes en los ordenadores que usaban casetes, como por ejemplo detección de errores. Otra función interesante sería indicar el nombre, comienzo y tamaño de los archivos para poder grabar varios archivos en una casete y acceder a cualquiera de ellos de forma independiente. Quizás lo añada en posteriores versiones.

No hay comentarios:

Publicar un comentario