Потоковое воспроизведение с микрофона, добавление эффекта и сохранение в файл WAV с помощью библиотеки Android Tarsos
Примечания: я использую android studio и в настоящее время использую новейшую аудиотеку tarsos, которая должна быть совместимой с android, и фактически успешно добавила библиотеку в мой проект android studio. Ранее я безуспешно пытался использовать библиотеки JTransforms и Minim. РЕДАКТИРОВАНИЕ 23.08.17: обнаружены и исправлены некоторые ошибки, размещен текущий код, но по-прежнему нет прогресса в решении настоящей проблемы, кратко изложенной ниже:
Резюме: в пятом блоке кода, который я разместил, в строке 15, которая закомментирована, мне нужно знать, как заставить эту строку работать, а не выдавать ошибку компиляции
Я пытаюсь сделать запись с микрофона, а во время записи использовать фильтр dsp BandPass из библиотеки tarsos и вывести результаты в файл.wav. Я могу успешно транслировать микрофон в файл.wav, следуя этому руководству, используя импорт android.media, но это не позволяет мне добавить фильтр BandPass, а использование функций импорта tarsos не позволяет мне использовать сохранение как и методы.wav, которые есть в этом руководстве, я знаю, что что-то упустил и / или что-то делаю не так, но я гуглял это почти неделю и не нашел работающего решения, я нашел только ссылки на java-файлы, которые находятся внутри библиотеки, что бесполезно, так как я не смог найти учебники о том, как правильно их использовать. Что я делаю неправильно? Вот соответствующий код для метода tarsos, который я пытаюсь использовать:
связанный импорт и "глобальные" переменные
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.media.AudioFormat;
import android.media.AudioTrack;
import be.tarsos.dsp.AudioDispatcher;
import be.tarsos.dsp.AudioProcessor;
import be.tarsos.dsp.filters.BandPass;
import be.tarsos.dsp.io.android.AudioDispatcherFactory;
//start the class
AudioRecord alteredRecord = null;
AudioDispatcher dispatcher;
float freqChange;
float tollerance;
private static final int RECORDER_BPP = 16;
private static final String AUDIO_RECORDER_FOLDER = "Crowd_Speech";
private static final String AUDIO_RECORDER_TEMP_FILE = "record_temp.raw";
private static final int RECORDER_SAMPLERATE = 44100;
private static final int RECORDER_CHANNELS = AudioFormat.CHANNEL_IN_MONO;
private static final int RECORDER_AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT;
private int bufferSize = 1024;
private Thread recordingThread = null;
//set the min buffer size in onCreate event
bufferSize = AudioRecord.getMinBufferSize(RECORDER_SAMPLERATE,
RECORDER_CHANNELS, RECORDER_AUDIO_ENCODING)*4;
Это запускает запись микрофона внутри метода onClick, и, комментируя / раскомментируя одно из 2 "работающих" значений переменных, я могу переключаться между фильтром или без фильтра (функции Android или Tarsos), когда вызывается метод startRecording
if(crowdFilter && running==0 && set==0){//crowd speech mode, start talking
Icons(2,"");
running=4;//start recording from mic, apply bandpass filter and save as wave file using TARSOS import
//running=5;//start recording from mic, no filter, save as wav file using android media import
freqChange = Globals.minFr[Globals.curUser];
tollerance = 40;
set=1;
startRecording();
}
Метод начала записи:
private void startRecording() {
if (running == 5) {//start recording from mic, no filter, save as wav file using android media library
alteredRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, RECORDER_SAMPLERATE, RECORDER_CHANNELS,RECORDER_AUDIO_ENCODING, bufferSize);
alteredRecord.startRecording();
isRecording = true;
recordingThread = new Thread(new Runnable() {
@Override
public void run() {
writeAudioDataToFile();
}
}, "Crowd_Speech Thread");
recordingThread.start();
}
if (running == 4) {//start recording from mic, apply bandpass filter and save as wave file using TARSOS library
dispatcher = AudioDispatcherFactory.fromDefaultMicrophone(RECORDER_SAMPLERATE, bufferSize, 0);
AudioProcessor p = new BandPass(freqChange, tollerance, RECORDER_SAMPLERATE);
dispatcher.addAudioProcessor(p);
isRecording = true;
dispatcher.run();
recordingThread = new Thread(new Runnable() {
@Override
public void run() {
writeAudioDataToFile();
}
}, "Crowd_Speech Thread");
recordingThread.start();
}
}
Кнопка остановки записи внутри метода onClick
if(crowdFilter && (running==4 || running==5) && set==0) {//crowd speech finished talking
Icons(1, "");
stopRecording();
set = 1;
}
Оба случая хороши до этого момента, если при запуске ==4 (применен фильтр tarsos dsp), программа вылетает. если я использую запуск ==5 (способ android.media без фильтра), все остальное работает нормально и сохраняет файл, но эффект BandPass не применяется. если я пытаюсь поменять alteredRecord = new AudioRecord... с помощью tarsos dispatcher = AudioDispatcherFactory... (например, dispatcher = new AudioRecord...), они будут несовместимы и даже не будут думать о компиляции. (Вот почему строка 15 в следующем методе закомментирована)
private void writeAudioDataToFile(){
byte data[] = new byte[bufferSize];
String filename = getTempFilename();
FileOutputStream os = null;
try {
os = new FileOutputStream(filename);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
int read = 0;
if(null != os){
while(isRecording){
if(running==4)
{
//read = dispatcher.(data, 0, bufferSize);
}
if(running==5)
{
read = alteredRecord.read(data, 0, bufferSize);
}
if(AudioRecord.ERROR_INVALID_OPERATION != read){
try {
os.write(data);
} catch (IOException e) {
e.printStackTrace();
}
}
}
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void stopRecording(){
if(null != alteredRecord) {
isRecording = false;
int i = alteredRecord.getState();
if (i == 1) {
running = 0;
alteredRecord.stop();
alteredRecord.release();
alteredRecord = null;
recordingThread = null;
}
}
if(null !=dispatcher){
isRecording = false;
running = 0;
dispatcher.stop();
recordingThread = null;
}
copyWaveFile(getTempFilename(),getFilename());
deleteTempFile();
}
private void deleteTempFile() {
File file = new File(getTempFilename());
file.delete();
}
private void copyWaveFile(String inFilename,String outFilename){
FileInputStream in = null;
FileOutputStream out = null;
long totalAudioLen = 0;
long totalDataLen = totalAudioLen + 36;
long longSampleRate = RECORDER_SAMPLERATE;
int channels = 1;
long byteRate = RECORDER_BPP * RECORDER_SAMPLERATE * channels/8;
byte[] data = new byte[bufferSize];
try {
in = new FileInputStream(inFilename);
out = new FileOutputStream(outFilename);
totalAudioLen = in.getChannel().size();
totalDataLen = totalAudioLen + 36;
WriteWaveFileHeader(out, totalAudioLen, totalDataLen,
longSampleRate, channels, byteRate);
while(in.read(data) != -1){
out.write(data);
}
in.close();
out.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private void WriteWaveFileHeader(
FileOutputStream out, long totalAudioLen,
long totalDataLen, long longSampleRate, int channels,
long byteRate) throws IOException {
byte[] header = new byte[44];
header[0] = 'R';header[1] = 'I'; header[2] = 'F';header[3] = 'F';// RIFF/WAVE header
header[4] = (byte) (totalDataLen & 0xff);
header[5] = (byte) ((totalDataLen >> 8) & 0xff);
header[6] = (byte) ((totalDataLen >> 16) & 0xff);
header[7] = (byte) ((totalDataLen >> 24) & 0xff);
header[8] = 'W';header[9] = 'A';header[10] = 'V';header[11] = 'E';header[12] = 'f';header[13] = 'm';header[14] = 't';header[15] = ' ';// 'fmt ' chunk
header[16] = 16;header[17] = 0;header[18] = 0;header[19] = 0;// 4 bytes: size of 'fmt ' chunk
header[20] = 1;header[21] = 0;header[22] = (byte) channels;header[23] = 0;// format = 1
header[24] = (byte) (longSampleRate & 0xff);header[25] = (byte) ((longSampleRate >> 8) & 0xff);header[26] = (byte) ((longSampleRate >> 16) & 0xff);
header[27] = (byte) ((longSampleRate >> 24) & 0xff);header[28] = (byte) (byteRate & 0xff);header[29] = (byte) ((byteRate >> 8) & 0xff);
header[30] = (byte) ((byteRate >> 16) & 0xff); header[31] = (byte) ((byteRate >> 24) & 0xff);
header[32] = (byte) (2 * 16 / 8);header[33] = 0;// block align
header[34] = RECORDER_BPP;header[35] = 0;header[36] = 'd';header[37] = 'a';header[38] = 't';header[39] = 'a';
header[40] = (byte) (totalAudioLen & 0xff);header[41] = (byte) ((totalAudioLen >> 8) & 0xff);header[42] = (byte) ((totalAudioLen >> 16) & 0xff);
header[43] = (byte) ((totalAudioLen >> 24) & 0xff);// bits per sample
out.write(header, 0, 44);
}
1 ответ
Решено, вам нужно использовать функцию записи и не беспокоиться ни о каком из методов, необходимых для сохранения файла wav из функций импорта android.media, это сегмент рабочего кода из метода startRecording, который я изменил:
if (running == 4) {//start recording from mic, apply bandpass filter and save as wave file using TARSOS library
dispatcher = AudioDispatcherFactory.fromDefaultMicrophone(RECORDER_SAMPLERATE, bufferSize, 0);
AudioProcessor p = new BandPass(freqChange, tollerance, RECORDER_SAMPLERATE);
dispatcher.addAudioProcessor(p);
isRecording = true;
// Output
RandomAccessFile outputFile = new RandomAccessFile(getFilename(), "rw");
TarsosDSPAudioFormat outputFormat = new TarsosDSPAudioFormat(44100, 16, 1, true, false);
WriterProcessor writer = new WriterProcessor(outputFormat, outputFile);
dispatcher.addAudioProcessor(writer);
recordingThread = new Thread(new Runnable() {
@Override
public void run() {
dispatcher.run();
}
}, "Crowd_Speech Thread");
recordingThread.start();
}