Главная > Soft > Как записать звук на Java в byte массив

Как записать звук на Java в byte массив

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

Sound recording to byte array

На сайте уже есть пример про запись звука с помощью Java. Пример простой и пишет звук только в файл.

AudioSystem.write(m_audioInputStream, m_targetType, m_outputFile);

Для упрощение задачи можно просто прочитать потом файл в byte массив, но метод «AudioSystem.write» может вызываться и с stream в качестве последнего аргумента. Это бы сразу решило проблему, но не так всё просто. При попытке вызвать метод «write» с каким-либо stream происходит ошибка: stream length not specified.

AudioSystem.write(audio, AudioFileFormat::Type::WAVE, process.getOutputStream());

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

Проблему можно решить, если самостоятельно реализовать AudioSystem.write для WAVE формата.

Начать нужно с класса, который будет отвечать за формирование корректного заголовка WAVE формата:

package info.privateblog.sound.wave;

import java.io.ByteArrayOutputStream;
import java.io.IOException;

public class NewWaveWriter {
	private ByteArrayOutputStream outStream = new ByteArrayOutputStream();

    private int mSampleRate;
    private int mChannels;
    private int mSampleBits;

    private int mBytesWritten;

    public NewWaveWriter(int sampleRate) throws IOException {
        this.mSampleRate = sampleRate;
        this.mChannels = 2;
        this.mSampleBits = 16;

        this.mBytesWritten = 0;
        outStream.write(new byte[44]);
    }

    public void write(byte[] src, int offset, int length) throws IOException {
        if (offset > length) {
            throw new IndexOutOfBoundsException(String.format("offset %d is greater than length %d", offset, length));
        }
        for (int i = offset; i < length; i+=4) {
            writeUnsignedShortLE(src[i], src[i+1]);
            writeUnsignedShortLE(src[i + 2], src[i + 3]);
            mBytesWritten += 4;
        }
    }

    public byte[] getByteBuffer() throws IOException {
    	byte[]result = outStream.toByteArray();
    	writeWaveHeader(result);
    	return result;
    }

    private void writeWaveHeader(byte[]file) throws IOException {
        int bytesPerSec = (mSampleBits + 7) / 8;
        
        int position = 0;
        position = setValue(file, position, "RIFF");
        position = setValue(file, position, mBytesWritten + 36);
        position = setValue(file, position, "WAVE");
        position = setValue(file, position, "fmt ");
        position = setValue(file, position, (int)16);
        position = setValue(file, position, (short) 1);
        position = setValue(file, position, (short) mChannels);
        position = setValue(file, position, mSampleRate);
        position = setValue(file, position, mSampleRate * mChannels * bytesPerSec);
        position = setValue(file, position, (short) (mChannels * bytesPerSec));
        position = setValue(file, position, (short) mSampleBits);
        position = setValue(file, position, "data");
        position = setValue(file, position, mBytesWritten);
    }

    private void writeUnsignedShortLE(byte sample1, byte sample2)
            throws IOException {
    	outStream.write(sample1);
    	outStream.write(sample2);
    }
    
    private static int setValue(byte[]buffer, int position, String value) {
    	for (int i = 0; i< value.length(); i++) {     		
                buffer[position + i] = (byte)value.charAt(i);     	
        }     	
        return position + value.length();     
    }     
    private static int setValue(byte[]buffer, int position, int value) { 		
                buffer[position + 3] = (byte)(value>>24);
		buffer[position + 2] = (byte)(value>>16);
		buffer[position + 1] = (byte)(value>>8);
		buffer[position + 0] = (byte)(value);
    	return position + 4;
    }
    private static int setValue(byte[]buffer, int position, short value) {
		buffer[position + 1] = (byte)(value>>8);
		buffer[position] = (byte)value;
    	return position + 2;
    }
}

Данный класс работает только с двухканальным 16-битным звуком. Он предоставляет нам метод write и getByteBuffer и решает проблему с получением byte массива с записанным звуком без использования промежуточного файла.

Пример использования:

public void run() {  
	try {  
		writer = new NewWaveWriter(44100);
		
		byte[]buffer = new byte[256];
		int res = 0;
		while((res = m_audioInputStream.read(buffer)) > 0) {
			writer.write(buffer, 0, res);
		}
	} catch (IOException e) {
		System.out.println("Error: " + e.getMessage());
	}  
}  

public byte[]getResult() throws IOException {
	return writer.getByteBuffer();
}
Categories: Soft Tags: ,
  1. Пока что нет комментариев.
  1. Пока что нет уведомлений.