Java - регулировка скорости воспроизведения файла WAV.

Я, вероятно, тупой, но не могу найти решение своей проблемы

(ПРИМЕЧАНИЕ. Я МОГУ найти множество людей, сообщающих об этой проблеме, похоже, что это произошло из-за более новой версии Java (возможно 1.5?). Возможно, SAMPLE_RATE больше не поддерживается? Я не могу найти любое решение).

Я пытаюсь настроить SAMPLE_RATE, чтобы ускорить / замедлить песню. Я могу успешно воспроизвести файл .wav без проблем, поэтому я посмотрел на FloatControl, который работал для регулировки громкости:

public void adjustVolume(String audioType, float gain) {
        FloatControl gainControl = null;

        gainControl = (FloatControl) clipSFX.getControl(FloatControl.Type.MASTER_GAIN);
                if(gain > MAX_VOLUME)
                    gain = MAX_VOLUME;
                if(gain < MIN_VOLUME)
                    gain = MIN_VOLUME;

            //set volume
            gainControl.setValue(gain);
    }

Но при попытке перевести этот принцип в SAMPLE_RATE я очень рано получаю ошибку на этом этапе:

    public void adjustVolume(String audioType, float gain) {
        FloatControl gainControl = null;

        gainControl = (FloatControl) clipSFX.getControl(FloatControl.Type.SAMPLE_RATE);
        //ERROR: Exception in thread "Thread-3" java.lang.IllegalArgumentException: Unsupported control type: Sample Rate

        //I haven't gotten this far yet since the above breaks, but in theory will then set value?
            gainControl.setValue(gain);
}

Все, что я нашел в Интернете, похоже, связано с вводом сигнала с микрофона или какой-либо внешней линии и, похоже, не переводится на использование аудиофайла, поэтому я не уверен, что мне не хватает. Любая помощь будет оценена по достоинству! Спасибо!


person Alan Pauley    schedule 29.09.2018    source источник
comment
если не поддерживается, то не поддерживается - возможно, вы ищете чьи-то хаки? хорошо - в любом случае я считаю те решения, которые полагаются на Flow Control и прочее мерцание - создайте новый формат, новую строку и так далее - до Победы!   -  person gpasch    schedule 30.09.2018
comment
Я не ищу взлома; Как вы говорите, если не поддерживается, значит, не поддерживается. Я хотел бы получить руководство о том, какими будут следующие альтернативные шаги или что является обычной практикой для настройки аудиофайлов в Java. Я не уверен, что понимаю, что вы имеете в виду, создавая новый формат и новую строку (или то, что может повлечь за собой и т. Д.) В качестве решения. Извините, если это очевидные ответы, но этот здоровый бизнес для меня в новинку.   -  person Alan Pauley    schedule 30.09.2018


Ответы (2)


Здесь у нас есть метод, который изменяет скорость - удваивая частоту дискретизации. В основном шаги следующие:

  • открыть аудиопоток файла
  • получить формат
  • создать новый формат с измененной частотой дискретизации
  • открыть строку данных в этом формате
  • читать из файла / аудиопотока и воспроизводить в строке

Здесь используются следующие концепции: SourceDataLine, AudioFormat и AudioInputStream. Если вы посмотрите учебник по javax.sound, вы найдете их или даже страницы классов. Теперь вы можете создать свой собственный метод (например, adjust (factor)), который просто получает новый формат, а все остальное остается прежним.

  public void play() {
    try {
      File fileIn = new File(" ....);
      AudioInputStream audioInputStream=AudioSystem.getAudioInputStream(fileIn);
      AudioFormat formatIn=audioInputStream.getFormat();
      AudioFormat format=new AudioFormat(formatIn.getSampleRate()*2, formatIn.getSampleSizeInBits(), formatIn.getChannels(), true, formatIn.isBigEndian());
          System.out.println(formatIn.toString());
          System.out.println(format.toString());
      byte[] data=new byte[1024];
      DataLine.Info dinfo=new DataLine.Info(SourceDataLine.class, format);
      SourceDataLine line=(SourceDataLine)AudioSystem.getLine(dinfo);
      if(line!=null) {
        line.open(format);
        line.start();
        while(true) {
          int k=audioInputStream.read(data, 0, data.length);
          if(k<0) break;
          line.write(data, 0, k);
        }
        line.stop();
        line.close();
      }
    }
    catch(Exception ex) { ex.printStackTrace(); }
  }
person gpasch    schedule 30.09.2018
comment
Это действительно здорово, спасибо! Мне пришлось реализовать новый поток (stackoverflow.com/questions/21373231/), чтобы приложение не зависало во время песни, но в остальном отлично работает. Спасибо! : D - person Alan Pauley; 30.09.2018

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

Звуковые значения размещаются в массиве, и курсор обычно перемещается от значения к значению. Но вы можете настроить прогресс на произвольную величину, например 1,5 кадра, и при необходимости создать взвешенное значение.

Предположим, что данные выглядят следующим образом:

  1. 0.5
  2. 0.8
  3. 0.2
  4. -0.1
  5. -0.5
  6. -0.7

Ваши данные воспроизведения (для скорости 1,5) будут

  1. 0.5
  2. (0.8 + 0.2)/2
  3. -0.1
  4. (-0.5 + -0.7)/2

Я знаю, что раньше на Stack Overflow были сообщения, которые более полно объясняют этот алгоритм. Простите, что не выследил их.

Я использую этот метод, чтобы разрешить изменение скорости в реальном времени при воспроизведении .wav в следующей библиотеке с открытым исходным кодом: AudioCue . Не стесняйтесь проверять код и использовать идеи в нем.

Ниже приведен метод, который создает стереопару звуковых значений из точки, которая находится между двумя звуковыми кадрами (данные имеют знаковые числа с плавающей запятой, в диапазоне от -1 до 1). Это из внутреннего класса AudioCuePlayer в AudioCue.java. Наверное, не самый простой для чтения. Считываемые звуковые данные находятся в массиве cue, а idx - это текущее местоположение "головки воспроизведения", которое проходит через этот массив. intIndex - это аудиокадр, а flatIndex - фактическое расположение кадра в массиве. Я использую кадры для отслеживания местоположения точки воспроизведения и вычисления весов интерполяции, а затем использую flatIndex для получения соответствующих значений из массива.

private float[] readFractionalFrame(float[] audioVals, float idx)
{
    final int intIndex = (int) idx;
    final int flatIndex = intIndex * 2;

    audioVals[0] = cue[flatIndex + 2] * (idx - intIndex) 
            + cue[flatIndex] * ((intIndex + 1) - idx);

    audioVals[1] = cue[flatIndex + 3] * (idx - intIndex) 
            + cue[flatIndex + 1] * ((intIndex + 1) - idx);

    return audioVals;
}

Буду рад уточнить, если будут вопросы.

person Phil Freihofner    schedule 02.10.2018