2010-07-21 19 views

Respuesta

7

Algunos con más detalle en lo que usted' Me gustaría lograr sería útil. Si los datos RAW en bruto son correctos para usted, simplemente use un FileInputStream y probablemente un Escáner para convertirlo en números. Pero déjame intentar darte un código de ejemplo significativo para que comiences:

Hay una clase llamada com.sun.media.sound.WaveFileWriter para este propósito.

InputStream in = ...; 
OutputStream out = ...; 

AudioInputStream in = AudioSystem.getAudioInputStream(in); 

WaveFileWriter writer = new WaveFileWriter(); 
writer.write(in, AudioFileFormat.Type.WAVE, outStream); 

Se podría implementar su propio AudioInputStream que hace lo vudú para convertir sus matrices numéricas en datos de audio.

writer.write(new VoodooAudioInputStream(numbers), AudioFileFormat.Type.WAVE, outStream); 

Como se mencionó @stacker, usted debe obtener familiarizarse con la API, por supuesto.

+0

Mi problema principal era ese vudú en sí mismo. Quería ver si estaba listo el código/clase que lo hizo. Creo que lo logré ahora, usando AudioSystem y AudioInputStream. El truco consistía en invertir el orden de los bytes en cada muestra de sonido antes de convertirlo a corto, ya que WAV codifica los valores numéricos en forma poco endian. Gracias, Yonatan. – yonatan

5

El paquete javax.sound.sample no es adecuado para procesar archivos WAV si necesita tener acceso a los valores de muestra reales. El paquete le permite cambiar el volumen, la frecuencia de muestreo, etc., pero si desea otros efectos (por ejemplo, agregar un eco), usted está solo. (El tutorial de Java insinúa que debería ser posible para procesar directamente los valores de la muestra, pero el escritor de tecnología promesas excesivas.)

Este sitio tiene una clase simple para los archivos de procesamiento WAV: http://www.labbookpages.co.uk/audio/javaWavFiles.html

+1

+1 para el enlace a la clase WavFile. Realmente no es mucho más simple que eso. – Grodriguez

5

leí archivos WAV a través de una AudioInputStream. El siguiente fragmento del Java Sound Tutorials funciona bien.

int totalFramesRead = 0; 
File fileIn = new File(somePathName); 
// somePathName is a pre-existing string whose value was 
// based on a user selection. 
try { 
    AudioInputStream audioInputStream = 
    AudioSystem.getAudioInputStream(fileIn); 
    int bytesPerFrame = 
    audioInputStream.getFormat().getFrameSize(); 
    if (bytesPerFrame == AudioSystem.NOT_SPECIFIED) { 
    // some audio formats may have unspecified frame size 
    // in that case we may read any amount of bytes 
    bytesPerFrame = 1; 
    } 
    // Set an arbitrary buffer size of 1024 frames. 
    int numBytes = 1024 * bytesPerFrame; 
    byte[] audioBytes = new byte[numBytes]; 
    try { 
    int numBytesRead = 0; 
    int numFramesRead = 0; 
    // Try to read numBytes bytes from the file. 
    while ((numBytesRead = 
     audioInputStream.read(audioBytes)) != -1) { 
     // Calculate the number of frames actually read. 
     numFramesRead = numBytesRead/bytesPerFrame; 
     totalFramesRead += numFramesRead; 
     // Here, do something useful with the audio data that's 
     // now in the audioBytes array... 
    } 
    } catch (Exception ex) { 
    // Handle the error... 
    } 
} catch (Exception e) { 
    // Handle the error... 
} 

Para escribir un WAV, encontré eso bastante complicado. En la superficie parece un problema circular, el comando que escribe se basa en un AudioInputStream como parámetro.

Pero, ¿cómo se escriben los bytes en un AudioInputStream? ¿No debería haber un AudioOutputStream?

Lo que encontré fue que uno puede definir un objeto que tiene acceso a los datos de bytes de audio sin procesar para implementar TargetDataLine.

