zoukankan      html  css  js  c++  java
  • 语音文件 pcm 静默(静音)判断

    转载:http://www.voidcn.com/relative/p-fwdkigvh-bro.html

    pcm 文件存储的是 原始的声音波型二进制流,没有文件头。

    (1)首先要确认 pcm文件的每个采样数据 采样位数,一般为8bit或16bit。

    (2)然后确定是双声道还是单声道,双声道是两个声道的数据交互排列,需要单独提取出每个声道的数据。

    (3)然后确定有没有符号位,如采样点位16bit有符号位的的范围为-32768~32767

    (4)确定当前操作系统的内存方式是大端,还是小端存储。具体看http://blog.csdn.net/u013378306/article/details/78904238

    (5)根据以上四条对pcm文件进行解析,转化为10进制文件

    注意:对于1-3可以在windows使用cooledit 工具设置参数播放pcm文件来确定具体参数,也可以使用以下java代码进行测试:

    本例子的语音为: 静默1秒,然后说 “你好”,然后静默两秒。pcm文件下载路径:http://download.csdn.net/download/u013378306/10175068

    package test;
    import java.io.File;  
    import java.io.FileInputStream;  
    import java.io.FileNotFoundException;  
    import java.io.IOException;  
    import java.io.InputStream;  
      
    import javax.sound.sampled.AudioFormat;  
    import javax.sound.sampled.AudioSystem;  
    import javax.sound.sampled.DataLine;  
    import javax.sound.sampled.LineUnavailableException;  
    import javax.sound.sampled.SourceDataLine;  
      
    public class test {  
      
        /** 
         * @param args 
         * @throws Exception 
         */  
        public static void main(String[] args) throws Exception {  
            // TODO Auto-generated method stub  
    
                File file = new File("3.pcm");  
                System.out.println(file.length());  
                int offset = 0;  
                int bufferSize = Integer.valueOf(String.valueOf(file.length())) ;  
                byte[] audioData = new byte[bufferSize];  
                InputStream in = new FileInputStream(file);  
                in.read(audioData);  
      
                  
                  
                float sampleRate = 20000;  
                int sampleSizeInBits = 16;  
                int channels = 1;  
                boolean signed = true;  
                boolean bigEndian = false;  
                // sampleRate - 每秒的样本数  
                // sampleSizeInBits - 每个样本中的位数  
                // channels - 声道数(单声道 1 个,立体声 2 个)  
                // signed - 指示数据是有符号的,还是无符号的  
                // bigEndian -是否为大端存储, 指示是否以 big-endian 字节顺序存储单个样本中的数据(false 意味着  
                // little-endian)。  
                AudioFormat af = new AudioFormat(sampleRate, sampleSizeInBits, channels, signed, bigEndian);  
                SourceDataLine.Info info = new DataLine.Info(SourceDataLine.class, af, bufferSize);  
                SourceDataLine sdl = (SourceDataLine) AudioSystem.getLine(info);  
                sdl.open(af);  
                sdl.start();  
                            for(int i=0;i<audioData.length;i++)
                                audioData[i]*=1;
                while (offset < audioData.length) {  
                    offset += sdl.write(audioData, offset, bufferSize);
                }  
        }
      
    
    } 

    如果测试通过确定了参数就可以对pcm文件进行解析,如下java代码对每个采样数据为16bits,单声道的pcm,在操作系统内存为小端存储下解析为10进制文件。

    package test;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileWriter;
    import java.io.InputStream;
    import java.math.BigInteger;
    
    public class ffff {
    
        /**
         * 采样位为16bits,小端存储,单声道解析为10进制文件
         * @param args
         */
        public static void main(String[] args) {
            try {
                File file = new File("3.pcm");
                System.out.println(file.length());
                System.out.println(file.length());
                int bufferSize = Integer.valueOf(String.valueOf(file.length()));
                byte[] buffers = new byte[bufferSize];
                InputStream in = new FileInputStream(file);
                in.read(buffers);
                String rs = "";
                for (int i = 0; i < buffers.length; i++) {
                    byte[] bs = new byte[2];
                    bs[0]=buffers[i+1];//小端存储,
                    bs[1]=buffers[i];
                    int s = Integer.valueOf(binary(bs, 10));
                    i = i + 1;
                    rs += " " + s;
    
                }
                writeFile(rs);
                in.close();
    
            } catch (Exception e) {
                e.printStackTrace();
    
            }
        }
    
        public static void writeFile(String s) {
            try {
    
                FileWriter fw = new FileWriter("hello3.txt");
    
                fw.write(s, 0, s.length());
                fw.flush();
                fw.close();
    
            } catch (Exception e) {
                e.printStackTrace();
            }
    
        }
    
        public static String binary(byte[] bytes, int radix) {
            return new BigInteger(bytes).toString(radix);// 这里的1代表正数
        }
    }

    执行完可以查看hello.txt ,可以看到一开始振幅很小,如下,基本不超过100:

    -15 -12 -18 -24 -17 -8 -8 -17 -22 -14 -5 -18 -47 -67 -60 -41 -28 -28 -23 -12 -6 -9 -13 -8 0 6 21 49 68 48 -2 -43 -47 -32 -22 -10 22 56 

    但说你好的时候,振幅变得很大:

     -2507 -2585 -2600 -2596 -2620 -2670 -2703 -2674 -2581 -2468 -2378 -2305 -2200 -2018 -1774 -1523 -1307 -1127 -962 -806 -652 -505 -384 -313 -281 -241 -163 

    然后静默两秒,振幅又变的很小:

    5 3 0 -4 -5 -6 -6 -7 -7 -8 -9 -8 -10 -10 -11 -10 -11 -11 -11 -11 -11 -11 -10 -9 -7 -6 -3 -2 -2 -3 -3 -3 -1 2 4 4

    具体波形图可以使用python代码显示:

    import numpy as np
    import pylab as pl
    import math
    import codecs
    file=codecs.open("hello3.txt","r") //原文代码file=codecs.open("hello3.txt","rb"),b是binary,以二进制方式读取,是错误的。
    lines=" "
    for line in file.readlines():
        lines=lines+line
    ys=lines.split(" ")
    yss=[]
    ays=list()
    axs=list()
    i=0
    max1=pow(2,16)-1
    for y in ys:
        if y.strip()=="":
            continue
        yss.append(y)
    
    for index in range(len(yss)):
    
        y1=yss[index]
    
        i+=1;
        y=int(y1)
    
        ays.append(y)
        axs.append(i)
    #print  i
    file.close()
    pl.plot(axs, ays,"ro")# use pylab to plot x and y
    pl.show()# show the plot on the screen

    得到波形图

     这里音频振幅与audacity中呈现的结果吻合,只是这里把振幅放大以便用肉眼去观察。

    2019-11-20 更新:

    经过实践发展,可以使用时间单位来检测该时间内的数据是否检测振幅。

    (数学不太好,随便用一个字符代替说明一下)

    设时间单位为t,音频采样率为S,如果连续的时间单位t时间内振幅很小(也可以计算分贝数),可以认为是静音(没有声音录入) 。

    待检验数据长度L=S*t,则检测目标是长度为L的数组,如果这个时间类振幅(分贝)数据小于阈值(threshold),则认为近似静音。

    例:采样率16000,2秒以外则认为没有声音输入。即 2*16000长度的数组内,所有数组低于一个阈值。

    stackoverflow答案:

    参考:https://stackoverflow.com/questions/5800649/detect-silence-when-recording

    How can I detect silence when recording operation is started in Java?

    Calculate the dB or RMS value for a group of sound frames and decide at what level it is considered to be 'silence'.

    What is PCM data?

    Data that is in Pulse-code modulation format.

    How can I calculate PCM data in Java?

    I do not understand that question. But guessing it has something to do with the speech-recognition tag, I have some bad news.

    This might theoretically be done using the Java Speech API. But there are apparently no 'speech to text' implementations available for the API (only 'text to speech').

    I have to calculate rms for speech-recognition project. But I do not know how can I calculate in Java.

    For a single channel that is represented by signal sizes in a double ranging from -1 to 1, you might use this method.

    /** Computes the RMS volume of a group of signal sizes ranging from -1 to 1. */
    public double volumeRMS(double[] raw) {
        double sum = 0d;
        if (raw.length==0) {
            return sum;
        } else {
            for (int ii=0; ii<raw.length; ii++) {
                sum += raw[ii];
            }
        }
        double average = sum/raw.length;
    
        double sumMeanSquare = 0d;
        for (int ii=0; ii<raw.length; ii++) {
            sumMeanSquare += Math.pow(raw[ii]-average,2d);
        }
        double averageMeanSquare = sumMeanSquare/raw.length;
        double rootMeanSquare = Math.sqrt(averageMeanSquare);
    
        return rootMeanSquare;
    }

    There is a byte buffer to save input values from the line, and what I should have to do with this buffer?

    If using the volumeRMS(double[]) method, convert the byte values to an array of double values ranging from -1 to 1. ;)

    笔者的思路是计算音频分贝值,可以参考通过pcm音频数据计算分贝

    以下代码转载自:https://blog.csdn.net/balijinyi/article/details/80284520

    很多场合我们需要动态显示实时声音分贝,下面列举三种计算分贝的算法。(以双声道为例,也就是一个short类型,最大能量值为32767)

    1:计算分贝 音频数据与大小

    首先我们分别累加每个采样点的数值,除以采样个数,得到声音平均能量值。
    然后再将其做100与32767之间的等比量化。得到1-100的量化值。
    通常情况下,人声分布在较低的能量范围,这样就会使量化后的数据大致分布在1-20的较小区间,不能够很敏感的感知变化。
    所以我们将其做了5倍的放大,当然计算后大于100的值,我们将其赋值100.

    //参数为数据,采样个数
    //返回值为分贝
    #define VOLUMEMAX   32767
    int SimpleCalculate_DB(short* pcmData, int sample)
    {
        signed short ret = 0;
        if (sample > 0){
            int sum = 0;
            signed short* pos = (signed short *)pcmData;
            for (int i = 0; i < sample; i++){
                sum += abs(*pos);
                pos++;
            }
            ret = sum * 500.0 / (sample * VOLUMEMAX);
            if (ret >= 100){
                ret = 100;
            }
       }
       return ret;
    }

    2:计算均方根(RMS) 即能量值

    static const float kMaxSquaredLevel = 32768 * 32768;
    constexpr float kMinLevel = 30.f;
     
    void Process(const int16_t* data, size_t length) 
    {
        float sum_square_ = 0;
        size_t sample_count_ = 0;
        for (size_t i = 0; i < length; ++i) {
            sum_square_ += data[i] * data[i];
        }
        sample_count_ += length;.
        float rms = sum_square_ / (sample_count_ * kMaxSquaredLevel);
        //20log_10(x^0.5) = 10log_10(x)
        rms = 10 * log10(rms);
        if (rms < -kMinLevel)
            rms = -kMinLevel;
        rms = -rms;
        return static_cast<int>(rms + 0.5);
    }

    3:获取音频数据最大的振幅(即绝对值最大)(0-32767),除以1000,得到(0-32)。从数组中获取相应索引所对应的分贝值。(提取自webrtc)

    const int8_t permutation[33] =
        {0,1,2,3,4,4,5,5,5,5,6,6,6,6,6,7,7,7,7,8,8,8,9,9,9,9,9,9,9,9,9,9,9};
        
    int16_t WebRtcSpl_MaxAbsValueW16C(const int16_t* vector, size_t length) 
    {
        size_t i = 0;
        int absolute = 0, maximum = 0;
        for (i = 0; i < length; i++) {
            absolute = abs((int)vector[i]);
            if (absolute > maximum) {
                maximum = absolute;
            }
        }
        if (maximum > 32767) {
            maximum = 32767;
        }
        return (int16_t)maximum;
    }
     
    void ComputeLevel(const int16_t* data, size_t length)
    {
        int16_t _absMax = 0;
        int16_t _count = 0;
        int8_t _currentLevel = 0;
        int16_t absValue(0);
        absValue = WebRtcSpl_MaxAbsValueW16(data,length);
        if (absValue > _absMax)
            _absMax = absValue;
        if (_count++ == 10) {
            _count = 0;
            int32_t position = _absMax/1000;
            if ((position == 0) && (_absMax > 250)){
                position = 1;
            }
            _currentLevel = permutation[position];
            _absMax >>= 2;
        }
    }
  • 相关阅读:
    1088
    1082 read number in chinese
    1079 total sales of supply chain
    1075 pat judge
    1074 reverse list
    1071 speech pattern
    ts 之 多种泛型的处理方式
    玩一下node中的child_process
    玩转 js 位运算
    记录一下尝试的class和function分别作为构造函数方法
  • 原文地址:https://www.cnblogs.com/passedbylove/p/11429213.html
Copyright © 2011-2022 走看看