2015-09-09 3 views
1

Я попытался изменить частоту дискретизации файла WAV с помощью javax.sound library.Невозможно прочитать один байт из AudioInputStream

После реализации моих желает методов я пробовал его, и это сработало! Но созданный файл .wav занимает всего пару байт. Если быть точным, то только 2.090 байт от первоначально ~ 50 МБ

Проблема в том, что Java не может прочитать один байт из моего открытого AudioInputStream.

FYI, используемый файл является продуктом преобразования mp3-файла в JLayer. Он отлично работает, с JLayer нет проблем, но я полагаю, что лучше, если я опубликую весь свой исходный код. Примечание. Я использовал множество System.out.printlns для ведения журнала.

Итак, сначала я импортирую все необходимые библиотеки, так как вы можете видеть, что я также импортировал JLayer.

import java.io.File; 
import java.io.IOException; 
import java.util.Arrays; 

import javax.sound.sampled.*; 
import javax.sound.sampled.AudioSystem; 
import javax.sound.sampled.UnsupportedAudioFileException; 
import javax.swing.JOptionPane; 

import javazoom.jl.converter.Converter; 
import javazoom.jl.decoder.JavaLayerException; 

public class MyConverter { 

    private static AudioInputStream lowResAis = null; 
    private static AudioInputStream audioInputStream = null; 

После установки этих глобальных констант я писал мой основной и предварительно выбранный демонстрационный файл для целей тестирования.

public static void main(String[] args) { 
     MyConverter converterdemo = new MyConverter(); 
     String wavfilepath = "C:\\Users\\JohnDoe\\Desktop\\Second Nature.wav"; 
     File f = new File(wavfilepath); 
     converterdemo.convertMp3toWav("C:\\Users\\JohnDoe\\Desktop\\Second Nature.mp3", "C:\\Users\\JohnDoe\\Desktop\\Second Nature.wav"); 
     converterdemo.compressWavFile(wavfilepath); 
     converterdemo.writeToSoundFile(f); 
     System.out.println("Everything worked!"); 
    } 

Это мой преобразования метод

public void convertMp3toWav(String sourcefilename, String targetfilename){ 
     Converter con = new Converter(); 
     try { 
      con.convert(sourcefilename, targetfilename); 
      System.out.println("Wav File converted!"); 
     } catch (JavaLayerException e) { 
      this.showOptionPane("Could not convert by using JLayer!"); 
     } 
    } 

Метод преобразования прекрасно (поверьте мне, нет никаких проблем с аудиофайле), но вот метод, о котором я я волнуясь немного

После добавления этой строки: System.out.println(AudioSystem.getAudioFileFormat(audioInputStream).getByteLength());

Я получаю следующее сообщение об ошибке: java.io.IOException: cannot read a single byte if frame size > 1

Я не могу прочитать из собственного потока.

public void compressWavFile(String sourcefilename){ 
     try { 
      InputStream musicfile = new BufferedInputStream(new FileInputStream(sourcefilename)); 
     audioInputStream = AudioSystem.getAudioInputStream(musicfile); 
      AudioFormat format = audioInputStream.getFormat(); 
      AudioFormat outDataFormat = new AudioFormat((float) 22000.0, 
        (int) 16, (int) 1, true, false); 
      if(AudioSystem.isConversionSupported(outDataFormat, format)){ 
       System.out.println("Conversion Supported!"); 
       lowResAis = AudioSystem.getAudioInputStream(outDataFormat, audioInputStream); 
      } 
      System.out.println("low res created!"); 
     } catch (UnsupportedAudioFileException e) { 
      this.showOptionPane("Audio File not Supported!"); e.printStackTrace(); 
     } catch (IOException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 
    } 

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

public void writeToSoundFile(File out){ 
     if(AudioSystem.isFileTypeSupported(AudioFileFormat.Type.WAVE, lowResAis)){ 
      try { 
       AudioSystem.write(lowResAis, AudioFileFormat.Type.WAVE, out); 
       System.out.println("New wav file written!"); 
      } catch (IOException e) { 
       e.printStackTrace(); 
       this.showOptionPane("Could not write to new Audio File!"); 
      } 
     } 
     try { 
      System.out.println("closing stream!"); 
      lowResAis.close(); 
      audioInputStream.close(); 

     } catch (IOException e) { 
      this.showOptionPane("Could not close Audio Stream!"); 
      e.printStackTrace(); 
     } 
    } 

Это последний бит только там писать вновь созданный AudioInputStream к данному File. Я также не думаю, что здесь все может пойти не так.

Я действительно думаю, что есть проблема с моими AudioInputStreams, потому что это единственный момент, когда что-то может пойти не так.

EDIT:

Я добавил новый метод "showOptionPane", чтобы остаться thread safe все время.

private void showOptionPane(String msg){ 
     SwingUtilities.invokeLater(new Runnable(){ 
      @Override 
      public void run() { 
       JOptionPane.showMessageDialog(null, msg, "Error", 
         JOptionPane.ERROR_MESSAGE); 
      } 

     }); 
    } 
+0

Не помещайте JOptionPane.showMessageDialog (...) в водосборный блоке, если вы уверены, что весь файл writeToSoundFile (...) запускается в потоке Swing Swing! –

+0

Почему я не должен? Это то же самое, как если бы я запускал System.out.println(); Также, как я упомянул, он предназначен только для ведения журнала и будет удален, когда будет завершен – Einstein

+1

Это не то же самое, что System.out.println (...), потому что он вызывает окно, используя библиотеку Swing. Библиотека Swing не является потокобезопасной, поэтому рисование с ней, если не в потоке пользовательского интерфейса, может привести к повреждению состояния пользовательского интерфейса. Именно по этой причине все обучающие программы Swing начинаются с встраивания вызовов рисования в SwingUtilities.invokeLater (....). Вы можете вызывать позже свою JOptionPane, если хотите, но вызов ее из потока, отличного от UI, является неправильным в каждой ситуации. Подробнее см. Http://docs.oracle.com/javase/tutorial/uiswing/concurrency/dispatch.html. –

ответ

0

Собственно, это была ошибка с AudioSystem.write Framework. Я попытался написать в тот же файл, откуда я получал исходный поток. Из-за какого-то колдовства AudioSystem сам не создает резервную копию памяти stream. Поэтому вы можете написать только AudioInputStream в другой файл.

Редактирование AudioSystem.write(lowResAis, AudioFileFormat.Type.WAVE, out);

и изменить его на что-то вроде "mysounddemo.wav" работал очень хорошо, до тех пор у него есть другое имя, чем sourcefilestream.

Для достижения результата я хотел, я просто перенес временный файл и заменить его в существующий файл

Files.move(Paths.get(appdatapath), Paths.get(wavfilepath), REPLACE_EXISTING);

0

Я не аудиопроф, но, оглянувшись, кажется, что сообщение об ошибке имеет немного больше смысла.

Аудио не является потоком байтов, а потоком кадров; где весь кадр предназначен для загрузки в систему воспроизведения, поскольку он представляет выходные или входные значения в один момент времени.

Эта структура, по-видимому, оптимизирована для считывания кадров. Таким образом, вероятно, вам понадобится прочитать один или несколько фреймов с помощью метода getBytes (...), в котором вы укажете довольно большой буфер байтов, чтобы записать несколько фреймов. По-видимому, он не отслеживает загрузку подкадра, поскольку использование части кадра не имеет смысла с точки зрения аудио.

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

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

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

+0

Нет размера рамки справа. Двойной канал составлял 4 байта на кадр, а один канал - 2 байта на кадр. Но я нашел решение, чтобы заставить его работать. Спасибо за ваши усилия :) – Einstein

+0

'PCM_SIGNED 44100.0 Гц, 16 бит, стерео, 4 байт/кадр, мало-endian', это был аудиоформат перед конвертированием. 'PCM_SIGNED 22000.0 Гц, 16 бит, моно, 2 байта/кадр, мало-endian' и этот после. Поэтому я думаю, что мой старый конвертер [dBpowerAMP] (http://www.chip.de/downloads/dBpowerAMP-Music-Converter_13008294.html) устарел, но то, что насмехается, - это качество.Оба файла, один из моей программы и другой из приобретаемого программного обеспечения, имеют одинаковый размер, но я думаю, что нет способа измерить, имеют ли они оба одинаковое качество. – Einstein

+0

Ну, я думаю, это будет зависеть от того, как вы определяете качество. Один из них нажимает больше кадров в минуту, у одного есть больше каналов, и у каждого есть больше информации для каждого кадра. Единственное, что они разделяют, это разрешение измерения. Какой аспект качества имеет значение для вас? Динамический отклик? Тогда важна высокая Гц. Способность различать заметки? Тогда имеет значение большое значение. Два канала звука (важны для стерео)? Тогда вы не можете идти моно. Я бы сказал, что единственная причина, по которой вы видите такую ​​небольшую разницу, вероятно, потому, что вы играете точно такой же канал, в обоих ушах. –

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