• //---------------------------------------------------------------------------

    #include <vcl.h>
    #pragma hdrstop

    #include "Unit1.h"
    //---------------------------------------------------------------------------
    #pragma package(smart_init)
    #pragma resource "*.dfm"
    TForm1 *Form1;
    //---------------------------------------------------------------------------
    __fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
    {
    }
    //---------------------------------------------------------------------------
    bool __fastcall TForm1::RunCmd(AnsiString cmd,TStringList *stringlist)
    {
    TMemoryStream *memstream=new TMemoryStream();
    AnsiString rn="\\r\\n";
    PROCESS_INFORMATION proc;
    STARTUPINFO start;
    SECURITY_ATTRIBUTES sa;
    long ret;
    unsigned long lngBytesread;
    HANDLE hReadPipe,hWritePipe;
    char *strBuff=(char *)malloc(256);
    if(strBuff==NULL)
    {
    return false;
    }
    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
    sa.bInheritHandle=true;
    sa.lpSecurityDescriptor=NULL;
    ret=CreatePipe(&hReadPipe,&hWritePipe,&sa,0);
    if(ret==0)
    {
    //创建管道失败
    ShowMessage("创建管道失败");
    return false;
    }
    memset(&start,0x00,sizeof(STARTUPINFO));
    start.cb=sizeof(STARTUPINFO);
    start.dwFlags=STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
    start.hStdOutput=hWritePipe;
    start.hStdError=hWritePipe;
    bool retc=CreateProcess(NULL,cmd.c_str(),NULL,NULL,true, 0, 0, NULL, &start, &proc);
    if(!retc)
    {
    return false;
    }
    CloseHandle(hWritePipe);
    unsigned long len;
    memstream->Position=0;
    while(true)
    {
    memset(strBuff,0x00,256);
    GetFileSize(hReadPipe,&len);
    ret = ReadFile(hReadPipe, strBuff, 256, &lngBytesread,NULL);
    if(ret==0)
    {
    break;
    }
    else
    {
    memstream->Write(strBuff,lngBytesread);
    }
    }
    CloseHandle(proc.hProcess);
    CloseHandle(proc.hThread);
    CloseHandle(hReadPipe);
    memstream->Position=0;
    free(strBuff);
    stringlist->LoadFromStream(memstream);
    memstream->Clear();
    delete memstream;
    return true;
    }
    void __fastcall TForm1::Button1Click(TObject *Sender)
    {
    TStringList *StrList=new TStringList;
    AnsiString strcmd=Edit1->Text;
    RunCmd(strcmd,StrList);
    Memo1->Clear();
    Memo1->Text=StrList->Text;
    }
    //---------------------------------------------------------------------------
  • 做播放器的话,嘿嘿,当然离不开皮肤技术了。。。

    [face01]实现皮肤技术有很多种方法,最简单的就是用第三方控件或者皮肤类,但别人的东西终究还是别人的东西,要不就是收费,要不就是有很多限制,所以,好东西呢,还是自己造比较好。。[face07]

    自己实现皮肤的话,首先要解决的就是不规则窗体的问题,因为皮肤界面千变万化,所以窗体是必须要适应皮肤变化的,这个实现起来比较容易,用几个API函数就可以实现了
    分别是:
    CreateRect() //当然还有别的API函数,用来创建多边形等,这个是创建矩形区域的
    RgnCombineRgn() //区域运算用的,主要是把不需要的区域删除,留下需要的界面
    SetWindowRgn() //用来重新设置窗体区域

    比如:现在要根据当前Image里的图片创建不规则窗体
    int x,y; HRGN WndRgn,TempRgn;
    WndRgn=CreateRectRgn(0,0,Image1->Width,Image1->Height);
    for(x=0;x<Width;x++)
    for(y=0;y<Height;y++)
    if(Image1->Canvas->Pixels[x][y]==clBlack)
    {
    TempRgn=CreateRectRgn(x,y,x+1,y+1); 创建需要删除的区域,以象素为单位
    CombineRgn(WndRgn,WndRgn,TempRgn,RGN_XOR); 剔除不必要的区域
    DeleteObject(TempRgn);
    }
    SetWindowRgn(Handle,WndRgn,true);

    就这么简单。。。

    至于按钮之类的,原理和这个都一样,不过要先创建一个所以图片,用来确定按钮在界面中的位置
  • 近来想做一个播放器,找了很久一直都找不到理想的解码器,不过最后还是想到winamp的插件。。。
    [face10]既然winamp基于插件来完成解码,那么它就一定有解码器,只要找到它的接口就可以是使用了

    去官方网站下载winamp的SDK,终于得知它的解码工作交给相应的Input插件模块。
    在 Pulgin 文件夹中有很多的 DLL(动态链接库)文件,其中的in_mp3.dll就是mp3的解码插件,这个插件可以解码AAC/Mpeg1/mpeg2/mpeg3的音频,还有一个in_wm.dll自然就是解码WMA的了

    从SDK文档中得知,每种插件都具有固定的数据结构,如Input模块的数据结构(In_Module ),Output模块的(Out_Module ),同时每种插件的dll都具有约定的入口函数,对于Input插件,其入口函数为
    __declspec( dllexport ) In_Module * winampGetInModule2()

    其结构体:
    typedef struct
    {
    int nVer; // 模块版本号
    char *szDesc; //模块描述信息
    HWND hMainWnd; // Winamp的主窗体句柄(由Winamp来填写)
    HINSTANCE hDllInstance; // DLL实例句柄(由Winamp来填写)
    char *szFileExt; // 扩展名过滤器,格式参见GetOpenFileName
    int nIsSeekable; // 是否可索引媒体,是-你可以拖动进度条,否-反之
    int UsesOutputPlug; // 是否使用输出插件?你想在这个模块里搞定一切?

    // 下面都是函数指针,将被Winamp调用
    void (*Config)(HWND hwndParent); // 配置对话框
    void (*About)(HWND hwndParent); // 关于对话框
    void (*Init)(); // 初始化
    void (*Quit)(); // 退出
    // szFile - 传入的文件名,szTitle - 传出的标题,nLen - 转出的时间长度,毫秒。
    // 如果szFile传NULL,则返回当前播放文件的信息
    void (*GetFileInfo)(char *szFile, char *szTitle, int *nLen);
    int (*InfoBox)(char *szFile, HWND hwndParent); // 弹出文件信息对话框
    int (*IsOurFile)(char *szFile); // 检查文件格式
    int (*Play)(char *szFile); // 开始播放文件szFile,返回0正常,-1错误
    void (*Pause)(); // 暂停处理
    void (*UnPause)(); // 取消暂停
    int (*IsPaused)(); // 是否斩停?1是暂停,0不是
    void (*Stop)(); // 停止播放
    int (*GetLength)(); // 取得长度,毫秒单位
    int (*GetOutputTime)(); // 获取当前时间,一般调用out模块的同名函数即可
    void (*SetOutputTime)(int nTime); // 索引到某一时刻
    void (*SetVolume)(int volume); // 音量调节,从0 - 255
    void (*SetPan)(int pan); // 左右声道平衡,从-127 - 127

    //下面的函数多和AVS、可视化效果、均衡器等有关,具体咱们用不到,就暂时不讲了。
    void (*SAVSAInit)(int maxlatency_in_ms, int srate);
    void (*SAVSADeInit)(); // call in Stop()
    void (*SAAddPCMData)(void *PCMData, int nch, int bps, int timestamp);
    int (*SAGetMode)();
    void (*SAAdd)(void *data, int timestamp, int csa);
    void (*VSAAddPCMData)(void *PCMData, int nch, int bps, int timestamp);
    int (*VSAGetMode)(int *specNch, int *waveNch);
    void (*VSAAdd)(void *data, int timestamp);
    void (*VSASetInfo)(int nch, int srate);
    int (*dsp_isactive)();
    int (*dsp_dosamples)(short int *samples, int numsamples, int bps, int nch, int srate);
    void (*EQSet)(int on, char data[10], int preamp);
    void (*SetInfo)(int bitrate, int srate, int stereo, int synched);

    Out_Module *outMod; // 看看,Winamp终于露出马脚了吧?
    } In_Module;


    这样通过这个入口函数,就可以轻易的得到了Input插件的 In_Module结构体,接下来的事情自然也就很容易了,有了这个结构体,插件的所有功能都可以调用了。

    不过单凭这个Input插件还是不能放出声音来的,因为Input插件虽然能完成mp3或者WMA的解码,但却没有把他们输出来,所以,就得自己写输出模块。不过,winamp有个OutPut插件可以完成输出功能,这就是Out_Wave.dll,加上这个插件,那么播放mp3就非常容易了。

    OutPut插件得入口函数
    __declspec( dllexport ) Out_Module * winampGetOutModule()
    其结构体为:Out_Module
    typedef struct
    {
    // 下面有些和In_Module一样,就不赘述了。
    int nVer;
    char *szDesc;
    int nId; // 自己给一个ID,不知道有什么用,反正大于65536就行了。
    HWND hMainWindow;
    HINSTANCE hDllInstance;
    void (*Config)(HWND hwndParent);
    void (*About)(HWND hwndParent);
    void (*Init)();
    void (*Quit)();
    // nSample - 采样率, nChannels - 声道数,1或2
    // nBitPerSamp - 每采样的位率,nBufLen、nPreBufLen - 缓冲长度,咱们用不到
    // 返回大于0正常播放,小于0失败
    int (*Open)(int nSample, int nChannels, int nBitPerSamp, int nBufLen, int nPreBufLen);
    void (*Close)(); // 关闭输出设备
    // pBuf - 内存数据块,nLen - 数据块的长度
    int (*Write)(char *pBuf, int len); // 返回0成功
  • 作为1.12版歌词串串烧的一个模块,主要用来分解LRC格式的歌词,该模块已经完全封装为一个类,方便调用


    下面是LRC格式的描述

    LRC 歌词是一种包含着“[*:*]”形式的“标签(tag)”的、基于纯文本的歌词专用格式。最早由郭祥祥先生(Djohan)提出并在其程序中得到应用。这种歌词文件既可以用来实现卡拉OK功能(需要专门程序),又能以普通的文字处理软件查看、编辑。当然,实际操作时通常是用专门的LRC歌词编辑软件进行高效编辑的。以下具体介绍LRC格式中的“标签”。
    时间标签(Time-tag)
    形式为"[mm:ss]"或"[mm:ss.fff]"(分钟数:秒数)。数字须为非负整数,比如"[12:34.5]"是有效的,而"[0x0C:-34.5]"无效。

    它可以位于某行歌词中的任意位置。一行歌词可以包含多个时间标签(比如歌词中的迭句部分)。根据这些时间标签,用户端程序会按顺序依次高亮显示歌词,从而实现卡拉OK功能。另外,标签无须排序。

    标识标签(ID-tags)

    其格式为"[标识名:值]"。大小写等价。以下是预定义的标签。

    [ar:艺人名]
    [ti:曲名]
    [al:专辑名]
    [by:编者(指编辑LRC歌词的人)]
    [offset:时间补偿值] 其单位是毫秒,正值表示整体提前,负值相反。这是用于总体调整显示快慢的。

    样例

    [ar:unknown]
    [ti:sample]
    [al:none]
    [by:me]
    [01:02.355][00:00]This line should be sung twice
    [00:05.7]And this one... once only.


    开发标准(供程序员阅读参考)

    以下列出了开发支持LRC格式的软件时应遵守的一些标准。

    无论是否在行首,行内凡具有“[*:*]”形式的都应认为是标签。(注意:其中的冒号并非全角字符“:”)
    凡是标签都不应显示。
    凡是标签,且被冒号分隔的两部分都为非负数,则应认为是时间标签。因此,对于非标准形式(非“[mm:ss]”)的时间标签也应能识别(如“[0:0]”)。
    凡是标签,且非时间标签的,应认为是标识标签。
    标识名中大小写等价。
    为了向后兼容,应对未定义的新标签作忽略处理。另应对注释标签([:])后的同一行内容作忽略处理。
    应允许一行中存在多个标签,并能正确处理。
    应能正确处理未排序的标签。
  • Unicode就是宽字符编码,它与ANSI编码最大的不同就是一个ANSI编码的位宽是8位,而Unicode的位宽是16位,比如:一个Unicode编码的字符 ’A’ 其16进制编码为0x0041,而同样的字符用ANSI编码就会表示为0x41;

    转入正题,不去管Unicode的编码方式,只要能把它识别出来就可以了。Windows SDK中提供了一个API函数,可以把ASCII编码转换为Unicode编码,那就是WideCharToMultiByte()函数,所以编码转换工作就全部交给这个函数了

    看一下WideCharToMultiByte()的原型声明:
    int WideCharToMultiByte(

    UINT CodePage, // code page
    DWORD dwFlags, // performance and mapping flags
    LPCWSTR lpWideCharStr, // address of wide-character string
    int cchWideChar, // number of characters in string
    LPSTR lpMultiByteStr, // address of buffer for new string
    int cchMultiByte, // size of buffer
    LPCSTR lpDefaultChar, // address of default for unmappable characters
    LPBOOL lpUsedDefaultChar // address of flag set when default char. used
    );
    第一个参数使用CP_ACP,第二个参数设为0,第三个参数就是要转换的Unicode字符串的指针,第四个参数,Unicode字符串长度,第五个参数转换到ANSI字符串的指针,第六个参数ANSI字符串的长度

    这样就可以把Unicode转换为ANSI编码了,但是还有一个问题,就是如何判断要读入的文件是Unicode编码的还是ANSI编码的?查了一下资料,发现所有Unicode编码的文本文件,其文件头2个字节一定是"0xff","0xfe",这样就好办了,只要先读出这两个字节,然后就可以判断是不是Unicode编码的了。

    if( psFirst[0] == ’\xff’ && psFirst[1] == ’\xfe’ )用这条语句就可以判断了。

    下面给出C++的一段文件读取代码,返回的就是AnsiString的字符串,这样就可以读取Unicode编码和非Unicode编码的文件了
    AnsiString TForm1::ReadUnicodeFromFile(const AnsiString &FileName)
    {
    char psFirst[2];
    AnsiString str;
    int iFileHandle = FileOpen( FileName, fmOpenRead );
    int iFileLength = FileSeek( iFileHandle, 0, 2 );
    FileSeek(iFileHandle, 0, 0);
    FileRead(iFileHandle, psFirst, 2); //读出文件的头2个字节,用来判断是否为Unicode编码
    if( psFirst[0] == ’\xff’ && psFirst[1] == ’\xfe’ )
    {
    wchar_t *wstr=new wchar_t[iFileLength+1];
    FileRead(iFileHandle, wstr, iFileLength);
    FileClose(iFileHandle);
    int nLen = wcslen(wstr)+1;
    char *buf = new char[2*nLen];
    WideCharToMultiByte(CP_ACP, 0, wstr, nLen, buf, 2*nLen, NULL, NULL);
    str = buf;
    delete[] buf;
    }
    else
    {
    char *pszBuffer = new char[iFileLength+1];
    FileSeek(iFileHandle, 0, 0);
    FileRead(iFileHandle, pszBuffer, iFileLength);
    FileClose(iFileHandle);
    str=pszBuffer;
    delete []pszBuffer;
    }
    return str;
    }