2017-07-26 15:56:20 fang_yang_wa 阅读数 2567

    TICC3200通过TLV320AIC3254录音,录到的音频是双声道(立体声)的,因为不需要两个声道的声音,同时也减少网络传输的压力,现在想将双声道录音改为单声道录音。

    首先想到是通过设置TLV320AIC3254录音芯片的寄存器,可以将双声道录音改为单声道的,于是在程序中芯片设置的地方找,找到:

    //配置音频芯片的寄存器:
    // Configure Audio Codec
    // 配置音频芯片:
    AudioCodecReset(AUDIO_CODEC_TI_3254, NULL);
    AudioCodecConfig(AUDIO_CODEC_TI_3254, AUDIO_CODEC_16_BIT, 16000,
    		AUDIO_CODEC_STEREO, AUDIO_CODEC_SPEAKER_ALL,
                      AUDIO_CODEC_MIC_ALL);
    跟踪过去:

//*****************************************************************************
//
//! Configure audio codec for smaple rate, bits and number of channels
//!
//! \param[in] codecId 			- Device id
//! \param[in] bitsPerSample 	- Bits per sample (8, 16, 24 etc..)
//!									Please ref Bits per sample Macro section
//! \param[in] bitRate 			- Sampling rate in Hz. (8000, 16000, 44100 etc..)
//! \param[in] noOfChannels 	- Number of channels. (Mono, stereo etc..)
//!									Please refer Number of Channels Macro section
//! \param[in] speaker 			- Audio out that need to configure. (headphone, line out, all etc..)
//!									Please refer Audio Out Macro section
//! \param[in] mic 				- Audio in that need to configure. (line in, mono mic, all etc..)
//!									Please refer Audio In Macro section
//!
//! \return 0 on success else -ve.
//
//*****************************************************************************
int AudioCodecConfig(unsigned char codecId, unsigned char bitsPerSample, unsigned short bitRate,
                      unsigned char noOfChannels, unsigned char speaker,  unsigned char mic)
{
    unsigned int	bitClk = 0;

    if(codecId == AUDIO_CODEC_TI_3254)
    {
        AudioCodecPageSelect(TI3254_PAGE_0);

        if(bitsPerSample == AUDIO_CODEC_16_BIT)
        {
            // Set I2S Mode and Word Length
            AudioCodecRegWrite(TI3254_AUDIO_IF_1_REG, 0x00); 	// 0x00 	16bit, I2S, BCLK is input to the device
                                                                // WCLK is input to the device,
        }
        else
        {
            return -1;
        }

        bitClk = bitsPerSample * bitRate * noOfChannels;
        if(bitClk == 512000)
        {
            AudioCodecPageSelect(TI3254_PAGE_0);

            AudioCodecRegWrite(TI3254_CLK_MUX_REG, 0x03);		// PLL Clock is CODEC_CLKIN
            AudioCodecRegWrite(TI3254_CLK_PLL_P_R_REG, 0x94);	// PLL is powered up, P=1, R=4
            AudioCodecRegWrite(TI3254_CLK_PLL_J_REG, 0x2A);		// J=42
            AudioCodecRegWrite(TI3254_CLK_PLL_D_MSB_REG, 0x00);	// D = 0

            AudioCodecRegWrite(TI3254_CLK_NDAC_REG, 0x8E);		// NDAC divider powered up, NDAC = 14
            AudioCodecRegWrite(TI3254_CLK_MDAC_REG, 0x81);		// MDAC divider powered up, MDAC = 1
            AudioCodecRegWrite(TI3254_DAC_OSR_MSB_REG, 0x01);	// DOSR = 0x0180 = 384
            AudioCodecRegWrite(TI3254_DAC_OSR_LSB_REG, 0x80);	// DOSR = 0x0180 = 384

            AudioCodecRegWrite(TI3254_CLK_NADC_REG, 0x95);    	// NADC divider powered up, NADC = 21
            AudioCodecRegWrite(TI3254_CLK_MADC_REG, 0x82);      // MADC divider powered up, MADC = 2
            AudioCodecRegWrite(TI3254_ADC_OSR_REG, 0x80);    	// AOSR = 128 ((Use with PRB_R1 to PRB_R6, ADC Filter Type A)
        }
        else
        {
            return -1;
        }


        // Configure Power Supplies
        AudioCodecPageSelect(TI3254_PAGE_1);		//Select Page 1

        AudioCodecRegWrite(TI3254_PWR_CTRL_REG, 0x08);	// Disabled weak connection of AVDD with DVDD
        AudioCodecRegWrite(TI3254_LDO_CTRL_REG, 0x01);	// Over Current detected for AVDD LDO
        AudioCodecRegWrite(TI3254_ANALOG_IP_QCHRG_CTRL_REG, 0x32); // Analog inputs power up time is 6.4 ms
        AudioCodecRegWrite(TI3254_REF_PWR_UP_CTRL_REG, 0x01);	// Reference will power up in 40ms when analog blocks are powered up


        if(speaker)
        {
            unsigned char	reg1;

            AudioCodecPageSelect(TI3254_PAGE_0);	//Select Page 0


            // ##Configure Processing Blocks
            AudioCodecRegWrite(TI3254_DAC_SIG_P_BLK_CTRL_REG, 0x2);  // DAC Signal Processing Block PRB_P2


            AudioCodecPageSelect(TI3254_PAGE_44);	// Select Page 44

            AudioCodecRegWrite(TI3254_DAC_ADP_FILTER_CTRL_REG, 0x04);   // Adaptive Filtering enabled for DAC


            AudioCodecPageSelect(TI3254_PAGE_1);	// Select Page 1

            reg1 = 0x00;

            if(speaker & AUDIO_CODEC_SPEAKER_HP)
            {
                //De-pop: 5 time constants, 6k resistance
                AudioCodecRegWrite(TI3254_HP_DRV_START_UP_CTRL_REG, 0x25);	// Headphone ramps power up time is determined with 6k resistance,
                                                                            // Headphone ramps power up slowly in 5.0 time constants

                //Route LDAC/RDAC to HPL/HPR
                AudioCodecRegWrite(TI3254_HPL_ROUTING_SEL_REG, 0x08);	// Left Channel DAC reconstruction filter's positive terminal is routed to HPL
                AudioCodecRegWrite(TI3254_HPR_ROUTING_SEL_REG, 0x08);	// Left Channel DAC reconstruction filter's negative terminal is routed to HPR

                reg1 |= 0x30;	// HPL and HPR is powered up
            }

            if(speaker & AUDIO_CODEC_SPEAKER_LO)
            {
                //Route LDAC/RDAC to LOL/LOR
                AudioCodecRegWrite(TI3254_LOL_ROUTING_SEL_REG, 0x08);	// Left Channel DAC reconstruction filter output is routed to LOL
                AudioCodecRegWrite(TI3254_LOR_ROUTING_SEL_REG, 0x08);	// Right Channel DAC reconstruction filter output is routed to LOR

                reg1 |= 0x0C;	// LOL and LOR is powered up
            }

            //Power up HPL/HPR and LOL/LOR drivers
            AudioCodecRegWrite(TI3254_OP_DRV_PWR_CTRL_REG, reg1);

            if(speaker & AUDIO_CODEC_SPEAKER_HP)
            {
                //Unmute HPL/HPR driver, 0dB Gain
                AudioCodecRegWrite(TI3254_HPL_DRV_GAIN_CTRL_REG, 0x00);		// HPL driver is not muted, HPL driver gain is 0dB
                AudioCodecRegWrite(TI3254_HPR_DRV_GAIN_CTRL_REG, 0x00);		// HPR driver is not muted, HPL driver gain is 0dB
            }

            if(speaker & AUDIO_CODEC_SPEAKER_HP)
            {
                //Unmute LOL/LOR driver, 0dB Gain
                AudioCodecRegWrite(TI3254_LOL_DRV_GAIN_CTRL_REG, 0x0E);	// LOL driver gain is 11dB
                AudioCodecRegWrite(TI3254_LOR_DRV_GAIN_CTRL_REG, 0x0E);	// LOL driver gain is 11dB
            }



            AudioCodecPageSelect(TI3254_PAGE_0);		//Select Page 0

            //DAC => 64dB
            AudioCodecRegWrite(TI3254_LEFT_DAC_VOL_CTRL_REG, 0x80);		// Digital Volume Control = 64.0dB silent Note: As per data sheet its reserved but on setting this value there is silent
            AudioCodecRegWrite(TI3254_RIGHT_DAC_VOL_CTRL_REG, 0x80);	// Digital Volume Control = 64.0dB silent Note: As per data sheet its reserved  but on setting this value there is silent


            AudioCodecPageSelect(TI3254_PAGE_0);		//Select Page 0

            //Power up LDAC/RDAC
            AudioCodecRegWrite(TI3254_DAC_CHANNEL_SETUP_1_REG, 0xD6);	// Left and Right DAC Channel Powered Up
            // Left DAC data Left Channel Audio Interface Data
            // Right DAC data is Left Channel Audio Interface Data
            // Soft-Stepping is disabled

            //Unmute LDAC/RDAC
            AudioCodecRegWrite(TI3254_DAC_CHANNEL_SETUP_2_REG, 0x00);	// When Right DAC Channel is powered down, the data is zero.
            // Auto Mute disabled
            // Left and Right DAC Channel not muted
            // Left and Right Channel have independent volume control
        }


        if(mic)
        {
            unsigned char reg1 = 0x00;	// TI3254_MICBIAS_CTRL_REG
            unsigned char reg2 = 0x00;	// TI3254_LEFT_MICPGA_P_CTRL_REG
            unsigned char reg3 = 0x00;	// TI3254_LEFT_MICPGA_N_CTRL_REG
            unsigned char reg4 = 0x00;	// TI3254_RIGHT_MICPGA_P_CTRL_REG
            unsigned char reg5 = 0x00;	// TI3254_RIGHT_MICPGA_N_CTRL_REG
            unsigned char reg6 = 0x00;	// TI3254_FLOAT_IP_CTRL_REG


            AudioCodecPageSelect(TI3254_PAGE_8);	// Select Page 8

            AudioCodecRegWrite(TI3254_ADC_ADP_FILTER_CTRL_REG, 0x04);   // Adaptive Filtering enabled for ADC


            AudioCodecPageSelect(TI3254_PAGE_0);		//Select Page 0

            AudioCodecRegWrite(TI3254_ADC_SIG_P_BLK_CTRL_REG, 0x2);	// ADC Signal Processing Block PRB_P2

            if(mic & AUDIO_CODEC_MIC_LINE_IN)
            {
                reg1 |= 0x40;	// MICBIAS powered up
                reg2 |= 0x40;	// IN1L is routed to Left MICPGA with 10k resistance
                reg3 |= 0x40;	// CM is routed to Left MICPGA via CM1L with 10k resistance
                reg4 |= 0x40;	// IN1R is routed to Right MICPGA with 10k resistance
                reg5 |= 0x40;	// CM is routed to Right MICPGA via CM1R with 10k resistance
                reg6 |= 0xC0;	// IN1L input is weakly driven to common mode. Use when not routing IN1L to Left and Right MICPGA and HPL, HPR
            }

            if(mic & AUDIO_CODEC_MIC_MONO)
            {
                reg1 |= 0x40;	// MICBIAS powered up
                reg2 |= 0x00;
                reg3 |= 0x10;
                reg4 |= 0x10;	// IN2R is routed to Right MICPGA with 10k resistance
                reg5 |= 0x40;	// CM is routed to Right MICPGA via CM1R with 10k resistance
                reg6 |= 0x10;	// IN2R input is weakly driven to common mode. Use when not routing IN2R to Left and Right MICPGA
            }

            if(mic & AUDIO_CODEC_MIC_ONBOARD)
            {
                reg1 |= 0x40;	// MICBIAS powered up
                reg2 |= 0x00;
                reg3 |= 0x04;
                reg4 |= 0x04;	// IN3R is routed to Right MICPGA with 10k resistance
                reg5 |= 0x40;	// CM is routed to Right MICPGA via CM1R with 10k resistance
                reg6 |= 0x04;	// IN3R input is weakly driven to common mode. Use when not routing IN3R to Left and Right MICPGA
            }

            AudioCodecPageSelect(TI3254_PAGE_1);     //Select Page 1

            AudioCodecRegWrite(TI3254_MICBIAS_CTRL_REG, reg1);

            //Route IN2L not routed
            AudioCodecRegWrite(TI3254_LEFT_MICPGA_P_CTRL_REG, reg2);

            //Route IN2R CM1L to LEFT_N with 10K input impedance
            AudioCodecRegWrite(TI3254_LEFT_MICPGA_N_CTRL_REG, reg3);

            //Route IN2R to RIGHT_P with 10K input impedance
            AudioCodecRegWrite(TI3254_RIGHT_MICPGA_P_CTRL_REG, reg4);

            //Route CM1R to RIGHT_M with 10K input impedance
            AudioCodecRegWrite(TI3254_RIGHT_MICPGA_N_CTRL_REG, reg5);

            AudioCodecRegWrite(TI3254_FLOAT_IP_CTRL_REG, reg6);


            //make channel gain 0dB, since 20K input
            //impedance is used single ended
            AudioCodecRegWrite(TI3254_LEFT_MICPGA_VOL_CTRL_REG, 0x5F);	// 0.0dB

            //Unmute Right MICPGA, Gain selection of 6dB to
            //make channel gain 0dB, since 20K input
            //impedance is used single ended
            AudioCodecRegWrite(TI3254_RIGHT_MICPGA_VOL_CTRL_REG, 0x5F);	// 0.0dB


            AudioCodecRegWrite(TI3254_LEFT_ADC_VOL_CTRL_REG, 0x68);		// -12dB  0x68
            AudioCodecRegWrite(TI3254_RIGHT_ADC_VOL_CTRL_REG, 0x68);	// -12dB



            AudioCodecPageSelect(TI3254_PAGE_0);	// Select Page 0

            //Power up LADC/RADC
            AudioCodecRegWrite(TI3254_ADC_CHANNEL_SETUP_REG, 0xC0);	// Left and Right Channel ADC is powered up

            //Unmute LADC/RADC
            AudioCodecRegWrite(TI3254_ADC_FINE_GAIN_ADJ_REG, 0x00);	// Left and Right ADC Channel Un-muted. Left and Right ADC Channel Fine Gain = 0dB,
        }

    }

    return 0;
}
    仔细查看发现在这个函数中限制了声道数(参数noOfChannels)必须为2,否则返回-1,没有想清楚这是为什么,于是先不改这里,继续找其它的地方,找到:

