Búscalo aquí:

Graficar una señal de audio en coordenadas de dispositivo [código]

Una vez que tenemos el vector de amplitud de una señal de audio, contamos con la información necesaria para poder realizar la representación gráfica de la señal de audio. Sin embargo, la graficación de esta señal de audio deberá realizarse teniendo en cuenta las transformaciones de dispositivo básicas. En este post presentamos el código fuente en Java para realizar esta tarea.


Debido a que en Java (y en cualquier otro lenguaje de programación), las coordenadas de origen se encuentran en la esquina superior izquierda, nos vemos en la obligación de realizar transformaciones de dispositivo, es decir, reubicar el origen de coordenadas, para una mejor visualización de la señal de audio.

Además graficaremos un eje de coordenadas y proveeremos métodos que faciliten el cambio de parámetros para una mejor visualización de la onda (color de la onda, del fondo, de los ejes, realizar zoom in zoom out, etc.). El código fuente en Java de la clase GuiSeñal del paquete GUI es el siguiente:

  1. package GUI;
  2. import java.awt.*;
  3. /**
  4. *
  5. * @author Jorge Valverde
  6. */
  7. public class GuiSeñal extends javax.swing.JPanel{
  8. /** Creates new form GuiSeñal */
  9. public GuiSeñal()
  10. {
  11. datosVoz = null;
  12. tyEje = 0;
  13. txEje = 0;
  14. alto = 0;
  15. ancho = 0;
  16. origen = 30; //distancia del borde lateral al origen de coordenadas
  17. distAlt = 20; //distancia del borde superior al maximo pico de una curva
  18. accionGraficar = false;
  19. graficarOnda = false;
  20. colorEje = Color.RED;
  21. colorSeñal = Color.BLUE;
  22. colorFondo = Color.BLACK;
  23. colorPitch = Color.RED;
  24. }
  25. public void setDatosVoz(double[] dataVoz)
  26. {
  27. this.datosVoz = dataVoz;
  28. }
  29. public void setAncho(int anch)
  30. {
  31. this.ancho=anch;
  32. }
  33. public void setAlto(int alt)
  34. {
  35. this.alto=alt;
  36. }
  37. public void setIntervalo(int interv)
  38. {
  39. this.intervalo = interv;
  40. }
  41. //Coloca la distancia del borde del panel al origen de coordenadas
  42. public void setInitOrigen(int dist)
  43. {
  44. this.origen = dist;
  45. }
  46. public void setFactorE(double fe)
  47. {
  48. this.factorE=fe;
  49. }
  50. public void setFactorEX(double fex)
  51. {
  52. this.factorEX=fex;
  53. }
  54. public void setFactorEY(double fey)
  55. {
  56. this.factorEY=fey;
  57. }
  58. public void setZoomOut()
  59. {
  60. if(this.factorEX>0 && this.factorEY>0)
  61. {
  62. this.factorEX +=0.001;
  63. this.factorEY +=0.001;
  64. }
  65. }
  66. public void setZoomIn()
  67. {
  68. if(this.factorEX-0.001>0 && this.factorEY-0.001>0)
  69. {
  70. this.factorEX -=0.001;
  71. this.factorEY -=0.001;
  72. }
  73. }
  74. public void setTY(int t)
  75. {
  76. this.tyEje+=t;
  77. }
  78. public void setTX(int t)
  79. {
  80. this.txEje+=t;
  81. }
  82. public void setAccionGraficar(boolean a)
  83. {
  84. this.accionGraficar = a;
  85. }
  86. public void setGraficarOnda(boolean a)
  87. {
  88. this.graficarOnda = a;
  89. }
  90. public void setGraficarPeriodo(boolean a)
  91. {
  92. this.graficarPeriodo = a;
  93. }
  94. public void setColorEjes(Color c)
  95. {
  96. this.colorEje = c;
  97. }
  98. public void setColorFondo(Color c)
  99. {
  100. this.colorFondo = c;
  101. }
  102. public void setColorOnda(Color c)
  103. {
  104. this.colorSeñal = c;
  105. }
  106. public double[] getVectorVoz()
  107. {
  108. return this.datosVoz;
  109. }
  110. public boolean getGraficarOnda()
  111. {
  112. return this.graficarOnda;
  113. }
  114. public Color getColorFondo()
  115. {
  116. return this.colorFondo;
  117. }
  118. private double[] cambiarCoordenadasDispositivo(double[] cp)
  119. {
  120. double[] p = new double[2];
  121. double wPV = ancho/2;
  122. double hPV = alto/2;
  123. p[0]=Math.round((cp[0]/2) + wPV - txEje);
  124. p[1]=Math.round(hPV-(cp[1]/2));
  125. return p;
  126. }
  127. private void graficarSeñal(Graphics g)
  128. {
  129. double[] puntoP=new double[2]; /*punto proyectado*/
  130. double[] punto2D=new double[2]; /*punto en coordenadas del dispositivo*/
  131. int xi=0,xf=0,yi=0,yf=0,i=0;
  132. double[] MCoordP = getVectorVoz();
  133. int total = MCoordP.length;
  134. int interv=intervalo;
  135. for(i=txEje; i<total-interv; i=i+interv )
  136. {
  137. puntoP[0]=i*factorEX;
  138. puntoP[1]=MCoordP[i]*factorEY;
  139. punto2D=cambiarCoordenadasDispositivo(puntoP);
  140. xi=(int)(punto2D[0]+origen)-(int)(ancho/2.0);
  141. yi=(int) punto2D[1]-tyEje+distAlt;
  142. puntoP[0]=(i+interv)*factorEX;
  143. puntoP[1]=MCoordP[i+interv]*factorEY;
  144. punto2D=cambiarCoordenadasDispositivo(puntoP);
  145. xf=(int)(punto2D[0]+origen)-(int)(ancho/2.0);
  146. yf=(int)punto2D[1]-tyEje+distAlt;
  147. g.setColor(colorSeñal);
  148. g.drawLine(xi,yi,xf,yf);
  149. }
  150. }
  151. public void resetear()
  152. {
  153. datosVoz = null;
  154. accionGraficar = false;
  155. graficarOnda = false;
  156. graficarPeriodo = false;
  157. }
  158. public void update(Graphics g)
  159. {
  160. paint(g);
  161. }
  162. public void paint(Graphics g)
  163. {
  164. imagenOnda = this.createImage(ancho, alto);
  165. pantallaOnda = imagenOnda.getGraphics();
  166. pantallaOnda.setColor(colorFondo);
  167. if(accionGraficar==true)
  168. {
  169. if(txEje<origen)
  170. {
  171. pantallaOnda.fillRect(0, 0, ancho, alto);
  172. pantallaOnda.setColor(colorEje);
  173. pantallaOnda.drawLine(origen,0,origen,alto);
  174. pantallaOnda.drawLine(0,alto/2-tyEje+distAlt,
  175. ancho,alto/2-tyEje+distAlt);
  176. }
  177. if(graficarOnda==true)
  178. {
  179. if( txEje < 0)
  180. {
  181. txEje = 0;
  182. }
  183. graficarSeñal(pantallaOnda);
  184. }
  185. }
  186. else
  187. {
  188. pantallaOnda.clearRect(0,0,ancho,alto);
  189. pantallaOnda.fillRect(0,0,ancho,alto);
  190. pantallaOnda.setColor(colorEje);
  191. pantallaOnda.drawLine(origen,0,origen,alto);
  192. pantallaOnda.drawLine(0,alto/2-tyEje+distAlt,
  193. ancho,alto/2-tyEje+distAlt);
  194. }
  195. g.drawImage(imagenOnda, 0, 0,this);
  196. }
  197. private double[] datosVoz;
  198. private int tyEje;
  199. private int txEje;
  200. private int alto=0,ancho=0;
  201. private int intervalo;
  202. private int origen;
  203. private int distAlt;
  204. private boolean graficarOnda;
  205. private boolean accionGraficar;
  206. private Image imagenOnda;
  207. private Graphics pantallaOnda;
  208. private Color colorEje;
  209. private Color colorFondo;
  210. private Color colorSeñal;
  211. private double factorE;
  212. private double factorEX;
  213. private double factorEY;
  214. }


