【问题标题】:Remove high frequency sound from streaming audio node js从流音频节点js中删除高频声音
【发布时间】:2019-05-10 20:02:32
【问题描述】:

我有一个小应用程序可以接受来自互联网的传入音频流,我正在尝试查找音调或连续哔声的频率。在提示音/哔声时,它是唯一会播放的东西。其余的音频是静音或说话。我正在使用 node-pitchfinder npm 模块来查找音调,当我使用我制作的 2,000Hz 的示例音频剪辑时,应用程序会打印出一到两赫兹内的频率。当我将音频流拉到网上时,我不断得到像 17,000 Hz 这样的结果。我的猜测是音频信号中有一些“噪音”,这就是 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)

【问题讨论】:

  • 当我连接到您在问题中提供的链接时,我收到以下错误 The file you requested could not be found。您能否提供您要分析的流的可下载 mp3 文件?
  • @Marius 看起来网站会不时更改 URL...这个目前正在运行:relay.broadcastify.com/m14td6p27nghf8r.mp3 如果这不起作用,您可以在此处找到流的链接: broadcastify.com/listen/feed/14082(大部分时间都是沉默的)。 “音调”的一个例子是:edispatches.com/orgs/general/…
  • 我下载了 mp3,发现 mp3 的采样率为 11025Hz(但在上面的代码中,音高检测器的采样率设置为 44100Hz YIN({ sampleRate: 44100}))。如果我将采样率调整为 11025Hz,那么我会得到大约 2200Hz 的输出。这会是预期的结果吗?
  • @Marius 下载的文件只是音调的一个示例。最终,我将从具有 44100 采样率的流式 mp3 文件中获取数据。很抱歉造成混乱
  • 当我在 VLC 中打开流式 mp3 文件时,我得到 22050 的采样率。我建议使用流中的采样率初始化 onFormat 中的音​​高检测算法。

标签: javascript node.js audio audio-streaming pcm


【解决方案1】:

原答案:

我无法为您提供解决方案,但(希望)给您足够的建议来解决问题。

我建议您将要分析的流的一部分保存到文件中,然后使用频谱分析仪(例如使用Audacity)查看该文件。这使您可以确定音频流中是否存在 17kHz 信号。

如果音频流中存在 17 kHz 信号,则您可以使用低通滤波器过滤音频流(例如,audio-biquad 类型为 lowpass,频率高于 2 kHz)。

如果音频中不存在 17 kHz 信号,那么您可以尝试增加缓冲区大小BUFSIZE(当前在您的代码中设置为 500)。在node-pitchfinder's GitHub page 的示例中,他们使用完整的音频文件进行音高检测。根据音高检测算法的实现方式,较大的音频数据块(即几秒钟)与非常短的块(在采样率 44100 下 500 个样本约为 11 毫秒)相比,结果可能会有所不同。从 BUFSIZE 的较大值开始(例如 44100 -> 1 秒),看看它是否有所不同。

python代码说明:该代码使用FFT (fast fourier transform)找出音频信号中存在哪些频率,然后搜索最高值的频率。这通常适用于简单的信号,如 2 kHz 正弦波。如果您想在 javascript 中实现它,您可以使用提供 FFT 实现的 dsp.js。然而,如果没有一些数字信号处理理论知识,要做到这一点是一个相当大的挑战。

附带说明:YIN algorithm 不使用 FFT,它基于 autocorrelation

更新

以下脚本使用audio-analyser的fft数据并搜索最大频率。这种方法非常基本,仅适用于只有一个频率占主导地位的信号。 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);
    }
}

【讨论】:

  • 感谢您的信息。我在音频文件中看不到 17kHz。我会尝试增加缓冲区大小。我认为 FFT 不会给你不同的频率,这就是我放弃尝试的原因。
  • 我拥有的代码使用 FFT 并返回该数据:npmjs.com/package/audio-analyser 有没有一种方法可以使用该信息来获取我正在寻找的内容?类似于python代码的工作方式?
  • 查看使用 FFT 的示例的更新答案。但是我认为您应该改用YIN 算法,因为它比基本的 FFT 方法更强大。