Увеличение / уменьшение скорости воспроизведения аудио AudioInputStream с Java - PullRequest
0 голосов
/ 02 июля 2018

Знакомство со сложным миром аудио с использованием Java. Я использую this библиотека , которую я в основном улучшил и опубликовал на Github.

Основным классом библиотеки является StreamPlayer , а в коде есть комментарии и он прост для понимания.


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

Понятия не имею, как мне реализовать такую ​​функциональность. Я имею в виду, что я могу сделать при записи аудио с частотой дискретизации targetFormat? Я должен перезапускать звук снова и снова каждый раз ....

AudioFormat targetFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, sourceFormat.getSampleRate()*2, nSampleSizeInBits, sourceFormat.getChannels(),
                nSampleSizeInBits / 8 * sourceFormat.getChannels(), sourceFormat.getSampleRate(), false);

Код воспроизведения аудио:

/**
 * Main loop.
 *
 * Player Status == STOPPED || SEEKING = End of Thread + Freeing Audio Resources.<br>
 * Player Status == PLAYING = Audio stream data sent to Audio line.<br>
 * Player Status == PAUSED = Waiting for another status.
 */
@Override
public Void call() {
    //  int readBytes = 1
    //  byte[] abData = new byte[EXTERNAL_BUFFER_SIZE]
    int nBytesRead = 0;
    int audioDataLength = EXTERNAL_BUFFER_SIZE;
    ByteBuffer audioDataBuffer = ByteBuffer.allocate(audioDataLength);
    audioDataBuffer.order(ByteOrder.LITTLE_ENDIAN);

    // Lock stream while playing.
    synchronized (audioLock) {
        // Main play/pause loop.
        while ( ( nBytesRead != -1 ) && status != Status.STOPPED && status != Status.SEEKING && status != Status.NOT_SPECIFIED) {
            try {
                //Playing?
                if (status == Status.PLAYING) {

                    // System.out.println("Inside Stream Player Run method")
                    int toRead = audioDataLength;
                    int totalRead = 0;

                    // Reads up a specified maximum number of bytes from audio stream   
                    //wtf i have written here xaxaxoaxoao omg //to fix! cause it is complicated
                    for (; toRead > 0
                            && ( nBytesRead = audioInputStream.read(audioDataBuffer.array(), totalRead, toRead) ) != -1; toRead -= nBytesRead, totalRead += nBytesRead)

                        // Check for under run
                        if (sourceDataLine.available() >= sourceDataLine.getBufferSize())
                            logger.info(() -> "Underrun> Available=" + sourceDataLine.available() + " , SourceDataLineBuffer=" + sourceDataLine.getBufferSize());

                    //Check if anything has been read
                    if (totalRead > 0) {
                        trimBuffer = audioDataBuffer.array();
                        if (totalRead < trimBuffer.length) {
                            trimBuffer = new byte[totalRead];
                            //Copies an array from the specified source array, beginning at the specified position, to the specified position of the destination array
                            // The number of components copied is equal to the length argument. 
                            System.arraycopy(audioDataBuffer.array(), 0, trimBuffer, 0, totalRead);
                        }

                        //Writes audio data to the mixer via this source data line
                        sourceDataLine.write(trimBuffer, 0, totalRead);

                        // Compute position in bytes in encoded stream.
                        int nEncodedBytes = getEncodedStreamPosition();

                        // Notify all registered Listeners
                        listeners.forEach(listener -> {
                            if (audioInputStream instanceof PropertiesContainer) {
                                // Pass audio parameters such as instant
                                // bit rate, ...
                                listener.progress(nEncodedBytes, sourceDataLine.getMicrosecondPosition(), trimBuffer, ( (PropertiesContainer) audioInputStream ).properties());
                            } else
                                // Pass audio parameters
                                listener.progress(nEncodedBytes, sourceDataLine.getMicrosecondPosition(), trimBuffer, emptyMap);
                        });

                    }

                } else if (status == Status.PAUSED) {

                    //Flush and stop the source data line 
                    if (sourceDataLine != null && sourceDataLine.isRunning()) {
                        sourceDataLine.flush();
                        sourceDataLine.stop();
                    }
                    try {
                        while (status == Status.PAUSED) {
                            Thread.sleep(50);
                        }
                    } catch (InterruptedException ex) {
                        Thread.currentThread().interrupt();
                        logger.warning("Thread cannot sleep.\n" + ex);
                    }
                }
            } catch (IOException ex) {
                logger.log(Level.WARNING, "\"Decoder Exception: \" ", ex);
                status = Status.STOPPED;
                generateEvent(Status.STOPPED, getEncodedStreamPosition(), null);
            }
        }

        // Free audio resources.
        if (sourceDataLine != null) {
            sourceDataLine.drain();
            sourceDataLine.stop();
            sourceDataLine.close();
            sourceDataLine = null;
        }

        // Close stream.
        closeStream();

        // Notification of "End Of Media"
        if (nBytesRead == -1)
            generateEvent(Status.EOM, AudioSystem.NOT_SPECIFIED, null);

    }
    //Generate Event
    status = Status.STOPPED;
    generateEvent(Status.STOPPED, AudioSystem.NOT_SPECIFIED, null);

    //Log
    logger.info("Decoding thread completed");

    return null;
}

