Android audio篇章(0)------音频基础概念

音频基础

1、 音频基础概念介绍:

1.1、声卡:

声卡 (Sound Card)也叫音频卡,是计算机多媒体系统中最基本的组成部分,是实现声波/数字信号相互转换的一种硬件。

声卡的基本功能是把来自话筒、磁带、光盘的原始声音信号加以转换,输出到耳机、扬声器、扩音机、录音机等声响设备,或通过音乐设备数字接口(MIDI)发出合成乐器的声音。

声卡其实说白了,就是:
ADC(模数转换器)-mic;通过麦克风采集到的模拟信号,转换成对应的数字信号。
DAC(数模转换器)-speaker。将从内存中读取到的音频文件数据,数字信号转换为模拟信号从喇叭播放出去。
下图为电脑端的声卡显示
电脑板载声卡

1.2、采样率

表示每秒采集多少次声音数据,以秒为单位,大部分会将其转换为ms(毫秒)计算.
采样率反应了 数字信号对模拟信号的还原度。

为了更加清晰的理解采样率的概念,我们贴出两个不同采样率的正弦波。
如下图:音频1 ,48KHz; 音频2 ,384KHz。
但是,改图并不能看出采样点不同的区别。我们往下看。
在这里插入图片描述
将波形放大——,便有了下面这幅图。
在红色的竖线范围内,是不是能够明显的看到384KHz的采样点非常密集,远远超过48KHz的采样点。是不是能更加真实的反应出模拟信号的正弦波了。
在这里插入图片描述

1.3、采样深度

表示每次采集声音数据的大小,单位为位(bit)。

音频的比特深度即采样精度是与音频文件的动态范围相挂钩的,说得直白一点,就是音频的声音大小的范围。
我们一般将数字音频的电平的峰值定为0dBFS,正常的电平都是0dBFS以下的负值。

以16bit的格式为例,该格式可取的动态范围为2的16次方即65536个单位,换算成dB,即20×log(65536)≈96dB。
也就是说,这个格式的动态范围为96dB。值得一提的是,以往实体唱片的CD格式都是采用16bit的采样精度的。
那么,其他比特深度的动态范围的计算也都是同理了。通过公式20×log(2^n)我们可以得出24bit和32bit的动态范围分别为144dB和193dB(取整数)。

位深度决定动态范围。采样声波时,为每个采样指定最接近原始声波振幅的振幅值。较高的位深度可提供更多可能的振幅值,产生更大的动态范围、更低的噪声基准和更高的保真度。

一般来说 PCM 数据中的波形幅值越大,代表音量越大,对于 PCM 音频数据而言,它的幅值(即该采样点采样值的大小)代表音量的大小。如果我们需要降低某个声道的音量,可以通过减小某个声道的数据的值来实现降低某个声道的音量。

1.4、帧(frame)

Frame是一个单位,用来描述数据量的多少。1单位的Frame = 1个采样点的字节数×声道数。

音频帧的播放时间
		= 采集一帧的时间 
		= 一个帧对应的采样样本的个数/采样频率
		= period_size / sample

2、 音频数据的处理:

2.1、调整音量

将一段mic+ref的 48K 16bit 4ch的音频原始数据(其中ch1、ch2为mic音;ch3、ch4位ref音)进行音量调制。
也就是说自定义 调整音频数据的音量大小。

“程序员之间最好的沟通方式就是 给我看你的代码”。

2.1.1 用位运算实现有符号16bit数据clamp
static inline int16_t clamp16(int sample) 
{
	if ((sample>>15) ^ (sample>>31))
		sample = 0x7FFF ^ (sample>>31);

	return sample;
}
2.1.2 dump音频原始数据

用于数据源或者重要转换节点处, 取出该节点的音频数据以文件的形式

void dump_data(const void *buffer, size_t bytes, char* str)
{
	FILE *fd = NULL;
	char name[1024];

	snprintf(name, 1024, "/data/misc/audioserver/audio_dump_48k_16bit_%s.pcm", str);
	fd = fopen(name, "ab+");
	if (fd == NULL) {
		ALOGE("open %s fail,(%s)%d, maybe adb shell touch %s",
	    							name, strerror(errno), errno, name);
		return;
	}
	fwrite(buffer, bytes, 1, fd);
	ALOGD("write %s -> %zu bytes", name, bytes);
	fclose(fd);

}
2.1.3 调整音量增益
void yy_audio_tuning(void)
{
    char* src = NULL;
    int32_t src_bytes = 1024;  //一包数据的大小

    pcm_read(pcm->alsa_pcm, src, src_bytes); //读取1K字节数据,写进src地址中

    char prop_value[100];
    int16_t* src_buff = (int16_t*)src;   //将char型的音频数据转化为int型

    if (property_get("audiohw.in.micvolume", prop_value, NULL) > 0) {
        float volume = atof(prop_value);
        int frame_size = src_bytes / 8;     //16bit 4ch,得到1K字节数据有多少 frames
        int16_t* des = src_buff;

        AALOGI("(+)%s, micvolume---- volume : %f", __func__, volume);

        for (int i = 0; i < frame_size; i++) {
            *des++ = clamp16(((int32_t)(*(src_buff + i * 4) * volume))); 		// 1ch: mic1
            *des++ = clamp16(((int32_t)(*(src_buff + 1 + i * 4) * volume)));    // 2ch: mic2

            des += 2;
        }
    }

    if (property_get("audiohw.in.refvolume", prop_value, NULL) > 0) {
        float volume = atof(prop_value);
        int frame_size = src_bytes / 8;     //16bit 4ch
        int16_t* des = src_buff;

        AALOGI("(+)%s, refvolume---- volume : %f", __func__, volume);

        for (int i = 0; i < frame_size; i++) {
            des += 2;

            *des++ = clamp16(((int32_t)(*(src_buff + 2 + i * 4) * volume))); 	// 3ch: ref1
            *des++ = clamp16(((int32_t)(*(src_buff + 3 + i * 4) * volume)));    // 4ch: ref2
        }
    }
    dump_data(src, src_bytes, "4ch_capture_input");

    pcm_write(src, src_bytes);
}

音频数据增益前后对比:

对ref音量增益 *10。执行命令:setprop audiohw.in.refvolume 10在这里插入图片描述

2.2、Fade-out 渐入 ,Fade-in 渐出

2.2.1、作用

采用淡进淡出的处理,也就是打开的时候,逐渐增大音量,关断的时候,逐渐减小音量。能够防止POP音的产生。

2.2.2、Fade-in

fade-in的波形:渐渐进入。
在这里插入图片描述
在这里插入图片描述
至于代码上的处理,则就比较简单了。对原始音频数据进行1/100,2/100,3/100…99/100等差比例的形式进行缓慢增加。

uint32_t Fader_In(void* buffer)
{
    int* pbuf = (int*)buffer;
    static int stepPosition = 0int mFadeInTime = 16*7; //mFadeInTime 为自己定义的,可以理解为 一段数据从1/100 -> 99/100的 fadein的时间
    int frameCont = mFadeInTime / 16 * 768; // 对于48K音频来说,假设alsa的一包数据为 16ms data = 768 frame
     
    double step = 1.00 / frameCont;

    if (stepPosition >= frameCont) {
        return 0;
    }

    for (int i = 0; i < 768; i++) {
        if (stepPosition < frameCont) {
            double stepTemp = stepPosition * step;
            double bufTemp = pbuf[i] * stepTemp;
            pbuf[i] = (int)bufTemp;
            stepPosition++;
        }
        else {
            break;
        }
    }
    return 0;
}
  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值