音频编解码、数据流、音效处理

音频编解码

音频编码

编码流程

音频编码用于把PCM数据通过一定编码器压缩成对应的数据。

编码架构

音频解码

音频解码 用于把数据通过一定解码器转换成PCM数据。

 解码架构

编解码格式

按压缩程度区分:

不压缩的格式(UnCompressed Audio Format):PCM数据、wav(PCM类型)。
无损压缩格式(Lossless Compressed Audio Format):FLAC、APE、dts、m4a。
有损压缩格式(Lossy Compressed Audio Format):mp3、wma、wav(ADPCM等

按数据来源区分:

流媒体格式(一帧一帧的数据):sbc、msbc、cvsd、aac。
文件格式(音频文件):mp3、wav、wma、flac、ape、m4a、amr、dts、alac。
既可以用于流媒体解码又可以用于文件解码的格式:MSBC

按技术标准区分:

标准格式(国际通用):mp3、wav、wma、flac、ape、m4a、sbc、msbc、cvsd。
自定义格式(仅本司SDK可以使用):G729(wtg)、G726、MTY、wtgv2。

文件名后缀

一般音频格式都有其固定的文件名后缀,如mp3格式:123.mp3
同一文件名后缀,格式可能不同。如wav后缀,可能是adpcm或者dts格式;mp4后缀,可能是m4a或者alac格式。
同一格式,文件名后缀也可能不同。如dts格式,可能是dts或者wav后缀。

格式检查

一般流媒体格式的数据不需要做格式检查,由应用层指定解码格式,如sbc等。
文件解码格式会有格式检查,格式检查正确后才使用对应的解码器解码,如wma等。
mp3的格式检查较弱,如果是其他格式的音频(如wav)使用mp3解码器格式检查,也有可能检查正确,因此,mp3解码器应放在最后检查

空间冲突

代码里面的空间分配在sdk_ld.c中,编译之后的空间占用可以在sdk.map中查看。
overlay里面的空间是共用的,同时只能一个分支。比如正在使用overlay_wma的时候就不能同时使用overlay_mp3,因此假设需要用mp3解码做提示音的时候,就需要把overlay_mp3中的mp3_mem等段定义全部放到非overlay区域中去。

音频数据流

数据流简介

数据流 是一串连续不断的数据的集合,就象水管里的水流,在水管的一端一点一点地供水,而在水管的另一端看到的是一股连续不断的水流。

示例:

// 数据流节点初始化
audio_decoder_open(&audio_dec.decoder); // 打开解码任务
audio_eq_open(&eq); // 打开eq
audio_dac_new_channel(&dac_hdl, &default_dac); 	// 打开DAC
// 创建数据流
stream = audio_stream_open(&audio_dec.decoder, stream_resume_cb);
// 数据流串联
struct audio_stream_entry *entries[4];
entries[0] = &audio_dec.decoder.entry;
entries[1] = &eq.entry;
entries[2] = &default_dac.entry;
audio_stream_add_list(stream, entries, 3);

音频数据流分流处理

分流 是在原数据流的基础上拷贝一份数据,从这份数据开始重新创建一条新的数据流。

示例:

// 数据流节点初始化
audio_decoder_open(&audio_dec.decoder); // 打开解码任务
audio_eq_open(&eq); // 打开eq
audio_eq_open(&eq1); // 打开eq1
audio_dac_new_channel(&dac_hdl, &default_dac); 	// 打开DAC
audio_iis_open(&iis);                  //  打开IIS
// 创建数据流
stream = audio_stream_open(&audio_dec.decoder, stream_resume_cb);
// 添加解码
audio_stream_add_first(stream, &audio_dec.decoder.entry);
// 添加解码第一路输出流
audio_stream_add_entry(&audio_dec.decoder.entry, &eq.entry);
audio_stream_add_entry(&eq.entry, &default_dac.entry);
// 添加解码第一路输出流
audio_stream_add_entry(&audio_dec.decoder.entry, &eq1.entry);
audio_stream_add_entry(&eq1.entry, &iis.entry);

 音频数据流群组处理

群组 是把多个数据流关联上,当激活尾部的数据流时,能依次激活群组里面的其他数据流。

 示例:

// 创建数据流
stream0 = audio_stream_open(&dec0.decoder, stream0_resume_cb);
stream1 = audio_stream_open(&dec1.decoder, stream1_resume_cb);
stream_n = audio_stream_open(&mixer.decoder, stream_n_resume_cb);
// 数据流0
audio_stream_add_first(stream0, &dec0.decoder.entry);
audio_stream_add_entry(&dec0.decoder.entry, &mix0.entry);
// 数据流1
audio_stream_add_first(stream1, &dec1.decoder.entry);
audio_stream_add_entry(&dec1.decoder.entry, &mix1.entry);
// 数据流n
audio_stream_add_first(stream_n, &mixer.entry);
audio_stream_add_entry(&mixer.entry, &default_dac.entry);
// 关联数据流,把数据流n作为群组的尾部数据流
audio_stream_group_add_entry(&test_group, &mix0.entry);
audio_stream_group_add_entry(&test_group, &mix1.entry);
mixer.entry.group = &test_group;

注意事项

1.自定义数据流节点 - 输入输出buf相同

输入输出buf相同是指数据流节点直接更改 in->data 里面的内容,不使用额外的buf来作为输出,例如数字音量处理。

// 数据处理
static int demo_data_handler(struct audio_stream_entry *entry,
                             struct audio_data_frame *in,
                             struct audio_data_frame *out)
{
    struct demo_hdl *demo = container_of(entry, struct demo_hdl, entry);
    // 设置输出信息,这里输出和输入相同
    out->data = in->data;
    out->data_len = in->data_len;
    if (in->data_len - in->offset > 0) {// 数据处理
        demo_deal(demo, in->data + in->offset / 2, in->data_len - in->offset);
    }
    return in->data_len;
}

2.自定义数据流节点 - 输入输出buf不同

输入输出buf不同是指数据流节点处理后,用额外的buf来保存输出数据,例如变采样处理。

static int demo_data_handler(struct audio_stream_entry *entry,
                             struct audio_data_frame *in,
                             struct audio_data_frame *out)
{
    struct demo_hdl *demo = container_of(entry, struct demo_hdl, entry);
    out->sample_rate = demo->out_sr;// 设置输出信息
    if (demo->remain_len) {// 输出剩余数据
        out->data_len = demo->remain_len;
        out->data = demo->remain_buf;
        return 0;
    }
    int len = demo_deal(demo, in->data, in->data_len);// 数据处理
    out->data_len = demo->remain_len;// 更改信息
    out->data = demo->remain_buf;
    return len;
}

3.添加数据流节点1

以数组方式添加节点,调用audio_stream_add_list()函数添加。该方式可以快速把多个节点链接在一起,方便更改顺序等。

// 数组方式添加节点                                    
struct audio_stream_entry *entries[8] = {NULL};        
u8 entry_cnt = 0;                                      
entries[entry_cnt++] = &dec->decoder.entry;            
entries[entry_cnt++] = &dec->eq_drc->entry;            
entries[entry_cnt++] = &default_dac->entry;            
audio_stream_add_list(dec->stream, entries, entry_cnt);

4.添加数据流节点2

以单个方式依次添加节点,调用audio_stream_add_entry()函数添加。该方式的第一个节点需要用audio_stream_add_first()函数。

// 单个方式依次添加节点
audio_stream_add_first(dec->stream, &dec->decoder.entry);
audio_stream_add_entry(&dec->decoder.entry, 
                       &dec->eq_drc->entry);
audio_stream_add_entry(&dec->eq_drc->entry, 
                       &default_dac->entry);

5.添加数据流节点3

在原数据流中添加节点。调用audio_stream_add_head()/audio_stream_add_tail()函数添加。该方式用于在已经创建好的数据流链表中加入节点,所有方式的添加都是在数据流使用之前处理。

// 在原数据流中添加节点
audio_stream_add_head(dec->stream, &dec->eq_drc1->entry);
audio_stream_add_tail(dec->stream, &dec->dac->entry);

6.参数处理

数据流节点处理函数中,*in参数带有一些音频信息,如:采样率、声道等。当前数据流节点对于信息敏感的,需要处理该参数。例如eq处理。

static int eq_data_handler(struct audio_stream_entry *entry,
                             struct audio_data_frame *in,
                             struct audio_data_frame *out)
{
    struct eq_hdl *eq = container_of(entry, struct eq_hdl, entry);
    if (eq->sr != in->sample_rate) {//参数判断
        eq->sr = in->sample_rate;
        audio_eq_set_samplerate(eq, eq->sr);
    }
    int len = eq_deal(eq, in->data, in->data_len);// 数据处理
    out->data_len = eq->remain_len;// 更改信息
    out->data = eq->remain_buf;
    return len;
}

7.本次没输出完,下次继续输出

数据流节点处理本次没有输出完成的情况下,下次数据流过来应该先输出上一次的剩余数据,全部输出完后才继续处理。

// data_handler函数里的处理
if (demo->remain_len) { // 输出剩余数据
    struct audio_data_frame frame;
    memcpy(&frame, in, sizeof(frame));// 复制原输出信息
    // 更改信息
    frame.sample_rate = demo->out_sr;
    frame.data_len = demo->remain_len;
    frame.data = demo->remain_buf;
    audio_stream_run(entry, &frame);// 数据流输出
    if (demo->remain_len) {// 还没输出完,退出
        return 0;
    }
}
// 数据处理
int len = demo_deal(demo, in->data, in->data_len);

8.激活机制

数据流节点处理处理数据时,如果本次不能处理完全部的 in->data_len数据,那么上层应用应该挂起该数据流。当该节点可以再次处理时激活数据流。如果是后级数据流无法输出,则由后级无法输出的数据流节点激活。

static int demo_data_handler(struct audio_stream_entry *entry,
                             struct audio_data_frame *in,
                             struct audio_data_frame *out)
{
    struct demo_hdl *demo = container_of(entry, struct demo_hdl, entry);
    run_len = in->data_len > 512 ? 512 : in->data_len;
    demo->need_resume = (run_len == in->data_len) ? 0 : 1;
    demo_deal(demo, in->data, run_len);// 处理数据
    out->data_len = run_len;
    out->data = demo->buf;
    return len;
}
// 当可以再次处理的时候再激活
if (demo->need_resume) {
    audio_stream_resume(&demo->entry);
}

音效处理

声音的特性

音调 是指声音的高低,就是我们平时所说的高音,低音。音调由物体振动的频率决定的,频率越高,声音的音调就越高,反正,音调则低。

响度 是指声音的强弱,也就是我们日常生活中常说的声音的大小。声音的响度是由物体振动时的振幅决定的。

音色 是指声音的特色、品质,由声音波形的谐波频谱和包络决定。主要与发声体的材料、结构有关。同样是演奏《梁祝》,我们却能听出二胡演奏和钢琴演奏的不同,因为二者的结构不同,发出声音的音色不同。

 音效处理简介

音效处理就是把一段音频进行二次编辑,达到改变音乐风格等目的。

 更改音调、响度

1.简单的更改声音的音调就可以达到声音变化的效果。比如,把一段48Khz的音频改用8Khz来播放,就可以达到类似树懒(疯狂动物城,闪电)说话的效果。

2.更改声音的响度是改变了声音的大小。一般我们会针对不同的频率来调节响度,比如,把低频的响度增加,就可以达到重低音的效果。

高级处理

一般采用叠加的方式来处理音频。
叠加相对独立的音频,比如KTV里面的麦克风和音乐背景声的叠加。
叠加反向音频,这种情况下音频会被消除,比如接打电话时的降噪功能。
反复叠加某一段音频,这种就是混响(回声)的工作机制。

补充 - 相位

相位 (phase)是对于一个波,特定的时刻在它循环中的位置:一种它是否在波峰、波谷或它们之间的某点的标度。相位发生在周期性的运动之中。相位最直接的理解是角度。这个角度存在于匀速圆周运动之中。实际应用过程中,可能会用到相位处理来辅助其他音效处理。比如做麦克风降噪的时候,就用到反向相位来消除噪声。

音效示例-EQ\DRC

EQ 均衡器(Equalizer),用来调节音频中各种频率的幅度值。比如通过调节eq来得到重低音、流行音等音效。也用于补偿喇叭、麦克风等物理元器件引起的缺陷
DRC Dynamic Range Control动态范围控制提供压缩和放大能力。一般用于eq之后,防止eq调节幅度太大溢出。

音效示例-高低音

高低音 是EQ/DRC的一种简单应用,仅调节音频的高音和低音两个频点(默认125Hz、12000Hz)。

音效示例-混响、回音

混响 reverberation(残响),当一个声音发出后,当它碰到障碍物后会反射,碰到下一个障碍物会再反射,不停反射直至它的能量消失为止。这个持续在空间中反覆反射动作形成的声音集成,就是残响。
回音 echo算是混响的一种,当回声与原始声音直接的间隔较大时,如 >200ms,我们耳朵能分辨出两个声音的就是 Echo

音效示例-噪声抑制

噪声抑制 noisegate,这里是指混响过程中的麦克风声音门限处理。

音效示例-啸叫抑制

啸叫抑制 howling suppression。声源与扩音设备之间因距离过近等问题导致能量发生自激,产生啸叫。啸叫不仅会影响听觉,也会烧坏音响设备。技术上通过移频器(升高或降低输入音频信号的频率,改变频率的输出信号再次进入系统不会和原始信号频率叠加)、陷波器(通过降低啸叫频率点处增益,破坏啸叫产生的增益条件)等方式抑制啸叫。

音效示例-变声

变声 pitch,通过改变声音的频率等达到变速变调的效果,如女声变男声、怪兽音等。

环绕音 surround,是指通过放置音源于不同的位置,通过解码器解码,把声音按照不同时间在不同的音箱里播放出来。环绕声完美地再现声音的层次感,增加了听者对临场感的体验。这里指利用现代电声技术在不改变左右声道扬声器位置的情况下,对左右声道的各频率成分的音量与相位分别进行调节,形成音响的全方位的空间立体感

等响度 equalloudness。声音实际响度和人耳实际感受的响度并不完全呈线性关系,在小音量的时候,人耳对中高频的听觉会有生理性衰减,音量越小,这种衰减越明显。等响度控制其作用是在低音量时提升高频和低频成分的音量,使得低、中、高部分的响度比例保持和在大音量时的响度比例相同

音效示例-人声消除

人声消除 vocal_remove,是一种可以将立体声歌曲的人声消除的技术。人声的声波波形在歌曲的两个声道是相同或者相似的,因此,我们可以简单采取两个声道相减的办法来消除立体声歌曲中的人声。但是,这样做有时会损失歌曲中的低音。因此,常用的人声消除会对低音进行补偿等。

ENC Environmental Noise Cancellation,环境降噪技术,能有效抑制反向环境噪声。我们主要通过agc 数字放大、aec 回声消除、es 回声压制、ans 降噪模块等技术手段实现降噪功能。

ANC Active Noise Control,主动降噪,是麦克风收集外部的环境噪音,通过机器学习等方式,把环境噪声变换为一个反相的声波加到喇叭端,最终人耳听到的声音是:环境噪音+反相的环境噪音,两种噪音叠加从而实现感官上的噪音降低。

  • 30
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python是一种功能强大的程语言,可用于视音频解码。首先,Python提供了许多第三方库,如OpenCV、PIL、Pygame和MoviePy等,使得处理图像和视频变得非常方便。这些库可以用于读取、处理和显示图像和视频文件。 对于音频解码,Python也有相应的库和工具。例如,Pydub是一个用于处理音频文件的流行库,它可以用于读取、剪辑、合并和转换不同格式的音频文件。此外,Python还支持音频码格式,如WAV、MP3和FLAC,可以使用相应的库进行解码处理。 使用Python进行视音频解码的好处之一是它的跨平台性。Python支持多个操作系统,包括Windows、Linux和Mac OS,因此可以在不同的平台上进行视音频处理。此外,Python还可以与其他程语言进行集成,如C++和Java,以提供更多的功能和性能。 尽管Python在视音频解码方面具有一定的能力,但对于高性能的需求和复杂的算法,其他语言如C或C++可能更适合。此外,Python在处理大型视频文件时可能会受到性能限制,需要更高级别的处理。因此,对于大型和高性能的视音频解码任务,可能需要结合其他程语言来实现。 总之,Python作为一种多用途的程语言,可以用于简单的视音频解码任务。它提供了丰富的库和工具,使得处理图像和音频变得更加容易和便捷。然而,在处理复杂的算法和大型文件时,可能需要结合其他语言来获得更高的性能和功能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值