Envía los saludos de la temporada con una tarjeta de Navidad de destello del piano

Cansado de esas tarjetas navideñas animadas pasadas de moda con Papá Noel riéndose, un trineo volando en el cielo con un grupo de árboles y luces brillantes.?

Este año no tienes excusa. Voy a mostrarte cómo construir un teclado virtual que toca Jingle Bells. Incluso podrás extender el concepto y agregar más canciones y notas al piano..

Para esto, usaré una combinación de Tweenlite, Flex SDK, Flash IDE y Flash Develop..




Paso 1: Nuevo archivo

Inicie Adobe Flash y cree un archivo ActionScript 3 Flash..

Paso 2: Propiedades

Abra las propiedades y configure el FPS a 30 y el tamaño del escenario a 600 x 380 px.

Paso 3: Capa 1

Renombra la capa 1 como "fondo" y crea un rectángulo blanco de 580x360. Conviértalo en un símbolo de clip de película llamado "frame_mc" y establezca su posición en x: 10 y: 10.

Paso 4: Drop Shadow

Agrega un filtro de sombra con los siguientes parámetros:

Paso 5: Capa de teclado

Agregue una nueva capa llamada "teclado", cree un rectángulo primitivo 60x190 con un radio de esquina de 5 píxeles y sin trazo. Conviértalo en un símbolo de clip de película y llámelo "keybase_mc".

Paso 6: Capas de reflexión y brillo

Pulsa F8 y crea un nuevo clip de vídeo llamado "key_mc". Cree dos capas más dentro de key_mc (además de la que ya está presente con keybase_mc). Nómbrelos: "reflexión" y "resplandor". Copia el marco a las capas recién creadas..

Paso 7: glow_mc

NOTA: He cambiado los colores de frame_mc por un tiempo para que pueda ver las modificaciones en las teclas. Seleccione el movieclip en la capa luminosa, llámelo "glow_mc", abra los filtros y edítelos de acuerdo con la siguiente imagen:

Paso 8: reflection_mc

Seleccione el movieclip en la capa de reflexión, asígnele el nombre "reflection_mc", abra los filtros y edítelos para que coincidan con la imagen siguiente:

Paso 9: base_mc

ahora seleccione el movieclip en la capa base, llámelo "base_mc", abra los filtros y edítelos para que coincidan con la imagen de abajo:

Paso 10: Clave

Copia y pega la clave hasta que obtengas 7 instancias. Colóquelos uniformemente en el escenario..

Paso 11: Alinear

Abra la herramienta de alineación y haga clic en el icono "espaciado horizontal".

Paso 12: Notas

Crea una nueva capa, llámala "notas". Entonces escribe C D E F G A B en las teclas agregue el texto a un nuevo movieclip. Abra los filtros de movieclip y edítelos como la imagen a continuación:

Paso 13: Números

Crea una nueva capa, llámala "números". Escriba los números del 1 al 7, esto representará los números que presionará en su teclado para resaltar la tecla de tarjeta electrónica.

Paso 14: Flex SDK Path

Vaya a editar> preferencias> ActionScript> configuración de ActionScript 3.0 y localice su ruta de Flex SDK (necesitará esto para incrustar archivos directamente a través del código).

Paso 15: Desarrollar Flash

Abra Flash Develop (solo uso esta causa, me gusta mucho más que el editor ActionScript habitual del IDE de flash al escribir paquetes) y cree 2 archivos AS3 en blanco. Llámalos "Main.as" y "Background.as", guárdalos en la misma carpeta que tu .fla.

Paso 16: Clase de documento

Inside Flash IDE establece Main.as como su clase de documento.

Paso 17: Botón de reproducción automática

Cree un clip de película de reproducción automática y asígnele el nombre "autoplay_mc". Este será un botón de reproducción automática..

Paso 18: Los copos de nieve

Para crear algunos copos de nieve cayendo, cree un nuevo clip de vídeo, dibuja un círculo blanco pequeño dentro y agrega un identificador de vinculación de "SnowFlake".

Paso 19: Main.as

En Flash Develop, abra Main.as, defina su clase Main extendiendo un movieclip y cree una función llamada Main.