AudioCaptureRendererConfigure(AUDIO_CODEC_16_BIT, 16000, AUDIO_CODEC_MONO, RecordPlay, 1);
    跟踪过去:

//*****************************************************************************
//
//! Initialize the AudioCaptureRendererConfigure
//!
//! \param[in] bitsPerSample 	- Number of bits per sample
//! \param[in] bitRate 			- Bit rate
//! \param[in] noOfChannels 	- Number of channels
//! \param[in] RxTx 			- Play or record or both
//! \param[in] dma			 	- 1 for dma and 0 for non-dma mode
//! 
//!
//! \return None.
//
//*****************************************************************************

void AudioCaptureRendererConfigure(unsigned char bitsPerSample,
                                    unsigned short bitRate,
                                    unsigned char noOfChannels,
                                    unsigned char RxTx,
                                    unsigned char	dma)
{
    unsigned long	bitClk;

    bitClk = bitsPerSample * bitRate * noOfChannels;

    if(dma)
    {
        if(bitsPerSample == 16)
        {
            MAP_PRCMI2SClockFreqSet(512000);
            MAP_I2SConfigSetExpClk(I2S_BASE,512000,bitClk,I2S_SLOT_SIZE_16|
                                    I2S_PORT_DMA);
        }
    }

    if(RxTx == I2S_MODE_RX_TX)
    {
        MAP_I2SSerializerConfig(I2S_BASE,I2S_DATA_LINE_1,I2S_SER_MODE_RX,
                                 I2S_INACT_LOW_LEVEL);
    }

    if(RxTx & I2S_MODE_TX)
    {
        MAP_I2SSerializerConfig(I2S_BASE,I2S_DATA_LINE_0,I2S_SER_MODE_TX,
                                 I2S_INACT_LOW_LEVEL);
    }

}
    发现这里的声道数没有做限制,而且最终传到了I2S的设置。于是猜想这个应该可以,将声道数的参数改过之后发现变成了单声道。




