Установите правильный размер буфера для аудиопотока PCM в node.js - PullRequest
0 голосов
/ 11 февраля 2019

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

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

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

Проблема заключается в том, что я проверил это спакет узла speaker , который позволяет мне подключаться напрямую к моим динамикам.Метод _read в My Readable получает количество запрошенных байтов.Мои динамики (или их драйверы?) Запрашивают столько данных, сколько им нужно, поскольку они ничего не буферизируют.Поэтому количество запрашиваемых данных точно соответствует количеству данных, поступающих для этой частоты дискретизации.

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

export default class Input extends Writable {

    readSamples (size, time) {
        this.lastRead = time

        // If our buffer is smaller than what's requested, fill it up with silence
        if (this.buffer.length < size) {
            let drainedBuffer = Buffer.concat([this.buffer, this.silence(size - this.buffer.length)])
            this.buffer = this.buffer.slice(this.buffer.length)

            return drainedBuffer
        }

        // Unshift the first _size_ elements from the buffer
        let buffer = this.buffer.slice(0, size)
        this.buffer = this.buffer.slice(size)

        return buffer
    }

    _write (chunk, encoding, next) {
        // Calculate how many samples we should be receiving by now
        let timeDifference = process.hrtime(this.lastRead)
        let timeDifferenceInNs = timeDifference[0] * NS_PER_SEC + timeDifference[1]

        const channels = 2
        const samplingRate = 44100

        let samplesInChunk = chunk.length / channels
        let samplesRequired = Math.floor(timeDifferenceInNs / NS_PER_SEC * samplingRate)

        if (samplesInChunk < samplesRequired) {
            this.buffer = Buffer.concat([this.buffer, this.silence(samplesRequired - samplesInChunk)])
        }

        this.buffer = Buffer.concat([this.buffer, chunk])

        next()
    }

}

.

class Mixer extends Readable {

    _read (size) {
        if (typeof size === 'undefined') {
            // Calculate the number of samples that should be requested
            // if size is not specified.

            let timeSinceLastRead = process.hrtime(this.lastReadTime)

            let nanosecondsSinceLastRead = timeSinceLastRead[0] * NS_PER_SEC + timeSinceLastRead[1]
            let samples = nanosecondsSinceLastRead / NS_PER_SEC * this.options.samplingRate

            size = samples
        }

        this.lastReadTime = process.hrtime()

        // this.inputs also includes an input that only
        // emits silence. This way even when no other inputs are
        // connected, there's still some silent data coming through
        // for proper timing

        let buffers = this.inputs.map(input => {
            return input.readSamples(size, this.lastReadTime)
        })

        let mixedBuffer = this.mixingFunction(buffers)
        this.push(mixedBuffer)
    }

}

Мои вопросы сейчас:

  • Как правильно буферизовать данные и отправлять только столько данных, сколько имеется (плюс молчание), а не полагаться на то, сколько данных запрашивается у цели потока?
  • Это правильноподход к буферизации входных данных в классе Input, когда они становятся доступными, и возвращать их только при вызове readSamples?Как мне убедиться, что время Mixer вызова readSamples совпадает с тем, что аудиоисточники записывают свои данные на вход, а правильный вход всегда доступен?

Глядя на этот код при написании этогоЯ обнаружил одну вещь, которую мне также нужно учитывать: во входных данных молчание необходимо добавлять только в начале при получении данных через _write, чтобы получить правильное начальное смещение относительно других входных данных.Если поток PCM этого входа когда-либо замолкает, он также будет транслировать кодированную PCM тишину, поэтому нет необходимости искусственно добавлять тишину в конце.

...