JACKY_ZZ[猫猫爱吃鱼]

春风拂面两颊红,秋叶洒地一片金。 夏荷摇曳一身轻,冬雪覆盖大地银。
posts - 30, comments - 123, trackbacks - 0, articles - 0

[C/C++] 自己的mp3播放器【带频谱】

Posted on 2009-02-04 09:22 jacky_zz 阅读(13411) 评论(54)  编辑 收藏 引用 所属分类: C/C++

开发了有一段时间了,说来还真是很长时间了。有兴趣可以参考我在Codeproject上发布的三篇文章。
http://www.codeproject.com/KB/audio-video/DSound_Spectrum.aspx
http://www.codeproject.com/KB/audio-video/DirectSound_Spectrum2.aspx
http://www.codeproject.com/KB/audio-video/DSound_Spectrum3.aspx
以上三篇文章或多或少都存在一些问题,其中最普遍的问题是频谱显示的并不是实时(real-time)的。但截图显示的这个版本是准确的了(我认为)。源码还在整理中,整理完毕后即发布到Codeproject上。

实现思路:
1、MP3解码由libmad开源解码库完成;
2、两个线程:播放线程,频谱线程,其中频谱线程使用的数据来自播放线程;
3、播放线程总是通过解码器获取4608字节数据,为什么要这个数字?这个是看了网上的相关文档和自己不断的测试得到的,这个数据可以有效的降低因为声卡回放引入的延时,少于这个数字将播放不正常;
4、频谱线程根据当前播放的时间获取到正在播放的PCM数据,使用FFT计算后绘图显示。

版本历史:
2009-02-08:支持拖拽,mp3,wma,ogg,wav支持,支持拖拽音频文件到exe启动播放 
2009-02-09:BUG修复:在单CPU上出现线程死锁,在双CPU上未出现
2009-03-27:支持APE格式
2009-03-30:支持FLAC格式 
2009-05-04:添加暂停、停止操作,结束一直以来朋友们提出没有暂停和停止操作的历史
2009-05-05:为播放、暂停、停止,退出操作添加系统热键,分别为Ctrl+1、Ctrl+2、Ctrl+3、Ctrl+4
2009-05-06:为播放、暂停、停止,退出操作添加系统热键, 分别为A、D、S、X,即按A或Ctrl+1为播放,D或Ctrl+2为暂停,S或Ctrl+3为停止、X或Ctrl+4为退出

最新版本下载
=======================================================
it's a long story to tell about this article, if you are interest in this project, please visit those three articles were posted on codeproject.
http://www.codeproject.com/KB/audio-video/DSound_Spectrum.aspx
http://www.codeproject.com/KB/audio-video/DirectSound_Spectrum2.aspx
http://www.codeproject.com/KB/audio-video/DSound_Spectrum3.aspx
those three atticles have some bugs, and the main problem is spectrum display what is not real-time. but this version is real-time(i think).
the latest version support mp3, wma, wav, ogg, ape, flac now,  and you can download it with this link: AudioPlayer_20090506065.zip

Feedback

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2009-02-04 10:22 by tangxinfa
有点意思

# re: 自己的mp3播放器【带频谱】[未登录]  回复  更多评论   

2009-02-04 12:06 by kenlistian
频谱处理很麻烦.不过现成代码提到这倒很很少.

# re: 自己的mp3播放器【带频谱】[未登录]  回复  更多评论   