Comience importando estas clases dentro de su paquete:

 importar flash.display.MovieClip; import flash.events.Event; import flash.events.MouseEvent; import flash.events.TimerEvent; importar flash.display.StageScaleMode; import flash.events.KeyboardEvent; import flash.utils.Timer; import com.greensock.TweenLite; import com.greensock.easing. *; Importar fondo; // crearemos una clase de fondo basada en el ruido perlin y una matriz de transformación junto con algunas formas rellenas para que podamos tener una transición de nieve agradable y suave; // Acabo de agarrar kirupas snow y lo empaqueté -> http://troyworks.com/blog/2008/11/26/flash-kirupa-snow-in-as30/ import flash.media.Sound;

Paso 20: Variables

Dentro de tu clase define estas variables:

 // este será nuestro fondo privado var _background: Background; // esta será nuestra tormenta de nieve privada var snow: Snow; // -> notas // Incrustar de esta manera requiere FLEX SDK -> el método alternativo es agregar estos sonidos a su biblioteca y agregarles una identificación de enlace. [Incrustar (fuente = "activos / A.mp3")] privado var A: Clase; var a privado: Sonido = nuevo A () como sonido; [Incrustar (fuente = "activos / B.mp3")] privado var B: Class; var privado: sonido = nuevo B () como sonido; [Incrustar (fuente = "activos / C.mp3")] privado var C: Class; var privado: sonido = nuevo C () como sonido; [Incrustar (fuente = "activos / D.mp3")] privado var D: Class; var privado: sonido = nuevo D () como sonido; [Incrustar (fuente = "activos / E.mp3")] privado var E: Class; Variedad privada: Sonido = nuevo E () como sonido; [Incrustar (fuente = "activos / F.mp3")] privado var F: Class; privado var f: Sound = new F () as Sound; [Incrustar (fuente = "activos / G.mp3")] privado var G: Class; var var privado: Sonido = nuevo G () como sonido; // Almacenar las notas en una matriz facilitará el enlace al teclado privado var notes: Array = [c, d, e, f, g, a, b] // Secuencia de notas para la música privada var noteSequence: Array = [f, f, f, f, f, f, a, d, e, f, g, g, g, g, e, e, d, b, a, f, d, c] // Nota actual que se está reproduciendo en privado var curnote: Número = 0 // Secuencia de demora que la música debe tener entre las notas private var noteDelay: Array = [100, 100, 300, 100, 100, 300, 100, 100 , 100,100,200, 100, 100, 200, 90, 100, 90,100, 100, 120, 120, 120, 120, 300] // Temporizador para reproducir la música privada tun tunímetro: Temporizador = nuevo Temporizador (noteDelay [0]);

Paso 21: Principal ()

La funcion principal

 // La función principal espera a que la línea de tiempo principal se agregue a la función pública de la etapa Main (): void addEventListener (Event.ADDED_TO_STAGE, addedToStage); 

Paso 22: Inicializar

Después de agregarse a la etapa, inicializaremos el fondo y el teclado virtual:

 // cuando se agrega al escenario, podemos establecer un modo de escala de escenario, el fondo y el inicio de la función privada de piano virtual addedToStage (e: Event): void stage.scaleMode = StageScaleMode.NO_SCALE; addBackground (); startMachine (); 

Paso 23: Efectos de fondo

Vamos a resolver el fondo en movimiento y la nieve:

 // agrega la función privada de fondo addBackground (): void _background = new Background (150,150); // la razón por la cual el tamaño es más pequeño es porque requiere mucha CPU _background.x = 5; // para dar un margen blanco para el marco _background.y = 5; _background.width = 570 // amplíelo al tamaño correcto _background.height = 350 frame_mc.addChild (_background); // agrega fondo al marco snow = new Snow (570, 350) // crea una instancia de tormenta de nieve

Paso 24: Teclado virtual

La inicialización del teclado virtual.

 private function startMachine (e: MouseEvent = null): void // asocia los eventos del teclado stage.addEventListener (KeyboardEvent.KEY_DOWN, onkeyDown) stage.addEventListener (KeyboardEvent.KEY_UP, onkeyUp) // asocia un método de reproducción automática al botón de reproducción automática. .addEventListener (MouseEvent.CLICK, startAutoPlay); autoplay_mc.buttonMode = true; // asocia las notas a las teclas var i: int = 0 while (i < 7)  this["key"+i].note = notes[i] i++  //makes the highlight of the keys disappear lowlightKeys(); 

Paso 25: Resaltar