Esto requiere implementar muchos métodos, pero la mayoría puede permanecer en forma ficticia ya que no son necesarios para escribir datos en un archivo. El método clave para implementar es read(byte[] buffer, int bufferoffset, int numberofbytestoread).

Como este método probablemente se llamará varias veces, también debe haber una variable de instancia que indique qué tan lejos avanzaron los datos, y actualizar eso como parte del método anterior read.

Cuando haya implementado este método, entonces el objeto se puede utilizar para crear un nuevo AudioInputStream que a su vez se puede utilizar con:

AudioSystem.write(yourAudioInputStream, AudioFileFormat.WAV, yourFileDestination) 

A modo de recordatorio, un AudioInputStream se pueden crear con un TargetDataLine como fuente.

En cuanto a la manipulación de la directa de los datos, que han tenido buen éxito que actúa sobre los datos en el búfer en el bucle más interior del fragmento de ejemplo anterior, audioBytes.

Mientras estás en ese bucle interno, puede convertir los bytes en enteros o flotadores y se multiplica un valor volume (que van desde 0.0 a 1.0) y luego convertirlos de nuevo a pequeños bytes endian.

Creo que dado que tiene acceso a una serie de muestras en ese búfer, también puede utilizar varios algoritmos de filtrado DSP en esa etapa. En mi experiencia, he descubierto que es mejor hacer cambios de volumen directamente en los datos en este búfer porque entonces puede hacer el incremento más pequeño posible: un delta por muestra, minimizando la posibilidad de clics debido a discontinuidades inducidas por el volumen.

Encuentro que las "líneas de control" para el volumen proporcionado por Java tienden a situaciones donde los saltos en volumen causarán clics, y creo que esto se debe a que los deltas solo se implementan en la granularidad de una sola lectura de tampón (a menudo en el rango de un cambio por 1024 muestras) en lugar de dividir el cambio en piezas más pequeñas y agregarlas una por muestra. Pero no estoy al tanto de cómo se implementaron los Controles de Volumen, así que por favor tome esa conjetura con un grano de sal.

En general, Java.Sound ha sido un verdadero dolor de cabeza para descubrir. Me falla el tutorial por no incluir un ejemplo explícito de escribir un archivo directamente desde bytes. Me falla el tutorial para enterrar el mejor ejemplo de la codificación Reproducir un archivo en la sección "Cómo convertir ...". Sin embargo, hay MUCHA información valiosa GRATIS en ese tutorial.


EDIT: 12/13/17

Desde entonces, he utilizado el siguiente código para escribir un archivo de audio de PCM en mis propios proyectos. En lugar de implementar TargetDataLine, se puede ampliar InputStream y usar eso como un parámetro para el método AudioInputStream.write.

public class StereoPcmInputStream extends InputStream 
{ 
    private float[] dataFrames; 
    private int framesCounter; 
    private int cursor; 
    private int[] pcmOut = new int[2]; 
    private int[] frameBytes = new int[4]; 
    private int idx; 

    private int framesToRead; 

    public void setDataFrames(float[] dataFrames) 
    { 
     this.dataFrames = dataFrames; 
     framesToRead = dataFrames.length/2; 
    } 

    @Override 
    public int read() throws IOException 
    { 
     while(available() > 0) 
     { 
      idx &= 3; 
      if (idx == 0) // set up next frame's worth of data 
      { 
       framesCounter++; // count elapsing frames 

       // scale to 16 bits 
       pcmOut[0] = (int)(dataFrames[cursor++] * Short.MAX_VALUE); 
       pcmOut[1] = (int)(dataFrames[cursor++] * Short.MAX_VALUE); 

       // output as unsigned bytes, in range [0..255] 
       frameBytes[0] = (char)pcmOut[0]; 
       frameBytes[1] = (char)(pcmOut[0] >> 8); 
       frameBytes[2] = (char)pcmOut[1]; 
       frameBytes[3] = (char)(pcmOut[1] >> 8); 

      } 
      return frameBytes[idx++]; 
     } 
     return -1; 
    } 