2017-08-14 17:21:53 mwh1280 阅读数 2226


ENCODING_PCM_16BIT:

public static byte[] byteMerger(byte[] byte_1){  
            byte[] byte_2 = new byte[byte_1.length*2];  
            for (int i = 0; i < byte_1.length; i++) {
                if(i%2 == 0){
                    byte_2[2*i] =  byte_1[i];
                    byte_2[2*i+1] =  byte_1[i+1];
                }else{
                    byte_2[2*i] =  byte_1[i-1];
                    byte_2[2*i+1] =  byte_1[i];
                }
            }
            return byte_2;  

 } 



ENCODING_PCM_8BIT:


public static byte[] byteMerger(byte[] byte_1){  
            byte[] byte_2 = new byte[byte_1.length*2];  
            for (int i = 0; i < byte_1.length; i++) {
                    byte_2[2*i] =  byte_1[i];
                    byte_2[2*i+1] =  byte_1[i];
            }
            return byte_2;  
 }

2017-10-17 17:24:01 u013270727 阅读数 3911
  • 本篇文件介绍 音频PCM数据的单声道、双声道之间的转换

下面介绍函数参数:
一、StereoToMono函数。
双声道转单声道
参数1:资源buffer(双声道数据)
参数2:资源buffer大小(双声道数据大小)
参数3:转换后buffer(单声道数据)

