找回密码
 欢迎注册
查看: 27222|回复: 13

[原创] 有声的世界,聊一聊音频

[复制链接]
发表于 2011-1-5 21:53:47 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?欢迎注册

×
之前突然对音频解码的库感兴趣了,于是下载了一个最为推荐的mp3解码库:mpg123-1.12.3-x86.zip 12-Jul-2010 17:37  276K  可以在http://www.mpg123.de/download.shtml中找到(最新版本,注意其它版本可能有大的bug)
最新源码包在:http://sourceforge.net/projects/mpg123/files/

编译测试文件mpg123_to_wav.c文件,得到了wav 。嗯,libmpg123可将mp3解码成PCM数据文件,再加个文件头就成了WAV文件格式(实际上不用转成wav,因为我就读wav也是要解析它)。
如果了解了wav格式,我们就可以摆脱libsndfile这个庞大的库了。
先了解一下,WAV文件格式,很有用啰。
参考地址:https://ccrma.stanford.edu/courses/422/projects/WaveFormat/    (个人发现的最好参考)
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2011-1-6 10:37:27 | 显示全部楼层
wav-sound-format.gif
上图即为Microsoft WAVE 文件格式图。
由wave格式头+ PCM数据文件 构成
从上面的,可以看出,
ChunkID="RIFF" 的ASCII标识
ChunkSize=整个wave文件大小 - 8字节 (即除去ChunkID和ChunkSize占用的8字节)
接下来的是标识"WAVEfmt "共占8字节
接下来的,可以看下图,很清楚。
wave-bytes.gif
也就是说mpg123可以得到PCM数据块(即wave文件中的data部分),并且有文件大小,SampleRate,Subchunk1Size,声道数,AudioFormat(PCM)。
这样手工就可以打造出完整的wave文件来。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2011-1-6 11:07:02 | 显示全部楼层
然后,我们用src包中的测试文件mpg123_to_wav.c,过程:先将某mp3文件解码,得到PCM数据及相关采样信息,再通过sndfile(已包含此头文件)进行打包成WAV文件(此步骤略显轻松)。
然后,我们用WinHex打开生成的这个my.wav文件,看看它的头部是什么
test.GIF
从最左边可以看出,这个wav文件有43,416,620bytes =0x 02967C24 +8 bytes (注意是小端模式)
SampleRate=44100 =0x 0000AC44
从第41字节开始的4字节为PCM数据块的大小=0x 02967C00 bytes=43,416,620bytes -44
从第45字节开始存放PCM数据.....
所以,也不需要sndfile.h,注释掉
  1. /*
  2.         mpg123_to_wav.c

  3.         copyright 2007 by the mpg123 project - free software under the terms of the LGPL 2.1
  4.         see COPYING and AUTHORS files in distribution or http://mpg123.org
  5.         initially written by Nicholas Humfrey
  6. */

  7. #include <stdio.h>
  8. #include <string.h>
  9. #include "mpg123.h"
  10. //#include "sndfile.h"




  11. void usage()
  12. {
  13.         printf("Usage: mpg123_to_wav <input> <output>\n");
  14.         exit(99);
  15. }

  16. void cleanup(mpg123_handle *mh)
  17. {
  18.         /* It's really to late for error checks here;-) */
  19.         mpg123_close(mh);
  20.         mpg123_delete(mh);
  21.         mpg123_exit();
  22. }

  23. int main(int argc, char *argv[])
  24. {
  25.         //SNDFILE* sndfile = NULL;
  26.         //SF_INFO sfinfo;
  27.         mpg123_handle *mh = NULL;
  28.         unsigned char* buffer = NULL;
  29.         size_t buffer_size = 0;
  30.         size_t done = 0;
  31.         int  channels = 0, encoding = 0;
  32.         long rate = 0;
  33.         int  err  = MPG123_OK;


  34.         off_t samples = 0;

  35.         if (argc!=3) usage();
  36.         printf( "Input file: %s\n", argv[1]);
  37.         printf( "Output file: %s\n", argv[2]);
  38.        
  39.         err = mpg123_init();
  40.         if( err != MPG123_OK || (mh = mpg123_new(NULL, &err)) == NULL
  41.             /* Let mpg123 work with the file, that excludes MPG123_NEED_MORE messages. */
  42.             || mpg123_open(mh, argv[1]) != MPG123_OK
  43.             /* Peek into track and get first output format. */
  44.             || mpg123_getformat(mh, &rate, &channels, &encoding) != MPG123_OK )
  45.           
  46.         {
  47.                 fprintf( stderr, "Trouble with mpg123: %s\n",
  48.                          mh==NULL ? mpg123_plain_strerror(err) : mpg123_strerror(mh) );
  49.                 cleanup(mh);
  50.                 return -1;
  51.         }

  52.         if(encoding != MPG123_ENC_SIGNED_16)
  53.         { /* Signed 16 is the default output format anyways; it would actually by only different if we forced it.
  54.              So this check is here just for this explanation. */
  55.                 cleanup(mh);
  56.                 fprintf(stderr, "Bad encoding: 0x%x!\n", encoding);
  57.                 return -2;
  58.         }
  59.         /* Ensure that this output format will not change (it could, when we allow it). */
  60.         mpg123_format_none(mh);
  61.         mpg123_format(mh, rate, channels, encoding);

  62.         /* Buffer could be almost any size here, mpg123_outblock() is just some recommendation.
  63.            Important, especially for sndfile writing, is that the size is a multiple of sample size. */
  64.         buffer_size = mpg123_outblock( mh );
  65.        
  66.         buffer = malloc( buffer_size );

  67.         //bzero(&sfinfo, sizeof(sfinfo) );
  68.         //**************************************************
  69.         //memset(&sfinfo,'\0', sizeof(sfinfo));
  70.        
  71.         //sfinfo.samplerate = rate;
  72.         //sfinfo.channels = channels;
  73.         //sfinfo.format = SF_FORMAT_WAV|SF_FORMAT_PCM_16;
  74.         printf("Creating 16bit WAV with %i channels and %liHz.\n", channels, rate);
  75.        
  76.         printf("%d\n",buffer_size);
  77.         
  78.         //sndfile = sf_open(argv[2], SFM_WRITE, &sfinfo);
  79.         //if(sndfile == NULL){ fprintf(stderr, "Cannot open output file!\n"); cleanup(mh); return -2; }

  80.         do
  81.         {       
  82.                 //printf("%d\n",buffer_size);
  83.                 //printf("%d\n",MPG123_OK);
  84.                 err = mpg123_read( mh, buffer, buffer_size, &done );  
  85.                            //通过mpg123_read得到done字节数的PCM数据到buffer中,buffer_size仅共参考,无太大意义。
  86.                    //每循环一次,解码块指针后移,buffer为对应PCM数据,直到文件尾。
  87.                 //printf("Good\n");
  88.                 //system("Pause");
  89.                
  90.                 //sf_write_short( sndfile, (short*)buffer, done/sizeof(short) );
  91.                 samples += done/sizeof(short);
  92.                 /* We are not in feeder mode, so MPG123_OK, MPG123_ERR and MPG123_NEW_FORMAT are the only possibilities.
  93.                    We do not handle a new format, MPG123_DONE is the end... so abort on anything not MPG123_OK. */
  94.         } while (err==MPG123_OK);

  95.         if(err != MPG123_DONE)
  96.         fprintf( stderr, "Warning: Decoding ended prematurely because: %s\n",
  97.                  err == MPG123_ERR ? mpg123_strerror(mh) : mpg123_plain_strerror(err) );

  98.         //sf_close( sndfile );

  99.         samples /= channels;
  100.         printf("%li samples written.\n", (long)samples);
  101.         cleanup(mh);
  102.         return 0;
  103. }
