Módulo Sound
Sound es un pequeño chip de sonido: cuatro canales de tono, uno de ruido y dos de sonidos grabados (muestras PCM), más un secuenciador que toca la música del Music Tracker. Cada orden (una nota, un efecto) se mezcla con lo que ya suena, sin parar tu programa.
use Sound
Llama a Sound.init() una vez, al empezar: sin ella no suena nada.
Plataformas
| Windows | ESP32 / ESP32-S3 | ESP32-P4 | ESP32-S31 | |
|---|---|---|---|---|
| Por dónde suena | La tarjeta de sonido del PC | La salida del perfil de la placa: I2S, buzzer o ninguna (null) | La salida del perfil de la placa | La salida del perfil de la placa |
Sound.SAMPLE_RATE | 22050 | La del perfil: 22050 o 44100 | La del perfil: 22050 o 44100 | La del perfil: 22050 o 44100 |
La salida de cada placa, sus pines y su frecuencia de muestreo se eligen en su perfil: lo cuenta Pantalla, sonido, SD y red. Si el perfil tiene el sonido apagado, Sound.init() devuelve false y no suena nada. En Windows, SAMPLE_RATE vale 44100 si la variable de entorno JARU_AUDIO_SAMPLE_RATE vale 44100; otro valor se ignora.
Una canción o un efecto suenan igual a 22050 y a 44100: el tono de las notas, el tempo, las envolventes y el ruido no dependen de la frecuencia de muestreo. 44100 solo da un sonido más limpio en los agudos y en las muestras, a cambio del doble de trabajo de mezcla.
Con el buzzer (un altavoz que da un solo tono), el módulo hace menos:
- Suena un canal a la vez: el de número más bajo de los que suenan, del 0 al 4.
- Todas las ondas suenan como una cuadrada, con la frecuencia redondeada a hercios enteros. El volumen, y el ciclo de
pulse(), cambian lo fuerte que suena. - El ruido suena como un tono de 80 a 4000 Hz, según su periodo.
- Las rampas (
fade,slideFreq,slideDuty,slideNoise) saltan directamente al valor final. envelope,setRelease,vibrato,pwmytremolono hacen nada, aunque devuelventrue.- No hay muestras ni música:
loadSample,playSample,unloadSampley las funciones de la música devuelvenfalse.
Con null no suena nada: las funciones aceptan las órdenes y devuelven true, e isPlaying() e isMusicPlaying() devuelven false.
El bloque audio del boot.cfg
La salida de la placa se elige en la página Audio de ESP32 Tools, que la escribe en el bloque audio del boot.cfg (ver Pantalla, sonido, SD y red). En Windows no se usa. Estos son sus campos:
| Campo | Qué es | De serie |
|---|---|---|
enable | Enciende el sonido. Apagado, Sound.init() devuelve false. | false |
backend | La salida: i2s, buzzer o null. | null |
sample_rate | 22050 o 44100. Con otro valor, la VM avisa al arrancar y usa 22050. | 22050 |
block_frames | Las muestras que mezcla de una vez, de 64 a 512 (fuera de ese rango se ajusta). Con menos, los efectos suenan antes, pero hay menos margen contra los cortes. | 512 en ESP32 Tools (256 si falta en el boot.cfg) |
i2s_port | El puerto I2S. | 0 |
mclk, bclk, ws, dout, din | Los pines del bus I2S; -1 es ninguno. | -1 |
buzzer_pin | El pin del buzzer. Con -1, usa el de dout. | -1 |
stereo | Manda la mezcla, que es mono, por los dos canales. | true |
mclk_multiple | Cuántas veces la frecuencia de muestreo va el reloj MCLK: 256 o 384, y además 128 en el ESP32 y el S3, o 512 en el P4 y el S31. Otro valor cuenta como 256. | 256 |
task_core | El núcleo de la tarea que mezcla el sonido: "0", "1" o "opposite", el contrario al de la VM. | "opposite" |
task_priority, task_stack | La prioridad de esa tarea (0 cuenta como 5) y su pila, en bytes (como poco, 4096). | 5, 6144 |
Los canales
| Canal | Constante | Qué suena | Sus funciones |
|---|---|---|---|
| 0 a 3 | Tonos: cuadrada, triangular, sierra, pulso y seno | tone, pulse, slideFreq, slideDuty, vibrato, pwm, tremolo, fade, envelope, setRelease | |
| 4 | Ruido | noise, slideNoise, tremolo, fade, envelope, setRelease | |
| 5 y 6 | S0, S1 | Muestras PCM | playSample |
stop, setVolume e isPlaying valen para los siete. La música usa los canales 0 a 4: ver La música y los efectos.
Volumen, duración y prioridad
- El volumen va de 0 a 255 (
Sound.MAX_VOLUME). Entone,pulse,noisey las funciones de volumen, un valor fuera se ajusta; enfadeyenvelope, es un error. - La duración va en milisegundos, de 0 a 65535. Con 0, que es lo de serie, la nota suena hasta
stop(). Se cuenta en pasos de 1/240 de segundo, redondeando hacia arriba. - La prioridad va de 0 a 255, y de serie es 0. Una nota nueva en un canal que suena se descarta si la que suena tiene más prioridad; la función devuelve
trueigualmente. Cuando una nota con duración acaba, o constop(), el canal vuelve a prioridad 0.
Lo que devuelven las funciones
Casi todas devuelven true si aceptan la orden y false si no pueden cumplirla (sin init(), sin memoria, con el buzzer lo que no tiene). Con un argumento mal no lanzan un error: lo devuelven, como valor:
- con un número de argumentos que no es el suyo,
ERR_INVALID_ARG_NUMBER; - con un argumento de otro tipo,
ERR_TYPE_MISMATCH; - con un canal, una frecuencia, una onda, un ciclo, un periodo, una duración o una prioridad fuera de rango,
ERR_INVALID_VALUE.
try no captura esos errores, y en un if un valor de error cuenta como verdadero. Para saber si una orden ha ido bien, compárala con true:
use Sound
Sound.init()
var ok = Sound.tone(9, 440, 200) // no hay canal 9
if (ok != true) then
println("No ha sonado: ", ok) // No ha sonado: Error 8
end
Más sobre los valores de error en Errores y excepciones.
Funciones
| Función | Qué hace | Devuelve |
|---|---|---|
init() | Arranca el sonido. | true o false |
close() | Para el sonido y descarga las muestras y la música. | true |
tone(canal, frecuencia, volumen, ...) | Toca una nota. | true |
pulse(canal, frecuencia, volumen, ciclo, ...) | Toca una nota de onda de pulso. | true |
noise(volumen, periodo, ...) | Toca ruido. | true |
stop(canal) | Calla un canal. | true |
stopAll() | Calla todos, en seco. | true |
setVolume(canal, volumen) | Cambia el volumen de lo que suena en un canal. | true |
setMasterVolume(volumen) | Cambia el volumen de todo. | true |
isPlaying(canal) | Dice si un canal suena. | true o false |
fade(canal, volumen, ms) | Lleva el volumen de un canal a otro poco a poco. | true |
slideFreq(canal, frecuencia, ms) | Desliza el tono de un canal. | true |
slideDuty(canal, ciclo, ms) | Desliza el ciclo de una onda de pulso. | true |
slideNoise(periodo, ms) | Desliza el periodo del ruido. | true |
envelope(canal, ataque, caída, sostenido, liberación) | Pone la envolvente de un canal. | true |
setRelease(canal, ms) | Pone solo la liberación de un canal. | true |
vibrato(canal, profundidad, ritmo, retardo) | Hace temblar el tono. | true |
pwm(canal, profundidad, ritmo, retardo) | Hace temblar el ciclo de una onda de pulso. | true |
tremolo(canal, profundidad, ritmo, retardo) | Hace temblar el volumen. | true |
loadSample(id, bytes, frecuencia) | Carga un sonido grabado. | true o false |
playSample(canal, id, frecuencia, bucle) | Toca un sonido grabado. | true o false |
unloadSample(id) | Descarga un sonido grabado. | true o false |
loadMusic(fichero o bytes) | Carga una canción. | true o false |
playMusic(bucle) | Toca la canción desde el principio. | true o false |
seekMusic(ms) | Salta a un punto de la canción. | true o false |
pauseMusic() | Pausa la canción. | true |
resumeMusic() | Sigue con la canción. | true |
stopMusic() | Para la canción. | true |
setMusicVolume(volumen) | Cambia el volumen de la canción. | true |
isMusicPlaying() | Dice si la canción suena. | true o false |
unloadMusic() | Descarga la canción. | true o false |
init
init() arranca la salida de sonido y devuelve true. Devuelve false si no puede: en el PC, sin tarjeta de sonido; en la placa, con el sonido apagado en el perfil o si la salida no arranca.
Sin init() no suena nada: las órdenes se guardan, y cuando hay 128 sin tocar, las funciones empiezan a devolver false.
close
close() para la salida de sonido y descarga las muestras y la música. Devuelve true. Para que vuelva a sonar, llama otra vez a init() y vuelve a cargar lo que necesites.
tone
tone(canal, frecuencia, volumen, onda, duración, prioridad) toca una nota en un canal de tono. Los tres últimos argumentos son opcionales.
use Sound
Sound.init()
Sound.tone(0, 440, 200) // un la que suena hasta stop(0)
Sound.tone(1, 65.406, 180, Sound.TRIANGLE, 500) // un do grave, medio segundo
Sound.tone(2, 880, 255, Sound.SQUARE, 100, 5) // un pitido con prioridad 5
pause(600)
Sound.stop(0)
| Parámetro | Qué es |
|---|---|
canal | De 0 a 3. |
frecuencia | En hercios, de más de 0 a 65535. Admite decimales, para afinar con precisión: 65.406. |
volumen | De 0 a 255. Fuera de ese rango se ajusta. |
onda | SQUARE (la de serie), TRIANGLE, SAW, PULSE (con el ciclo al 50 %) o SINE. |
duración | En milisegundos, de 0 a 65535. Con 0, la de serie, suena hasta stop(). |
prioridad | De 0 a 255; de serie, 0. |
La nota sustituye a la que sonara en el canal, salvo que esa tenga más prioridad. Si el canal tiene envolvente o LFO, la nota los usa.
pulse
pulse(canal, frecuencia, volumen, ciclo, duración, prioridad) toca una nota de onda de pulso. ciclo es la parte del periodo que la onda está arriba, en tanto por ciento, de 1 a 99: con 50 es una cuadrada, y cuanto más se aleja de 50, más fino suena. La duración y la prioridad son opcionales, como en tone().
use Sound
Sound.init()
Sound.pulse(0, 330, 200, 12, 400) // ciclo del 12 %, 400 ms
pause(500)
Un ciclo fuera de 1 a 99 da ERR_INVALID_VALUE.
noise
noise(volumen, periodo, modo, duración, prioridad) toca ruido en el canal 4. Los tres últimos argumentos son opcionales.
use Sound
Sound.init()
Sound.noise(255, 6, Sound.NOISE_WHITE, 400) // una explosión corta
pause(500)
| Parámetro | Qué es |
|---|---|
volumen | De 0 a 255. Fuera de ese rango se ajusta. |
periodo | De 1 a 65535. El ruido cambia cada periodo ciclos de un reloj de 22050 Hz (el mismo a 44100): cuanto mayor, más grave. |
modo | NOISE_WHITE (el de serie), NOISE_PERIODIC, NOISE_SHORT o NOISE_DARK: ver Constantes. |
duración | En milisegundos, de 0 a 65535. Con 0, suena hasta stop(4). |
prioridad | De 0 a 255; de serie, 0. |
stop
stop(canal) calla un canal, del 0 al 6. Si el canal tiene liberación (envelope o setRelease), la nota se apaga poco a poco; si no, en seco.
stopAll
stopAll() calla los siete canales, en seco, sin liberación. La canción no se para: sigue y vuelve a sonar en su siguiente nota. Para pararla, stopMusic().
setVolume
setVolume(canal, volumen) cambia el volumen de lo que suena en un canal, del 0 al 6. El volumen va de 0 a 255; fuera de ese rango se ajusta. Un canal que no suena no cambia, aunque la función devuelve true.
En los canales de muestras (S0, S1), el volumen se queda para los sonidos siguientes: un sonido grabado suena al volumen que tenía su canal, o a 255 si estaba a 0 (la primera vez). Cámbialo después de playSample().
setMasterVolume
setMasterVolume(volumen) cambia el volumen de todo lo que suena, música incluida, de 0 a 255. Fuera de ese rango se ajusta.
isPlaying
isPlaying(canal) devuelve true mientras el canal suena: una nota, la cola de su liberación, una muestra o una nota de la canción. Con un canal que no existe, devuelve false.
use Sound
Sound.init()
Sound.tone(0, 440, 200, Sound.SQUARE, 100)
pause(50)
println(Sound.isPlaying(0)) // true
pause(200)
println(Sound.isPlaying(0)) // false
Las órdenes se aplican en el siguiente bloque de sonido: justo después de tone(), isPlaying() puede dar todavía false durante unos milisegundos.
fade
fade(canal, volumen, ms) lleva el volumen de lo que suena en un canal, del 0 al 4, hasta volumen (de 0 a 255) en ms milisegundos (de 0 a 65535).
use Sound
Sound.init()
Sound.tone(0, 220, 255)
Sound.fade(0, 0, 1000) // se apaga en un segundo
pause(1100)
Sound.stop(0) // fade no calla el canal: hay que pararlo
Un fade a 0 deja el canal sonando en silencio: isPlaying() sigue dando true hasta stop(). Con un canal que no suena, no hace nada.
slideFreq
slideFreq(canal, frecuencia, ms) desliza el tono de la nota que suena en un canal, del 0 al 3, hasta frecuencia (de más de 0 a 65535 Hz, con decimales) en ms milisegundos. Es el portamento.
use Sound
Sound.init()
Sound.tone(0, 1200, 200, Sound.SAW, 300)
Sound.slideFreq(0, 200, 300) // un disparo láser que baja
pause(400)
La siguiente nota del canal cancela el deslizamiento. Con un canal que no suena, no hace nada.
slideDuty
slideDuty(canal, ciclo, ms) desliza el ciclo de la onda de pulso que suena en un canal, del 0 al 3, hasta ciclo (de 1 a 99) en ms milisegundos.
slideNoise
slideNoise(periodo, ms) desliza el periodo del ruido que suena en el canal 4 hasta periodo (de 1 a 65535) en ms milisegundos.
envelope
envelope(canal, ataque, caída, sostenido, liberación) pone la envolvente de un canal, del 0 al 4: cómo sube, baja y se apaga el volumen de cada nota.
use Sound
Sound.init()
Sound.envelope(0, 10, 150, 128, 300) // ataque rápido, baja a la mitad, cola de 300 ms
Sound.tone(0, 440, 220, Sound.SQUARE, 400)
pause(800)
| Parámetro | Qué es |
|---|---|
canal | De 0 a 4. |
ataque | Milisegundos que tarda la nota en subir hasta su volumen. |
caída | Milisegundos que tarda en bajar hasta el sostenido. |
sostenido | El nivel en que se queda mientras dura la nota, de 0 a 255: una parte del volumen de la nota (255 es todo). |
liberación | Milisegundos que tarda en apagarse al acabar la nota o con stop(). |
Los tiempos van de 0 a 65535. La envolvente se queda en el canal para las notas siguientes, hasta que la cambies. Con el ataque y la caída a 0, la nota suena a su volumen hasta que acaba.
setRelease
setRelease(canal, ms) pone solo la liberación de un canal, del 0 al 4: los milisegundos (de 0 a 65535) que tarda en apagarse una nota al acabar o con stop(). No cambia el resto de la envolvente.
vibrato
vibrato(canal, profundidad, ritmo, retardo) hace temblar el tono de un canal, del 0 al 3.
use Sound
Sound.init()
Sound.vibrato(0, 80, 60, 150) // un tercio de la profundidad, 6 Hz, tras 150 ms
Sound.tone(0, 523.25, 200, Sound.SINE, 800)
pause(900)
| Parámetro | Qué es |
|---|---|
profundidad | De 0 a 255. Con 255, el tono sube y baja un semitono; con 0, el vibrato se apaga. |
ritmo | En décimas de hercio, de 0 a 255: 60 son 6 temblores por segundo. |
retardo | Opcional: los milisegundos (de 0 a 65535) que espera cada nota antes de empezar a temblar. |
pwm
pwm(canal, profundidad, ritmo, retardo) hace temblar el ciclo de la onda de pulso de un canal, del 0 al 3. Solo se oye con pulse() o con la onda PULSE. Con profundidad 255, el ciclo sube y baja 40 puntos; los demás argumentos son como los de vibrato().
tremolo
tremolo(canal, profundidad, ritmo, retardo) hace temblar el volumen de un canal, del 0 al 4 (también el ruido). Con profundidad 255, el volumen baja hasta el silencio en cada temblor; nunca sube por encima del de la nota. Los demás argumentos son como los de vibrato().
vibrato, pwm y tremolo son ajustes del canal: valen para la nota que suena y para las siguientes, hasta que los cambies (con profundidad 0 se apagan). Cada nota nueva empieza su temblor desde el principio, con su retardo. playMusic() y seekMusic() los borran de los canales 0 a 4, porque la música trae los suyos en cada instrumento.
loadSample
loadSample(id, bytes, frecuencia) carga un sonido grabado en una de las 32 ranuras (Sound.MAX_SAMPLES).
| Parámetro | Qué es |
|---|---|
id | La ranura, de 0 a 31. Si ya tenía un sonido, lo sustituye. |
bytes | Un Bytes con el sonido: un byte por muestra, en PCM de 8 bits con signo y mono. Cada byte va de -128 a 127. |
frecuencia | Las muestras por segundo del sonido: 8000, 11025, 22050... Mayor que 0. |
Devuelve true, o false si no queda memoria (o con el buzzer). Copia los bytes: después puedes reutilizar el Bytes. Una ranura fuera de 0 a 31, un Bytes vacío o una frecuencia de 0 dan ERR_INVALID_VALUE, y algo que no es un Bytes, ERR_TYPE_MISMATCH.
El método write(i, valor) de un Bytes guarda el byte bajo del número, así que un valor de -128 a 127 queda tal cual como muestra:
use Sound, Bytes
Sound.init()
// 0,2 s de diente de sierra, a 8000 muestras por segundo
var muestra = Bytes.new(1600)
for (var i = 0; i < 1600; i++)
muestra.write(i, (i % 32) * 8 - 128)
end
println(Sound.loadSample(0, muestra, 8000)) // true
loadSample no lee ficheros WAV ni ningún otro formato: solo los bytes que le das.
playSample
playSample(canal, id, frecuencia, bucle) toca el sonido de una ranura en un canal de muestras.
use Sound, Bytes
Sound.init()
var muestra = Bytes.new(1600)
for (var i = 0; i < 1600; i++)
muestra.write(i, (i % 32) * 8 - 128)
end
Sound.loadSample(0, muestra, 8000)
Sound.playSample(Sound.S0, 0, 0, false) // a su frecuencia: 0,2 s
pause(300)
Sound.playSample(Sound.S1, 0, 16000, false) // al doble: una octava más aguda y 0,1 s
pause(200)
| Parámetro | Qué es |
|---|---|
canal | Sound.S0 (5) o Sound.S1 (6). |
id | La ranura, de 0 a 31. |
frecuencia | Las muestras por segundo a las que lo toca. Con 0, las de loadSample. Con el doble, suena una octava más aguda y dura la mitad. |
bucle | true para que se repita hasta stop(). También vale un entero: 0 es false. |
Devuelve true, o false si la ranura está vacía (o con el buzzer). Un canal que no es 5 ni 6, una ranura fuera de 0 a 31 o una frecuencia negativa dan ERR_INVALID_VALUE. Lo que sonaba en el canal se corta.
unloadSample
unloadSample(id) descarga el sonido de una ranura, de 0 a 31, y libera su memoria. Devuelve true, o false si la ranura ya estaba vacía.
loadMusic
loadMusic(fichero) o loadMusic(bytes) carga una canción .jmu, el formato que genera el Music Tracker al compilar. No la toca: para eso, playMusic().
use Sound
Sound.init()
if (Sound.loadMusic("Music/Tema.jmu")) then
Sound.playMusic(true)
end
- La ruta es la de la canción en el proyecto, con
.jmu:Music/Tema.jmsse carga como"Music/Tema.jmu". También vale con unidad:"flash:Music/Tema.jmu","sd:Music/Tema.jmu"(las unidades, en File). - Con un
Bytes, carga sus bytes, que tienen que ser los de un.jmu. Los copia: después puedes reutilizar elBytes. - Devuelve
true, ofalsesi el fichero no existe, pasa de 65535 bytes, no es un.jmude esta versión o no queda memoria (y con el buzzer). Con otro tipo,ERR_TYPE_MISMATCH. - Solo hay una canción cargada: cargar otra para y sustituye la que hubiera.
- No lee WAV, MP3, OGG ni otros formatos de sonido. Para sonidos grabados,
loadSample.
playMusic
playMusic() toca la canción cargada desde el principio. Sin argumento, se repite si en el tracker tiene Loop song; playMusic(true) y playMusic(false) lo deciden por ti. Con bucle, al llegar al final vuelve al punto de bucle de la canción; sin bucle, se para.
Devuelve true, o false si no hay canción cargada (o con el buzzer).
Al empezar, calla los canales 0 a 4 (también los efectos que sonaran en ellos), les borra la envolvente y los LFO, y vuelve a poner el volumen de la canción.
seekMusic
seekMusic(ms) mueve la canción al punto que está a ms milisegundos del principio. playMusic() siempre empieza desde el principio: para empezar (o saltar) a otro punto, usa seekMusic(). Copy ms (seekMusic), en el tracker, copia al portapapeles los milisegundos en los que empieza la fila del cursor.
seekMusic() reconstruye cómo estaría la canción en ese punto (el instrumento de cada canal, el tempo, el transporte, el volumen, las envolventes y el vibrato) y conserva lo que estaba haciendo:
| Al llamarla, la canción está | Resultado |
|---|---|
| Sonando | Salta a ms y sigue sonando. |
| Pausada | Se queda en ms, pausada; resumeMusic() sigue desde ahí. |
| Parada (cargada sin arrancar) | Se queda en ms, pausada; resumeMusic() la arranca desde ahí. |
use Sound
Sound.init()
Sound.loadMusic("Music/Tema.jmu")
// Empezar la canción en el segundo 15
Sound.seekMusic(15000)
Sound.resumeMusic()
use Sound
Sound.init()
Sound.loadMusic("Music/Tema.jmu")
// Saltar mientras suena (por ejemplo, al estribillo)
Sound.playMusic(true)
Sound.seekMusic(30000) // salta a 0:30 y sigue
- Cuenta los cambios de tempo de la canción.
- Si
mspasa de lo que dura: si la canción tiene bucle, da la vuelta; si no, se queda al final. - Tras el salto, los canales se quedan en silencio hasta la siguiente nota.
- Llamar a
playMusic()después deseekMusic()vuelve a empezar desde el principio. Para arrancar desde el punto buscado,resumeMusic(). - Devuelve
true, ofalsesi no hay canción. Conmsnegativo,ERR_INVALID_VALUE.
pauseMusic
pauseMusic() pausa la canción y calla sus notas: con resumeMusic(), sigue en su siguiente nota. Devuelve true.
resumeMusic
resumeMusic() sigue con la canción pausada, o arranca la que dejó armada seekMusic(). Devuelve true.
stopMusic
stopMusic() para la canción y calla los canales 0 a 4, también los efectos que sonaran en ellos. La canción sigue cargada: playMusic() la vuelve a empezar. Devuelve true.
setMusicVolume
setMusicVolume(volumen) cambia el volumen de la canción, de 0 a 255 (fuera de ese rango se ajusta). Multiplica el volumen de cada canal de la canción. Devuelve true.
- Se oye desde la siguiente nota de cada canal, no en la que suena.
playMusic()yseekMusic()vuelven a poner el volumen de la canción, y la canción también lo cambia si lleva un comando de volumen. Llama asetMusicVolume()después deplayMusic().
isMusicPlaying
isMusicPlaying() devuelve true si la canción suena: ni parada, ni pausada.
unloadMusic
unloadMusic() para la canción y libera su memoria. Devuelve true, o false si no había ninguna cargada.
La música y los efectos
La canción y tus efectos comparten los canales 0 a 4. Los de muestras, S0 y S1, son solo tuyos.
- La canción toca sus notas con prioridad 0. Un efecto con más prioridad (y con duración) se queda el canal mientras suena, y la canción vuelve en su siguiente nota. Un efecto con prioridad 0 suena hasta la siguiente nota de la canción en ese canal.
playMusic()ystopMusic()callan los canales 0 a 4, con los efectos que tuvieran.- La canción pone sus instrumentos: mientras suena, un efecto en un canal de la canción usa la envolvente y los LFO del instrumento de ese canal. Al empezar,
playMusic()borra los tuyos.
Para que los efectos no peleen con la música:
- tócalos en un canal que la canción no use, o en
S0yS1si son sonidos grabados; - o dales prioridad y duración:
Sound.tone(3, 880, 255, Sound.SQUARE, 120, 10).
Cómo se escribe la canción, sus canales y sus instrumentos: Music Tracker.
Constantes
| Constante | Valor | Qué es |
|---|---|---|
Sound.SQUARE | 0 | Onda cuadrada. |
Sound.TRIANGLE | 1 | Onda triangular: suave. |
Sound.SAW | 2 | Onda de sierra: brillante. |
Sound.PULSE | 3 | Onda de pulso: con tone(), al 50 %; el ciclo se elige con pulse(). |
Sound.SINE | 4 | Onda senoidal: la más pura. |
Sound.NOISE_WHITE | 0 | Ruido blanco. |
Sound.NOISE_PERIODIC | 1 | Ruido que repite un dibujo corto: suena con algo de tono. |
Sound.NOISE_SHORT | 2 | Ruido de secuencia corta: metálico. |
Sound.NOISE_DARK | 3 | Ruido blanco filtrado: más apagado. |
Sound.S0 | 5 | El primer canal de muestras. |
Sound.S1 | 6 | El segundo canal de muestras. |
Sound.MAX_VOLUME | 255 | El volumen más alto. |
Sound.MAX_SAMPLES | 32 | Las ranuras de loadSample. |
Sound.SAMPLE_RATE | 22050 o 44100 | Las muestras por segundo a las que mezcla el sonido: ver Plataformas. |
Ejemplo completo
Una prueba de sonido: una melodía con envolvente, un láser, una explosión y un sonido grabado, uno detrás de otro.
use Sound, Bytes
if (Sound.init() != true) then
println("No hay sonido")
exit(0)
end
// Una melodía en el canal 0, con ataque corto y cola
Sound.envelope(0, 5, 80, 160, 120)
var notas = [523.25, 659.25, 783.99, 1046.5]
foreach (var nota in notas)
Sound.tone(0, nota, 200, Sound.PULSE, 150)
pause(180)
end
println("Melodía")
// Un láser: una sierra que baja
Sound.tone(1, 1500, 180, Sound.SAW, 250)
Sound.slideFreq(1, 150, 250)
pause(300)
println("Láser")
// Una explosión: ruido que se apaga
Sound.noise(255, 8, Sound.NOISE_DARK)
Sound.fade(4, 0, 600)
pause(650)
Sound.stop(4)
println("Explosión")
// Un sonido grabado: 0,1 s de cuadrada a 8000 muestras por segundo
var clic = Bytes.new(800)
for (var i = 0; i < 800; i++)
if ((i div 10) % 2 == 0) then
clic.write(i, 100)
else
clic.write(i, -100)
end
end
Sound.loadSample(0, clic, 8000)
Sound.playSample(Sound.S0, 0, 0, false)
pause(200)
println("Muestra")
Sound.close()
Lo que escribe:
Melodía
Láser
Explosión
Muestra
Trampas y problemas conocidos
- Sin
Sound.init()no suena nada. Las órdenes se guardan, y a las 128 las funciones devuelvenfalse. - Los errores no se lanzan: se devuelven.
tryno los captura, y en unifun valor de error cuenta como verdadero: compara contrue(ver Lo que devuelven las funciones). - Una nota descartada por prioridad también devuelve
true. fade()a 0 no calla el canal:isPlaying()sigue entruehastastop().playMusic()ystopMusic()cortan los efectos de los canales 0 a 4, yplayMusic()les borra la envolvente y los LFO.setMusicVolume()antes deplayMusic()se pierde.close()descarga las muestras y la música.- Las rampas solo actúan sobre lo que suena: con un canal parado,
fadey losslideno hacen nada, aunque devuelventrue. Los LFO y la envolvente, en cambio, se quedan para la siguiente nota.
Todos los problemas conocidos de JARU, juntos, están en Problemas conocidos.