    @Override 
    public int available() 
    { 
     // NOTE: not concurrency safe. 
     // 1st half of sum: there are 4 reads available per frame to be read 
     // 2nd half of sum: the # of bytes of the current frame that remain to be read 
     return 4 * ((framesToRead - 1) - framesCounter) 
       + (4 - (idx % 4)); 
    }  

    @Override 
    public void reset() 
    { 
     cursor = 0; 
     framesCounter = 0; 
     idx = 0; 
    } 

    @Override 
    public void close() 
    { 
     System.out.println(
      "StereoPcmInputStream stopped after reading frames:" 
       + framesCounter); 
    } 
} 

La fuente de datos a ser exportados aquí es en forma de estéreo flotadores van desde -1 a 1. El formato de la corriente resultante es de 16 bits, estéreo, poco-endian.

Omití skip y markSupported métodos para mi aplicación particular. Pero no debería ser difícil agregarlos si son necesarios.

2

En primer lugar, es posible que necesite conocer los encabezados y las posiciones de datos de una estructura WAVE, puede encontrar la especificación here. Tenga en cuenta que los datos son poco endian.

Hay un API que puede ayudarle a lograr su objetivo.

0

utilizo FileInputStream con un poco de magia:

byte[] byteInput = new byte[(int)file.length() - 44]; 
    short[] input = new short[(int)(byteInput.length/2f)]; 


    try{ 

     FileInputStream fis = new FileInputStream(file); 
     fis.read(byteInput, 44, byteInput.length - 45); 
     ByteBuffer.wrap(byteInput).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(input); 

    }catch(Exception e ){ 
     e.printStackTrace(); 
    } 

Sus valores de las muestras están en short[] input!

+0

lo que significa: file.length() - 44 ... ¿cómo llegaste a esos números –

+0

qué tamaño sería que int [] ??? –

4

Este es el código fuente para escribir directamente en un archivo wav. Solo necesita saber las matemáticas y la ingeniería de sonido para producir el sonido que desea. En este ejemplo, la ecuación calcula un ritmo binaural.

import java.io.ByteArrayInputStream; 
import java.io.File; 
import java.io.IOException; 
import javax.sound.sampled.AudioFileFormat; 
import javax.sound.sampled.AudioFormat; 
import javax.sound.sampled.AudioInputStream; 
import javax.sound.sampled.AudioSystem; 

public class Example 
{ 
    public static void main(String[] args) throws IOException { 

    double sampleRate = 44100.0; 
    double frequency = 440; 
    double frequency2 = 90; 
    double amplitude = 1.0; 
    double seconds = 2.0; 
    double twoPiF = 2 * Math.PI * frequency; 
    double piF = Math.PI * frequency2; 
    float[] buffer = new float[(int) (seconds * sampleRate)]; 
    for (int sample = 0; sample < buffer.length; sample++) 
    { 
     double time = sample/sampleRate; 
     buffer[sample] = (float) (amplitude * Math.cos((double)piF *time)* Math.sin(twoPiF * time)); 
    } 
    final byte[] byteBuffer = new byte[buffer.length * 2]; 
    int bufferIndex = 0; 
    for (int i = 0; i < byteBuffer.length; i++) { 
    final int x = (int) (buffer[bufferIndex++] * 32767.0); 
    byteBuffer[i] = (byte) x; 
    i++; 
    byteBuffer[i] = (byte) (x >>> 8); 
    } 
    File out = new File("out10.wav"); 
    boolean bigEndian = false; 
    boolean signed = true; 
    int bits = 16; 
    int channels = 1; 
    AudioFormat format; 
    format = new AudioFormat((float)sampleRate, bits, channels, signed, bigEndian); 
    ByteArrayInputStream bais = new ByteArrayInputStream(byteBuffer); 
    AudioInputStream audioInputStream; 
    audioInputStream = new AudioInputStream(bais, format,buffer.length); 
    AudioSystem.write(audioInputStream, AudioFileFormat.Type.WAVE, out); 
    audioInputStream.close(); 
    } 

} 

Si usted podría modificar esto para crear un sub bajo el hip hop que sería genial, porque eso es lo que actualmente estoy tratando de modificar este programa para hacerlo.

Cuestiones relacionadas