static short g_sSrcAudioBuf[640];
static short g_sDstAudioBuf[640];
//pdata:char * pdata(原始双声道音频数据)
//dlen:pdata数据长度
//单字节1280长度的数据pdata 转换为640双字节数据g_sSrcAudioBuf
memcpy(g_sSrcAudioBuf,pdata,dlen);
StereoToMono(g_sSrcAudioBuf,640,g_sDstAudioBuf)
//本地保存测试
fwrite(g_sSrcAudioBuf,2,320,fp);

void StereoToMono(const int16_t* src_audio,int samples_per_channel,int16_t* dst_audio) 
{
    for (int i = 0; i < samples_per_channel; i++) 
    {
        dst_audio[i] = (static_cast<int32_t>(src_audio[2 * i]) + src_audio[2 * i + 1]) >> 1;
    }
}

二、MonoToStereo函数。
单声道转双声道
参数1:资源buffer(单声道数据)
参数2:资源buffer大小(单声道数据大小)
参数3:转换后buffer(双声道数据)
代码:

static short g_sSrcAudioBuf[640];
static short g_sDstAudioBuf[640];
//g_sDstAudioBuf:单声道数据
//320:单声道数据大小
//g_sSrcAudioBuf:转换后buffer(双声道数据)
MonoToStereo(g_sDstAudioBuf,320,g_sSrcAudioBuf);
//本地保存测试
fwrite(g_sSrcAudioBuf,2,640,fp);

void MonoToStereo(const int16_t* src_audio,int samples_per_channel,int16_t* dst_audio) 
{
    for (int i = 0; i < samples_per_channel; i++) 
    {
        dst_audio[2 * i] = src_audio[i];
        dst_audio[2 * i + 1] = src_audio[i];
    }
}
2018-11-30 21:36:34 bberdong 阅读数 1706

原生Android只支持2 channel的录音。可是偏偏会有多mic的需求,比如说语音识别。目前已知TDM协议可以将多mic数据从kernel送到hal,从内核空间搬运到用户空间中。可是原生AudioRecord接口是完全不支持多channel录音数据的采集的,怎么修改,才能让原生进行支持呢?

我们就从AudioRecord的构造函数开始往下研究。无论行不行,都要研究出个所以然来!​我们如果写个录音app,我们一般这么使用AudioRecord:

int sampleRateInHz = 8000;
int audioEncodingBits = AudioFormat.ENCODING_PCM_16BIT;
int recordBufferSize = AudioRecord.getMinBufferSize(sampleRateInHz,     channelConfiguration, audioEncodingBits);
mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, 
    sampleRateInHz, channelConfiguration, audioEncodingBits,
                    recordBufferSize);

先说AudioRecord构造函数最后一个参数recordBufferSize。来自:

getMinBufferSize

//AudioRecord.java
static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) {
        int channelCount = 0;
        ...
        //根据channelMask得出channelCount
        //这里竟然有个6声道的,估计可以参考下
        case AudioFormat.CHANNEL_IN_5POINT1:
            channelCount = 6;
        ...
        int size = native_get_min_buff_size(sampleRateInHz, channelCount, audioFormat);
        ...
}

native_get_min_buff_size对应android_media_AudioRecord_get_min_buff_size:

//android_media_AudioRecord.cpp
static jint android_media_AudioRecord_get_min_buff_size(JNIEnv *env,  jobject thiz,jint sampleRateInHertz, jint channelCount, jint audioFormat) {
    size_t frameCount = 0;
    audio_format_t format = audioFormatToNative(audioFormat);
    status_t result = AudioRecord::getMinFrameCount(&frameCount,
            sampleRateInHertz,
            format,
            audio_channel_in_mask_from_count(channelCount));
    return frameCount * channelCount *      audio_bytes_per_sample(format);
}

这里传入的format是AudioFormat.ENCODING_PCM_16BIT,根据audio_bytes_per_sample:

//audio.h
static inline size_t audio_bytes_per_sample(audio_format_t format)
{
    ...
    case AUDIO_FORMAT_PCM_16_BIT:
      case AUDIO_FORMAT_IEC61937:
          size = sizeof(int16_t);
    ...
}

audio_bytes_per_sample返回的是sizeof(signed short) = 2.

status_t AudioRecord::getMinFrameCount(   
        size_t* frameCount,
        uint32_t sampleRate,
        audio_format_t format,
        audio_channel_mask_t channelMask)
{
    status_t status = AudioSystem::getInputBufferSize(sampleRate, format, channelMask, &size);
    ...
     //这里需要double一下
    // We double the size of input buffer for ping pong use of record buffer.
    // Assumes audio_is_linear_pcm(format)
    if ((*frameCount = (size * 2) / (audio_channel_count_from_in_mask(channelMask) *
            audio_bytes_per_sample(format))) == 0) {
        ALOGE("Unsupported configuration: sampleRate %u, format %#x, channelMask %#x",
            sampleRate, format, channelMask);
        return BAD_VALUE;
    }
}