Necesitamos crear una función para eliminar el resaltado de las teclas:

 función privada lowlightKeys () var i: int = 0 while (i < 7)  TweenLite.to(this["key" + i].glow_mc, 0.5,alpha:0 ); i++  

Paso 26: Eventos clave

Ahora manejemos los eventos Key up y Key down:

 función privada onkeyDown (e: KeyboardEvent): void var i: int = 0 switch (e.keyCode) caso 49: // código de tecla para 1 i = 0 break; caso 50: // código clave para 2 i = 1 interrupción; caso 51: // código clave para 3 i = 2 break; caso 52: // código clave para 4 i = 3 break; caso 53: // código clave para 5 i = 4 break; caso 54: // código clave para 6 i = 5 break; caso 55: // código clave para 7 i = 6 break;  notas [i] .play (); TweenLite.to (esta ["clave" + i] .glow_mc, 0.5, alpha: 1); // resalta la tecla función privada onkeyUp (e: KeyboardEvent): void var i: int = 0 switch (e.keyCode) case 49: i = 0 break; caso 50: i = 1 descanso; caso 51: i = 2 ruptura; caso 52: i = 3 ruptura; caso 53: i = 4 ruptura; caso 54: i = 5 ruptura; caso 55: i = 6 descanso;  TweenLite.to (esta ["clave" + i] .glow_mc, 0.5, alpha: 0); // resalta la tecla

Paso 27: Funciones de reproducción automática

Cómo iniciar y detener la reproducción automática.

 función privada startAutoPlay (e: MouseEvent = null) curnote = 0; // cada vez que comienzo la reproducción automática, reinicio la nota de reproducción actual tunetimer.delay = noteDelay [curnote] * 3 // esto aumenta el retardo establecido previamente tunetimer.addEventListener (TimerEvent.TIMER, autoPlayTune) // agrega un oyente al evento del temporizador para cada vez que se activa el temporizador tunetimer.start () // inicia el temporizador función privada stopAutoPlay (e: MouseEvent = null) tunetimer.stop () // detiene el temporizador tunetimer.removeEventListener (TimerEvent.TIMER, autoPlayTune) // elimina la evento

Paso 28: Actualizar

Actualización del teclado virtual junto con la música.

 función privada updateMachine (): void lowlightKeys (); // restablece las teclas resaltadas var i: int = 0 while (i < 7)  if (this["key" + i].note == noteSequence[curnote])  TweenLite.to(this["key" + i].glow_mc, 0.5,alpha:1 ); //if current note is the one associeated with the key then highlights it  i++  curnote++ //goes to next note if (curnote > noteSequence.length) curnote = 0; // restablece la nota actual stopAutoPlay (); // detiene la reproducción automática

Paso 29: El Código Completo

