Written by: Andrea Paola Pabón Ortega & Daniel Enrique Mejia Rueda
Revision by: Ing Holguer A. Becerra
DESCRIPCIÓN DEL PROYECTO:
El RTAWD DE0NANO es un reproductor de audio que permite al usuario escuchar música, observar la onda de audio en tiempo real a través de la pantalla touch 4.3inch-480x272-Touch-LCD. Para esto, por medio del NIOSII se leerán los archivos .WAV contenidos en una memoria SD, que se seleccionarán a través de una lista diseñada en el mismo, es decir en la pantalla se podrá observar una lista de canciones diseñadas previamente en el NIOSII, permitiendo al usuario seleccionar la canción que desea reproducir por medio de los botones de navegación contenidos en la pantalla, además la pantalla contara con algunos botones que facilitan la reproducción de canciones, como son play, stop, pause, los botones de navegación previamente mencionados para la lista entre otros. Una vez seleccionado el archivo a reproducir, se tratará la señal con el fin de calcular el histograma y así poder obtener una visualización estilo FFT (esto permitiera observar la señal de audio en estilo de barras), ya que el reproductor será estéreo la señal de audio será enviada a dos delta sigma (cada uno de 16 bits) encargados cada uno de una canal ya sea R o L. A continuación se presentará un diagrama de bloque con el Diseño:
Este proyecto consiste en el diseño de un reproductor de audio que permitirá al usuario además de escuchar música, observar la onda de audio en tiempo real a través de la pantalla touch 4.3 inch del proyecto
Ya que nuestro proyecto consiste en la reproducción de archivos de audio .wav, se ve la necesidad de estudiar este tipo de formato, así que a continuación se presentara su concepto y el código en maltab para abrir un archivo de este tipo.
Waveform Audio Format [.wav o wave]
" WAV (o WAVE), es un formato de audio digital normalmente sin compresión de datos desarrollado y propiedad de Microsoft y de IBM , Es un formato muy extendido entre los usuarios de PC, ya que funciona en cualquier aplicación de Windows. Se desaconseja su uso para internet por el gran tamaño de los archivos en este formato, admite archivos mono y estéreo a diversas resoluciones y velocidades de muestreo, su extensión es .wav."
* Abrir un archio .wav en MATLAB
Para la reproducción de un archivo de audio en Matlab, se emplea el comando "wavread", el cual es el encargado de leer el archivo de sonido, y para la reproducción del mismo se hace uso del comando "sound" el cual es el encargado de enviar el sonido a los parlantes del PC.
Código en Matlab para la reproducción de un archivo .wav
close all
clear all
hfile = 'HearYouMe.wav';
[y,Fs] = wavread(hfile);
sound(y,Fs);
* Donde para : >> [y, Fs] = wavread(filename)
- "Y corresponde a un vector cuyos elementos son los datos tomados en la grabación (muestreo)."
- "Fs es la tasa de muestreo en Hz utilizada para la grabación."
- "Filename corresponde al nombre del archivo de la grabación."
Conclusiones:
- los archivos de formato .wav son sonidos de buena calidad debido a que no presentan compresión de datos, y ademas presentan una frecuencia de grabación de 44100 Hz.
- Se debe tener en cuenta que para la reproducción en MATLAB de este tipo de archivos de sonido, estos deben estar ubicados en la misma carpeta donde se encuentra el programa diseñado en matlab, de lo contrario se debe escribir la dirección completa (ruta) donde se encuentra el archivo que se desea reproducir.
(*Por Andrea Pabón*)
Cómo graficar un archivo .wav
Se selecciono la canción Hear You Me de Jimmy Eat para ser graficada empleando Matlab, pero debido al tamaño tan grande del archivo (47.9MB con una duración de 4:40 minutos) se presentaron dificultades a la hora de ejecutar el programa.
Presentándose un erro "Out of memory", este error se debe a la insuficiencia de memoria del computador.
Para ver la capacidad de memoria (memoria disponible) se empleo el comando memory, como se ve a continuación:
Como solución se decidió recortar la canción (Audacity) , cuyo tiempo de reproducción era de 4:40 minutos, quedando de un tamaño de 16MB, con un tiempo de 1:35 minutos.
Una vez recortada se procedió a ejecutar el programa, obteniéndose la imagen que se ve a continuación.
Código para Graficar un archivo .WAV
clc
close all
clear all
cancion='jimmy.wav'; %% Señal de Audio
[only,fs,nbits]=wavread(cancion); %% Lee la señal de audio
Ts=1/fs; % Periodo de muestreo
L=length(only); %% Número de muestras
t=0:Ts:((L-1)*Ts);
figure
plot(t,only),title('Grafica de una señal .wav'),grid on, zoom on
xlabel('t')
ylabel('Señal de audio')
- Código Para Graficar un archivo .WAV
(*Por Andrea Pabón*)
- SOLUCIÓN AL PROBLEMA DE MEMORIA.
El día anterior surgió el inconveniente de poder graficar la señal de una canción completa por causa de la memoria del computador en la cual se estaba ejecutando el programa y se planteó la solución de recortar la señal utilizando por medio de un programa diferente a matlab (audiocity).
Este inconveniente se pudo solucionar diezmando las señales de cada uno de los canales utilizando matlab evitando que recurrir a recortar la señal, a continuación se muestran los resultados obtenidos
Código en Matlab
%lectura señal de audio
[s FS]=wavread('paramore.wav');
%separar canales r y l
canal_r=s(:,1);
canal_l=s(:,2);
%tiempo de la señal
tiempo=size(s,1)/FS;
%recorte señal
%s_r=canal_l(1:350000);
%wavplay(s_r,FS)
%invercion señal
s_inv=canal_r(end:-1:1);
%wavplay(s_inv,FS)
%diezmacion
s_di=canal_r(1:3:end);
wavplay(s_di,FS/3 )% Es necesario dividir la frecuencia de muestreo
% en el número de pasos que va a hacer el comando
% s_di para que la señal valla a la misma velocidad
% de la original en este ejemplo es 3.
%plot
subplot(2,1,1);plot(canal_r)
title('canal r')
subplot(2,1,2);plot(s_di)
title('canal r diezmado')
mediante la funcion whos de matlab podemos comprobar que la señal tiene menos bytes
(*Por Daniel Rueda*)
Graficar un archivo .wav Empleando FFT
Para el análisis en frecuencia de la señal de audio se emplea la transformada Rápida de Fourier, a continuación se presentara el código empleado para realizar la gráfica de la señal.
código para Gráficar un archivo .wav empleando FFT
clc
clear all
close all
%lectura señal de audio
[s FS]=wavread('paramore.wav');
%separar canales r y l
canal_r=s(:,1);
canal_l=s(:,2);
%tiempo de la señal
tiempo=size(s,1)/FS;
%inversion señal
s_inv=canal_r(end:-1:1);
%wavplay(s_inv,FS)
%diezmacion
s_dir=canal_r(1:3:end);
%wavplay(s_di,FS/3)% Es necesario dividir la frecuencia de muestreo
% en el número de pasos que va a hacer el comando
% s_di para que la señal valla a la misma velocidad
% de la original.
s_dil=canal_l(1:4:end);
%
s_di=[s_dir s_dil];
wavplay(s_di,FS/3)
%recorte señal
%s_r=s_di(1:50000);
%wavplay(s_r,FS)
%FFT
s_di_fft=abs(fft(s_di)); % abs se refiere al valor absoluto
% fft(x):es la transformada discreta de Fourier del vector s_di(archivo .wav)
%plot
subplot(3,1,1);plot(s), grid on
title('cancion normal')
subplot(3,1,2);plot(s_di), grid on
title('cancion diezmada')
subplot(3,1,3);plot(s_di_fft, 'r'), grid on
title('FFT de la cancion diezmada en frecuencia')
(*Por Andrea Pabón*)
Diseño Caja para el RTAW
Debido a que nuestro proyecto consiste en realizar un reproductor de música y está conformado por una fpga, una pantalla táctil y un plug es necesario crear un diseño del prototipo el cual contendrá todos componentes antes mencionados de forma que nuestro reproductor se físicamente llamativo.
Para la realización de este diseño se contó con el programa solidworks el cual nos permite crear un diseño en 3d de los componentes físicos que conforman nuestro reproductor y poder tener una idea más real de cómo quedaría el reproductor en la vida real.
A continuación se muestran las imágenes de los diseños que se hicieron
pantalla tactil
carcasa
ensamblaje final
Los diseños están sujetos a cambios a medida que el proyecto valla avanzando.
(*Por Daniel Mejia*)
Diseño del módulo de Sincronización de Vídeo:
Una vez entendido el formato .wav se procede a diseñar la interfaz gráfica del Reproductor, para esto se diseña el sincronizador de Vídeo como se explica a continuación.
Para el diseño del módulo de sincronización de vídeo de la pantalla touch de 4.3 inch, primero se procedió a calcular el reloj que controla el modulo, para esto nos basamos en los datos de los parámetros ubicados en la pagina 63 del datasheet. para una resolución 480x272 60Hz y se obtuvo un reloj de 9MHz como se ve en la siguiente figura.
Una vez obtenido el valor del reloj se diseña el Pll que genera el reloj de 9MHz empleando la herramienta Tools/MegaWizard.
Una vez diseñado el PLL y el modulo de sincronización VGA que se presenta a continuación (mi_vga), el cual fue diseñado con anterioridad al realizar la practica 4 del laboratorio, se realiza la instanciación de estos.
module mi_vga(clk, h_sync, v_sync, video_on, pixel_x, pixel_y);
parameter N=11;
input clk;
output h_sync, v_sync, video_on;
output [N-1:0] pixel_x;
output [N-1:0] pixel_y;
reg [N-1:0] counter_x;
reg [N-1:0] counter_y;
reg h_sync_reg;
reg v_sync_reg;
//parametros locales
localparam HD = 480; // horizontal display area
localparam HF = 2 ; // h. front (left) border
localparam HB = 2 ; // h. back (right) border
localparam HR = 41 ; // h. retrace
localparam VD = 272; // vertical display area
localparam VF = 2 ; // v. front (top) border
localparam VB = 2 ; // v. back (bottom) border
localparam VR = 10 ; // v. retrace
//contadores x & y
always @(posedge clk)
begin
counter_y <= counter_y;
if (counter_x >= (HD+HF+HB+HR-1))
begin
counter_x<=0;
if (counter_y >= (VD+VF+VB+VR-1))
begin
counter_y <= 0;
end
else
begin
counter_y <= counter_y + 1'b1;
end
end
else
begin
counter_x <= counter_x + 1'b1;
end
end
always @(posedge clk)
begin
h_sync_reg <= (counter_x >= (HD+HB)) && (counter_x <= (HD+HB+HR-1));
v_sync_reg <= (counter_y >= (VD+VB)) && (counter_y <= (VD+VB+VR-1));
end
assign h_sync = h_sync_reg;
assign v_sync = v_sync_reg;
assign video_on = (counter_x < (HD)) && (counter_y < (VD));
assign pixel_x = counter_x;
assign pixel_y = counter_y;
endmodule
Para verificar la resolución de la pantalla (480x272), se ubicaron en diferentes puntos de la pantalla 3 cuadriláteros, como se ve en la siguiente figura
La siguiente prueba consiste en simular los cuadros diseñados con anterioridad como botones, así al oprimir dentro del área de cada cuadrado este debe cambiar de color.
Para esto se instanció el módulo para el touch de la pantalla.
Este permite usar el touch sin necesidad del NIOS II . Además fue necesario reubicar los cuadrados de tal manera que queden alejados entre si, con el fin de evitar interferencia al tocar cada cuadrado, a continuación se puede observar la ubicación de los "botones", y el color que de estos al estar y no estar presionados.
PEN_DOWN sera 1'b0, al estar tocando la pantalla de lo contrario será 1'b1, para cambiar el color del "botón" por ejemplo el Azul [24'b0000ff] el PEN_DOWN sera 0, y si se encuentra dentro de los limites del cuadrado este cambiara a color verde [24'b00ff00] de lo contrario permanecerá azul.
Al oprimir un botón seguido de otro se detectaba un rebote en el cuadrado anterior, para evitar esto se empleo el Generate (ver siguiente figura) el cual consiste en un número de registros en cascada que generan un retraso en la señal, dicho retraso para este laboratorio sera de 0,1s, (Delay=N/Fs siendo N el número de registros) .
La señal a retrasar es TFT_ADC_PENIRQ_N la cual indica cuando es tocada la pantalla, (1'b0 --> oprime la pantalla) ahora la señal a utilizar sera PEN_DOWN la cual esta retrasada 0,1 segundos a TFT_ADC_PENIRQ_N.
A continuación puedes observar como cambian de color cada cuadrado al ser tocados.
ahora se insertara la siguiente imagen con el fin de que esta sea los botones de nuestro Reproductor, ya que tenia un formato .bmp se cambiara a formato .mif (Memory Initialization File), por medio del siguienre script (generado_ram_imag.m) en matlab proporcionado por el docente (Holguer A. Becerra)
Una vez hecha la conversión se abrió el archivo .mif
Ya que la imagen tiene un tamaño de 253x37 calculando el número de pixeles serian 9361, con esto se determina que deben haber 9361 datos de 12bits (ya que son 12bits por color)
con esto datos se crea la memoria ROM por medio de la herramienta MegaWizard con 12 bits y tamaño de 9361 words.
una vez creada la Rom, se instancia el modulo, y se ubica en la pantalla, ademas se adiciona un cambio de color al pulsar el botón play. (ver siguiente figura).
se adiciona el siguiente código en el mux de video
En el siguiente vídeo se puede observar esta primera prueba.
Ya que se requiere un botón para cada una de las funciones del reproductor como son play, stop, pause, adelantar y atrasar una canción, se procede a insertar cada uno de estos por separado, empleando una memoria para cada uno, a continuación se presentara el diseño para le botón play.
Primero se inserta la figura empleando la memoria ROM, como se realizo en la prueba anterior, seguido a esto se ubica el botón en la pantalla, ademas el botón debe cambiar de color al ser oprimido, para esto se crea la siguiente lógica PEN_DOWN se encuentra en estado alto y solo modifica su estado al tocar la pantalla (PEN_DOWN == 0), si tocamos la pantalla y se encuentra sobre el área del botón, play_oprimido sera igual a 1'b1 de lo contrario sera 1'b0. así que para el color de play, si play_oprimido=1'b1 sera el valor negado del color original (blanco -->negro, negro-->blanco) de lo contrario el botón tendrá el color original.
Para insertar cada botón se realiza en mismo procedimiento.
Una vez insertado los botones se procede a trabajar en el qsys y en el software de lectura del archivo .wav según CHU.
PRUEBA SD-CARDDE0-NANO.
Para leer archivos de la SD, primero se debe probar que se este cargando correctamente, para esto se descarga la siguiente Descargar Plantilla(prueba_sdhc_code.zip), se crea un nuevo proyecto en Eclipse (llamelo read_sd) y se copia la carpeta sd_card y el main.c que están contenidas en el archivo.zip descargado recientemente.
se conecta la SD como se ve en la siguiente figura.
y se diseña el Qsys:
Al terminar no olvidad guardar cambios y assign Base Addresses.
ahora se copia la instanciación generada desde el Qsys.
Una vez realizado la instanciación y conectado todo como se muestra en el siguiente código, se programa la DE0-NANO y el software, y se observara en el terminal del Nios que la Memoria SD se cargo con exito.
system u0 (
.clk_clk (sys_clk), // clk.clk
.reset_reset_n (reset_n), // reset.reset_n
//EPCS
.epcs_sdo (EPCS_ASDO), // epcs.sdo
.epcs_data0 (EPCS_DATA0), // .data0
.epcs_dclk (EPCS_DCLK), // .dclk
.epcs_sce (EPCS_NCSO), // .sce
//SDRAM
.sdram_dqm (DRAM_DQM), // sdram.dqm
.sdram_ras_n (DRAM_RAS_N), // .ras_n
.sdram_cs_n (DRAM_CS_N), // .cs_n
.sdram_we_n (DRAM_WE_N), // .we_n
.sdram_dq (DRAM_DQ), // .dq
.sdram_cke (DRAM_CKE), // .cke
.sdram_addr (DRAM_ADDR), // .addr
.sdram_ba (DRAM_BA), // .ba
.sdram_cas_n (DRAM_CAS_N), // .cas_n
//LED & SW
.pio_led_green_export (), // pio_led_green.export LED[7:1]
.pio_sw_export (SW[3:0]), // pio_sw.export
//SD-CARD
.spi_master_0_sclk (GPIO2[3]), // spi_master_0.sclk 6 GPIO2[28]
.spi_master_0_mosi (GPIO2[1]), // .mosi 3 GPIO2[26]
.spi_master_0_miso (GPIO2[5]), // .miso 8 GPIO2[30]
.spi_master_0_cs (GPIO2[0]) // .cs 2 GPIO2[24]
);
LEER UN ARCHIVO .WAV USANDO NIOS II
Para la lectura de archivos .wav se tomo como referencia el capitulo 18.7 AUDIO FILE PROCESSING del libro Embedded SoPC Design with Nios II Processor and VHDL Examples de Pong P. Chu, en el cual se encuentra toda la información requerida para el procesamiento de archivos de audio del formato .wav
"Formato de archivo WAV (o WAVE) es un subconjunto de RIFF (formato de archivo de intercambio de recursos) especificación para el almacenamiento de datos multimedia. Un archivo sencillo WAV frecuentemente contiene una sola porción WAVE. Una porción consiste en un sub-segmento fmt, que proporciona información sobre el formato de audio, y un sub-segmento de datos, que contiene los datos de audio reales. Por simplicidad, suponemos que el archivo WAV ha sido pre-procesado para conformar las especificaciones de controlador de audio":
* Formato de audio de datos: no comprimido, que se conoce como formato PCM (modulación por impulsos codificados)
* Número de canales: 2
* Frecuencia de muestreo: 48.000 muestras por segundo
* Resolución: 16 bits por muestra de datos.
Programa de conversión del formato de audio
Dado que el código de demostración sólo acepta un formato específico, el archivo de audio debe ser pre-procesado de antemano.
Una utilidad gratuita, es SoX ("Sound Exchange"), el cual es un procesador de audio de línea de comandos que puede convertir archivos de audio de un formato a otro formato, La siguiente declaración convierte un archivo WAV (input.wav) con parámetros arbitrarios a un archivo (output.wav) con el formato deseado:
sox input.wav -r 48000 -b 16 -c 2 -e signed output.wav
Las opciones -r,-b,-c y-e representan la frecuencia de muestreo, bits por muestra (resolución), número de canales, y el esquema de codificación, respectivamente. La opción signed significa que el tipo de codificación es PCM y los datos se almacenan en signed (complemento a dos). El archivo ejecutable y la documentación detallada de SoX se pueden encontrar en http://sox.sourceforge.net/. (tomado del libro Chu)
Una vez descargado el ejecutable, se convierte la canción Let Her Go de formato MP3 a WAV por medio del programa Free Audio Converter.
Ahora modificamos el archivo .wav con el fin de obtener el formato deseado empleando el SoX descargado con anterioridad.
A continuación se presenta el código que proporciona Chu para la lectura de archivos .wav
int read_wav_file ( char *file_name , alt_u32 *buf)
{
/* note that fget32 read in little endian, but id in big endian */
const alt_u32 RIFF_ID = 0x46464952; // ascii for FFIR
const alt_u32 WAVE_ID = 0x45564157; // ascii for EVAW
const alt_u32 FMT_ID = 0X20746d66; // ascii FOR \btmf
const alt_u32 DATA_ID = 0X61746164Ç; // ascii for ATAD
/*
Compruebe que el archivo se ajusta a nuestro formato específico.
Utilizamos cuatro constantes,
RIFF_ID, WAVE-ID, FMT_ID y DATA_ID, para la cadena de Identificación designado.
*/
FILE *fp;
alt_u32 r_id, w_id, f_id, d_id, srate, data_size;
alt_u16 compression, channel, res;
int i, s_size;
/* open the file*/
fp = fopen(file_name , "rb");
if(fp==NULL) {
printf("Error: no se puede abrir el archivo %s.\n" , file_name);
return(-1);
}
/*extract relevant chunk / subchunk info */
r_id = fget32(fp); // offset 0: "RIFF" chunk id
fskip(fp,4); // offset 4: chunk size
w_id = fget32(fp); // offset 8: "WAVE" chunk id
f_id = fget32(fp); // offset 12: "fmt" subchunk id
fskip(fp,4); // offset 16: subchunk size
compression = fget16(fp); // offset 20: 1 for PCM
channel = fget16(fp); // offset 22: 2 for stereo
strate = fget32(fp); // offset 24: 48k sampling rate
fskip(fp,4); // offset 28: byte rate
fskip(fp,2); // offset 32: block size
res = fget16(fp); // offset 34: 16 bits resolution
d_id= fget32(fp); // offset 36: "data" subchunk id
data_size = fget32(fp); // offset 40: # byte of data subchunk
/* check chunck / subchunk ids and parameters */
if ((r_id != RIFF_ID) || (w_id! = WAVE_ID) || (f_id!=FMT_ID) ||
(d_id != DATA_ID) || (compression != 1) || (channel != 2) ||
(strate != 48000) || (res != 16)){
printf ("Error: Formato de archivo de sonido incorrectos \n");
printf ("Debe ser PCM, 2 Canales, 48k velocidad, 16 bits resolución.\n");
printf ("RIFF/WAVE/fmt/data ids: %08x/ %08x/ %08x/ %08x\n",
r_id, w_id, f_id, d_id);
printf ("compression/channel/rate/res/data_size: %d/%d/%d/%d/%d\n",
compression, channel, srate, res, data_size);
fclose(fp);
return(-1);
}
s_size = data_size/4;
printf ("Archivo abierto.\n # muestras de datos de audio: %d\n", s_size);
for (i=0; i<s_size; i++){
// get 32-bit data
buf[i] = fget32(fp);
if (i%1000 == 0 )
printf (".");
}
fclose(fp);
printf("\nFile loaded.\n");
return(0);
}
Como primera prueba se transcribió el código anterior basándonos en las funciones de la siguiente página (http://elmchan.org/fsw/ff/00index_e.html) y se incluyeron las respectivas librerías
obteniendo como resultado:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <system.h>
#include <io.h>
#include "./sd_card/fatfs.h"
#include "./sd_card/diskio.h"
#include "./sd_card/ff.h"
#include "./sd_card/monitor.h"
#include <sys/alt_alarm.h>
/*=========================================================================*/
/* DEFINE: Definition of all local Data */
/*=========================================================================*/
static alt_alarm alarm2;
static unsigned long Systick = 0;
static volatile unsigned short Timer; /* 1000Hz increment timer */
FATFS Fatfs[_VOLUMES]; /* File system object for each logical drive */
/*=========================================================================*/
static void IoInit(void);
static alt_u32 TimerFunction (void *context);
void init_hw();
#define SIZE_OF_SONG 1024000
alt_u32 muestras_stereo[SIZE_OF_SONG];
FIL cancion;
FRESULT fr;
int read_wav_file ( char *file_name , alt_u32 *buf)
{
/* note that fget32 read in little endian, but id in big endian */
const alt_u32 RIFF_ID = 0x46464952; // ascii for FFIR
const alt_u32 WAVE_ID = 0x45564157; // ascii for EVAW
const alt_u32 FMT_ID = 0X20746d66; // ascii FOR \btmf
const alt_u32 DATA_ID = 0X61746164; // ascii for ATAD
/*
Compruebe que el archivo se ajusta a nuestro formato específico.
Utilizamos cuatro constantes,
RIFF_ID, WAVE-ID, FMT_ID y DATA_ID, para la cadena de Identificación designado.
*/
alt_u32 r_id, w_id, f_id, d_id, srate, data_size;
alt_u16 compression, channel, res;
int i, s_size;
int Byte_read_rid;
int Byte_read_wid;
int Byte_read_fid;
int Byte_read_compression;
int Byte_read_channel;
int Byte_read_srate;
int Byte_read_res;
int Byte_read_did;
int Byte_read_data_size;
int Byte_read_buf;
/* open the file*/
fr=f_open(&cancion, file_name, FA_READ);
if(fr) {
printf("Error: no se puede abrir el archivo %s.\n" , file_name);
return(-1);
}
/*extract relevant chunk / subchunk info */
fr = f_read (&cancion, &r_id,4, &Byte_read_rid); //fget32(fp); // offset 0: "RIFF" chunk id
fr = f_lseek(&cancion, 8); //fskip(fp,4); // offset 4: chunk size
fr = f_read (&cancion, &w_id, 4, &Byte_read_wid); //w_id = fget32(fp); // offset 8: "WAVE" chunk id
fr = f_read (&cancion, &f_id, 4, &Byte_read_fid); //f_id = fget32(fp); // offset 12: "fmt" subchunk id
fr = f_lseek(&cancion, 20); //fskip(fp,4); // offset 16: subchunk size
fr = f_read (&cancion, &compression, 2, &Byte_read_compression); //compression = fget16(fp); // offset 20: 1 for PCM
fr = f_read (&cancion, &channel, 2, &Byte_read_channel); //channel = fget16(fp); // offset 22: 2 for stereo
fr = f_read (&cancion, &srate,4, &Byte_read_srate); //strate = fget32(fp); // offset 24: 48k sampling rate
fr = f_lseek(&cancion, 32); //fskip(fp,4); // offset 28: byte rate
fr = f_lseek(&cancion, 34); //fskip(fp,2); // offset 32: block size
fr = f_read (&cancion, &res, 2, &Byte_read_res); //res = fget16(fp); // offset 34: 16 bits resolution
fr = f_read (&cancion, &d_id, 4, &Byte_read_did); //d_id= fget32(fp); // offset 36: "data" subchunk id
fr = f_read (&cancion, &data_size, 4, &Byte_read_data_size); //data_size = fget32(fp); // offset 40: # byte of data subchunk
printf ("rid=%x, w_id=%x, f_id=%x, compression=%x, channel=%x, srate=%d, res=%d,d_id=%x, data_size=%d\n",r_id,
w_id,f_id,compression,channel,srate,res,d_id,data_size);
/* check chunck / subchunk ids and parameters */
if ((r_id != RIFF_ID) || (w_id!= WAVE_ID) || (f_id!=FMT_ID) || (d_id != DATA_ID) || (compression != 1) || (channel != 2) || (srate != 48000) || (res != 16))
{
printf ("Error: Formato de archivo de sonido incorrectos \n");
printf ("Debe ser PCM, 2 Canales, 48k velocidad, 16 bits resolución.\n");
printf ("RIFF/WAVE/fmt/data ids: %08x/ %08x/ %08x/ %08x\n",r_id, w_id, f_id, d_id);
printf ("compression/channel/rate/res/data_size: %d/%d/%d/%d/%d\n",compression, channel, srate, res, data_size);
f_close(&cancion);
return(-1);
}
s_size = data_size/4; //da el tamaño de un archivo en word
printf ("Archivo abierto.\n # muestras de datos de audio: %d\n", s_size);
for (i=0; i<s_size; i++){
if(i>=SIZE_OF_SONG)
{
break;
}
// get 32-bit data
fr = f_read (&cancion, &buf[i], 4, &Byte_read_buf); //buf[i] = fget32(fp);
if (i%1000 == 0 || i==0)
printf ("%x. ",buf[i]);
}
f_close(&cancion);
printf("\nFile loaded.\n");
return(0);
}
int main(){
init_hw(); //inicialice hardware
printf("hola prueba\n");
read_wav_file("OUTPUT.WAV",&muestras_stereo[0]);
while(1){
}
}
Seguido a esto se procede a programar la NANO, y el software, con el fin de verificar si se abrió correctamente el archivo.
para verificar si es correcto se puede abrir el archivo en Matlab
Ya que la consola imprime cada 1000 muestras 4 bytes, se verifica en matlab los datos cada 4000, se puede observar que el archivo es abierto correctamente, ahora el siguiente paso es reproducir dicho archivo.
Para esto se realizo la siguiente lógica.
Donde un buffer FIFO (primero en entrar, primero en salir) es una instalación de almacenamiento “elástica” entre dos subsistemas, tiene dos señales de control wr y rd, para las operaciones de escritura y lectura. Cuando wr=1’b1, el dato de entrada es escrito en el buffer, La operación de lectura es algo engañoso. Ya que se puede leer en cualquier momento, la señal rd actúa como una señal de "eliminar". Cuando esta en alto, el primer elemento del búfer FIFO se elimina y el próximo elemento se encuentre disponible.
sclr permite eliminar cada elemento del buffer, y así al oprimir el botón de stop detener por completo la reproducción del archivo.
DATA_STEREO contiene el bus de datos de audio del canal derecho e izquierdo.
FULL_FIFO indica que el FIFO está lleno es decir no se puede escribir
EMPTY indica que el FIFO está vacío por lo tanto no se puede leer. (tomado de libro Chu)
Para el diseño del FIFO, se implemento la herramienta MegaWizard.
Ya que los datos se almacenan en signed (complemento a dos), se debe asegurar que no existan valores negativos, para esto se suma 16'd 32727 [((2^16)/2) -1= 32767]
y se emplea un monodac para cada canal (derecho e izquierdo)
////////////// MONO DAC DERECHA //////////////
dac_nano dac_nano_derecha
(
.DACout(R) , // output DACout_sig
.DACin(Data_stereo[15:0]+16'd32767) , // input [15:0] DACin_sig {~Data_mono[15],Data_mono[14:0]}
.Clk(clk_50) , // input Clk_sig
.Reset(1'b0) // input Reset_sig
);
////////////// MONO DAC IZQUIERDA //////////////
dac_nano dac_nano_izquierda
(
.DACout(L) , // output DACout_sig
.DACin(Data_stereo[31:16]+16'd32767) , // input [15:0] DACin_sig {~Data_mono[31],Data_mono[30:16]}
.Clk(clk_50) , // input Clk_sig
.Reset(1'b0) // input Reset_sig
);
Ya que el reproductor posee botones de play, pause, stop, next, y back se diseña la siguiente máquina de estados.
Para el diseño del software se emplea el programa Eclipse (lenguaje c).
Para el diseño del reproductor se realizan varias pruebas, la primera de ellas es la reproducción del archivo al oprimir el botón play. Seguida a esta se prueba el botón pause
El divisor de frecuencia permite reproducir todo tipo de archivo que cumpla con las características de formato, permitiendo que varié la frecuencia de muestreo.
Reproducción de canción de 8KHz
Reproducción de canción 11KHz
Reproducción de canción 16KHz
Reproducción de canción 22KHz
Reproducción de canción 44KHz
Reproducción de canción 48KHz
Reproducción de canción 96KHz
Beyonce Single Dadies
DISEÑO DEL HISTOGRAMA SE UNA SEÑAL.
Para nuestro proyecto se desea mostrar el histograma de la canción que está sonando en ese momento en la pantalla tactil por tal motivo se realizaron pruebas en matlab usando una señal sinoidal, a continuación se muestra el código.
clc
close all
FS=48000;
xx=0:30*pi/(1452*100):30*pi;
canal_r=sin(2*pi*1000*xx);
d=1;
tam=size(canal_r);
tiempo=33e-3; % 33ms
numeromuestras=round(FS*tiempo);
tam2=tam(2)/numeromuestras;
figure;title('Distribución Weibull'); xlabel('Observación'); ylabel('Frecuencia');
while(d<(tam2-1))
nueva=round(canal_r(1452*d:1452*(d+1))*(2^8/2)+(2^8/2));
x=1:1:255;
hist(nueva,x)
pause(tiempo)
d=d+1;
end
a continuación se muestra un video con la simulación obtenida en matlab:
Después de haber comprobado que se puede realizar un histograma de una señal realizando matlab procesemos a realizar el código verilog necesario para poder generar un histograma lo primero que se hiso fue crear un sub-modulo "histograma_audio" encargado de hacer el histograma que tiene 256 puntos(numero de registros) ya que se toma una señal de entrada de 8 bits, tomando una ventana de tiempo(33ms) para calcular el numero de veces que se repite una amplitud guardandola así en 256 registros de 8 bits cada ventana de tiempo.
A continuación se muestra el código empleado:
el siguiente paso es hacer el frecGen(
frecGen.v) el cual fue dado por el profesor en practicas pasadas
lo siguiente que se tiene que hacer es crear un NCO que se encarga de enviar la funcion sinoidal al "histograma_audio" esto se realiza con la herramienta Mega Wizard que posee Quartus.(Solo para probar el histograma en la pantalla y ver como se se veía según la pruebas de matlab)
Después de haber creado los 3 sub-módulos procedemos a instanciarlos en el módulo principal, así:
wire [31:0]signal;
wire [2047:0]histoGrama;
wire [9:0] fsin_o;
wire[8:0] div_freg;
wire OUT_FREG;
//=======================================================
// Structural coding
//=======================================================
assign signal[31:0]= (SW[2:0]==3'b000)? 32'd4492 :
(SW[2:0]==3'b001)? 32'd5042 :
(SW[2:0]==3'b010)? 32'd5660 :
(SW[2:0]==3'b011)? 32'd5995 :
(SW[2:0]==3'b100)? 32'd6725 :
(SW[2:0]==3'b101)? 32'd7559 :
(SW[2:0]==3'b110)? 32'd8478 :
32'd8985;
nco nco_inst
(
.phi_inc_i(signal[31:0]) , // input [31:0] phi_inc_i_sig
.clk(CLOCK_50) , // input clk_sig
.reset_n(1'b1) , // input reset_n_sig
.clken(1'b1) , // input clken_sig
.fsin_o(fsin_o[9:0]) // output [9:0] fsin_o_sig
);
frecGen frecGen_inst
(
.clock(CLOCK_50) , // input clock_sig
.IN(32'd520) , // input [31:0] IN_sig
.OUT(OUT_FREG) // output OUT_sig
);
histograma_audio histograma_audio_inst
(
.data_x({~fsin_o[9],fsin_o[8:2]}) , // input [7:0] data_x_sig
.clk_sampler(OUT_FREG) , // input clk_sampler_sig
.histograma(histoGrama[2047:0]) , // output [2047:0] histograma_sig
.clock_50(CLOCK_50) , // input clock_50_sig
.finish() // output finish_sig
);
endmodule
El siguiente paso fue ubicar los botones de play, pause, stop, adelantar y atrasar, en la pantalla táctil de tal forma que no tapara el histograma de la canción.
A continuación se muestra el código de los botones:
/// Ubicacion boton play /// 70x70
wire play_on= (CounterX > 11'd10) && (CounterX < 11'd80) &&(CounterY > 11'd55) && (CounterY < 11'd125);
wire [13:0]address_play=(CounterY-11'd55)*70+CounterX - 11'd10;
wire [11:0]rgb_play;
wire play_oprimido= ((PEN_DOWN==1'b0) & (real_x[31:0]>11'd10) & (real_x[31:0]<11'd80) & (real_y[31:0]>11'd55) & (real_y[31:0]<11'd125));
wire[23:0]color_boton_play={rgb_play[11:8],rgb_play[11:8],rgb_play[7:4],rgb_play[7:4],rgb_play[3:0],rgb_play[3:0]};
wire[23:0]color_play=play_oprimido?~color_boton_play[23:0]:color_boton_play[23:0];
////////////////////////////////// Ubicacion Boton pause /// 70x70 ///////////////////////////////////////////////////////////////////////////
wire pause_on= (CounterX > 11'd10) && (CounterX < 11'd80) &&(CounterY > 11'd147) && (CounterY < 11'd217);
wire [11:0]rgb_pause;
wire pause_oprimido= ((PEN_DOWN==1'b0) & (real_x[31:0]>11'd10) & (real_x[31:0]<11'd80) & (real_y[31:0]>11'd147) & (real_y[31:0]<11'd217));
wire[23:0]color_boton_pause={rgb_pause[11:8],rgb_pause[11:8],rgb_pause[7:4],rgb_pause[7:4],rgb_pause[3:0],rgb_pause[3:0]};
wire[23:0]color_pause=pause_oprimido?~color_boton_pause[23:0]:color_boton_pause[23:0];
//
wire [13:0]address_pause=(CounterY-11'd147)*70+CounterX-11'd10;
//////////////////////////////////// Ubicacion boton stop /// 70x70 /////////////////////////////////////////////////////////////////////////
wire stop_on= (CounterX > 11'd400) && (CounterX < 11'd470) &&(CounterY > 11'd101) && (CounterY < 11'd171);
wire [11:0]rgb_stop;
wire stop_oprimido= ((PEN_DOWN==1'b0) & (real_x[31:0]>11'd400) & (real_x[31:0]<11'd470) & (real_y[31:0]>11'd101) & (real_y[31:0]<11'd171));
wire[23:0]color_boton_stop={rgb_stop[11:8],rgb_stop[11:8],rgb_stop[7:4],rgb_stop[7:4],rgb_stop[3:0],rgb_stop[3:0]};
wire[23:0]color_stop=stop_oprimido?~color_boton_stop[23:0]:color_boton_stop[23:0];
//
wire [13:0]address_stop=(CounterY-11'd101)*70+CounterX-11'd400;
/// Ubicacion boton adelantar /// 70x70
wire adelantar_on= (CounterX > 11'd400) && (CounterX < 11'd470) &&(CounterY > 11'd191) && (CounterY < 11'd261);
wire [11:0]rgb_adelantar;
wire adelantar_oprimido= ((PEN_DOWN==1'b0) & (real_x[31:0]>11'd400) & (real_x[31:0]<11'd470) & (real_y[31:0]>11'd191) & (real_y[31:0]<11'd261));
wire[23:0]color_boton_adelantar={rgb_adelantar[11:8],rgb_adelantar[11:8],rgb_adelantar[7:4],rgb_adelantar[7:4],rgb_adelantar[3:0],rgb_adelantar[3:0]};
wire[23:0]color_adelantar=adelantar_oprimido?~color_boton_adelantar[23:0]:color_boton_adelantar[23:0];
//
wire [13:0]address_adelantar=(CounterY-11'd191)*70+CounterX-11'd400;
/// Ubicacion boton atrasar /// 70x70
wire atrasar_on= (CounterX > 11'd400) && (CounterX < 11'd470) &&(CounterY > 11'd12) && (CounterY < 11'd81);
wire [11:0]rgb_atrasar;
wire atrasar_oprimido= ((PEN_DOWN==1'b0) & (real_x[31:0]>11'd400) & (real_x[31:0]<11'd470) & (real_y[31:0]>11'd12) & (real_y[31:0]<11'd81));
wire[23:0]color_boton_atrasar={rgb_atrasar[11:8],rgb_atrasar[11:8],rgb_atrasar[7:4],rgb_atrasar[7:4],rgb_atrasar[3:0],rgb_atrasar[3:0]};
wire[23:0]color_atrasar=atrasar_oprimido?~color_boton_atrasar[23:0]:color_boton_atrasar[23:0];
//
wire [13:0]address_atrasar=(CounterY-11'd12)*70+CounterX-11'd400;
/////MUX DE VIDEO ////
always@(negedge TFT_DCLK)
begin
if(!TFT_DE)
begin
TFT_RED<=8'h00;
TFT_GREEN <=8'd0;
TFT_BLUE <=8'd0;
end
else
begin
TFT_RED<=8'h00;
TFT_GREEN<=8'h00;
TFT_BLUE <=8'h00;
if(bar_on)
begin
TFT_RED<=Data_histo[15:8];
TFT_GREEN<=Data_histo2[7:0]+Data_histo2[15:8];
TFT_BLUE <=Data_histo[7:0];
end
else if(play_on)
begin
TFT_RED <=color_play[23:16];
TFT_GREEN<=color_play[15:8];
TFT_BLUE <=color_play[7:0];
end
else if(pause_on)
begin
TFT_RED <=color_pause[23:16];
TFT_GREEN<=color_pause[15:8];
TFT_BLUE <=color_pause[7:0];
end
else if(atrasar_on)
begin
TFT_RED <=color_atrasar[23:16];
TFT_GREEN<=color_atrasar[15:8];
TFT_BLUE <=color_atrasar[7:0];
end
else if(stop_on)
begin
TFT_RED <=color_stop[23:16];
TFT_GREEN<=color_stop[15:8];
TFT_BLUE <=color_stop[7:0];
end
else if(adelantar_on)
begin
TFT_RED <=color_adelantar[23:16];
TFT_GREEN<=color_adelantar[15:8];
TFT_BLUE <=color_adelantar[7:0];
end
end
end
a continuación se muestra del histograma de la señal sinoidal en la pantalla tactil y los botones de reproducción.
Después de haber comprobado que se puede graficar en la pantalla táctil un histograma de una señal por medio de Quartus se procede a reemplazar el módulo NCO que se encargaba de enviar la señal sinodal por la señal de la canción.
a continuación se observan los videos del proyecto funcionando, en la parte inferior de la pagina se podra encontrar el archivo .zip(
wav_player.zip) que contiene el codigo completo de la descripción de hardware y el software nios II.
Enjoy!