2009-02-04 13:46 by jacky_zz
哦,是吗,你对频谱处理有独到的见解?有机会交流一下?
我的QQ:59502553

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2009-02-04 14:26 by 路青飞
不错。
支持一下。
坐在小板凳上观注你。

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2009-02-09 08:13 by audioer
你好,我找这方面的代码找了好久,自己也研究了好久,但就是没有找到好的代码,你的给我很大帮助,我想问一下,你的项目工程是否可以在Visual C++ 6.0下编译?有机会好好交流
QQ:543644213

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2009-02-09 08:16 by audioer
你的QQ有码?我想联系你,谢谢

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2009-02-09 08:33 by audioer
我在Visual Studio 2008中编译出现错误:MWrapper.obj : error LNK2019: 无法解析的外部符号 _WMCreateSyncReader@12,该符号在函数 "void __cdecl WMA_Reader_Init(struct _WMA_SYNC_READER *,class CStream *,int,short)" (?WMA_Reader_Init@@YAXPAU_WMA_SYNC_READER@@PAVCStream@@HF@Z) 中被引用
1>C:\Documents and Settings\Administrator\桌面\WinDirectAudio_20081212\Debug\WinDirectAudio.exe : fatal error LNK1120: 1 个无法解析的外部命令

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2009-02-09 08:52 by jacky_zz
TO audioer,QQ:59502553
在VS2008里编译,需要有Windows Media Format 9以上的支持。
PS:这个程序很大程度上参考了YoYoPlayer(Java开发),有兴趣的话可以参考以下地址:
http://www.blogjava.net/hadeslee/archive/2008/07/29/218161.html

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2009-02-14 19:29 by abowan
期待发布源码··

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2009-02-17 14:29 by 阿愚
您好!我现在在学C#,在用“千千静听”播放音乐时,觉得里面的频谱显示很有意思,也想自己弄一个,不知您能否指导一下?我的邮箱:eyu66@126.com
Skype用户名:eyu660

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2009-04-15 18:02 by 云枫
小弟目前工作中要涉及这方面的内容,可不可以吧你的源代码发到我邮箱,万分感谢!
邮箱:zhidanzzd@163.com
QQ:253074385

# 小弟目前正想用这个可不可以  回复  更多评论   

2009-04-22 15:19 by
谢谢大侠,我的邮箱是dxgsoft@126.com

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2009-04-30 00:20 by zzfajia
请问楼主用什么开发平台。
我在VC++2008上出现以下错误,请赐教。

1>IO.obj : error LNK2001: 无法解析的外部符号 _mad_timer_zero
1>IO.obj : error LNK2019: 无法解析的外部符号 _mad_timer_add,该符号在函数 "protected: virtual void __thiscall CMP3In::GetDataInternal(void * *,unsigned long *)" (?GetDataInternal@CMP3In@@MAEXPAPAXPAK@Z) 中被引用
1>IO.obj : error LNK2019: 无法解析的外部符号 _mad_synth_frame,该符号在函数 "protected: virtual void __thiscall CMP3In::GetDataInternal(void * *,unsigned long *)" (?GetDataInternal@CMP3In@@MAEXPAPAXPAK@Z) 中被引用

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2009-04-30 10:42 by jacky_zz
没有安装libmad,或没有找到libmad位置

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2009-05-03 10:42 by zzfajia
谢谢楼主指教!
可为什么VC6找不到*.lib文件有提示,VC2005,2008却没有。
另外添加了libmad.lib文件以后,又出现下列错误。这又是什么问题,可能又是找不到什么类库吧?
请指教。不胜感谢!!!

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2009-05-03 10:44 by zzfajia
谢谢楼主指教!
可为什么VC6找不到*.lib文件有提示,VC2005,2008却没有。
另外添加了libmad.lib文件以后,又出现下列错误。这又是什么问题,可能又是找不到什么类库吧?
请指教。不胜感谢!!!