Aquí está el código completo de Main.as

 paquete import flash.display.MovieClip; import flash.events.Event; import flash.events.MouseEvent; import flash.events.TimerEvent; importar flash.display.StageScaleMode; import flash.events.KeyboardEvent; import flash.utils.Timer; import com.greensock.TweenLite; import com.greensock.easing. *; Importar fondo; importar nieve import flash.media.Sound; clase pública Main extiende MovieClip private var _background: Background; nieve privada var: nieve; [Incrustar (fuente = "activos / A.mp3")] privado var A: Clase; var a privado: Sonido = nuevo A () como sonido; [Incrustar (fuente = "activos / B.mp3")] privado var B: Class; var privado: sonido = nuevo B () como sonido; [Incrustar (fuente = "activos / C.mp3")] privado var C: Class; var privado: sonido = nuevo C () como sonido; [Incrustar (fuente = "activos / D.mp3")] privado var D: Class; var privado: sonido = nuevo D () como sonido; [Incrustar (fuente = "activos / E.mp3")] privado var E: Class; Variedad privada: Sonido = nuevo E () como sonido; [Incrustar (fuente = "activos / F.mp3")] privado var F: Class; privado var f: Sound = new F () as Sound; [Incrustar (fuente = "activos / G.mp3")] privado var G: Class; var var privado: Sonido = nuevo G () como sonido; Notas de var privado: Array = [c, d, e, f, g, a, b] Nota de var privada: Array = [f, f, f, f, f, f, a, d, e, f, g, g, g, g, g, e, e, d, b, a, f, d, c] private var curnote: Número = 0 private var noteDelay: Array = [100, 100, 300, 100, 100, 300, 100, 100, 100,100,200, 100, 100, 200, 90, 100, 90,100, 100, 120, 120, 120, 120, 300] tunetímero de var privado: Temporizador = nuevo Temporizador (noteDelay [0]); función pública Main (): void addEventListener (Event.ADDED_TO_STAGE, addedToStage);  función privada addedToStage (e: Event): void stage.scaleMode = StageScaleMode.NO_SCALE; addBackground (); startMachine ();  // agrega la función privada de fondo addBackground (): void _background = new Background (150,150); _background.x = 5; _background.y = 5; _background.width = 570 _background.height = 350 frame_mc.addChild (_background); nieve = nieve nueva (570, 350); frame_mc.addChild (snow);  función privada startMachine (e: MouseEvent = null): void stage.addEventListener (KeyboardEvent.KEY_DOWN, onkeyDown) stage.addEventListener (KeyboardEvent.KEY_UP, onkeyUp) autoplay_mc.buttonMode = true; var i: int = 0 while (i < 7)  this["key"+i].note = notes[i] i++  lowlightKeys();  private function lowlightKeys()  var i:int = 0 while (i < 7)  TweenLite.to(this["key" + i].glow_mc, 0.5,alpha:0 ); i++   private function onkeyDown(e:KeyboardEvent):void  var i:int=0 switch(e.keyCode)  case 49: i=0 break; case 50: i=1 break; case 51: i=2 break; case 52: i=3 break; case 53: i=4 break; case 54: i=5 break; case 55: i=6 break;  notes[i].play(); TweenLite.to(this["key" + i].glow_mc, 0.5,alpha:1 );  private function onkeyUp(e:KeyboardEvent):void  var i:int=0 switch(e.keyCode)  case 49: i=0 break; case 50: i=1 break; case 51: i=2 break; case 52: i=3 break; case 53: i=4 break; case 54: i=5 break; case 55: i=6 break;  TweenLite.to(this["key" + i].glow_mc, 0.5,alpha:0 );  //AUTO PLAY FUNCTIONS private function startAutoPlay(e:MouseEvent = null)  curnote = 0; tunetimer.delay = noteDelay[curnote] * 3 tunetimer.addEventListener(TimerEvent.TIMER, autoPlayTune) tunetimer.start()  private function stopAutoPlay(e:MouseEvent = null)  tunetimer.stop() tunetimer.removeEventListener(TimerEvent.TIMER, autoPlayTune)  private function autoPlayTune(e:TimerEvent)  if(curnote noteSequence.length) curnote = 0; stopAutoPlay (); 

Paso 30: Clase de fondo

Ahora en la clase de fondo. Comenzaremos importando estas clases ...

 importar flash.display.Shape; import flash.events.Event; importar flash.display.Sprite; importar flash.display.MovieClip; importar flash.display.Bitmap; importar flash.display.BitmapData; importar flash.display.BlendMode; importar flash.geom.ColorTransform; import flash.geom.Rectangle; importar flash.geom.Point; importar flash.geom.Matrix; importar flash.filters.ColorMatrixFilter; importar flash.display.GradientType; importar flash.display.Graphics; importar flash.display.InterpolationMethod; importar flash.display.SpreadMethod;

Paso 31: Variables

… Luego definiendo las variables:

 // Noise private var dir: Array private var punto: Point private var bd: BitmapData; privado var bmp: mapa de bits; private var bdmultiply: BitmapData; var bms privados: Sprite; private var rect: Rectangle private var cmf: ColorMatrixFilter; private var w: Number private var h: Number // Linear Gradient Fill private var gshape: Shape private var gmetrics: Rectangle private var gmatrix: Matrix private var gtype: String private var gspread: String private var ginterpolation: String private var gcolours: Array private var galphas: Array private var gratios: Array // Solid Fill private var sshape: Shape

Paso 32: Función inicial

Aquí está la función inicial:

 Fondo de la función pública ($ ancho: Número = 100, $ altura: Número = 100) w = $ ancho h = $ altura rect = nuevo Rectángulo (0, 0, w, h); punto = nuevo punto (0, 0); dir = [punto nuevo (1, 262), punto nuevo (400, 262)]; // este es solo para dar un fondo sólido a toda la etapa initBackgroundSolid (); // esto controlará el contraste y la saturación del ruido initColorMatrix (); // habrá dos fondos de ruido, esto los iniciará initBackgroundNoise (); // se agrega un gradiente para que no nos pongamos ruidosos ... (¿lo entiendes? ¿demasiado ruido te hace ruidoso? jaja ... hmmm) initBackgroundGradient (); 