getInputBufferSize直接看hal层:

//audio_hw.c
static size_t get_input_buffer_size(uint32_t sample_rate,
                                    audio_format_t format,
                                    int channel_count,
                                    bool is_low_latency)
{
    ...
        //这里是(8000*20)/1000
        size = (sample_rate * AUDIO_CAPTURE_PERIOD_DURATION_MSEC) / 1000;
        size *= sizeof(short) * channel_count;
    ...
}

size = (8000*20)/1000 * 2 * 2 = 640,get_input_buffer_size返回640.

目前这种场景getMinFrameCount取得frameCount = (640 *2) / (2 * 2) = 320

getMinBufferSize将返回320 * 2 * 2 = 1280,调用完构造函数之后,AudioRecord将通过audioBuffSizeCheck将这个值设置生效(函数名字是check,我觉得这个地方不太合理。)

private void audioBuffSizeCheck(int audioBufferSize) throws IllegalArgumentException {                                                                                   
        // NB: this section is only valid with PCM data.
        // To update when supporting compressed formats
        //只支持无压缩的pcm
        int frameSizeInBytes = mChannelCount
            * (AudioFormat.getBytesPerSample(mAudioFormat));
        //检查用户设置的这个值是不是frameSizeInBytes的整数倍
        if ((audioBufferSize % frameSizeInBytes != 0) || (audioBufferSize < 1)) {
            throw new IllegalArgumentException("Invalid audio buffer size " + audioBufferSize
                    + " (frame size " + frameSizeInBytes + ")");
        }
        //存到这里。作为录音数据的buffer
        mNativeBufferSizeInBytes = audioBufferSize;
    }

然后,通过调用native_setup将值传入native层。

//android_media_AudioRecord.cpp
static jint
android_media_AudioRecord_setup
{
    ...
    size_t frameSize = channelCount * bytesPerSample;
    //这里还是上文说的320
    size_t frameCount = buffSizeInBytes / frameSize;
    ...
    const status_t status = lpRecorder->set(
            ...
            frameCount
            ...
            );
}

然后这个函数会调用AudioRecord set接口.

//AudioRecord.cpp
status_t AudioRecord::set(//参数省略)
{
    ...
    //上层请求的frameCount
    // mFrameCount is initialized in openRecord_l
    mReqFrameCount = frameCount;
    ...
    size_t frameCount = mReqFrameCount;
    ...
    //temp有可能会被openRecord修订
    size_t temp = frameCount;
    ...
    sp<IAudioRecord> record = audioFlinger->openRecord(
                                            ...
                                            &temp,
                                            ...
    );
}

然后设置到AudioFlinger端:

//services/audioflinger/Tracks.cpp
AudioFlinger::PlaybackThread::Track::Track(/*省略参数*/)
{
    ...
        if (sharedBuffer == 0) {
        mAudioTrackServerProxy = new AudioTrackServerProxy(mCblk, mBuffer, frameCount,mFrameSize, !isExternalTrack(), sampleRate);
    } else {
        mAudioTrackServerProxy = new StaticAudioTrackServerProxy(mCblk, mBuffer, frameCount,mFrameSize);
    }
    ...
}

本文的主题是研究多声道录音,所以先就此打住。

回到前文,如果需要支持多声道,需要看看第四个参数

channelConfiguration

取值范围只有这些:

public static final int CHANNEL_IN_DEFAULT = 1;
    // These directly match native
    public static final int CHANNEL_IN_LEFT = 0x4;
    public static final int CHANNEL_IN_RIGHT = 0x8;
    public static final int CHANNEL_IN_FRONT = 0x10;
    public static final int CHANNEL_IN_BACK = 0x20;
    public static final int CHANNEL_IN_LEFT_PROCESSED = 0x40;
    public static final int CHANNEL_IN_RIGHT_PROCESSED = 0x80;
    public static final int CHANNEL_IN_FRONT_PROCESSED = 0x100;
    public static final int CHANNEL_IN_BACK_PROCESSED = 0x200;
    public static final int CHANNEL_IN_PRESSURE = 0x400;
    public static final int CHANNEL_IN_X_AXIS = 0x800;
    public static final int CHANNEL_IN_Y_AXIS = 0x1000;
    public static final int CHANNEL_IN_Z_AXIS = 0x2000;
    public static final int CHANNEL_IN_VOICE_UPLINK = 0x4000;
    public static final int CHANNEL_IN_VOICE_DNLINK = 0x8000;
    public static final int CHANNEL_IN_MONO = CHANNEL_IN_FRONT;
    public static final int CHANNEL_IN_STEREO = (CHANNEL_IN_LEFT | CHANNEL_IN_RIGHT);
    /** @hide */
    public static final int CHANNEL_IN_FRONT_BACK = CHANNEL_IN_FRONT | CHANNEL_IN_BACK;
    // CHANNEL_IN_ALL is not yet defined; if added then it should match AUDIO_CHANNEL_IN_ALL

刚开始没看明白为什么这么定义。直到看到了...往下看,后面会说

//AudioRecord.java
public AudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat,                                                                              
            int bufferSizeInBytes)
    throws IllegalArgumentException {
        //调用另外一个重载的构造函数
        this((new AudioAttributes.Builder())
                    .setInternalCapturePreset(audioSource)
                    .build(),
                (new AudioFormat.Builder())                 .setChannelMask(getChannelMaskFromLegacyConfig(channelConfig,
                                        true/*allow legacy configurations*/))
                    .setEncoding(audioFormat)
                    .setSampleRate(sampleRateInHz)
                    .build(),
                bufferSizeInBytes,
                AudioManager.AUDIO_SESSION_ID_GENERATE);
    }