1>------ 已启动生成: 项目: WinDirectAudio, 配置: Debug Win32 ------
1>WinDirectAudio : warning PRJ0009 : 未能打开生成日志进行写入。
1>请确保该文件未被其他进程打开并且未被写保护。
1>正在链接...
1>BasicPlayer.obj : error LNK2019: 无法解析的外部符号 __RTC_CheckEsp,该符号在函数 "__int64 __cdecl as_long(union _LARGE_INTEGER)" (?as_long@@YA_JT_LARGE_INTEGER@@@Z) 中被引用
1>FastFourierTransform.obj : error LNK2001: 无法解析的外部符号 __RTC_CheckEsp
1>Utils.obj : error LNK2001: 无法解析的外部符号 __RTC_CheckEsp
1>WinDirectAudio.obj : error LNK2001: 无法解析的外部符号 __RTC_CheckEsp

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2009-05-09 16:13 by 轮子
大侠,能否给我一份源代码。
songhualei_1@163.com
不胜感激

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2009-05-20 17:13 by 谢涛
你的频普我借用了,嘿嘿。不知道你有没有做过用DMO解码wma格式的音频数据流,放到directsound缓冲区播放的例子没?
我目前想用DMO处理wma 然后用DSound 播放,可惜屡试不成功!希望给我点介意,邮箱:xietao1984513@163.com

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2009-05-21 09:34 by jacky_zz
这个版本支持wma的解码嘛,只不过是使用COM接口的方式。DMO没有试过,但流程差不多一样吧,都是获取PCM格式的数据,然后播放。

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2009-07-03 16:56 by 海岛
今天用VC2005编译了你的程序,过程当中碰到很多问题,后来安装了WMSDK,DIRECTXSDK,也碰到上面说的mad_time_zero等问题,后来我拷贝了一个libmad.lib进来,然后把libogg和libvorbis等目录都删除掉,在源代码中也把相应的地方去掉,只保留libmad,最后终于编译通过。
谢谢。
我将好好的阅读你的代码以加强理解。
在此也想问一下,你的暂停和恢复是什么解决的。

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2009-07-06 16:50 by jacky_zz
用事件来控制。

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2009-07-28 20:50 by lyon
首先很感谢楼主能够提供这方面的知识和源码!我也看了楼主在Codeproject上的文章,但因为水平有限,第一篇还勉强能够看懂,第二篇楼主可能改变比较大,我看源码也不太懂。本人也正在做音频频谱显示方面,在实时性方面也搞得不好。请问楼主是怎样解决这个实时性问题的?在取数据和显示方面希望能够详细说说,我想其他人也应该很有兴趣。请多多指教!谢谢

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2009-07-30 16:11 by czbe
你好,最近对MP3播放和频谱显示很有兴趣,正在找这方面的例子,能否把源代码发一份给我?不胜感激!cathaychen@gmail.com

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2009-08-03 08:27 by jacky_zz
To lyon:
你好,我现在的实现在实时性上是达到了目的,但现在我现在的这个实现也存在一定的问题:在单CPU的计算机上,播放线程和频谱线程CPU占用率较高,15~30%之间;在双CPU的计算机上(我的)测试呢,没有启动QQ2009的情况呢,CPU占用率在0~3%之间,一旦启动QQ2009,一下子就飚升上去了,在10~25%之间。

我把获取实时的代码贴在这里:
===========System.h===========
#pragma once

#ifndef INCLUDE_SYSTEM
#define INCLUDE_SYSTEM

typedef __int64 jlong;
typedef unsigned int juint;
typedef unsigned __int64 julong;
typedef long jint;
typedef signed char jbyte;

#define CONST64(x) (x ## LL)
#define NANOS_PER_SEC CONST64(1000000000)
#define NANOS_PER_MILLISEC 1000000

jlong as_long(LARGE_INTEGER x);
void set_high(jlong* value, jint high);
void set_low(jlong* value, jint low);

class System
{
private:
static jlong frequency;
static int ready;

static void init()
{
LARGE_INTEGER liFrequency = {0};
QueryPerformanceFrequency(&liFrequency);
frequency = as_long(liFrequency);
ready = 1;
}
public:
static jlong nanoTime()
{
if(ready != 1)
init();

LARGE_INTEGER liCounter = {0};
QueryPerformanceCounter(&liCounter);
double current = as_long(liCounter);
double freq = frequency;
return (jlong)((current / freq) * NANOS_PER_SEC);
}
};

#endif

===========System.cpp===========
#include "System.h"

inline void set_low(jlong* value, jint low)
{
*value &= (jlong)0xffffffff << 32;
*value |= (jlong)(julong)(juint)low;
}

inline void set_high(jlong* value, jint high)
{
*value &= (jlong)(julong)(juint)0xffffffff;
*value |= (jlong)high << 32;
}

jlong as_long(LARGE_INTEGER x) {
jlong result = 0; // initialization to avoid warning
set_high(&result, x.HighPart);
set_low(&result, x.LowPart);
return result;
}

