¿Java ha incorporado bibliotecas para audio _synthesis_?

Nota: NO quiero “leer el archivo de audio foo.bar y reproducirlo”.

Quiero generar programáticamente archivos de audio sobre la marcha y reproducirlos.

¿Tiene Java incorporado en las bibliotecas para esto, o esto cae en las bibliotecas dependientes del sistema?

¡Gracias!

Esta publicación del foro de Sun tiene un código interesante para generar tonos de sintonía. Además, dado que el formato de archivo WAV no es demasiado complicado, puede crear una tabla que represente la forma de onda deseada y luego escribirla en un archivo. Hay algunos ejemplos alrededor, por ejemplo, un convertidor de audio en bruto y cómo escribir un archivo wav .

Usando el enfoque de Andrew , aquí hay un ejemplo que juega una escala de temperamento igual .

import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.SourceDataLine; public class Tone { public static void main(String[] args) throws LineUnavailableException { final AudioFormat af = new AudioFormat(Note.SAMPLE_RATE, 8, 1, true, true); SourceDataLine line = AudioSystem.getSourceDataLine(af); line.open(af, Note.SAMPLE_RATE); line.start(); for (Note n : Note.values()) { play(line, n, 500); play(line, Note.REST, 10); } line.drain(); line.close(); } private static void play(SourceDataLine line, Note note, int ms) { ms = Math.min(ms, Note.SECONDS * 1000); int length = Note.SAMPLE_RATE * ms / 1000; int count = line.write(note.data(), 0, length); } } enum Note { REST, A4, A4$, B4, C4, C4$, D4, D4$, E4, F4, F4$, G4, G4$, A5; public static final int SAMPLE_RATE = 16 * 1024; // ~16KHz public static final int SECONDS = 2; private byte[] sin = new byte[SECONDS * SAMPLE_RATE]; Note() { int n = this.ordinal(); if (n > 0) { double exp = ((double) n - 1) / 12d; double f = 440d * Math.pow(2d, exp); for (int i = 0; i < sin.length; i++) { double period = (double)SAMPLE_RATE / f; double angle = 2.0 * Math.PI * i / period; sin[i] = (byte)(Math.sin(angle) * 127f); } } } public byte[] data() { return sin; } } 

Este enfoque de bajo nivel puede ser adecuado para plataformas más antiguas y menos capaces. También considere javax.sound.midi ; aquí se muestra un ejemplo completo y el tutorial de Sonido sintetizado se cita aquí .

La forma más fácil de hacerlo es con las bibliotecas MIDI de java:

 int channel = 0; // 0 is a piano, 9 is percussion, other channels are for other instruments int volume = 80; // between 0 et 127 int duration = 200; // in milliseconds try { Synthesizer synth = MidiSystem.getSynthesizer(); synth.open(); MidiChannel[] channels = synth.getChannels(); // -------------------------------------- // Play a few notes. // The two arguments to the noteOn() method are: // "MIDI note number" (pitch of the note), // and "velocity" (ie, volume, or intensity). // Each of these arguments is between 0 and 127. channels[channel].noteOn( 60, volume ); // C note Thread.sleep( duration ); channels[channel].noteOff( 60 ); channels[channel].noteOn( 62, volume ); // D note Thread.sleep( duration ); channels[channel].noteOff( 62 ); channels[channel].noteOn( 64, volume ); // E note Thread.sleep( duration ); channels[channel].noteOff( 64 ); Thread.sleep( 500 ); // -------------------------------------- // Play a C major chord. channels[channel].noteOn( 60, volume ); // C channels[channel].noteOn( 64, volume ); // E channels[channel].noteOn( 67, volume ); // G Thread.sleep( 3000 ); channels[channel].allNotesOff(); Thread.sleep( 500 ); synth.close(); } catch (Exception e) { e.printStackTrace(); } 

Capacidades midi incorporadas de Java

El Java 8 JRE estándar tiene definitivamente lo que usted solicitó específicamente: una biblioteca de sintetizadores incorporada. Se describe con cierto detalle en Sintetizar sonido .

Un ejemplo bastante refinado proporciona un acceso de teclado visual a un sintetizador de música muestreado.

La biblioteca javax.sound.midi contiene instrumentos y la capacidad de reproducir notas sobre ellos, en base a la tecnología de instrumento MIDI y de muestra. Los sonidos no son tan auténticos como los de la línea clásica de instrumentos musicales Kurzweil, pero el marco admite ese nivel de sofisticación si desea hacer su propio muestreo en múltiples rangos de tono para un solo instrumento y resolver los detalles de una transición bastante fluida entre rangos.

Ejemplo trivial para una vista rápida del uso

Aquí hay una clase de ejemplo trivial.

 import javax.sound.midi.MidiSystem; import javax.sound.midi.Synthesizer; import javax.sound.midi.MidiChannel; public class PlayMidiNote { private static void sleep(int iUSecs) { try { Thread.sleep(iUSecs); } catch (InterruptedException e) { } } public static void main(String[] args) throws Exception { if (args.length < 3 && args.length > 4) { System.out.println("usage: java PlayNote <8.bit.midi.note.number> <8.bit.velocity>  []"); System.exit(1); } int iMidiKey = Math.min(127, Math.max(0, Integer.parseInt(args[0]))); int iVelocity = Math.min(127, Math.max(0, Integer.parseInt(args[1]))); int iUSecsDuration = Math.max(0, Integer.parseInt(args[2])); int iChannel = args.length > 3 ? Math.min(15, Math.max(0, Integer.parseInt(args[3]))) : 0; Synthesizer synth = MidiSystem.getSynthesizer(); synth.open(); MidiChannel[] channels = synth.getChannels(); MidiChannel channel = channels[iChannel]; channel.noteOn(iMidiKey, iVelocity); sleep(iUSecsDuration); channel.noteOff(iMidiKey); synth.close(); } } 

El uso de multi-threading o una implementación de javax.sound.midi.Sequencer como los disponibles en GitHub.com proporcionará la estructura necesaria para realmente hacer música.

Generación de forma de onda

Si desea generar sus propias formas de onda en lugar de utilizar muestras, la respuesta a su pregunta es “No”, sin embargo, puede reproducir con este sintetizador de tonos que escribí para esta pregunta. Tiene varias características.

  • Control de tono (en ciclos por segundo)
  • Control de amplitud (en pasos digitales)
  • Estadísticas mín. Y máx. Para indicar cuando la amplitud es demasiado alta (lo que provoca la distorsión del tono del recorte de audio)
  • Control de la duración del tono (en segundos)
  • Control de la amplitud relativa de un número arbitrario de armónicos (que determina la calidad del tono o la forma de onda, que es uno de varios factores que crean el timbre de una nota musical)

Este sintetizador carece de muchas características de sintetizadores de generación de onda de alto nivel.

  • Modificación en tiempo real de la amplitud del armónico principal y otros armónicos durante la duración de una nota
  • No reproduce secuencias de notas (pero podría modificarse para hacerlo con relativa facilidad)
  • Tampoco existe la posibilidad de cambiar de fase los armónicos (pero eso es una deficiencia algo irrelevante, ya que las características de fase de los armónicos no son algo que el oído humano sea capaz de detectar)

SimpleSynth es una buena demostración de

  • Los principios de la síntesis de audio
  • El efecto tímbrico de los armónicos
  • El uso de Java para generar matrices de muestra de audio
  • La colocación de muestras en una matriz de bytes
  • La presentación de dicho conjunto de bytes al sistema operativo a través de una línea

La terminología de audio digital estándar se usó para nombres constantes y variables. La matemática se explica en los comentarios.

 import javax.sound.sampled.AudioSystem; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.SourceDataLine; class SimpleSynth { private static int SAMPLE_BITS = 16; private static int CHANNELS = 1; private static boolean SIGNED_TRUE = true; private static boolean BIG_ENDIAN_FALSE = false; private static float CDROM_SAMPLE_FREQ = 44100; private SourceDataLine line; private double dDuration; private double dCyclesPerSec; private double dAmplitude; private double[] adHarmonics; private double dMin; private double dMax; public SimpleSynth(String[] asArguments) throws Exception { dDuration = Double.parseDouble(asArguments[0]); dCyclesPerSec = Double.parseDouble(asArguments[1]); dAmplitude = Double.parseDouble(asArguments[2]); adHarmonics = new double[asArguments.length - 3]; for (int i = 0; i < adHarmonics.length; ++ i) adHarmonics[i] = Double.parseDouble( asArguments[i + 3]); AudioFormat format = new AudioFormat( CDROM_SAMPLE_FREQ, SAMPLE_BITS, CHANNELS, SIGNED_TRUE, BIG_ENDIAN_FALSE); line = AudioSystem.getSourceDataLine(format); line.open(); line.start(); } public void closeLine() { line.drain(); line.stop(); } public void playSound() { // allocate and prepare byte buffer and its index int iBytes = (int) (2.0 * (0.5 + dDuration) * CDROM_SAMPLE_FREQ); byte[] ab = new byte[iBytes]; int i = 0; // iterate through sample radian values // for the specified duration short i16; double dSample; double dRadiansPerSample = 2.0 * Math.PI * dCyclesPerSec / CDROM_SAMPLE_FREQ; double dDurationInRadians = 2.0 * Math.PI * dCyclesPerSec * dDuration; dMin = 0.0; dMax = 0.0; for (double d = 0.0; d < dDurationInRadians; d += dRadiansPerSample) { // add principle and the dot product of harmonics // and their amplitudes relative to the principle dSample = Math.sin(d); for (int h = 0; h < adHarmonics.length; ++ h) dSample += adHarmonics[h] * Math.sin((h + 2) * d); // factor in amplitude dSample *= dAmplitude; // adjust statistics if (dMin > dSample) dMin = dSample; if (dMax < dSample) dMax = dSample; // store in byte buffer i16 = (short) (dSample); ab[i ++] = (byte) (i16); ab[i ++] = (byte) (i16 >> 8); } // send the byte array to the audio line line.write(ab, 0, i); } public void printStats() { System.out.println("sample range was [" + dMin + ", " + dMax + "]" + " in range of [-32768, 32767]"); if (dMin < -32768.0 || dMax > 32767.0) System.out.println("sound is clipping" + "(exceeding its range)," + " so use a lower amplitude"); } public static void main(String[] asArguments) throws Exception { if (asArguments.length < 3) { System.err.println("usage: java SimpleSynth" + " " + " " + " " + " [" + " [...]]"); System.err.println("pure tone:" + " java SimpleSynth 1 440 32767"); System.err.println("oboe-like:" + " java SimpleSynth 1 440 15000 0 1 0 .9"); System.err.println("complex:" + " java SimpleSynth 1 440 800 .3" + " .5 .4 .2 .9 .7 5 .1 .9 12 0 3" + " .1 5.2 2.5 .5 1 7 6"); System.exit(0); } SimpleSynth synth = new SimpleSynth(asArguments); synth.playSound(); synth.closeLine(); synth.printStats(); System.exit(0); } } 

Temas de estudio para boost la experiencia de síntesis de música

Hay algunos temas para leer si desea convertirse en un experto en sintetizador digital.

  • Audio digital
  • Muestreo de señal
  • Criterios de Nyquist
  • Cómo arrancar armónicos en un instrumento de cuerda (como una guitarra)
  • Trigonometría básica, específicamente la función sinusoidal

¿Has mirado a JSyn ? No creo que las bibliotecas Java Core puedan hacer lo que quieras.

Ver Java Sound API .

Mirando un poco más, también encontré a Jass .

Jcollider es una interfaz Java para el servidor de síntesis SuperCollider. Si quieres sintetizar música, esto facilitará las cosas (se abstrae del generador de tonos a un sintetizador, se ocupa de la generación de gráficos, elimina los sintetizadores silenciados del gráfico de síntesis hasta que se vuelven a necesitar, repara las señales entre sintetizadores). dinámicamente, etc.).