注意看这一行:

.setChannelMask(getChannelMaskFromLegacyConfig(channelConfig,
                                        true/*allow legacy configurations*/))

做一个兼容性转换。最终结果还是前面那些。关键是这里是Mask(中文叫掩码)。Android中有很多这种用法

private static int getChannelMaskFromLegacyConfig(int inChannelConfig,
            boolean allowLegacyConfig) {
        int mask;
        switch (inChannelConfig) {
        case AudioFormat.CHANNEL_IN_DEFAULT: // AudioFormat.CHANNEL_CONFIGURATION_DEFAULT
        case AudioFormat.CHANNEL_IN_MONO:
        case AudioFormat.CHANNEL_CONFIGURATION_MONO:
            mask = AudioFormat.CHANNEL_IN_MONO;
            break;
        case AudioFormat.CHANNEL_IN_STEREO:
        case AudioFormat.CHANNEL_CONFIGURATION_STEREO:
            mask = AudioFormat.CHANNEL_IN_STEREO;
            break;
        case (AudioFormat.CHANNEL_IN_FRONT | AudioFormat.CHANNEL_IN_BACK):
            mask = inChannelConfig;
            break;
        default:
            throw new IllegalArgumentException("Unsupported channel configuration.");
        }
​
        if (!allowLegacyConfig && ((inChannelConfig == AudioFormat.CHANNEL_CONFIGURATION_MONO)
                || (inChannelConfig == AudioFormat.CHANNEL_CONFIGURATION_STEREO))) {
            // only happens with the constructor that uses AudioAttributes and AudioFormat
            throw new IllegalArgumentException("Unsupported deprecated configuration.");
        }
​
        return mask;
    }

getChannelMaskFromLegacyConfig根本没对超过2个的声道就行处理。包括AudioFormat里的hide参数:

/** @hide */
    public static final int CHANNEL_IN_5POINT1 = (CHANNEL_IN_LEFT |
            CHANNEL_IN_RIGHT | CHANNEL_IN_FRONT | CHANNEL_IN_BACK |
            CHANNEL_IN_LEFT_PROCESSED | CHANNEL_IN_RIGHT_PROCESSED);

看了是打算先占个位置,将来会支持这种5.1声道的方式。那我们岂不是可以加上个同样的定义,比如说7.1声道:

/** @hide */
    public static final int CHANNEL_IN_7POINT1 = (CHANNEL_IN_LEFT |
            CHANNEL_IN_RIGHT | CHANNEL_IN_FRONT | CHANNEL_IN_BACK |
            CHANNEL_IN_LEFT_PROCESSED | CHANNEL_IN_RIGHT_PROCESSED|
            CHANNEL_IN_FRONT_PROCESSED | CHANNEL_IN_BACK_PROCESSED);

虽然感觉不太对。对应的,getChannelMaskFromLegacyConfig就需要做添加,不然直接抛出IllegalArgumentException:

//AudioRecord.java-getChannelMaskFromLegacyConfig
case AudioFormat.CHANNEL_IN_7POINT1:
            mask = AudioFormat.CHANNEL_IN_7POINT1:
            break;

然后,计算bufferSize的地方也要修改:

case AudioFormat.CHANNEL_IN_7POINT1:
            channelCount = 8;

再往下,貌似是不会被参数检查所拦截了。

只有channelCount够了,frameCount才能对,bufferSize才能对应。不然数据就错乱了。因为声道数,格式等决定了每一帧所需要的缓存空间!

另外一个设置channel_count的地方:

//hal/audio_hw.c
struct pcm_config pcm_config_audio_capture = {
    .channels = 2,
    .period_count = AUDIO_CAPTURE_PERIOD_COUNT,
    .format = PCM_FORMAT_S16_LE,
};

普通录音场景给了个默认的2 channel.然后

static int adev_open_input_stream(struct audio_hw_device *dev,
                                  audio_io_handle_t handle,
                                  audio_devices_t devices,
                                  //注意这个:
                                  struct audio_config *config,
                                  struct audio_stream_in **stream_in,
                                  audio_input_flags_t flags __unused,
                                  const char *address __unused,
                                  audio_source_t source)
{
    ...
    in->config = pcm_config_audio_capture;//此时是默认值2
    ...
    //这里会取出应用层设置下来的channel_count
    int channel_count = audio_channel_count_from_in_mask(config->channel_mask);
    //如果应用设置的不是2.修改之
    in->config.channels = channel_count;
    ...
}

这些都改完之后,全编译一把,然后:

int channelConfiguration = AudioFormat.CHANNEL_IN_7POINT1;
int audioEncodingBits = AudioFormat.ENCODING_PCM_16BIT;
int sampleRateInHz = 8000;
int recordBufferSize = AudioRecord.getMinBufferSize(sampleRateInHz, channelConfiguration, audioEncodingBits);
LogD("recordBufferSize = " + String.valueOf(recordBufferSize));

recordBufferSize = 5120(之前立体声1280的四倍)

表明,修改成功!Oh yeah!当然,这么修改之后,frameCount还是320(5120/(声道数×每个采样的字节数)).知道为啥定义帧的概念了吧。

稍等,好坑!

AudioRecord: set(): inputSource 6, sampleRate 8000, format 0x1, channelMask 0x3fc
...
audio_hw_primary: adev_open_input_stream: enter: sample_rate(8000) channel_mask(0xc)