LARGE_INTEGER liFrequency = {0};
BOOL gSupportPerformanceFrequency = QueryPerformanceFrequency(&liFrequency);
jlong System::frequency = as_long(liFrequency);
int System::ready = 1;

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2009-08-03 08:34 by jacky_zz
TO lyon:
获取数据的原理,我在文章里已提到,我在google上查到一篇文章,是原Winamp的作者写的,他提到,做实时频谱分析,首先需要通过FFT转换,而FFT转换的计算量与传入的数据长度成正比,也就是说你传入的数据越多,计算量就越大,继而花费CPU的时间就越多,为了减少因为FFT的计算量,就需要减少传入的数据量,但使用waveOutXXX或DirectSound输出时,PCM数据量太少的话,是会出现断音的,经过作者不断的测试,终于找到一个合适的数值,就是4608。也就是你每次先获取4608个PCM数据,先将PCM数据输出到waveOutXXX或DirectSound,然后通过线程同步的方式将PCM数据传入到频谱分析线程,此线程负责FFT计算,然后绘图。

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2009-08-06 10:42 by lyon
谢谢楼主的回复,如果是DSound的次缓冲区和计算的数据都取4608大小吗?还有显示时是把计算出来的频率全部显示出来还是只显示部分,需要选择频率吗?因为我现在显示的是全部,但效果不如楼主的好!

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2009-08-10 14:49 by jacky_zz
DSound的缓冲区大小与读写缓冲区大小无关,读写缓冲区越大,解码耗费的时间就多,反之就小。而DSound的缓冲区一般都设置为两秒的数据量。而频谱分析,在我的实例里我从环形缓冲区(我设置为1秒的数据量)获取512字节的数据,通过FFT,再对前256(也就是总数据量512的一半)个数据分析,绘图。

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2009-08-11 16:07 by lyon
明白了,现在我做的显示效果虽然没有楼主的好,但也好很多啦!再次谢谢楼主的热心帮忙。我正跟着楼主的步伐继续做下去,希望以后多多交流!

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2009-08-12 11:04 by jacky_zz
TO lyon:
嗯,这个只能是慢慢的去实验才能得到最终的效果。
PS:通过QQ可以和我联系,59502553。

# re: 自己的mp3播放器【带频谱】[未登录]  回复  更多评论   

2009-08-22 11:29 by hao
期待 能给份源码
820156394@qq.com

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2009-09-17 09:11 by mayer
最近研究这个,看到这个非常兴奋,希望lz能给份源码,非常感谢!

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2009-09-17 09:13 by mayer
最近研究这个,看到这个非常兴奋,希望lz能给份源码,非常感谢!
Email: ytzyxhk@163.com

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2009-09-18 17:15 by jacky_zz
TO ALL:
近期硬盘分区表损坏,所有数据全部丢失(损失惨重)!!!包括AudioPlayer的源码,现在仅存的源码在www.codeproject.com上可以下载,感谢网友一直以来对此程序的关心,本打算公开,现在却因为硬盘问题而无法实现,深表歉意。

jacky_zz
2009-09-18

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2009-11-09 00:14 by littlemo
LZ你好,我正在学数字信号处理,主要在用MATLAB。现在有个问题是要用MATLAB来实现播放音频并且能够实时地显示出它的频谱图。
很想请教LZ这个频谱图实现的具体办法是怎样的。我有一些C++经验,我想依照LZ做的这个东西进行一下移植。请多指教。谢谢

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2009-11-09 00:15 by littlemo
哦刚才上一条忘记了,我的Email:ks_frank@foxmail.com
谢谢

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2009-11-09 10:21 by jacky_zz
流程:
(1)从文件读取pcm数据;
(2)将pcm数据写入到播放设备(waveOut或DirectSound);
(3)将pcm数据同步到DSP(FFT,绘图)。
其中:第一步,读取的数据不能太大,这个将直接影响后面2步的延时时间,延时时间越大,就不“实时”了,我在网上查的数据量大小是4608字节;第二步是标准操作,没有什么特别的;第三步,包含的工作有对pcm数据的FFT计算,以及频谱绘图。

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2009-12-17 22:00 by 王君
嗯。不错。不错。。。爽。。。

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2009-12-17 22:28 by 王君
学习中。。
能给分源码吗
万分感谢。。。
wj1025a@qq.com

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2009-12-21 18:02 by seliu
商用便携产品audio库:
fixed-point spectrum 库 (类似 winamp render,power均化)
fixed-point wma decode 库(wma7 8 9)
fixed-point 31 bands IIR equalizer 库
fixed-point tempo 库(0.5 ~ 2.0)
fixed-point sample rate convert (重采样)库

