使用PainterEngine开发音乐播放器

 

 

这是一个支持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->runtimescreen_widthscreen_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->runtimee);

    //处理按键消息

    PX_ObjectPostEvent(pApp->root,e);

}