Paso 33: initColorMatrix ()

Esta función controlará el contraste y la saturación del ruido, es un filtro muy potente..

 función privada initColorMatrix (): void cmf = new ColorMatrixFilter ([2, 0, 0, 0, -20, // red 0, 2, 0, 0, -20, // green 0, 0, 2, 0, -20, // azul 0, 0, 0, 1, -20]); // alfa

Paso 34: Color de fondo sólido

Este es solo para dar un sólido fondo a toda la etapa..

 función privada initBackgroundSolid (): void sshape = new Shape (); sshape.graphics.beginFill (0x170a02,1) sshape.graphics.drawRect (0, 0, w, h); sshape.graphics.endFill (); addChild (sshape)

Paso 35: ruidos

Los ruidos:

 función privada initBackgroundNoise (): void // first noise bd = new BitmapData (w, h, false, 0); bmp = nuevo mapa de bits (bd); bmp.smoothing = true; addChild (bmp); // segundo ruido que se superpone al primer ruido a través de un modo de mezcla de superposición bdmultiply = new BitmapData (w, h, false, 0); bms = nuevo Sprite (); bms.addChild (nuevo mapa de bits (bdmultiply)) addChild (bms) bms.blendMode = "overlay"; // representa el fondo de manera que el ruido parece estar moviendo addEventListener (Event.ENTER_FRAME, renderBG); 

Paso 36: Máscara

Aquí está la máscara de degradado:

 función privada initBackgroundGradient () // esta es una caja de gradiente básica con alfa y girada 90º para que comience desde arriba hacia abajo en lugar de izquierda-derecha gshape = new Shape (); gmetrics = new Rectangle (); gmatrix = nueva matriz (); gtype = GradientType.LINEAR; gspread = SpreadMethod.PAD; ginterpolation = InterpolationMethod.LINEAR_RGB; gcolours = [0x170a02, 0x170a02]; galphas = [0, 1]; gratios = [0, 255]; gmatrix.createGradientBox (w, h, (Math.PI / 180) * 90); gshape.graphics.clear (); gshape.graphics.beginGradientFill (gtype, gcolours, galphas, gratios, gmatrix, gspread, ginterpolation); gshape.graphics.drawRect (0, 0, w, h); gshape.graphics.endFill (); addChild (gshape)

Paso 37: Render

Es tiempo de render!

 función privada renderBG (evento: Evento): void // actualiza noise direction dir [0] .x- = 1.5 dir [0] .y- = 0 // estos están aquí para que juegue con dir [1] .x - = 0 // estos están aquí para que juegues con dir [1] .y - = 0 // estos están aquí para que juegues con // define el primer mapa de bits de fondo para tener un ruido perlin bd.perlinNoise (w, h, 2, 10, falso, verdadero, 7, verdadero, dir); // tiempo de coloración (jugar con estos valores) bd.colorTransform (rect, new ColorTransform (1, 0.7, 0.5)); // aplica el contraste de brillo y las modificaciones de saturación realizadas anteriormente bd.applyFilter (bd, rect, point, cmf) // el otro ruido perlin bdmultiply.perlinNoise (w, h, 3, 21, false, true, 7, true, dir ) // los otros colors perlin noise bdmultiply.colorTransform (rect, new ColorTransform (1, 0.6, 0.4)); 

Paso 38: Completa la clase de fondo

Aquí está toda la clase de fondo:

 paquete import flash.display.Shape; import flash.events.Event; importar flash.display.Sprite; importar flash.display.MovieClip; importar flash.display.Bitmap; importar flash.display.BitmapData; importar flash.display.BlendMode; importar flash.geom.ColorTransform; import flash.geom.Rectangle; importar flash.geom.Point; importar flash.geom.Matrix; importar flash.filters.ColorMatrixFilter; importar flash.display.GradientType; importar flash.display.Graphics; importar flash.display.InterpolationMethod; importar flash.display.SpreadMethod; la clase pública Fondo amplía MovieClip private var dir: Array private var point: Point private var bd: BitmapData; privado var bmp: mapa de bits; private var bdmultiply: BitmapData; var bms privados: Sprite; private var rect: Rectangle private var cmf: ColorMatrixFilter; private var w: Number private var h: Number private var gshape: Shape private var gmetrics: Rectangle private var gmatrix: Matrix private var gtype: String private var gspread: String private var ginterpolation: String private var gcolours: Array private var gifs: Array private var gratios: Array private var sshape: Shape public function Background ($ width: Number = 100, $ height: Number = 100) w = $ width h = $ height rect = new Rectangle (0, 0, w, h) ; punto = nuevo punto (0, 0); dir = [punto nuevo (1, 262), punto nuevo (400, 262)]; initBackgroundSolid (); initColorMatrix (); initBackgroundNoise (); initBackgroundGradient ();  función privada initColorMatrix (): void cmf = nuevo ColorMatrixFilter ([2, 0, 0, 0, -20, // rojo 0, 2, 0, 0, -20, // verde 0, 0, 2, 0 , -20, // azul 0, 0, 0, 1, -20]); // alpha función privada initBackgroundSolid (): void sshape = new Shape (); sshape.graphics.beginFill (0x170a02,1) sshape.graphics.drawRect (0, 0, w, h); sshape.graphics.endFill (); addChild (sshape) función privada initBackgroundNoise (): void bd = new BitmapData (w, h, false, 0); bmp = nuevo mapa de bits (bd); bmp.smoothing = true; addChild (bmp); bdmultiply = new BitmapData (w, h, false, 0); bms = nuevo Sprite (); bms.addChild (nuevo mapa de bits (bdmultiply)) addChild (bms) bms.blendMode = "overlay"; addEventListener (Event.ENTER_FRAME, renderBG);  función privada initBackgroundGradient () gshape = new Shape (); gmetrics = new Rectangle (); gmatrix = nueva matriz (); gtype = GradientType.LINEAR; gspread = SpreadMethod.PAD; ginterpolation = InterpolationMethod.LINEAR_RGB; gcolours = [0x170a02, 0x170a02]; galphas = [0, 1]; gratios = [0, 255]; gmatrix.createGradientBox (w, h, (Math.PI / 180) * 90); gshape.graphics.clear (); gshape.graphics.beginGradientFill (gtype, gcolours, galphas, gratios, gmatrix, gspread, ginterpolation); gshape.graphics.drawRect (0, 0, w, h); gshape.graphics.endFill (); addChild (gshape) función privada renderBG (evento: Evento): void dir [0] .x- = 1.5 dir [0] .y- = 0 dir [1] .x- = 0 dir [1] .y - = 0 bd.perlinNoise (w, h, 2, 10, false, true, 7, true, dir); bd.colorTransform (rect, nuevo ColorTransform (1, 0.7, 0.5)); bd.applyFilter (bd, rect, point, cmf) bdmultiply.perlinNoise (w, h, 3, 21, false, true, 7, true, dir) bdmultiply.colorTransform (rect, new ColorTransform (1, 0.6, 0.4)) ; 

Paso 39: Nieve

La clase de nieve no es mía, fue escrita por Troy Gardner, simplemente la adapté de la línea de tiempo a un paquete, por eso no estoy comentando el código. Crea un "Snow.as" y copia este código dentro.

 paquete import flash.display.MovieClip; import flash.events.Event; import flash.utils.Dictionary; La clase pública Snow extiende MovieClip var snowflakes: Array = new Array (); var snowflakeProps: Dictionary = new Dictionary (true); var max_snowsize: Number = .04; // pixels var snowflakesCnt: Number = 150; var oheight: Número; var owidth: Número; Función pública Snow ($ width, $ height): void owidth = $ width; oheight = $ altura; // cantidad para (var i: int = 0; ioight + 10) dO.y = -20;  if (dO.x> owidth + 20) dO.x = - (owidth / 2) + Math.random () * (1.5 * owidth); dO.y = -20;  else if (dO.x<-20)  dO.x= -(owidth/2)+Math.random()*(1.5*owidth); dO.y = -20;      

Conclusión

Mis habilidades musicales no son las mejores, la música puede sonar un poco extraña. Oh, bueno, con este tutorial ahora deberías poder crear tus propias canciones con más notas y diferentes tonos :). Espero que te haya gustado este tutorial, encontrarás el código comentado y las versiones de cs4 y cs3 en el archivo zip. Gracias por leer!