android系统中: 怎么在 设置--声音--usb音频 无声音设备管理 添加一个USB设备

下次自动登录
现在的位置:
& 综合 & 正文
linphone在android中,怎么读取音频之探索过程!终于找到它是怎么读声音的!
/linphone/console/linphonec.c
main (int argc, char *argv[]) {
linphonec_vtable.call_state_changed=linphonec_call_state_
linphonec_vtable.notify_presence_recv = linphonec_notify_presence_
linphonec_vtable.new_subscription_request = linphonec_new_unknown_
linphonec_vtable.auth_info_requested = linphonec_prompt_for_
linphonec_vtable.display_status = linphonec_display_
linphonec_vtable.display_message=linphonec_display_
linphonec_vtable.display_warning=linphonec_display_
linphonec_vtable.display_url=linphonec_display_
linphonec_vtable.text_received=linphonec_text_
linphonec_vtable.dtmf_received=linphonec_dtmf_
linphonec_vtable.refer_received=linphonec_display_
linphonec_vtable.notify_recv=linphonec_notify_
linphonec_vtable.call_encryption_changed=linphonec_call_encryption_
if (! linphonec_init(argc, argv) ) exit(EXIT_FAILURE);
linphonec_main_loop (linphonec);
linphonec_finish(EXIT_SUCCESS);
exit(EXIT_SUCCESS); /* should never reach here */
linphone_init:
static int
linphonec_init(int argc, char **argv)
//g_mem_set_vtable(&dbgtable);
* Set initial values for global variables
mylogfile = NULL;
#ifndef _WIN32
snprintf(configfile_name, PATH_MAX, "%s/.linphonerc",
getenv("HOME"));
snprintf(zrtpsecrets, PATH_MAX, "%s/.linphone-zidcache",
getenv("HOME"));
#elif defined(_WIN32_WCE)
strncpy(configfile_name,PACKAGE_DIR "\\linphonerc",PATH_MAX);
mylogfile=fopen(PACKAGE_DIR "\\" "linphonec.log","w");
printf("Logs are redirected in" PACKAGE_DIR "\\linphonec.log");
snprintf(configfile_name, PATH_MAX, "%s/Linphone/linphonerc",
getenv("APPDATA"));
snprintf(zrtpsecrets, PATH_MAX, "%s/Linphone/linphone-zidcache",
getenv("APPDATA"));
/* Handle configuration filename changes */
switch (handle_configfile_migration())
case -1: /* error during file copies */
fprintf(stderr,
"Error in configuration file migration\n");
case 0: /* nothing done */
case 1: /* migrated */
#ifdef ENABLE_NLS
if (NULL == bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR))
perror ("bindtextdomain failed");
#ifndef __ARM__
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
textdomain (GETTEXT_PACKAGE);
printf ("NLS disabled.\n");
linphonec_parse_cmdline(argc, argv);
if (trace_level & 0)
if (logfile_name != NULL)
mylogfile = fopen (logfile_name, "w+");
if (mylogfile == NULL)
mylogfile =
fprintf (stderr,
"INFO: no logfile, logging to stdout\n");
linphone_core_enable_logs(mylogfile);
linphone_core_disable_logs();
* Initialize auth stack
auth_stack.nitems=0;
* Initialize linphone core
linphonec=linphone_core_new (&linphonec_vtable, configfile_name, factory_configfile_name, NULL);
linphone_core_set_zrtp_secrets_file(linphonec,zrtpsecrets);
linphone_core_enable_video(linphonec,vcap_enabled,display_enabled);
if (display_enabled && window_id != 0)
printf ("Setting window_id: 0x%x\n", window_id);
linphone_core_set_native_video_window_id(linphonec,window_id);
linphone_core_enable_video_preview(linphonec,preview_enabled);
if (!(vcap_enabled || display_enabled)) printf("Warning: video is disabled in linphonec, use -V or -C or -D to enable.\n");
#ifdef HAVE_READLINE
* Initialize readline
linphonec_initialize_readline();
#if !defined(_WIN32_WCE)
* Initialize signal handlers
signal(SIGTERM, linphonec_finish);
signal(SIGINT, linphonec_finish);
#endif /*_WIN32_WCE*/
看里面的**************Initialize linphone core******************
* Initialize linphone core
linphonec=linphone_core_new (&linphonec_vtable, configfile_name, factory_configfile_name, NULL);
linphone_core_set_zrtp_secrets_file(linphonec,zrtpsecrets);
linphone_core_enable_video(linphonec,vcap_enabled,display_enabled);
if (display_enabled && window_id != 0)
printf ("Setting window_id: 0x%x\n", window_id);
linphone_core_set_native_video_window_id(linphonec,window_id);
linphone_core_enable_video_preview(linphonec,preview_enabled);
if (!(vcap_enabled || display_enabled)) printf("Warning: video is disabled in linphonec, use -V or -C or -D to enable.\n");
*************************
-----------&linphonecore.c------&[linphone_core_new]
LinphoneCore *linphone_core_new(const LinphoneCoreVTable *vtable,
const char *config_path, const char *factory_config_path, void * userdata)
LinphoneCore *core=ms_new(LinphoneCore,1);
linphone_core_init(core,vtable,config_path, factory_config_path, userdata);
-----------&linphonecore.c------&[linphone_core_init]
static void linphone_core_init (LinphoneCore * lc, const LinphoneCoreVTable *vtable, const char *config_path,
const char *factory_config_path, void * userdata)
memset (lc, 0, sizeof (LinphoneCore));
lc-&ringstream_autorelease=TRUE;
memcpy(&lc-&vtable,vtable,sizeof(LinphoneCoreVTable));
linphone_core_set_state(lc,LinphoneGlobalStartup,"Starting up");
ortp_init();
lc-&dyn_pt=96;
linphone_core_assign_payload_type(lc,&payload_type_pcmu8000,0,NULL);
linphone_core_assign_payload_type(lc,&payload_type_gsm,3,NULL);
linphone_core_assign_payload_type(lc,&payload_type_pcma8000,8,NULL);
linphone_core_assign_payload_type(lc,&payload_type_speex_nb,110,"vbr=on");
linphone_core_assign_payload_type(lc,&payload_type_speex_wb,111,"vbr=on");
linphone_core_assign_payload_type(lc,&payload_type_speex_uwb,112,"vbr=on");
linphone_core_assign_payload_type(lc,&payload_type_telephone_event,101,"0-11");
linphone_core_assign_payload_type(lc,&payload_type_g722,9,NULL);
#if defined(ANDROID) || defined (__IPHONE_OS_VERSION_MIN_REQUIRED)
/*shorten the DNS lookup time and send more retransmissions on mobiles:
- to workaround potential packet losses
- to avoid hanging for 30 seconds when the network doesn't work despite the phone thinks it does.
_linphone_core_configure_resolver();
#ifdef ENABLE_NONSTANDARD_GSM
PayloadType *
pt=payload_type_clone(&payload_type_gsm);
pt-&clock_rate=11025;
linphone_core_assign_payload_type(lc,pt,-1,NULL);
pt-&clock_rate=22050;
linphone_core_assign_payload_type(lc,pt,-1,NULL);
payload_type_destroy(pt);
#ifdef VIDEO_ENABLED
linphone_core_assign_payload_type(lc,&payload_type_h263,34,NULL);
linphone_core_assign_payload_type(lc,&payload_type_theora,97,NULL);
linphone_core_assign_payload_type(lc,&payload_type_h263_1998,98,"CIF=1;QCIF=1");
linphone_core_assign_payload_type(lc,&payload_type_mp4v,99,"profile-level-id=3");
linphone_core_assign_payload_type(lc,&payload_type_h264,102,"profile-level-id=428014");
linphone_core_assign_payload_type(lc,&payload_type_vp8,103,NULL);
linphone_core_assign_payload_type(lc,&payload_type_x_snow,-1,NULL);
/* due to limited space in SDP, we have to disable this h264 line which is normally no more necessary */
/* linphone_core_assign_payload_type(&payload_type_h264,-1,"packetization-mode=1;profile-level-id=428014");*/
/*add all payload type for which we don't care about the number */
linphone_core_assign_payload_type(lc,&payload_type_ilbc,-1,"mode=30");
linphone_core_assign_payload_type(lc,&payload_type_amr,-1,"octet-align=1");
linphone_core_assign_payload_type(lc,&payload_type_amrwb,-1,"octet-align=1");
linphone_core_assign_payload_type(lc,&payload_type_lpc1015,-1,NULL);
linphone_core_assign_payload_type(lc,&payload_type_g726_16,-1,NULL);
linphone_core_assign_payload_type(lc,&payload_type_g726_24,-1,NULL);
linphone_core_assign_payload_type(lc,&payload_type_g726_32,-1,NULL);
linphone_core_assign_payload_type(lc,&payload_type_g726_40,-1,NULL);
linphone_core_assign_payload_type(lc,&payload_type_aal2_g726_16,-1,NULL);
linphone_core_assign_payload_type(lc,&payload_type_aal2_g726_24,-1,NULL);
linphone_core_assign_payload_type(lc,&payload_type_aal2_g726_32,-1,NULL);
linphone_core_assign_payload_type(lc,&payload_type_aal2_g726_40,-1,NULL);
linphone_core_assign_payload_type(lc,&payload_type_silk_nb,-1,NULL);
linphone_core_assign_payload_type(lc,&payload_type_silk_mb,-1,NULL);
linphone_core_assign_payload_type(lc,&payload_type_silk_wb,-1,NULL);
linphone_core_assign_payload_type(lc,&payload_type_silk_swb,-1,NULL);
linphone_core_assign_payload_type(lc,&payload_type_g729,18,"annexb=no");
linphone_core_handle_static_payloads(lc);
ms_init();
/* create a mediastreamer2 event queue and set it as global */
/* This allows to run event's callback in linphone_core_iterate() */
lc-&msevq=ms_event_queue_new();
ms_set_global_event_queue(lc-&msevq);
lc-&config=lp_config_new(config_path);
if (factory_config_path)
lp_config_read_file(lc-&config,factory_config_path);
lc-&sal=sal_init();
sal_set_user_pointer(lc-&sal,lc);
sal_set_callbacks(lc-&sal,&linphone_sal_callbacks);
sip_setup_register_all();
sound_config_read(lc);
net_config_read(lc);
rtp_config_read(lc);
codecs_config_read(lc);
sip_config_read(lc); /* this will start eXosip*/
video_config_read(lc);
//autoreplier_config_init(&lc-&autoreplier_conf);
lc-&presence_mode=LinphoneStatusO
misc_config_read(lc);
ui_config_read(lc);
#ifdef TUNNEL_ENABLED
lc-&tunnel=linphone_core_tunnel_new(lc);
if (lc-&tunnel) linphone_tunnel_configure(lc-&tunnel);
if (lc-&vtable.display_status)
lc-&vtable.display_status(lc,_("Ready"));
lc-&auto_net_state_mon=lc-&sip_conf.auto_net_state_
linphone_core_set_state(lc,LinphoneGlobalOn,"Ready");
-----------&linphonecore.c------&在linphone_core_init中调用sound_config_read,初始化声音设备
在sound_config_read中:将声卡添加到_MSSndCardManager
#ifdef __linux
/*alsadev let the user use custom alsa device within linphone*/
devid=lp_config_get_string(lc-&config,"sound","alsadev",NULL);
if (devid){
MSSndCard *card=ms_alsa_card_new_custom(devid,devid);
//获取声卡
ms_snd_card_manager_add_card(ms_snd_card_manager_get(),card); //将声卡设备添加到声卡管理器中
tmp=lp_config_get_int(lc-&config,"sound","alsa_forced_rate",-1);
if (tmp&0) ms_alsa_card_set_forced_sample_rate(tmp);
MSSndCard * ms_alsa_card_new_custom(const char *pcmdev, const char *mixdev){
ms_warning("Alsa support not available in this build of mediastreamer2");
return NULL;
void ms_alsa_card_set_forced_sample_rate(int samplerate){
ms_warning("Alsa support not available in this build of mediastreamer2");
说明这里还没有拿到真正的声卡设备:
在-----------&linphonecore.c------&linphone_core_init中
有这么一段:
/* retrieve all sound devices */
build_sound_devices_table(lc);
devid=lp_config_get_string(lc-&config,"sound","playback_dev_id",NULL);
linphone_core_set_playback_device(lc,devid);
devid=lp_config_get_string(lc-&config,"sound","ringer_dev_id",NULL);
linphone_core_set_ringer_device(lc,devid);
devid=lp_config_get_string(lc-&config,"sound","capture_dev_id",NULL);
linphone_core_set_capture_device(lc,devid);
看一下linphone_core_set_capture_device---&
int linphone_core_set_capture_device(LinphoneCore *lc, const char * devid){
MSSndCard *card=get_card_from_string_id(devid,MS_SND_CARD_CAP_CAPTURE);
lc-&sound_conf.capt_sndcard=
//将声卡放到了LinphoneCore的sound_conf中
if (card &&
linphone_core_ready(lc))
lp_config_set_string(lc-&config,"sound","capture_dev_id",ms_snd_card_get_string_id(card));
找到了!!!!!
mscommong.c ---》ms_init中:
ms_message("Registering all soundcard handlers");
cm=ms_snd_card_manager_get();
for (i=0;ms_snd_card_descs[i]!=NULL;i++){
ms_snd_card_manager_register_desc(cm,ms_snd_card_descs[i]);
但是ms_snd_card_descs从哪里来呢?
上面有定义,且定义了好多中desc ,发现有个 msandroid_sound_card_desc 安卓声卡描述
而msandroid_sound_card_desc 在msandroid.cpp中有定义:
MSSndCardDesc msandroid_sound_card_desc = {
/*.driver_type=*/"ANDROID SND",
/*.detect=*/ msandroid_sound_detect,
/*.init=*/msandroid_sound_init,
/*.set_level=*/msandroid_sound_set_level,
/*.get_level=*/msandroid_sound_get_level,
/*.set_capture=*/msandroid_sound_set_source,
/*.set_control=*/NULL,
/*.get_control=*/NULL,
/*.create_reader=*/msandroid_sound_read_new,
/*.create_writer=*/msandroid_sound_write_new,
/*.uninit=*/msandroid_sound_uninit,
/*.duplicate=*/msandroid_sound_duplicate
在mssndcard.c中发现只要有了声卡描述desc就可以创建声卡了:
MSSndCard * ms_snd_card_new(MSSndCardDesc *desc){
return ms_snd_card_new_with_name(desc,NULL);
MSSndCard * ms_snd_card_new_with_name(MSSndCardDesc *desc,const char* name) {
MSSndCard *obj=(MSSndCard *)ms_new(MSSndCard,1);
obj-&desc=
obj-&name=name?ms_strdup(name):NULL;
obj-&data=NULL;
obj-&id=NULL;
obj-&capabilities=MS_SND_CARD_CAP_CAPTURE|MS_SND_CARD_CAP_PLAYBACK;
if (desc-&init!=NULL)
desc-&init(obj);
所以我们进入msandroid_sound_card_desc的init方法就可以了
可是非常遗憾!!!
void msandroid_sound_init(MSSndCard *card){
这个方法为空:
难道白找了?
继续看,在msandroid.cpp中发现:!!!!!!!
sound_read_setup这个方法:
*************************************************************
static void sound_read_setup(MSFilter *f){
ms_debug("andsnd_read_preprocess");
msandroid_sound_read_data *d=(msandroid_sound_read_data*)f-&
jmethodID constructor_id=0;
jmethodID min_buff_size_
//jmethodID set_notification_
JNIEnv *jni_env = ms_get_jni_env();
d-&audio_record_class = (jclass)jni_env-&NewGlobalRef(jni_env-&FindClass("android/media/AudioRecord"));
if (d-&audio_record_class == 0) {
ms_error("cannot find
android/media/AudioRecord\n");
constructor_id = jni_env-&GetMethodID(d-&audio_record_class,"&init&", "(IIIII)V");
if (constructor_id == 0) {
ms_error("cannot find
AudioRecord (int audioSource, int sampleRateInHz, \
int channelConfig, int audioFormat, int bufferSizeInBytes)");
min_buff_size_id = jni_env-&GetStaticMethodID(d-&audio_record_class,"getMinBufferSize", "(III)I");
if (min_buff_size_id == 0) {
ms_error("cannot find
AudioRecord.getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat)");
d-&buff_size = jni_env-&CallStaticIntMethod(d-&audio_record_class,min_buff_size_id,d-&rate,2/*CHANNEL_CONFIGURATION_MONO*/,2/*
ENCODING_PCM_16BIT */);
d-&read_chunk_size = d-&buff_size/4;
d-&buff_size*=2;/*double the size for configuring the recorder: this does not affect latency but prevents "AudioRecordThread: buffer overflow"*/
if (d-&buff_size & 0) {
ms_message("Configuring recorder with [%i] bits
rate [%i] nchanels [%i] buff size [%i], chunk size [%i]"
,d-&nchannels
,d-&buff_size
,d-&read_chunk_size);
ms_message("Cannot configure recorder with [%i] bits
rate [%i] nchanels [%i] buff size [%i] chunk size [%i]"
,d-&nchannels
,d-&buff_size
,d-&read_chunk_size);
d-&read_buff = jni_env-&NewByteArray(d-&buff_size);
d-&read_buff = (jbyteArray)jni_env-&NewGlobalRef(d-&read_buff);
if (d-&read_buff == 0) {
ms_error("cannot instanciate read buff");
//卧槽,原来是用Android的AudioRecord类来跟音频打交道!!!!
d-&audio_record =
jni_env-&NewObject(d-&audio_record_class
,constructor_id
,2/*CHANNEL_CONFIGURATION_MONO*/
ENCODING_PCM_16BIT */
,d-&buff_size);
d-&audio_record = jni_env-&NewGlobalRef(d-&audio_record);
if (d-&audio_record == 0) {
ms_error("cannot instantiate AudioRecord");
d-&min_avail=-1;
d-&read_samples=0;
d-&ticker_synchronizer = ms_ticker_synchronizer_new();
d-&outgran_ms=20;
d-&start_time=-1;
d-&framesize=(d-&outgran_ms*d-&rate)/1000;
d-&started=
// start reader thread
rc = ms_thread_create(&d-&thread_id, 0, (void*(*)(void*))msandroid_read_cb, d);
ms_error("cannot create read thread return code
is [%i]", rc);
d-&started=
}**********************************************************************
读到了声音后有个回调方法:在这里能拿到音频对应的byte buffer!!!!!
static void* msandroid_read_cb(msandroid_sound_read_data* d) {
mblk_t *m;
jmethodID read_id=0;
jmethodID record_id=0;
set_high_prio();
JNIEnv *jni_env = ms_get_jni_env();
record_id = jni_env-&GetMethodID(d-&audio_record_class,"startRecording", "()V");
if(record_id==0) {
ms_error("cannot find AudioRecord.startRecording() method");
//start recording
jni_env-&CallVoidMethod(d-&audio_record,record_id);
// int read (byte[] audioData, int offsetInBytes, int sizeInBytes)
read_id = jni_env-&GetMethodID(d-&audio_record_class,"read", "([BII)I");
if(read_id==0) {
ms_error("cannot find AudioRecord.read() method");
while (d-&started && (nread=jni_env-&CallIntMethod(d-&audio_record,read_id,d-&read_buff,0, d-&read_chunk_size))&0) {
m = allocb(nread,0);
jni_env-&GetByteArrayRegion(d-&read_buff, 0,nread, (jbyte*)m-&b_wptr);
//ms_error("%i octets read",nread);
m-&b_wptr +=
d-&read_samples+=nread/(2*d-&nchannels);
compute_timespec(d);
ms_mutex_lock(&d-&mutex);
ms_bufferizer_put (&d-&rb,m);
ms_mutex_unlock(&d-&mutex);
ms_thread_exit(NULL);
-----------&linphonecore.c------&在linphone_core_init中[初始化mediastream2]
ms_init();
/* create a mediastreamer2 event queue and set it as global */
/* This allows to run event's callback in linphone_core_iterate() */
lc-&msevq=ms_event_queue_new();
ms_set_global_event_queue(lc-&msevq);
&&&&推荐文章:
【上篇】【下篇】您所在位置: &
&nbsp&&nbsp&nbsp&&nbsp
基于Android的音频比对系统的设计与实现.pdf54页
本文档一共被下载:
次 ,您可免费全文在线阅读后下载本文档
文档加载中...广告还剩秒
需要金币:250 &&
优秀硕士学位论文,完美PDF格式,支持自由编辑复制。
你可能关注的文档:
··········
··········
学号 MM22001XXXXXXXXXX611
硕士学位论文
硕士学位论文
硕硕士士学学位位论论文文
基于Android的音频比对系统的设计
AAnnddrrooiidd
学位申请人
学 科 专 业: 电子与通信工程
指 导 教 师: 黄本雄 教授
答 辩 日 期: ..3300
AThesis Submittedin Partial Fulfillmentof theRequirements
AThesis Submittedin Partial Fulfillmentof theRequirements
AATThheessiissSSuubbmmiitttteeddiinnPPaarrttiiaallFFuullffiillllmmeennttoofftthheeRReeqquuiirreemmeennttss
fortheDegreefortheMasterof Engineering
fortheDegreefortheMasterof Engineering
ffoorrtthheeDDeeggrreeeeffoorrtthheeMMaasstteerrooffEEnnggiinneeeerriinngg
Design andImplementationofaAndroid-Based
Design andImplementationofaAndroid-Based
DDeessiiggnnaannddIImmpplleemmeennttaattiioonnooffaaAAnnddrrooiidd--BBaasseedd
AudioComparison System
AudioComparison System
AAuuddiiooCCoommppaarriissoonnSSyysstteemm
CCaannddiiddaattee
::XXuuJJiinngg
:Electronics and Communications Engineering
:Electronics and Communications Engineering
正在加载中,请稍后...第1章 &音频系统对于一部嵌入式设备来说,除了若干基础功能外(比如手机通话、短信),最重要的可能就是多媒体了——那么一个最简单的问题,什么是多媒体呢?这个术语对应的英文单词是“Multi-Media”,直译过来就是多媒体。名称就很好地解释了它的含义,我们引用Wikipedia上对其的详细定义:Multimedia is media and content that uses a com/' target='_blank'>bination of differentcontent ms. This contrasts with media that use >图 13?1 音频的录制、存储和回放引用自http://en.wikipedia.org/wiki/File:A-D-A_Flow.svg&录制过程:·&&&&&&&&首先,音频采集设备(比如Microphone)捕获声音信息,初始数据是模拟信号·&&&&&&&&模拟信号通过模-数转换器(ADC)处理成计算机能接受的二进制数据·&&&&&&&&上一步得到的数据根据需求进行必要的渲染处理,比如音效调整、过滤等等·&&&&&&&&处理后的音频数据理论上已经可以存储到计算机设备中了,比如硬盘、USB设备等等。不过由于这时的音频数据体积相对庞大,不利于保存和传输,通常还会对其进行压缩处理。比如我们常见的mp3音乐,实际上就是对原始数据采用相应的压缩算法后得到的。压缩过程根据采样率、位深等因素的不同,最终得到的音频文件可能会有一定程度的失真另外,音视频的编解码既可以由纯完成,也同样可以借助于专门的硬件芯片来完成&回放过程:回放过程总体上是录制过程的逆向操作。l&从存储设备中取出相关文件,并根据录制过程采用的编码方式进行相应的解码l&音频系统为这一播放实例选定最终匹配的音频回放设备l&解码后的数据经过音频系统设计的路径传输l&音频数据信号通过数模转换器(DAC)变换成模拟信号l&模拟信号经过回放设备,还原出原始声音&本章着重讲解的是音频“回放过程”。1.1.3 音频采样前面我们说过,数字音频系统需要将声波波形信号通过ADC转换成计算机支持的二进制,进而保存成音频文件,这一过程叫做音频采样(Audio Sampling)。音频采样是众多数字信号处理的一种,它们的基本原理都是类似的(比如视频的采样和音频采样本质上也没有太大区别)。可想而知,采样(Sampling)的核心是把连续的模拟信号转换成离散的数字信号。它涉及到如下几点:l&样本(Sample)这是我们进行采样的初始资料,比如一段连续的声音波形l&采样器(Sampler)采样器是将样本转换成终态信号的关键。它可以是一个子系统,也可以指一个操作过程,甚至是一个算法,取决于不同的信号处理场景。理想的采样器要求尽可能不产生信号失真l&量化(Quantization)采样后的值还需要通过量化,也就是将连续值近似为某个范围内有限多个离散值的处理过程。因为原始数据是模拟的连续信号,而数字信号则是离散的,它的表达范围是有限的,所以量化是必不可少的一个步骤l&编码(Coding)计算机的世界里,所有数值都是用二进制表示的,因而我们还需要把量化值进行二进制编码。这一步通常与量化同时进行&整个流程如下图所示:&图 13?2& PCM流程&PCM(Pulse-code modulation)俗称脉冲编码调制,是将模拟信号数字化的一种经典方式,得到了非常广泛的应用。比如数字音频在计算机、DVD以及数字电话等系统中的标准格式采用的就是PCM。它的基本原理就是我们上面的几个流程,即对原始模拟信号进行抽样、量化和编码,从而产生PCM流。另外,我们可以调整PCM的以下属性来达到不同的采样需求:l&采样速率(Sampling Rate)在将连续信号转化成离散信号时,就涉及到采样周期的选择。如果采样周期太长,虽然文件大小得到控制,但采样后得到的信息很可能无法准确表达原始信息;反之,如果采样的频率过快,则最终产生的数据量会大幅增加,这两种情况都是我们不愿意看到的,因而需要根据实际情况来选择合适的采样速率。由于人耳所能辨识的声音范围是20-20KHZ,所以人们一般都选用44.1KHZ(CD)、48KHZ或者96KHZ来做为采样速率l&采样深度(Bit Depth)我们知道量化(Quantization)是将连续值近似为某个范围内有限多个离散值的处理过程。那么这个范围的宽度以及可用离散值的数量会直接影响到音频采样的准确性,这就是采样深度的意义图 13?3 采用4-bit深度的PCM,引自Wikipedia&比如上图是一个采用4位深度进行量化得到的PCM。因为4bit最多只能表达16个数&#2),所以图中最终量化后的数值依次为7、9、11、12、13、14、15等等。这样的结果显然是相对粗糙的,存在一定程度的失真。当位深越大,所能表达的数值范围越广,上图中纵坐标的划分也就越细致,从而使得量化的值越接近原始数据。1.1.4 Nyquist–Shannon采样定律由Harry Nyquist和Claude Shannon总结出来的采样规律,为我们选择合适的采样频率提供了理论依据。这个规律又被称为“Nyquist sampling theorem”或者“The sampling theorem”,中文通常译为“奈奎斯特采样理论”。它的中心思想是:“当对被采样的模拟信号进行还原时,其最高频率只有采样频率的一半”。换句话说,如果我们要完整重构原始的模拟信号,则采样频率就必须是它的两倍以上。比如人的声音范围是20-20kHZ,那么选择的采样频率就应该在40kHZ左右,数值太小则声音将产生失真现象,而数值太大也无法明显提升人耳所能感知的音质。1.1.5 声道和立体声我们在日常生活中会经常听到单声道、双声道这些专业词语,那么它们代表什么意思呢?一个声道(AudioChannel),简单来讲就代表了一种独立的音频信号,所以双声道理论上就是两种独立音频信号的混合。具体而言,如果我们在录制声音时在不同空间位置放置两套采集设备(或者一套设备多个采集头),就可以录制两个声道的音频数据了。后期对采集到的声音进行回放时,通过与录制时相同数量的外放扬声器来分别播放各声道的音频,就可以尽可能还原出录制现场的真实声音了。声道的数量发展经历了几个重要阶段,分别是:l&Monaural (单声道)早期的音频录制是单声道的,它只记录一种音源,所以在处理上相对简单。播放时理论上也只要一个扬声器就可以了——即便有多个扬声器,它们的信号源也是一样的,起不到很好的效果l&Stereophonic(立体声)之所以称为立体声,是因为人们可以感受到声音所产生的空间感。大自然中的声音就是立体的,比如办公室里键盘敲击声,马路上鸣笛,人们的说话声等等。那么这些声音为什么会产生立体感呢?我们知道,当音源发声后(比如你右前方有人在讲话),音频信号将分别先后到达人类的双耳。在这个场景中,是先传递到右耳然后左耳,并且右边的声音比左边稍强。这种细微的差别通过大脑处理后,我们就可以判断出声源的方位了。这个原理现在被应用到了多种场合。在音乐会的录制现场,如果我们只使用单声道采集,那么后期回放时所有的音乐器材都会从一个点出来;反之,如果能把现场各方位的声音单独记录下来,并在播放时模拟当时的场景,那么就可以营造出音乐会的逼真氛围。为了加深大家的理解,我们特别从某双声道音频文件中提取出它的波形:图 13?4 双声道音频文件&顺便以这个图为例子再说一下音频的采样频率。我们把上图进行放大,如下所示:图 13?5 采样频率实例&图上共有三个采样点,时间间隔是从4:26.:26.790908左右,也就是说在大约0.000045秒的时间里采集了两个点,因而采样频率就是:1/(0.)=44kHZ,这和此音频文件所标记的采样率是一致的l&4.1 Surround Sound(4.1环绕立体声)随着技术的发展和人们认知的提高,单纯的双声道已不能满足需求了,于是更多的声道数逐渐成为主流,其中被广泛采用的就有四声道环绕立体声。其中的“4”代表了四个音源,位置分别是前左(Front-Left)、前右(Front-Right)、后左(Rear-Left)、后右(Rear-Right)。而小数点后面的1,则代表了一个低音喇叭(Subwoofer),专门用于加强低频信号效果l&5.1 Surround Sound(5.1环绕立体声)相信大家都听过杜比数字技术,这是众多5.1环绕声中的典型代表。另外还有比如DTS、SDDS等都属于5.1技术。5.1相对于4.1多了一个声道,位置排列分别是前左、前右、中置(Center Channel)和两个Surround Channel,外加一个低音喇叭。根据ITU(InternationalTelecommunication Union)的建议,5.1环绕技术各扬声器位置图如下所示:&图 13?6& ITU发布的5.1环绕技术推荐方位图&即:l&各扬声器和听者距离是一致的,因而组成一个圆形l&角度分布:前左和前右分别是+22.5/-22.5度(看电影时),以及+30/-30度(听音乐时);中置总是为0度;后面的两个环绕器分别为+110/-110度1.1.6 Weber–Fechner law估计知道这个定律的人比较少,它是音频系统中计算声音大小的一个重要依据。从严格意义上讲,它并不只适用于声音感知,而是人体各种感观(听觉、视觉、触觉)与刺激物理量之间的一条综合规律。其中心思想用公式表达就是:△I/I=C其中△I表示差别阈值,I是原先的刺激量,而C则是常量。换句话说,就是能引起感观变化的刺激差别量与原先的刺激量比值是固定的。这样子说可能比较抽象,我们举个例子来说。场合1. 去商店买一瓶水,原本2块钱的东西卖到了5块钱场合2. 买一辆奔驰车,原先价格是一百万,现在涨了3块钱这两种场景下,前后的价格虽然都是相差3元,但对我们造成的主观感受是有很大不同的。显然在第一种情况下,我们会觉得很贵而可能选择不买;而后者则对我们基本不会产生任何影响。这是因为引起感观变化的刺激量并不单单取决于前后变化量的绝对差值,同时也与原来的刺激量有很大关系。对于特定的场合,上述公式中的C值是固定的。比如有的人觉得2块钱的东西卖3元就是贵了,有的人则能接受2块钱的东西卖4块,对于不同的人C值是会有差异的。这就是德国心理物理学家ErnstHeinrich Weber发现的规律,后来的学生GustavFechner把这一发现系统地用公式表达出来,就是上述公式所表达的韦伯定律。后来,Fechner在此基础上又做了改进。他提出刺激量和感知是呈对数关系的,即当刺激强度以几何级数增长时,感知强度则以算术级数增加。这就是Weber–Fechner law,如下公式所示:S =&C&log&R那么这对音频系统有什么指导意义呢?我们知道,系统音量是可调的,比如分为0-20个等级。这些等级又分别对应不同的输出电平值,那么我们如何确定每一个等级下应该设置的具体电平值呢?你可能会想到平均分配。没错,这的确是一种方法,只不过按照这样的算法所输出的音频效果在用户听来并不是最佳的,因为声音的变化不连续。一个更好的方案就是遵循Weber–Fechnerlaw,而采用对数的方法。在Android系统中,这一部分计算具体的实现在audiosystem.cpp文件中,大家有兴趣的话可以自行阅读了解下。1.1.7 音频的几种文件格式前面小节我们分析了音频采样的基本过程,它将连续的声音波形转换成为若干范围内的离散数值,从而将音频数据用二进制的形式在计算机系统中表示。不过音频的处理并没有结束,我们通常还需要对上述过程产生的数据进行格式转化,然后才最终存储到设备中。要特别注意文件格式(FileFormat)和文件编码器(Codec)的区别。编码器负责将原始数据进行一定的前期处理,比如压缩算法以减小体积,然后才以某种特定的文件格式进行保存。Codec和File Format不一定是一对一的关系,比如常见的AVI就支持多种音频和视频编码方式。本小节所讲述的以文件格式为主。我们把数字音频格式分为以下几种:l& 不压缩的格式(UnCompressed Audio Format)比如前面所提到的PCM数据,就是采样后得到的未经压缩的数据。PCM数据在和Mac系统上通常分别以wav和aiff后缀进行存储。可想而知,这样的文件大小是比较可观的l&无损压缩格式(Lossless Compressed Audio Format)这种压缩的前提是不破坏音频信息,也就是说后期可以完整还原出原始数据。同时它在一定程度上可以减小文件体积。比如FLAC、APE(Monkey’sAudio)、WV(WavPack)、m4a(Apple Lossless)等等l&有损压缩格式(Lossy Compressed Audio Format)无损压缩技术能减小的文件体积相对有限,因而在满足一定音质要求的情况下,我们还可以进行有损压缩。其中最为人熟知的当然是mp3格式,另外还有iTunes上使用的AAC,这些格式通常可以指定压缩的比率——比率越大,文件体积越小,但效果也越差&至于采用哪一种格式,通常要视具体的使用场景而定。}

我要回帖

更多关于 声音和音频设备 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信