这是一个支持WAV格式的音乐带有频谱与进度控制的简易音乐播放器,可以在windows android iOS上无缝移植.目前仅支持wav 44100HZ 16bits格式的音乐播放,不过对于格式支持的拓展,应该不是什么难事。
目前代码和示范程序都可以在PainterEngine.com中下载到
1.为了编译源码,作为示范你需要先安装mingw(当然其它平台和其它编译器也可,你可以参照PainterEngine.com中的教程部分),运行cmd,输入gcc –v,验证你的mingw是否安装成功
2.访问PainterEngine.com,下载PainterEngine
3.访问PainterEngine.com的实例,下载音乐播放器的源码
4.将解压出来的project中的代码文件,替换PainterEngine/project,并将bin中的gbk_18.pxf放在PainterEngine/project文件夹中
5.进入PainterEngine/platform/windows,运行mingw_build.bat,等待编译完成即可
#ifndef PAINTERENGINE_APPLICATION_H
#define PAINTERENGINE_APPLICATION_H
#include "PainterEngine_Startup.h"
#include "platform/modules/px_audio.h"
typedef struct
{
PX_Runtime runtime;
//根对象,选择音乐按钮,暂停按钮,文件选择器,进度条
PX_Object *root,*button_play,*button_pause,*explorer_file,*sliderbar_progress;
//音频数据
PX_SoundData sounddata;
//混音器
PX_SoundPlay soundplay;
//字模
PX_FontModule fm;
}PX_Application;
extern PX_Application App;
px_bool PX_ApplicationInitialize(PX_Application *App,px_int screen_Width,px_int screen_Height);
px_void PX_ApplicationUpdate(PX_Application *App,px_dword elpased);
px_void PX_ApplicationRender(PX_Application *App,px_dword elpased);
px_void PX_ApplicationPostEvent(PX_Application *App,PX_Object_Event e);
#endif
#include "PainterEngine_Application.h"
//////////////////////////////////////////////////////////////////////////
//更多代码实例/教程 访问www.PainterEngine.com
//////////////////////////////////////////////////////////////////////////
PX_Application App;
//返回path目录中文件夹个数
px_int APP_ExplorerGetPathFolderCount(const px_char *path,const char *filter)
{
return PX_FileGetDirectoryFileCount(path,PX_FILEENUM_TYPE_FOLDER,filter);
}
//返回path目录中文件个数
px_int APP_ExplorerGetPathFileCount(const px_char *path,const char *filter)
{
return PX_FileGetDirectoryFileCount(path,PX_FILEENUM_TYPE_FILE,"wav\0WAV\0Wav\0");
}
//取得path目录中文件夹名称
px_int APP_ExplorerGetPathFolderName(const char path[],int count,char FileName[][260],const char *filter)
{
return PX_FileGetDirectoryFileName(path,count,FileName,PX_FILEENUM_TYPE_FOLDER,filter);
}
//取得path目录中文件名称,当然加上过滤器仅接受wav文件
px_int APP_ExplorerGetPathFileName(const char path[],int count,char FileName[][260],const char *filter)
{
return PX_FileGetDirectoryFileName(path,count,FileName,PX_FILEENUM_TYPE_FILE,"wav\0WAV\0Wav\0");
}
//当explorer
px_void PX_ApplicationOnExpolrerConfirm(PX_Object *pObject,PX_Object_Event e,px_void *ptr)
{
PX_Application *pApp=(PX_Application *)ptr;
px_char sPath[260];
//取得选择的目录
PX_Object_ExplorerGetPath(pObject,sPath,0);
if (sPath[0])
{
//清理之前的播放音乐
PX_SoundPlayClear(&pApp->soundplay);
//释放之前的音乐数据
PX_SoundStaticDataFree(&pApp->sounddata);
//加载音乐播放数据
if(!PX_LoadSoundFromFile(&pApp->runtime.mp_resources,&pApp->sounddata,sPath))return;
//将播放数据导入混音器
PX_SoundPlayAdd(&pApp->soundplay,PX_SoundCreate(&pApp->sounddata,PX_FALSE));
//设置进度条范围
PX_Object_SliderBarSetRange(pApp->sliderbar_progress,0,pApp->sounddata.size);
}
}
//当选择音乐按钮被点击后
px_void PX_ApplicationOnButtonPlay(PX_Object *pObject,PX_Object_Event e,px_void *ptr)
{
PX_Application *pApp=(PX_Application *)ptr;
//打开文件选择菜单
PX_Object_ExplorerOpen(pApp->explorer_file);
}
//当暂停播放音乐被点击后
px_void PX_ApplicationOnButtonPause(PX_Object *pObject,PX_Object_Event e,px_void *ptr)
{
PX_Application *pApp=(PX_Application *)ptr;
if (PX_SoundPlayGetDataCount(&pApp->soundplay)==0)
{
//没有音乐播放,直接返回
return;
}
if (pApp->soundplay.pause)
{
//如果当前暂停播放,将按钮文本设置为暂停
PX_Object_PushButtonSetText(pObject,"暂停");
//继续播放音乐
PX_SoundPlayPause(&pApp->soundplay,PX_FALSE);
}
else
{
//如果当前正在播放,将按钮文本设置为继续
PX_Object_PushButtonSetText(pObject,"继续");
//暂停播放音乐
PX_SoundPlayPause(&pApp->soundplay,PX_TRUE);
}
}
px_void PX_ApplicationOnProgressChanged(PX_Object *pObject,PX_Object_Event e,px_void *ptr)
{
PX_Application *pApp=(PX_Application *)ptr;
px_int offset;
//将按钮文本设置为继续
PX_Object_PushButtonSetText(pApp->button_pause,"继续");
//暂停播放
PX_SoundPlayPause(&pApp->soundplay,PX_TRUE);
//播放数据偏移应该至少4字节对齐(2声道,16位),我这里直接8字节对齐
offset=PX_Object_SliderBarGetValue(pObject);
offset=offset/8*8;
//重新设置播放偏移量
pApp->soundplay.Sounds[0].offset=offset;
}
px_bool PX_ApplicationInitialize(PX_Application *pApp,px_int screen_width,px_int screen_height)
{
px_int x=5,y=5;
PX_ApplicationInitializeDefault(&pApp->runtime, screen_width, screen_height);
//初始化混音器
PX_SoundPlayInitialize(&pApp->runtime.mp_game,&pApp->soundplay);
//*初始化播放器
PX_AudioInitialize(&pApp->soundplay);
//初始化字模库
if(!PX_FontModuleInitialize(&pApp->runtime.mp_resources,&pApp->fm))return PX_FALSE;
//加载字模库
if(!PX_LoadFontModuleFromFile(&pApp->fm,"gbk_18.pxf"))return PX_FALSE;
//创建根对象
pApp->root=PX_ObjectCreate(&pApp->runtime.mp_ui,PX_NULL,0,0,0,0,0,0);
//创建文件浏览器
pApp->explorer_file=PX_Object_ExplorerCreate(&pApp->runtime.mp_ui,pApp->root,0,0,pApp->runtime.surface_width,pApp->runtime.surface_height,&pApp->fm,
APP_ExplorerGetPathFolderCount,
APP_ExplorerGetPathFileCount,
APP_ExplorerGetPathFolderName,
APP_ExplorerGetPathFileName,
""
);
//绑定确定事件
PX_ObjectRegisterEvent(pApp->explorer_file,PX_OBJECT_EVENT_EXECUTE,PX_ApplicationOnExpolrerConfirm,pApp);
//创建播放按钮
pApp->button_play=PX_Object_PushButtonCreate(&pApp->runtime.mp_ui,pApp->root,x,y,128,24,"选择音乐",&pApp->fm);
//绑定播放函数
PX_ObjectRegisterEvent(pApp->button_play,PX_OBJECT_EVENT_EXECUTE,PX_ApplicationOnButtonPlay,pApp);
x+=136;
//创建暂停按钮
pApp->button_pause=PX_Object_PushButtonCreate(&pApp->runtime.mp_ui,pApp->root,x,y,128,24,"暂停",&pApp->fm);
//绑定播放函数
PX_ObjectRegisterEvent(pApp->button_pause,PX_OBJECT_EVENT_EXECUTE,PX_ApplicationOnButtonPause,pApp);
//创建播放进度条
y+=32;
x=10;
pApp->sliderbar_progress=PX_Object_SliderBarCreate(&pApp->runtime.mp_ui,pApp->root,x,y,pApp->runtime.surface_width-20,32,PX_OBJECT_SLIDERBAR_TYPE_HORIZONTAL,PX_OBJECT_SLIDERBAR_STYLE_BOX);
PX_Object_SliderBarSetSliderButtonLength(pApp->sliderbar_progress,32);
PX_Object_SliderBarSetRange(pApp->sliderbar_progress,0,100);
//绑定进度条事件
PX_ObjectRegisterEvent(pApp->sliderbar_progress,PX_OBJECT_EVENT_VALUECHANGED,PX_ApplicationOnProgressChanged,pApp);
return PX_TRUE;
}
px_void PX_ApplicationUpdate(PX_Application *pApp,px_dword elpased)
{
}
#define FRAME_SIZE 1024
px_void PX_ApplicationRender(PX_Application *pApp,px_dword elpased)
{
px_int lheight;
px_complex frame[FRAME_SIZE];
px_double hanning[FRAME_SIZE];
px_int16 uframe[FRAME_SIZE];
px_surface *pRenderSurface=&pApp->runtime.RenderSurface;
//清理画布
PX_RuntimeRenderClear(&pApp->runtime,PX_OBJECT_UI_DEFAULT_BACKGROUNDCOLOR);
//开始绘制频谱
pApp->sliderbar_progress->Width=pRenderSurface->width-20.f;
//抓取1024长度的音频帧做分析
if(PX_SoundPlayReadCurrentPlayingData(&pApp->soundplay,0,0,uframe,1024))
{
px_int i;
PX_Object_SliderBarSetValue(pApp->sliderbar_progress,pApp->soundplay.Sounds[0].offset);
//构建汉宁窗
PX_WindowFunction_hanning(hanning,FRAME_SIZE);
for(i=0;i<FRAME_SIZE;i++)
{
//归一化,加窗
frame[i].re=uframe[i]*1.0/32768.0*hanning[i];
frame[i].im=0;
}
//快速傅里叶变换
PX_FFT(frame,frame,FRAME_SIZE);
//计算功率谱,然后缩放一下
for(i=0;i<FRAME_SIZE/2;i++)
{
frame[i].re=PX_sqrtd(frame[i].re*frame[i].re+frame[i].im*frame[i].im)/50;
}
//依据傅里叶变换共轭对称性,只需要处理一半频谱
for (i=0;i<FRAME_SIZE/2;i++)
{
px_int j;
//计算最终高度
lheight=(px_int)(frame[i].re>1?pRenderSurface->height:pRenderSurface->height*frame[i].re);
lheight=(px_int)(lheight*0.85);
//绘制频谱像素
for(j=0;j<lheight;j++)
{
px_uchar r=(px_uchar)(j*255/lheight);
r=(r>255?255:r);
PX_SurfaceDrawPixel(pRenderSurface,pRenderSurface->width/2+i,pRenderSurface->height-1-j,PX_COLOR(255-r,r,255-r,255-r));
PX_SurfaceDrawPixel(pRenderSurface,pRenderSurface->width/2-i,pRenderSurface->height-1-j,PX_COLOR(255-r,r,255-r,255-r));
}
}
}
//渲染控件
PX_ObjectRender(pRenderSurface,pApp->root,elpased);
}
px_void PX_ApplicationPostEvent(PX_Application *pApp,PX_Object_Event e)
{
PX_ApplicationEventDefault(&pApp->runtime, e);
//处理按键消息
PX_ObjectPostEvent(pApp->root,e);
}