//gnu.org/licenses/gpl.html>
Thisisfreesoftware:
youarefreetochangeandredistributeit.
ThereisNOWARRANTY,totheextentpermittedbylaw.Type"showcopying"
and"showwarranty"fordetails.
ThisGDBwasconfiguredas"i486-linux-gnu"...
(gdb)bplayer_run
Breakpoint1at0x804f845:
fileplayer.c,line2700.
(gdb)r
Startingprogram:
/home/yuxu/test/madplaytest1.mp3test2.mp3test3.mp3
[Threaddebuggingusinglibthread_dbenabled]
MPEGAudioDecoder0.15.2(beta)-Copyright(C)2000-2004RobertLeslieetal.
[NewThread0xb7bf66b0(LWP6832)]
[SwitchingtoThread0xb7bf66b0(LWP6832)]
Breakpoint1,player_run(player=0xbfe087dc,argc=3,argv=0xbfe08a98)atplayer.c:
2700
warning:
Sourcefileismorerecentthanexecutable.
2700{
(gdb)s
2704player->playlist.entries=argv;
(gdb)
2705player->playlist.length=argc;
(gdb)s
2710if((player->options&PLAYER_OPTION_TTYCONTROL)&&
(gdb)p/xplayer->options
$1=0x40
(gdb)p/xPLAYER_OPTION_TTYCONTROL
$2=0x40
(gdb)s
54return__open_alias(__path,__oflag,__va_arg_pack());
(gdb)
2538tty_fd=open(TTY_DEVICE,O_RDONLY);
(gdb)bt
#0player_run(player=0xbfe087dc,argc=3,argv=0xbfe08a98)atplayer.c:
2538
#10x0804b9fdinmain(argc=3,argv=0xbfe08a94)atmadplay.c:
816
(gdb)s
2539if(tty_fd==-1){
(gdb)bt
#0player_run(player=0xbfe087dc,argc=3,argv=0xbfe08a98)atplayer.c:
2539
#10x0804b9fdinmain(argc=3,argv=0xbfe08a94)atmadplay.c:
816
(gdb)s
2546if(tcgetattr(tty_fd,&save_tty)==-1){
(gdb)
-------------------------------
强调一点:
这里主要意义就在于后面的播放过程中,要读取用户的键盘输入,就是用read(tty_fd,&key,1)来处理的
4.player_run():
setup_filters()==>addfilter(player,tty_filter,player)
将会执行代码段:
#ifdefined(USE_TTY)
if((player->options&PLAYER_OPTION_TTYCONTROL)&&
addfilter(player,tty_filter,player)==-1)
return-1;
#endif
而在addfilter()中==>filter_new()中malloc()一个新filter==>filter_init()初始化:
filter->func=tty_filter()
player->output.filters将指向这个新分配的filter,而且这个新分配的filter->func为tty_filter():
特别说明:
tty_filter()==>readkey()==>swtich...case处理各类键盘输入的命令:
voidfilter_init(structfilter*filter,filter_func_t*func,void*data,structfilter*chain)
{
filter->func=func;
...
}
intaddfilter(structplayer*player,filter_func_t*func,void*data)
{
structfilter*filter;
filter=filter_new(func,data,player->output.filters);
...
player->output.filters=filter;
return0;
}
==========================================
有关tty_filter函数调用链的说明:
1):
流程:
tty_filter()==>command=readkey(blocking:
阻塞否)==>switch(command)...case处理各种命令:
如:
KEY_STOP,KEY_PAUSE
KEY_FORWARD,KEY_BACK,KEY_QUIT,KEY_GAINDECR,KEY_GAININCR,KEY_GAINZERO,
如下为清理过的流程示意图:
提醒一点:
KEY_PAUSE:
readkey
(1)即,当Pause时,读键操作是阻塞的,即进入readkey()执行以下代码:
do
count=read(tty_fd,&key,1);
while(count==-1&&errno==EINTR);
当无键按下时,read()阻塞睡眠,用top查看果然没有占用cpu了
而总的流程是这样的:
先tty_filter()查看有无key按下,此时是readkey(0)不阻塞形式,无键按下即返回之,有键按下则执行命令,见下面的
代码框架,再去每次取40,000Bmp3数据进行mp3解码,然后送往alsa驱动去pcm播放,再又回到tty_filter查看有无键按下
就是这样的一个播放循环,后面还会讲到
2)代码框架:
enummad_flowtty_filter(void*data,structmad_frame*frame)
{
command=rea
dkey(0);
if(command==-1)
returnMAD_FLOW_BREAK;
again:
switch(command){
caseKEY_STOP:
...
caseKEY_PAUSE:
stop_audio(player,stopped);
command=readkey
(1);
...
break;
caseKEY_FORWARD:
caseKEY_CTRL('n'):
case'>':
player->control=PLAYER_CONTROL_NEXT;
gotostop;
caseKEY_QUIT:
caseKEY_CTRL('c'):
case'Q':
player->control=PLAYER_CONTROL_STOP;
gotostop;
caseKEY_GAINDECR:
caseKEY_GAININCR:
caseKEY_GAINZERO:
caseKEY_GAININFO:
{
switch(command){
caseKEY_GAINDECR:
db=set_gain(player,GAIN_ATTAMP|GAIN_RELATIVE,-0.5);
break;
caseKEY_GAININCR:
db=set_gain(player,GAIN_ATTAMP|GAIN_RELATIVE,+0.5);
break;
caseKEY_GAINZERO:
db=set_gain(player,GAIN_ATTAMP,0);
break;
default:
db=set_gain(player,0,0);
break;
}
...........
stop:
stop_audio(player,1);
}
============================================
5.音量Gain设定:
set_gain(player,0,0);==>
db=player->output.voladj_db+player->output.attamp_db;
player->output.gain=db?
mad_f_tofixed(pow(10,db/20)):
MAD_F_ONE;
player->output.gain保存了最终的音量db设定值
那么音量Gain是如何执行的呢?
1.gain_filter的初始化设定:
player_run()==>setup_filters()==>
addfilter(player,gain_filter,&player->output.gain);
==>player->output.filters指向的filterlist中挂上了gain_filter,同时,还有前面所说的tty_filter
gain_filter用于调节音量,而tty_filter用于读取键盘输入确定用户的命令输入
补充参数初始值来源:
main()==>get_options()==>依据选项"-a或-A"对参数进行player->output.attamp_db,player->output.voladj_db设定初
始值
case'a':
player->output.attamp_db=get_decibels(optarg);
preamp=1;
break;
case'A':
player->output.voladj_db=get_decibels(optarg);
2.音量调节的方法:
play_all()==>play_one()==>decode()==>mad_decoder_run()==>run_sync()==>decoder->filter_func()==>
decoder->filter_func(decoder->cb_data,stream,frame)==>gain_filter()
==>
#definemad_f_mul(x,y)((x)*(y))
frame->sbsample[ch][s][sb]=mad_f_mul(frame->sbsample[ch][s][sb],gain);
==>
mad_synth_frame()==>synth_full()==>dct32()
也就是:
调用gain_filter进行对mp3声音原始数据进行gain处理,也就是直接将gain融入mp3数据中,然后再离散余弦变换
解码mp3文件的编码,这样声音音量级别就提高了,也就是音量调节是纯软件的,直接对数据进行的处理变换
============================================
6.播放mp3的工作机理:
1)主架构流程:
player_run()==>setup_tty()==>openTTY_DEVICE
==>setup_filters()==>gain_filter,tty_filter加入filterlist
==>audio_control_init(&control,AUDIO_COMMAND_INIT)+player->mand(&control)==>audio_alsa()=>
init()==>snd_pcm_open()
==>play_all()===核心函数
==>audio_control_init(&control,AUDIO_COMMAND_FINISH)+player->mand(&control)==>stop()==>
snd_pcm_drop(),snd_pcm_prepare()
play_all()核心函数:
==>处理随机播放:
见下代码,不多说,easy
if(player->options&PLAYER_OPTION_SHUFFLE){
srand(time(0));
for(i=0;ij=rand()%count;
tmp=playlist->entries[i];
playlist->entries[i]=playlist->entries[j];
playlist->entries[j]=tmp;
}
}
==>重要参数说明:
count=playlist->length;播放mp3文件数量
playlist->current:
当前所播放的mp3的位置
playlist->entries[playlist->current]:
当前播放的mp3文件
==>开始播放一首mp3:
若播放失败,则该