以上均可arm优化,可同时用于不小于200M 速度的arm;适用于windows、wince和embedded linux(fixed-point wma decode arm优化需要arm gcc 3.23以上).
可提供10分钟内全速运行的测试库,需要购买用于便携产品的全速运行库。
不提供源代码,除非用fixed-point SRS WOW HD源代码交换(请用 arm gcc 3.23以上生成 arm 验证库).

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2009-12-22 11:32 by seliu
4608个pcm同步spectrum是在声卡不会overrun或underrun情况下才正确。
我采用的同步方法是:取decode pcm stream片段时记录该片段在总的decode stream中的position;设置callback到wavout或dsound中获取playback pcm stream已播放pcm总数;计算两个stream的延迟来同步spectrum(linux下oss:SNDCTL_DSP_GETODELAY SNDCTL_DSP_GETOSPACE 或alsa:snd_pcm_delay).

有感兴趣的朋友吗?

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2009-12-23 10:07 by jacky_zz
经测试,4608这个值是不会出现overrun的,我参看了很多开源的winamp插件,这个值出现的频率很高。
PS:你提到的这个方案我原来也考虑过,好像效果并不是很好,显示的频谱与当前播放好像不符合。用waveOut呢,延时比较大;用DirectSound,采用通知点的方式呢,也不是最好的处理办法。如果能计算出playback的数值,那就是最准确的了。不知你有何更好的办法??
我的QQ是59502553,交流下?

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2009-12-23 18:04 by seliu
确实出现overrun的情况不多见;大量IO harddisk的时候,很容易出现underrun的情况了,这时声音很cut,specturm如果不调整就可能不同步了。
video player同步问题和specturm同步本质一样的。先期的player多数采用audio stream为基准同步image stream,现在多采用独立时钟tick同步audio stream和image stream,这是很多开源player采用的方法,最重要的原因之一是方便移植,因为audio stream为基准同步需要get delay,这和sound card关联太大。但是audio stream为基准同步是效率最高的,不需耗cpu去取基准时钟。

# re: 自己的mp3播放器【带频谱】[未登录]  回复  更多评论   

2009-12-24 09:13 by jacky_zz
你做了这方面的工作了吗?

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2009-12-24 09:49 by seliu
当然,我在vs仿真,然后download到商用便携产品。
老是整ffmpeg干吗呢,请教是有商业用途还是个人爱好?

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2010-03-01 17:30 by 西毒
可以加一下我的Q吗,
有些东西想请教一下

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2010-03-01 17:31 by 西毒
不记得写QQ:812306014

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2010-03-04 09:16 by 西毒
请问哪里可以下载到楼主播放器(AudioPlayer_20090506065.zip, 不是codeproject上的那三个)的源代码?
或者请人发一份到我邮箱:812306014@qq.com
谢谢!

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2010-03-16 19:15 by Mike
我也在研究这个MP3的频谱显示问题,有好多细节的东西想咨询,我已经加你的qq了,如果你在线的话,麻烦加我一下:674273293,希望能分享到你的代码:我的邮箱:zhangzhiwen.1713@163.com

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2012-02-17 17:30 by liaogang
/* size of the read/write-ahead, as specified by Java */
int bufferSizeInBytes;
int bitsPerSample;
int frameSize; // storage size in Bytes

DS_Info结构里面的这些字段是什么意思?

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2012-04-28 16:30 by 刘洋
我正在做一个关于频谱分析器的毕设,很高兴看到你的文章,也很希望得到你的帮助,我的QQ:654979544
麻烦您加一下,谢谢

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2013-06-12 22:16 by 路过,打酱油的
用了一下你没代码的播放器,CPU略高,应该是实现不太合理。。。

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2013-08-16 17:45 by 王蕾
看效果很炫,能否给下源代码?或者好心的人发给我一份!777leilei@163.com,谢谢!

# re: 自己的mp3播放器【带频谱】  回复  更多评论   

2014-03-21 15:36 by 秋叶
拿走了,不客气。

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理