2015-04-10 4 views
4

Я не так разбираюсь в Java, поэтому, пожалуйста, держите его достаточно простым. Я, однако, попытаюсь понять все, что вы публикуете. Вот моя проблема.Преобразование Фурье байтового массива

Я написал код для записи звука с внешнего микрофона и сохранил его в .wav. Хранение этого файла имеет значение для целей архивирования. Мне нужно сделать FFT сохраненного звука.

Мой подход к загрузке wav-файла в виде байтового массива и его преобразование с проблемой: 1. Есть заголовок, способ, которым мне нужно избавиться, но я должен уметь это сделать и 2 У меня есть байтовый массив, но большинство, если не все алгоритмы FFT, я нашел в Интернете и попытался выполнить патч в моей проектной работе со сложными/двумя двойными массивами.

Я попытался обойти обе эти проблемы и, наконец, смог построить свой массив FFT в виде графика, когда узнал, что он просто возвращает мне «0». Файл .wav в порядке, я могу воспроизвести его без проблем. Я подумал, может быть преобразование байтов в двойников была проблема для меня, так вот мой подход к тому, что (я знаю, что это не очень)

byte ByteArray[] = Files.readAllBytes(wav_path); 
String s = new String(ByteArray); 
double[] DoubleArray = toDouble(ByteArray); 
// build 2^n array, fill up with zeroes 
boolean exp = false; 
int i = 0; 
int pow = 0; 
while (!exp) { 
    pow = (int) Math.pow(2, i); 
    if (pow > ByteArray.length) { 
     exp = true; 
    } else { 
     i++; 
    } 
} 
System.out.println(pow); 
double[] Filledup = new double[pow]; 
for (int j = 0; j < DoubleArray.length; j++) { 
    Filledup[j] = DoubleArray[j]; 
    System.out.println(DoubleArray[j]); 
} 
for (int k = DoubleArray.length; k < Filledup.length; k++) { 
    Filledup[k] = 0; 
} 

Это функция, я использую, чтобы преобразовать массив байтов в два раза массив:

public static double[] toDouble(byte[] byteArray) { 
    ByteBuffer byteBuffer = ByteBuffer.wrap(byteArray); 
    double[] doubles = new double[byteArray.length/8]; 
    for (int i = 0; i < doubles.length; i++) { 
     doubles[i] = byteBuffer.getDouble(i * 8); 
    } 
    return doubles; 
} 

Заголовок, который находится там, я знаю это, но это должно быть самой маленькой проблемой прямо сейчас. Я преобразовал массив байтов в двойной массив, а затем заполнил этот массив до следующей степени 2 с нулями, так что БПФ может фактически работать (ему нужен массив из 2^n значений). Используемый мной алгоритм БПФ получает два двойных массива в качестве входных данных, один из которых является реальным, а другой - мнимой частью. Я читал, что для этого, чтобы работать, мне пришлось бы оставить мнимый массив пустым (но его длина была такой же, как реальный массив).

Стоит упомянуть: я записываю 44100 кГц, 16 бит и моно.

При необходимости я отправлю БПФ, который я использую.

Если я пытаюсь напечатать значения двойной массив, я получаю вид странные результаты:

... 
-2.0311904060823147E236 
-1.3309975624948503E241 
1.630738286366793E-260 
1.0682002560745842E-255 
-5.961832069690704E197 
-1.1476447092561027E164 
-1.1008407401197794E217 
-8.109566204271759E298 
-1.6104556241572942E265 
-2.2081172620352248E130 
NaN 
3.643749694745671E-217 
-3.9085815506127892E202 
-4.0747557114875874E149 
... 

Я знаю, что где-то проблема лежит со мной с видом что-то очень простое, я должен быть в курсе, но Кажется, я не могу найти проблему. Наконец, мой вопрос: как я могу заставить это работать?

+0

Вопрос в том, как вы преобразовываете значение байта в двойное значение? Эта часть кода не отображается. Вы используете https://docs.oracle.com/javase/8/docs/api/java/lang/Byte.html#doubleValue--? – lschuetze

+0

Я включу функцию в код выше. –

+0

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

ответ

4

Там же заголовок так, как мне нужно, чтобы избавиться от [& hellip;]

Вы должны использовать javax.sound.sampled.AudioInputStream для чтения файла, если вы хотите, чтобы «пропустить» заголовок. Это полезно узнать, так как вам нужны данные в заголовке для интерпретации байтов, если вы заранее не знаете точный формат.

Я запись с 44100 кГц, 16 бит и моно.

Таким образом, это почти наверняка означает, что данные в файле, кодируется как 16-битные целые числа (short в Java номенклатурой).

В настоящий момент ваш код ByteBuffer делает предположение, что это уже 64-битная плавающая точка, и именно поэтому вы получаете странные результаты. Другими словами, вы используете reinterpreting двоичный файл short как будто бы double.

Что вам нужно сделать, это прочитать данные short, а затем конвертировать в double.

Например, вот элементарное рутина, чтобы сделать такие, как вы пытаетесь сделать (с поддержкой 8-, 16-, 32- и 64-разрядное целое число PCM):

import javax.sound.sampled.*; 
import javax.sound.sampled.AudioFormat.Encoding; 
import java.io.*; 
import java.nio.*; 

static double[] readFully(File file) 
throws UnsupportedAudioFileException, IOException { 
    AudioInputStream in = AudioSystem.getAudioInputStream(file); 
    AudioFormat  fmt = in.getFormat(); 

    byte[] bytes; 
    try { 
     if(fmt.getEncoding() != Encoding.PCM_SIGNED) { 
      throw new UnsupportedAudioFileException(); 
     } 

     // read the data fully 
     bytes = new byte[in.available()]; 
     in.read(bytes); 
    } finally { 
     in.close(); 
    } 

    int bits = fmt.getSampleSizeInBits(); 
    double max = Math.pow(2, bits - 1); 

    ByteBuffer bb = ByteBuffer.wrap(bytes); 
    bb.order(fmt.isBigEndian() ? 
     ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN); 

    double[] samples = new double[bytes.length * 8/bits]; 
    // convert sample-by-sample to a scale of 
    // -1.0 <= samples[i] < 1.0 
    for(int i = 0; i < samples.length; ++i) { 
     switch(bits) { 
      case 8: samples[i] = (bb.get() /max); 
        break; 
      case 16: samples[i] = (bb.getShort()/max); 
        break; 
      case 32: samples[i] = (bb.getInt() /max); 
        break; 
      case 64: samples[i] = (bb.getLong()/max); 
        break; 
      default: throw new UnsupportedAudioFileException(); 
     } 
    } 

    return samples; 
} 

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

Это верно. реальной часть аудио массив выборки из файла, мнимой части представляет собой массив одинаковой длины, наполненный 0 в, например:

double[] realPart = mySamples; 
double[] imagPart = new double[realPart.length]; 
myFft(realPart, imagPart); 

Подробнее ... "How do I use audio sample data from Java Sound?"

+0

Это было невероятно полезно для понимания процесса чтения из wav-файла, спасибо. –

1

Образцы в волновом файле не будут уже 8-байтовыми двойными, которые могут быть скопированы непосредственно в соответствии с вашим опубликованным кодом.

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

Попробуйте 2 байта малознакомых знаковых чисел как вероятную возможность.

Смежные вопросы