Не стесняйтесь скачать и проверить библиотеку в одиночку, если хотите. :) Мне нужна помощь в этом ... Ссылка на библиотеку .

Ответы [ 2 ]

0 голосов
/ 06 июля 2018

Краткий ответ:

Чтобы ускорить разговор одного человека, используйте мою Sonic.java собственную реализацию Java моего алгоритма Sonic. Пример того, как его использовать, приведен в Main.Java . Версия на том же алгоритме на языке C используется Android AudioTrack. Для ускорения воспроизведения музыки или фильмов найдите библиотеку на основе WSOLA.

раздутый ответ:

Ускорение речи сложнее, чем кажется. Простое увеличение частоты дискретизации без настройки сэмплов приведет к тому, что колонки будут звучать как бурундуки. По сути, есть две хорошие схемы линейного ускорения речи, которые я слушал: схемы на основе фиксированных кадров, такие как WSOLA, и схемы с синхронным высотой звука, такие как PICOLA, которые используются Sonic для скоростей до 2X. Еще одна схема, которую я слушал, основана на БПФ, и IMO этих реализаций следует избегать. Я слышал слухи о том, что на основе FFT можно сделать хорошо, но ни одна версия с открытым исходным кодом, о которой я знаю, не была пригодна для использования в последний раз, когда я проверял, вероятно, в 2014 году.

Мне пришлось изобрести новый алгоритм для скоростей, превышающих 2X, поскольку PICOLA просто отбрасывает целые периоды основного тона, что хорошо работает, если вы не отбрасываете два периода основного тона подряд. Для скорости, превышающей 2х, Sonic смешивает порции сэмплов из каждого периода основного тона, сохраняя некоторую информацию о частоте каждого из них. Это хорошо работает для большинства речи, хотя некоторые языки, такие как венгерский, имеют настолько короткие части речи, что даже PICOLA искажает некоторые фонемы. Тем не менее, общее правило, согласно которому вы можете отбросить один период основного тона без искажения фонем, в большинстве случаев работает хорошо.

Синхронные по тональности схемы фокусируются на одном динамике и, как правило, делают этот динамик более четким, чем схемы с фиксированным кадром, за счет распечатки неречевых звуков. Тем не менее, улучшение синхронных схем основного тона по сравнению со схемами с фиксированным кадром трудно услышать на скоростях, меньших, чем приблизительно 1,5, для большинства динамиков. Это связано с тем, что алгоритмы с фиксированным кадром, такие как WSOLA, в основном эмулируют синхронные схемы основного тона, такие как PICOLA, когда имеется только один динамик, и для каждого кадра необходимо отбрасывать не более одного периода основного тона. В этом случае математика работает в основном так же, если WSOLA хорошо настроена на динамик. Например, если он способен выбрать звуковой сегмент +/- один кадр за раз, то фиксированный кадр 50 мс позволит WSOLA эмулировать PICOLA для большинства громкоговорителей с основной высотой тона> 100 Гц. Тем не менее, мужчина с глубоким голосом, скажем, 95 Гц, будет убит WSOLA с использованием этих настроек. Кроме того, части речи, например, в конце предложения, где существенно снижаются наши основные высоты тона, также могут быть разделены WSOLA, когда параметры не оптимально настроены. Кроме того, WSOLA обычно падает на скорости, превышающие 2X, где, как и PICOLA, он начинает сбрасывать несколько периодов основного тона подряд.