复制代码
test.jpg
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2011-1-6 11:23:03 | 显示全部楼层
既然上面我们得到了PCM数据和相关信息,这还不能放出音频来,还需要启动音频设备,将数据喂进去播放。
但总不能打包成WAV文件,再输出到磁盘,再用现有的支持其库进行播放。直接读存数据(buffer)与上面的mpg123_read无缝连接就爽了。
首先想到的是portaudio 也是开源的。
里面有一个正弦波的播放示例如下:
portaudio测试成功.rar (71.4 KB, 下载次数: 5)
感觉还是不爽,考虑再三,发现用
waveOutOpen
waveOutPrepareHeader
waveOutWrite
waveOutRestart
等系列函数是不错的。

关于这一部分的经典参考资料(现在鲜有这样的书了)
声音与音乐.rar (123.41 KB, 下载次数: 4)

调用低级api  waveOut***的正弦波声音输出测试示例,使用双缓冲区技术,防止播放的停顿,更多说明请参考上面的资料。
正弦波.rar (21.93 KB, 下载次数: 9)
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2011-1-6 21:21:38 | 显示全部楼层
事实证明上面的思路可行了(win7环境),我写了个简单的:
mp3_wav_play.rar (143.07 KB, 下载次数: 17)
命令行启动,示图:
gg.jpg