Al utilizar este código, se visualizará en un panel de Java una onda como se visualiza en la imagen al inicio de este post. Espero les sea de utilidad.


Quieres leer más post como éste???...suscribete aquí!!!

15 comentarios:

  1. la graficacion de este tipo de ondas es lo que hacen los reproductores como el winamp, pero si hay archivos demasiado extensos resultaria demasiado pesado cargar toda esa cantidad de datos a memoria, debe haber alguna manera de acelerar el proceso?

    ResponderEliminar
  2. En la linea 190 no comprendo las condiciones del bucle for, ¿puedes confirmarme los valores de la formación del bucle? Gracias

    ResponderEliminar
  3. Disculpen, tienes razon, en la linea 190 hubo un pequeño error al momento de pasar el código de java en HTML, la línea completa ya esta corregida en el post, saludos y gracias por leer este blog

    ResponderEliminar
  4. Cuando las ondas de audio a graficar tienen una longitud extensa, como por ejemplo, la de un archivo de una canción, que tiene una duracion de 2-3 minutos, entonces, lo que se hace es samplear la onda y graficarla por instantes de tiempo. Es decir, graficar la onda en el instante de tiempo t, luego en el instante t+1, y así sucesivamente hasta terminar con toda la onda.

    Es por eso que en los reproductores se puede observar que la onda va apareciendo conforme pasa el tiempo, saludos.

    ResponderEliminar
  5. Jhaira: Antes de nada, muchisimas gracias pro este codigo Juan Carlos.
    En la linea 240, if (txEje), me genera error, y no puedo compilar, esta variable no deberia compararse con algún valor?

    ResponderEliminar
  6. Hola Jhaira, si sorry a todos los lectores, ahi hubo un error al momento de pasar el codigo de Java a HTML ^^!

    La linea 240 de manera correcta es:
    if( txEje < origen )

    Ya esta corregida en el post tambien, gracias

    PD: Soy Jorge Carlos :P

    ResponderEliminar
  7. Jhaira: Hola Jorge Carlos, lo siento mucho por el nombre, :), y muchisimas gracias pro tu colaboración.
    Te comento que tengo el mismo problema que manuel (Lectura y Graficación de Señales de Audio en Java[codigo]). No se gráfica la onda de sonido. Realice las recomendaciones qeu le hiciste a Manuel, pero no encontre mi error.
    Caracteristicas de la aplicación:
    Trabajo dentro de un Jframe, con los siguientes componentes de interfaz:
    1) menuAbrir(Jbutton)
    2) grafico (JPanel)
    3) panelSeñal (GuiSeñal), que se encuentra dentro de "grafico"
    4) fc (JFileChooser)

    1. El audio qeu estoy utilizando para la prueba es dominaiting.wav
    2. Este archivo es leido y correctamente almacenado en el vector datosVoz. (No tengo problemas en esta parte)
    3. Se ejecuta el audio del archivo wav (Reproducir.java), pero no muestra la onda de sonido. Pienso qeu se debe a la clase GuiSeñal. Cuando compilo me devuelve un error: "java.lang.IlegalArgumentException: Width (0) and height (0)can not be <=0. "
    Bueno cambie los valores de ancho y alto, y como es de suponer entra en un ciclo sin fin.

    Nose si tiene tb algo qeu ver con la posición donde se encuentra ubicado mi jpanel (grafico). O las distancias de "origen = 30" y "disAlt=20"

    Por favor, indicame donde esta mi equivocación, o qeu recomendaciones me das.

    Muchisimas gracias por tu tiempo!

    pd: Solo utilice el código de tu blob. Solo añadi dentro de GuiSeñal, al final del código las definiciones de dos variables: "private boolean graficaPeriodo" y "private Color colorPitch", debido a que no se encontraban previamente definidas.
    pd2: Espero haber sido explicita :)

    ResponderEliminar
  8. La posicion del JPanel no creo que tenga que ver, tal vez te este faltando colocar el JPanel sobre un contenedor(dentro), es decir, agregarlo en un JFrame,

    Tal vez este faltando darle el repaint al JPanel para que actualice el pintado.

    Para probar lo que esta sucediendo y encuentres el error, coloca impresiones de pantalla en las funciones de graficación (o en todo caso dale al Debug que lo haga), así podrás saber el error y corregirlo :D

    saludos,

    ResponderEliminar
  9. Jhaira:
    Tengo un Jframe, dentro de este tengo a un JPanel llamado grafico (como en tu ejemplo), dentro de este panel se encuentra otro llamado panelSeñal que es del tipo GuiSeñal.
    1. El repaint es solo para panelSeñal, verdad?
    2. Otra consulta, los valores definidos, siguiendo los consejos sobre las impresiones de pantalla, observe que los valores enviados del método graficarSeñal() en la linea 7 es 0. Debido a que la variable finterv tiene como valor 0. En la línea 8 el valor enviado de fex = 0.0 y fey = 0.0. Tengo que cambiar estos valores?
    Agradeciendo nuevamente tu colaboración , me despido.

    ResponderEliminar
  10. El repaint se hace al elemento panelSeñal.
    Los valores de fex y fey los debes de colocar segun tu conveniencia, eso lo explico un poco más en este comentario

    ResponderEliminar
  11. hola pro favor pdorias eplicarme que es txEje y tyEje y que valores deberia tener porque no se en donde cambian esos valores, solo le envias "0" a tyEje en la funcion graficarSeñal() y txEje que valor tiene "0"ambien??

    saludos

    ResponderEliminar
  12. Buenas.
    El codigo presenta una asignacion de createimage(ancho,alto) en la linea 233, con this, pero en el codigo no aparece definido ni como metodo ni como atributo de la clase, me podria hacer el favor de decirme de q tipo si es un atributo o en q consiste el metodo....Gracias por las rutinas y por la atencian a este comentario

    ResponderEliminar
  13. El metodo createImage(ancho,alto) es un metodo propio del paquete awt (Abstract Window Toolkit) de Java el cual consiste en crear un objeto Image el cual luego es referenciado a la variable respectiva.

    El uso del puntero this es debido a que la clase GuiSeñal es extendida desde javax.swing.JPanel.

    Espero les sirva de ayuda. saludos.

    ResponderEliminar
  14. Hola, me encantó tu publicación, pero como podría obtener la frecuencia del vector, es decir el vector, solo está guardando información de la amplitud de la onda?

    ResponderEliminar
  15. hola, disculpen pero es la primera ves q entro a este blog y estoy un poko perdido, me podrian explicar como hacer la parte visual o decirme donde esta explicado graciasss

    ResponderEliminar

Bienvenido a jcGeorge's Blog!!!

Por favor deja tu comentario, consulta o sugerencia, procura mantener habilitado tu perfil de Blogger o deja un enlace a tu blog o web.

Gracias por leer este blog!!!

Related Posts Plugin for WordPress, Blogger...