Подтвердить что ты не робот

Java - чтение, управление и запись WAV файлов

В программе Java лучше всего читать аудиофайл (WAV файл) в массив чисел (float[], short[],...) и записать WAV файл из массива чисел?

4b9b3361

Ответ 1

Дополнительная информация о том, что вы хотели бы достичь, была бы полезна. Если необработанные данные WAV подходят для вас, просто используйте FileInputStream и, возможно, сканер, чтобы превратить его в числа. Но позвольте мне попытаться дать вам полезный пример кода, чтобы вы начали:

Для этой цели существует класс com.sun.media.sound.WaveFileWriter.

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

AudioInputStream in = AudioSystem.getAudioInputStream(in);

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

Вы можете реализовать свой собственный AudioInputStream, который делает все, что угодно, voodoo, чтобы превратить ваши массивы чисел в аудиоданные.

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

Как упоминалось @stacker, вы должны сами ознакомиться с API.

Ответ 2

Пакет javax.sound.sample не подходит для обработки файлов WAV, если вам нужен доступ к фактическим значениям выборки. Пакет позволяет вам изменять громкость, частоту дискретизации и т.д., Но если вам нужны другие эффекты (например, добавление эха), вы сами по себе. (В учебном пособии по Java намечено, что нужно обрабатывать значения выборки напрямую, но технический писатель перепрограммирован.)

Этот сайт имеет простой класс для обработки файлов WAV: http://www.labbookpages.co.uk/audio/javaWavFiles.html

Ответ 3

Я читаю файлы WAV через AudioInputStream. Следующий фрагмент из Java Sound Tutorials работает хорошо.

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 
      // now in the audioBytes array...
    }
  } catch (Exception ex) { 
    // Handle the error...
  }
} catch (Exception e) {
  // Handle the error...
}

Чтобы написать WAV, я нашел это довольно сложным. На поверхности это кажется круговой проблемой, команда, которая пишет, опирается на AudioInputStream как параметр.

Но как вы пишете байты в AudioInputStream? Не должно быть AudioOutputStream?

Я обнаружил, что можно определить объект, который имеет доступ к необработанным байтовым данным для реализации TargetDataLine.

Для этого требуется множество методов, но большинство из них может оставаться в фиктивной форме, поскольку они не требуются для записи данных в файл. Ключевым способом реализации является read(byte[] buffer, int bufferoffset, int numberofbytestoread).

Поскольку этот метод, вероятно, будет вызываться несколько раз, должна быть также переменная экземпляра, указывающая, насколько далеко продвинулись данные, и обновить это как часть вышеописанного метода read.

Когда вы внедрили этот метод, ваш объект может быть использован для создания нового AudioInputStream, который, в свою очередь, может использоваться с:

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

Напоминаем, что AudioInputStream может быть создан с TargetDataLine в качестве источника.

Что касается прямого управления данными, я имел хороший успех, действуя на данные в буфере в самой внутренней петле примера фрагмента выше, audioBytes.

Пока вы находитесь в этом внутреннем цикле, вы можете преобразовать байты в целые числа или поплавки и умножить значение volume (от 0.0 до 1.0), а затем преобразовать их обратно в маленькие байты.

Я считаю, что, поскольку у вас есть доступ к серии образцов в этом буфере, вы также можете задействовать различные формы алгоритмов фильтрации DSP на этом этапе. По моему опыту я обнаружил, что лучше делать изменения объема непосредственно на данных в этом буфере, потому что тогда вы можете сделать наименьший возможный прирост: одна дельта на образец, минимизируя вероятность кликов из-за разрывов, вызванных томом.

Я нахожу, что "контрольные строки" для объема, предоставленного Java, имеют тенденцию к ситуациям, когда скачки в томе будут вызывать клики, и я считаю, что это происходит потому, что дельта реализуется только при детализации одного прочитанного буфера (часто в диапазон одного изменения на 1024 выборки) вместо того, чтобы делить изменение на более мелкие куски и добавить их по одному на образец. Но я не знаю, как были реализованы регуляторы громкости, поэтому, пожалуйста, возьмите эту гипотезу с солью.

Всем и всем, Java.Sound - настоящая головная боль, чтобы понять. Я ошибаюсь в Tutorial, чтобы не включать явный пример записи файла непосредственно из байтов. Я виноват в учебнике по хорошему примеру воспроизведения "Воспроизведение файла" в разделе "Как преобразовать...". Однако в этом учебнике есть много полезной информации.


РЕДАКТИРОВАТЬ: 12/13/17

С тех пор я использовал следующий код для записи звука из файла PCM в своих собственных проектах. Вместо реализации TargetDataLine можно расширить InputStream и использовать его как параметр для метода 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 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);
    }
}

Исходные данные, которые будут экспортироваться здесь, представлены в виде стереопотоков в диапазоне от -1 до 1. Формат результирующего потока - 16 бит, стерео, мало-endian.

Я пропустил методы skip и markSupported для моего конкретного приложения. Но им не составит труда добавить их, если они понадобятся.

Ответ 5

Это исходный код для записи непосредственно в wav файл. Вам просто нужно знать математику и звуковую технику, чтобы произвести звук, который вы хотите. В этом примере уравнение вычисляет бинауральный ритм.

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();
    }

}

Если вы можете изменить это, чтобы создать суббас хип-хопа, который был бы классным, потому что в настоящее время я пытаюсь изменить эту программу.

Ответ 6

Прежде всего, вам может понадобиться знать заголовки и позиции данных структуры WAVE, здесь вы можете найти спецификацию здесь. Имейте в виду, что данные немногочисленны.

Там API, который может помочь вам достичь вашей цели.

Ответ 7

Волновые файлы поддерживаются пакет javax.sound.sample

Так как это не тривиальный API, вы должны прочитать статью/учебник, в котором представлен API, например

Звук Java, введение

Ответ 8

Я использую FileInputStream с некоторой магией:

    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();
    }

Ваши значения образцов находятся в short[] input!