接下来就可用双缓冲技术,加上适当的控制就可以播放完整个mp3文件了。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2011-1-6 23:33:56 | 显示全部楼层
强大,mpg123更新的好快。
Top News
2011-01-06 Thomas: mpg123 1.13.1
New Year, new bugs (from last year) -- and their fixes!
This release fixes a stupid regression in 1.13.0 that broke MPG123_FORCE_FLOAT and MPG123_FORCE_8BIT (--float and --8bit command line switches), caused by the introduction of 24 bit format support. Furthermore, it has been suggested to ignore the private / extension header bit in the parser's stream checking and so it is now.

mpg123-1.13.1-static-x86.zip 06-Jan-2011 14:15  190K
mpg123-1.13.1-static-x86.zip.asc 06-Jan-2011 14:15  196  
mpg123-1.13.1-x86-debug.zip 06-Jan-2011 14:15  544K
mpg123-1.13.1-x86-debug.zip.asc 06-Jan-2011 14:15  196  
mpg123-1.13.1-x86.zip 06-Jan-2011 14:16  318K
mpg123-1.13.1-x86.zip.asc 06-Jan-2011 14:16  196
及src
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2011-1-7 21:05:37 | 显示全部楼层
一个完整的mp3解码->PCM数据->低级wavOut***  播放测试(XP ,win7通过),双缓冲,消息处理。

mp3_wav_play.rar (184.97 KB, 下载次数: 33)
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2011-1-9 22:55:36 | 显示全部楼层
mp3播放小结,像其它编码格式的类似,比如flac,对于视频的,推荐ffmpeg。
mpg123 1.13.1 编译
mingw +msys  (windows环境)
------------------------------------
./configure
make
make install
----------------------------------
得到 静态库  libmpg123.a  (对于dll版本可直接下载)
---------------------------------
可自行编译\doc\examples中的代码
特别是mpg123_to_wav.c
关键函数:
mpg123_read( mh, buffer, buffer_size, &done );用于将mp3解码出PCM数据
PCM数据大小为buffer_size(建议大小),实际送入buffer中的数据大小为done字节。
通常buffer_size=done,一般只有解码到尾部时,不够了,就有buffer_size>done。
每调用一次,解码下一段,直到解码完毕,小解码片段为4608字节在44100下约播放26ms 所以通过设置的更大一点,比如:buffer_size=buffer_size*114=4608*114 约播放3s。
---------------------------------
sndfile仅仅用于简单的WAV格式封装(数据为上面解码出的PCM数据,格式头通过mpg123_getformat等已知)
所以可自己实现wav的封装,而不用sndfile。
---------------------------------
对mpg123_to_wav.c简单的扩展应用:
一个完整的mp3解码->PCM数据->低级wavOut***  播放测试(XP ,win7通过),双缓冲,消息处理。
---------------------------------
mingw下编译(得到集成版本):
gcc -c  mp3_play.c -Iinclude -Llib -lmpg123 -lwinmm
windres mp3_resource.rc resource.o
gcc mp3_play.o resource.o -Llib -lmpg123 -lwinmm -Wl,-subsystem,windows -o mp3_play.exe

./mp3.play.exe
---------------------------------
gg.jpg
集成版:
mp3_play_test.zip (329.49 KB, 下载次数: 6)

评分

参与人数 1鲜花 +20 收起 理由
wayne + 20 好帅耶 !!!

查看全部评分

毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2011-1-21 21:25:41 | 显示全部楼层
8# G-Spider

好像SDL也可以播放音频的吧
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2011-1-21 21:57:02 | 显示全部楼层
9# wayne
应该可以的,主要还是各种格式的解码,像flac也要解码出wav数据。视频麻烦一点,有音频流和视频流(也就是一帧帧的图,gif和胶片式电影很好的反映了这一点),用Direct Show功能更强大,几乎支持所有的音视频格式(DirectX早期版本包含其SDK),唯一有点遗憾就是微软的似乎很少开源的。

之前用Direct Show写了个简单的测试程序:
DShow Src.rar (127.17 KB, 下载次数: 5)
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
您需要登录后才可以回帖 登录 | 欢迎注册

本版积分规则

小黑屋|手机版|数学研发网 ( 苏ICP备07505100号 )

GMT+8, 2024-4-25 18:19 , Processed in 0.051297 second(s), 20 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表