看这log,AudioRecord:set之后,adev_open_input_stream之前会改变到这个channelMask.跟了下代码,发现是这里在搞怪:

audio_io_handle_t AudioPolicyManager::getInputForDevice(
    ...
    audio_channel_mask_t channelMask,
    ...)
{
    ...
    audio_channel_mask_t profileChannelMask = channelMask;
    for (;;) {
        //就是这里
        profile = getInputProfile(device,   address,profileSamplingRate, profileFormat,     profileChannelMask,profileFlags);
        if (profile != 0) {
            break; // success
        } else if (profileFlags & AUDIO_INPUT_FLAG_RAW) {
            profileFlags = (audio_input_flags_t) (profileFlags & ~AUDIO_INPUT_FLAG_RAW); // retry
        } else if (profileFlags != AUDIO_INPUT_FLAG_NONE) {
            profileFlags = AUDIO_INPUT_FLAG_NONE; // retry
        } else { // fail
            return input;
        }
    }
    ...
}

我们来看看getInputProfile

sp<IOProfile> AudioPolicyManager::getInputProfile(audio_devices_t device,const String8& address,uint32_t& samplingRate,audio_format_t& format,audio_channel_mask_t& channelMask,audio_input_flags_t flags)
{
    // Choose an input profile based on the requested capture parameters: select the first available
    // profile supporting all requested parameters.
    for (size_t i = 0; i < mHwModules.size(); i++)
    {
        if (mHwModules[i]->mHandle == 0) {
            continue;
        }   
        for (size_t j = 0; j < mHwModules[i]->mInputProfiles.size(); j++)
        {
            sp<IOProfile> profile = mHwModules[i]->mInputProfiles[j];
            // profile->log();
            if (profile->isCompatibleProfile(/*一堆参数*/) {
​
                return profile;
            }
        }
        //恕老夫眼拙,没看出来和上面的for循环有什么区别?????
        for (size_t j = 0; j < mHwModules[i]->mInputProfiles.size(); j++)
        {
            sp<IOProfile> profile = mHwModules[i]->mInputProfiles[j];
            // profile->log();
            if (profile->isCompatibleProfile(/*一堆参数同上*/) {
                                              
                return profile;
            }   
        }   
    }   
    return NULL;
}

基于请求的capture参数,选择一个input profile,选中第一个可用的。

看了会儿相关代码,都要跟吐了。不过,我感觉基本就是改

audio_policy_configuration.xml(Android O新加入的)或者audio_policy.conf了,加入8.1声道的支持。比如这样:

<mixPort name="primary input" role="sink">
                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                             samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000"
                             channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO,AUDIO_CHANNEL_IN_FRONT_BACK,AUDIO_CHANNEL_IN_8"/>

相应的,audo-base也要改一下。

//audio-base.h
//这个值应该要和java定义的对应0x3fc
AUDIO_CHANNEL_IN_8 = 1020u

再后来,发现,读入的时候,需要在这里加一下,不然,没法识别

//libmedia/TypeConverter.cpp
template <>
const InputChannelConverter::Table InputChannelConverter::mTable[] = {
    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_IN_MONO),
    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_IN_STEREO),
    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_IN_FRONT_BACK),
    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_IN_6),
    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_IN_VOICE_UPLINK_MONO),
    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_IN_VOICE_DNLINK_MONO),
    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_IN_VOICE_CALL_MONO),
    MAKE_STRING_FROM_ENUM(AUDIO_CHANNEL_IN_8),
    TERMINATOR
};

因为:

//Serializer.cpp
status_t AudioProfileTraits::deserialize(_xmlDoc */*doc*/, const _xmlNode *root, PtrElement &profile,
                                         PtrSerializingCtx /*serializingContext*/)
{
    string samplingRates = getXmlAttribute(root, Attributes::samplingRates);
    string format = getXmlAttribute(root, Attributes::format);
    string channels = getXmlAttribute(root, Attributes::channelMasks);
    profile = new Element(formatFromString(format, gDynamicFormat),
                          //这里
                          channelMasksFromString(channels, ","),
                          samplingRatesFromString(samplingRates, ","));
    
    profile->setDynamicFormat(profile->getFormat() == gDynamicFormat);
    profile->setDynamicChannels(profile->getChannels().isEmpty());
    profile->setDynamicRate(profile->getSampleRates().isEmpty());
    
    return NO_ERROR;
}

这样改完之后。就支持8channel录音了。当然了。。。如果tinyalsa的实现不支持,pcm_open的时候恐怕是要报错的。那就是另外一个话题了.

2019-05-15 14:56:03 weixin_43364172 阅读数 409

从大网可以拿到通话双方的录音文件,都是PCM编码的WAV文件。有客户的需求是只要一个通话录音文件,但是,要求文件包含双声道,每个声道对应通话一方。
在原来WAV文件头解析的基础上,完成起来还是比较容易的。

public class WavFileUtil {
    private static Logger LOG = LoggerFactory.getLogger(WavFileUtil.class);

    private static final int HEAD_LENGTH = 12;

    private static final int FORMAT_LENGTH = 24;

    private static boolean isWav(byte[] head) {
        return ("RIFF".equals(new String(head, 0, 4, ISO_8859_1)) &&
                "WAVE".equals(new String(head, 8, 4, ISO_8859_1)));
    }

    private static void fileTooSmall(byte[] file) {
        if (file.length < HEAD_LENGTH + FORMAT_LENGTH) {
            LOG.warn("file is too small, size if {}.", file.length);
            throw new IvcFileException();
        }
    }

    private static int headSize() {
        return HEAD_LENGTH + FORMAT_LENGTH;
    }

