Удалить высокочастотный звук из потокового аудио узла js - PullRequest
0 голосов
/ 10 декабря 2018

У меня есть небольшое приложение, которое принимает входящий аудиопоток из Интернета, и я пытаюсь найти частоту тона или непрерывного звукового сигнала.Во время тона / звукового сигнала это единственное, что будет играть.Остальная часть аудио - это либо молчание, либо разговор.Я использую модуль npm-pitchfinder npm, чтобы найти тон, и когда я использую образец аудиоклипа, который я сделал с частотой 2000 Гц, приложение выводит частоту в пределах одной или двух Гц.Когда я подключаю аудиопоток к сети, я получаю результаты, например, 17 000 Гц.Я предполагаю, что в звуковом сигнале есть некоторый «шум», и это то, что воспринимает модуль node-pitchfinder.

Можно ли отфильтровать этот шум в режиме реального времени, чтобы получить точную частоту?

Потоковый аудиофайл: http://relay.broadcastify.com/fq85hty701gnm4z.mp3

Код ниже:

const fs = require('fs');
const fsa = require('fs-extra');
const Lame     = require('lame');
const Speaker  = require('speaker');
const Volume   = require('pcm-volume');
const Analyser = require('audio-analyser')
const request  = require('request')
const Chunker  = require('stream-chunker');
const { YIN } = require('node-pitchfinder')
const detectPitch = YIN({ sampleRate: 44100})
//const BUFSIZE  = 64;
const BUFSIZE  = 500;

var decoder   = new Lame.Decoder(); 
decoder.on('format', function(format){onFormat(format)});

var chunker  = Chunker(BUFSIZE);
chunker.pipe(decoder);

var options = {
    url: 'http://relay.broadcastify.com/fq85hty701gnm4z.mp3',
    headers: {
        "Upgrade-Insecure-Requests": 1,
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
        "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Safari/605.1.15"
    }
}

var audio_stream = request(options);
//var audio_stream = fs.createReadStream('./2000.mp3');

audio_stream.pipe(chunker);

function onFormat(format)
{
    //if (volume == "undefined")
    volume = 1.0;

    vol      = new Volume(volume);
    speaker  = new Speaker(format);

    analyser = createAnalyser(format);
    analyser.on('data', sample);

    console.log(format);
    vol.pipe(speaker);  
    vol.pipe(analyser); 
    decoder.pipe(vol);
    vol.setVolume(volume);
}

function createAnalyser(format)
{
    return new Analyser({
        fftSize: 8,
            bufferSize: BUFSIZE,
            'pcm-stream': {
            channels: format.channels,
            sampleRate: format.sampleRate,
            bitDepth: format.bitDepth
        }
    });
}

var logFile = 'log.txt';
var logOptions = {flag: 'a'};

function sample()
{
    if (analyser) {

        const frequency = detectPitch(analyser._data)
        console.log(frequency)
    }
}

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

Я нашел код, который предположительно делает это с python

def getFreq( pkt ):
    #Use FFT to determine the peak frequency of the last chunk
    thefreq = 0

    if len(pkt) == bufferSize*swidth:
        indata = np.array(wave.struct.unpack("%dh"%(len(pkt)/swidth), pkt))*window

        # filter out everything outside of our bandpass Hz
        bp = np.fft.rfft(indata)
        minFilterBin = (bandPass[0]/(sampleRate/bufferSize)) + 1
        maxFilterBin = (bandPass[1]/(sampleRate/bufferSize)) - 1
        for i in range(len(bp)):
            if i < minFilterBin: 
                bp[i] = 0
            if i > maxFilterBin: 
                bp[i] = 0

        # Take the fft and square each value
        fftData = abs(bp)**2

        # find the maximum
        which = fftData[1:].argmax() + 1

        # Compute the magnitude of the sample we found
        dB = 10*np.log10(1e-20+abs(bp[which]))
        #avgdB = 10*np.log10(1e-20+abs(bp[which - 10:which + 10].mean()))

        if dB >= minDbLevel:
            # use quadratic interpolation around the max
            if which != len(fftData)-1:
                warnings.simplefilter("error")
                try:
                    y0, y1, y2 = np.log(fftData[which-1:which+2:])
                    x1 = (y2 - y0) * .5 / (2 * y1 - y2 - y0)
                except RuntimeWarning:
                    return(-1)
                # find the frequency and output it
                warnings.simplefilter("always")
                thefreq = (which + x1) * sampleRate/bufferSize
            else:
                thefreq = which * sampleRate/bufferSize
        else:
            thefreq = -1
        return(thefreq)

