【问题标题】:How do I use JTransforms to analyze and display the frequency spectrum of audio samples from ExoPlayer?如何使用 JTransforms 分析和显示 ExoPlayer 中音频样本的频谱?
【发布时间】:2018-07-26 08:41:07
【问题描述】:

目前,我正在使用 JTransforms 对我从 MediaCodecAudioRenderer 复制的音频样本执行 FFT,并使用挂钩自定义复制解码缓冲区,从使用 RTMP MediaSource 的 ExoPlayer 播放器。

我得到的是 4096(或 4608...是的,有些 MP3 奇怪地具有非 2 次方样本大小,我不知道为什么)-长度 ByteBuffer。这就是我必须放入 FloatFFT_1DDoubleFFT_1D 对象的内容,对吗?

现在我的代码如下:

crf.setHook((dupe, format) -> {
        if(currentMediaSource == mMediaSourceAudio) {
            byte[] data = new byte[dupe.limit()];
            dupe.position(0);
            dupe.get(data);

            if(format != null) {
                new FFTTask(data, format).execute();
            }
            Log.i("straight_from_renderer", data.length+" "+format);
        }
    });
...
...
private class FFTTask extends AsyncTask<Void, Void, float[]> {
    byte[] bufferContents;
    MediaFormat format;
    FFTTask(byte[] samples, MediaFormat format) {
        this.bufferContents = samples;
        this.format = format;
    }
    float[] floatMe(short[] pcms) {
        float[] floaters = new float[pcms.length];
        for (int i = 0; i < pcms.length; i++) {
            floaters[i] = pcms[i];
        }
        return floaters;
    }
    short[] shortMe(byte[] bytes) {
        short[] out = new short[bytes.length / 2]; // will drop last byte if odd number
        ByteBuffer bb = ByteBuffer.wrap(bytes);
        for (int i = 0; i < out.length; i++) {
            out[i] = bb.getShort();
        }
        return out;
    }
    float[] directFloatMe(byte[] bytes) {
        float[] out = new float[bytes.length / 2]; // will drop last byte if odd number
        ByteBuffer bb = ByteBuffer.wrap(bytes);
        for (int i = 0; i < out.length; i++) {
            out[i] = bb.getFloat();
        }
        return out;
    }
    private double db2(double r, double i, double maxSquared) {
        return 5.0 * Math.log10((r * r + i * i) / maxSquared);
    }
    double[] convertToDb(double[] data, double maxSquared) {
        data[0] = db2(data[0], 0.0, maxSquared);
        int j = 1;
        for (int i=1; i < data.length - 1; i+=2, j++) {
            data[j] = db2(data[i], data[i+1], maxSquared);
        }
        data[j] = data[0];
        return data;
    }
    @Override
    protected float[] doInBackground(Void... voids) {
        //WARNING: bufferContents is from a 2-channel 48k bitrate audio, so convert to mono first?
        /*
        byte[] oneChannel = new byte[bufferContents.length/2];
        for(int i = 0; i < oneChannel.length; i+=2) {
            oneChannel[i] = bufferContents[i*2+2];
            oneChannel[i+1] = bufferContents[i*2+3];
        }
        */
        float[] dataAsFloats = floatMe(shortMe(bufferContents));
        int fftLen = dataAsFloats.length/2;
        fft = new FloatFFT_1D(fftLen);
        fft.complexForward(dataAsFloats);
        String log = "";
        float[] magnitudes = new float[dataAsFloats.length/2];
        float magMax = 0;
        int maxIndex = 0;
        float dominantFreq = 0;
        for(int i = 0; i < dataAsFloats.length/2; i++) {
            float re = dataAsFloats[2*i];
            float im = dataAsFloats[2*i+1];
            magnitudes[i] = (float)(Math.sqrt(re * re + im * im) / 1e7);
            //log += re+" "+im+" "+magnitudes[i]+"\n";
            if(magnitudes[i] > magMax) {
                magMax = (float)(magnitudes[i]);
                maxIndex = i;
            }
        }
        dominantFreq = format.getInteger(MediaFormat.KEY_SAMPLE_RATE) * maxIndex / fftLen;
        Log.i("fft_results", magMax+" "+dominantFreq);
        return magnitudes;
    }
    @Override
    protected void onPostExecute(float[] res) {
        super.onPostExecute(res);
        //fftListener.onFFTResultsAvailable(res);
        caView.feedFFTMagnitudes(res);
    }
}

我把通道拆分代码注释掉了,因为我不确定是要批发那个字节数组,还是按通道拆分它,然后把其中一个通道的数据放进去。

但是最终的res 值真的很嘈杂——例如,幅度是混乱的,没有像通常的音频分析图像那样显示出明显的模式——相反,它只是一个紧凑的锯齿形,从频率高于 20000 Hz。

我做错了什么?

【问题讨论】:

    标签: android signal-processing


    【解决方案1】:

    事实证明,我做错的是字节到短的转换。现在我只是使用这个函数将两个字节转换为一个短字节:

    private short getSixteenBitSample(byte high, byte low) {
        return (short)((high << 8) | (low & 0xff));
    }
    

    然后将 short 转换为转换数组中的浮点数。然后将该转换数组通过 FFT。

    【讨论】:

    • 我正在尝试使用 exoplayer 构建音频可视化器。你能提供我你的原始音频字节的完整实现-> fft 转换部分。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-05
    • 2012-09-18
    • 2013-08-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多