    /**
     * resolve wav file head.
     * ChunkID,ChunkSize,Format, everyone 4 bytes.
     */
    public static int fileSize(byte[] file) {
        fileTooSmall(file);

        byte[] head = copyOfRange(file, 0, HEAD_LENGTH);

        if (isWav(head)) {
            return ByteBuffer.wrap(copyOfRange(head, 4, 8))
                    .order(LITTLE_ENDIAN)
                    .getInt() + 8;
        } else {
            LOG.warn("file format error: expected {}, actual {}.",
                    "[82, 73, 70, 70, *, *, *, *, 87, 65, 86, 69]",
                    head);
            throw new IvcFileException();
        }
    }

    public static AudioFormat fileFormat(byte[] file) {
        fileTooSmall(file);

        byte[] head = copyOfRange(file, 0, HEAD_LENGTH);

        if (isWav(head)) {
            byte[] format = copyOfRange(file, 12, HEAD_LENGTH + FORMAT_LENGTH);
            String chuckID = new String(format, 0, 4, ISO_8859_1);
            int chunkSize = ByteBuffer.wrap(copyOfRange(format, 4, 8))
                    .order(LITTLE_ENDIAN).getInt();
            int audioFmt = ByteBuffer.wrap(copyOfRange(format, 8, 10))
                    .order(LITTLE_ENDIAN).getShort();
            int channels = ByteBuffer.wrap(copyOfRange(format, 10, 12))
                    .order(LITTLE_ENDIAN).getShort();
            int sampleRate = ByteBuffer.wrap(copyOfRange(format, 12, 16))
                    .order(LITTLE_ENDIAN).getInt();
            int byteRate = ByteBuffer.wrap(copyOfRange(format, 16, 20))
                    .order(LITTLE_ENDIAN).getInt();
            int frameSize = ByteBuffer.wrap(copyOfRange(format, 20, 22))
                    .order(LITTLE_ENDIAN).getShort();
            int sampleSizeInBits = ByteBuffer.wrap(copyOfRange(format, 22, 24))
                    .order(LITTLE_ENDIAN).getShort();

            return new AudioFormat(PCM_SIGNED, sampleRate,
                    sampleSizeInBits, channels, frameSize, sampleRate, false);
        } else {
            LOG.warn("file is not a wav.");
            throw new IvcFileException();
        }
    }

    public static void merge(final byte[] left, final byte[] right, final String path) {
        int leftSize = fileSize(left);
        int rightSize = fileSize(right);
        int mergeSize = mergeSizeField(leftSize, rightSize);
        int mergeDataSize = mergeDataSize(leftSize, rightSize);

        try (RandomAccessFile file = new RandomAccessFile(path, "rw")) {
            file.write(mergeHead(left, mergeSize));
            file.write(dataChunkHead(mergeDataSize));
            int max = Math.max(leftSize, rightSize);
            for (int i = headSize() + 8; i < max + 8; i += 2) {
                file.write(read(left, i));
                file.write(read(right, i));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static byte[] read(final byte[] content, int offset) {
        if (content.length > offset) {
            return copyOfRange(content, offset, offset + 2);
        } else {
            return "\0\0".getBytes(ISO_8859_1);
        }
    }

    private static int mergeSizeField(int left, int right) {
        int max = Math.max(left - 8, right - 8);
        return max * 2;
    }

    private static int mergeDataSize(int left, int right) {
        int max = Math.max(left - headSize() - 8, right - headSize() - 8);
        return max * 2;
    }

    private static byte[] mergeHead(final byte[] left, final int mergeSize) {
        AudioFormat format = fileFormat(left);
        ByteBuffer size = ByteBuffer.allocate(4).order(LITTLE_ENDIAN).putInt(mergeSize);

        ByteBuffer channels = ByteBuffer.allocate(2).order(LITTLE_ENDIAN).putShort((short) 2);
        ByteBuffer sampleRate = ByteBuffer.allocate(4).order(LITTLE_ENDIAN)
                .putInt((int) format.getSampleRate());
        ByteBuffer byteRate = ByteBuffer.allocate(4).order(LITTLE_ENDIAN)
                .putInt((int) format.getSampleRate() * 2 * format.getSampleSizeInBits() / 8);
        ByteBuffer blockAlign = ByteBuffer.allocate(2).order(LITTLE_ENDIAN)
                .putShort((short) (2 * format.getSampleSizeInBits() / 8));
        ByteBuffer bitsPerSample = ByteBuffer.allocate(2).order(LITTLE_ENDIAN)
                .putShort((short) format.getSampleSizeInBits());

        ByteBuffer head = ByteBuffer.allocate(headSize());
        head.put(left, 0, 4);

        head.put(size.array());
        head.put(left, 8, 14);

        head.put(channels.array());
        head.put(sampleRate.array());
        head.put(byteRate.array());
        head.put(blockAlign.array());
        head.put(bitsPerSample.array());
        return head.array();
    }

    private static byte[] dataChunkHead(final int length) {
        ByteBuffer head = ByteBuffer.allocate(8);
        head.put("data".getBytes(ISO_8859_1));
        ByteBuffer size = ByteBuffer.allocate(4).order(LITTLE_ENDIAN).putInt(length);
        head.put(size.array());
        return head.array();
    }
}

文件合并的入口是merge方法,另外,mergeHead方法生成新的文件头,dataChunkHead方法生成新的数据chunk头。

使用实际的录音文件做一下测试:

    @Test
    public void merge1() {
        try {
            byte[] left = Files.readAllBytes(Paths.get("recordFiles/", "in-1.wav"));
            byte[] right = Files.readAllBytes(Paths.get("recordFiles/", "out-1.wav"));

            merge(left, right, "recordFiles/11111.wav");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

生成的文件正确,通话的一方占据一个声道。

没有更多推荐了,返回首页