Что касается положительных моментов, WSOLA сделает большинство звуков, включая музыку, понятными, если не высококачественными. Принимать негармоничные многоголосные звуки и изменять скорость без существенных искажений невозможно с помощью схем перекрытия и добавления (OLA), таких как WSOLA и PICOLA. Чтобы сделать это хорошо, потребуется разделить разные голоса, изменить их скорости независимо и смешать результаты вместе. Однако большая часть музыки достаточно гармонична, чтобы звучать нормально с WSOLA.

Оказывается, что низкое качество WSOLA при> 2X является одной из причин, по которой люди редко слушают на более высоких скоростях, чем 2X. Людям просто не нравится это. Когда Audible.com переключился с WSOLA на алгоритм, подобный Sonic, на Android, они смогли увеличить поддерживаемый диапазон скоростей с 2X до 3X. Я не слушал iOS в последние несколько лет, но с 2014 года Audible.com на iOS было жалко слушать с 3-кратной скоростью, так как они использовали встроенную iOS WSOLA библиотеку. С тех пор они, вероятно, исправили это.

0 голосов
/ 03 июля 2018

Глядя на библиотеку, которую вы связали, не похоже, что стоит начинать именно с этой проблемы со скоростью воспроизведения; есть ли причина, по которой вы не используете AudioTrack ? Кажется, поддерживает все, что вам нужно.

РЕДАКТИРОВАТЬ 1: AudioTrack специфичен для Android, но вопрос ОП основывается на десктопном javaSE; Я оставлю это здесь для дальнейшего использования.

1. Использование AudioTrack и настройка скорости воспроизведения (Android)

Благодаря ответу на другой пост SO ( здесь ), опубликован класс, который использует встроенный AudioTrack для обработки регулировки скорости во время воспроизведения.

public class AudioActivity extends Activity {
AudioTrack audio = new AudioTrack(AudioManager.STREAM_MUSIC,
        44100,
        AudioFormat.CHANNEL_OUT_STEREO,
        AudioFormat.ENCODING_PCM_16BIT,
        intSize, //size of pcm file to read in bytes
        AudioTrack.MODE_STATIC);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //read track from file
        File file = new File(getFilesDir(), fileName);

        int size = (int) file.length();
        byte[] data = new byte[size];

        try {
            FileInputStream fileInputStream = new FileInputStream(file);
            fileInputStream.read(data, 0, size);
            fileInputStream.close();

            audio.write(data, 0, data.length);
        } catch (IOException e) {}
    }

    //change playback speed by factor
    void changeSpeed(double factor) {
        audio.setPlaybackRate((int) (audio.getPlaybackRate() * factor));
    }
}

Это просто использует файл для потоковой передачи всего файла в одной команде записи, но вы можете изменить его иначе (метод setPlayBackRate - это основная часть, которая вам нужна).

2. Применение собственной настройки скорости воспроизведения

Теория регулировки скорости воспроизведения осуществляется двумя способами:

  • Отрегулируйте частоту дискретизации
  • Изменение количества образцов за единицу времени

Поскольку вы используете начальную частоту дискретизации (потому что я предполагаю, что вам нужно сбросить библиотеку и остановить звук при настройке частоты дискретизации?), Вам придется настроить количество выборок в единицу времени.

Например, для ускорения воспроизведения аудиобуфера вы можете использовать этот псевдокод (в стиле Python), найденный благодаря Coobird ( здесь ).

original_samples = [0, 0.1, 0.2, 0.3, 0.4, 0.5]

def faster(samples):
    new_samples = []
    for i = 0 to samples.length:
        if i is even:
            new_samples.add(0.5 * (samples[i] + samples[i+1]))
    return new_samples

faster_samples = faster(original_samples)

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

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

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


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

...