2010-01-14 12 views
16

Nota: NO quiero "leer el archivo de audio foo.bar y reproducirlo".¿Java tiene bibliotecas incorporadas para audio _synthesis_?

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

¿El Java han construido en las bibliotecas para esto, o lo hace este otoño en las bibliotecas dependientes del sistema?

Gracias!

Respuesta

0

This Sun forum post tiene algún código interesante para la generación de tonos pecado. 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, p. a raw audio converter y how to write a wav file.

+3

El enlace al foro de Sun está muerto. – RealHowTo

+0

IIRC, la discusión incluyó un [enfoque] (http://stackoverflow.com/a/7782749/230513) debido a [Andrew Thompson] (http://stackoverflow.com/users/418556/andrew-thompson), citado [aquí] (http://stackoverflow.com/a/2065693/230513). – trashgod

39

Usando Andrew's approach, he aquí un ejemplo que juega un equal tempered scale.

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; se muestra un ejemplo completo here y el tutorial Synthesizing Sound se cita here.

+0

El paquete Java javax.sound.sampled no es una biblioteca de síntesis. Simplemente facilita el paso de representaciones digitales que ya fueron sintetizadas al sistema de audio nativo del sistema operativo. –

+0

@DouglasDaseeco: Gracias por dibujar esta distinción; mientras que el que preguntaba quería "generar archivos de audio de manera programática sobre la marcha", he actualizado la respuesta para citar su razón de ser para considerar alternativas. – trashgod

+0

Gracias por apreciar la distinción. El asker no era muy específico acerca de si la biblioteca buscada era una biblioteca de generación de formas de onda o una biblioteca de tell-OS-to-play-waveform. Puede disfrutar probando un pequeño programa usando la biblioteca midi. Produce notas en la forma en que lo hace un sintetizador de muestreo. Menos útil para notas de órgano o sonido sintético, pero mucho mejor para producir los sonidos de instrumentos tradicionales. –

1

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.).

7

La forma más sencilla de hacerlo es con el de Java en las bibliotecas MIDI construidos:

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" (i.e., 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(); 
    } 
+0

¡Esta es una gran solución, voy a estar dando vueltas con esto mucho! Aclamaciones – Stevo

4

de Java incorporado en las capacidades Midi

El off-the-shelf Java 8 JRE definitivamente tiene lo que usted específicamente solicitado: una biblioteca de sintetizador incorporada. Se describe con cierto detalle en Synthesizing Sound.

Un quite refined example proporciona un acceso visual teclado a un sintetizador de música muestreada.

La biblioteca contiene instrumentos que javax.sound.midi y la capacidad de reproducir notas sobre ellos, basado en la tecnología MIDI y de instrumento muestreado. 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.

Trivial Ejemplo de Vista rápida del uso

He aquí un ejemplo trivial de clase.

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> 
        <usec.duration> [<midi.channel>]"); 
      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 múltiples hilos o un implementaciones de javax.sound.midi.Sequencer como los disponibles en GitHub.com proporcionará la estructura necesaria para hacer realidad la música.

forma de onda Generación

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

  • de control de tono (en ciclos por segundo)
  • de control de amplitud (en pasos digitales)
  • min y estadísticas max para indicar cuando la amplitud es demasiado alta (que provoca la distorsión del tono de recorte de audio)
  • control de la duración del tono (en segundos)
  • de control de la amplitud relativa de un número arbitrario de armónicos (que determina la calidad del tono o 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 de los principales armónicos armónicos y otros sobre la duración de una nota
  • No juega secuencias de notas (pero podría ser modificado para hacerlo con relativa facilidad)
  • n facilidad de desplazamiento de fase de los armónicos o bien (pero que es una deficiencia tanto irrelevante, ya que las características de fase de los armónicos no es algo que el oído humano es capaz de detectar)

SimpleSynth es una buena demostración de

  • Los principios de la síntesis de audio
  • El efecto timbral de 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 un byte tales matriz al sistema operativo a través de una línea

La terminología de audio digital estándar se utilizaba 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" 
        + " <duration>" 
        + " <tone.cycles.per.sec>" 
        + " <amplitude>" 
        + " [<relative.amplitude.harmonic.2>" 
        + " [...]]"); 
      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 aumentar la síntesis de la música Experiencia

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

  • digital de audio
  • señal de muestreo
  • criterios de Nyquist
  • Cómo desplumar armónicos en un instrumento de cuerda (como una guitarra)
  • trigonometría básica, específicamente la función seno
Cuestiones relacionadas