1 Ответ

0 голосов
/ 15 декабря 2018

Оригинальный ответ:

Я не могу предоставить вам решение, но (надеюсь) дать вам достаточно советов для решения проблемы.

Я бы порекомендовал вамсохраните часть потока, который вы хотите проанализировать, в файл, а затем посмотрите на файл с помощью анализатора спектра (например, с помощью Audacity ).Это позволяет вам определить, присутствует ли сигнал 17 кГц в аудиопотоке.

Если сигнал 17 кГц присутствует в аудиопотоке, то вы можете отфильтровать аудиопоток с помощью фильтра нижних частот (например, аудио-биквад с типом lowpass и частотой где-то выше 2 кГц).

Если в аудиосигнале отсутствует сигнал 17 кГц, вы можете попытаться увеличить размер буфера BUFSIZE (в настоящее время в вашем коде установлено значение 500).В примере на странице GitHub node-pitchfinder они используют полный аудиофайл для определения высоты звука.В зависимости от того, как реализован алгоритм обнаружения основного тона, результат может отличаться для больших фрагментов аудиоданных (т.е. нескольких секунд) по сравнению с очень короткими фрагментами (500 выборок - это около 11 мс при частоте дискретизации 44100).Начните с большого значения для BUFSIZE (например, 44100 -> 1 секунда) и посмотрите, имеет ли это значение.

Объяснение кода Python: код использует FFT (быстрое преобразование Фурье) , чтобы узнать, какие частоты присутствуют в аудиосигнале, а затем ищет частоту с наибольшим значением.Это обычно хорошо работает для простых сигналов, таких как синусоида 2 кГц.Вы можете использовать dsp.js , который обеспечивает реализацию FFT, если вы хотите реализовать ее в javascript.Тем не менее, сделать это правильно, не зная теории цифровой обработки сигналов.

В качестве примечания: алгоритм YIN не использует БПФ, он основан на автокорреляция .

Обновление

Следующий скрипт использует данные fft audio-analyser и ищет максимальную частоту.Этот подход очень простой и хорошо работает только для сигналов, где доминирует только одна частота.Алгоритм YIN гораздо лучше подходит для обнаружения основного тона, чем этот пример.

const fs = require('fs');
const Lame = require('lame');
const Analyser = require('audio-analyser')
const Chunker = require('stream-chunker');

var analyser;
var fftSize = 4096;

var decoder = new Lame.Decoder();
decoder.on('format', format => {
    analyser = createAnalyser(format);
    decoder.pipe(analyser);
    analyser.on('data', processSamples);
    console.log(format);
});

var chunker = Chunker(fftSize);
var audio_stream = fs.createReadStream('./sine.mp3');

audio_stream.pipe(chunker);
chunker.pipe(decoder);

function createAnalyser(format) {
    return new Analyser({
        fftSize: fftSize,
        frequencyBinCount: fftSize / 2,
        sampleRate: format.sampleRate,
        channels: format.channels,
        bitDepth: format.bitDepth
    });
}

function processSamples() {
    if (analyser) {
        var fftData = new Uint8Array(analyser.frequencyBinCount);
        analyser.getByteFrequencyData(fftData);

        var maxBin = fftData.indexOf(Math.max(...fftData));
        var thefreq = maxBin * analyser.sampleRate / analyser.fftSize;

        console.log(maxBin + " " + thefreq);
    }
}
...