如何从特定角度播放.wav文件(例如30秒)

时间:2019-06-30 02:28:33

标签: c++ winapi

我希望能够在我的程序中的某个特定时间(例如,进入歌曲的30秒)开始播放一首歌曲,该程序复制了一个广播电台,当您更改频道时,歌曲已经在播放或几乎在播放完成。

目前,我使用PlaySound,并将26首歌曲另存为wav文件和程序中的资源。我已经看到mciSendString是一个选项,但是我不明白在使用Windows API和C ++进行编码时如何设法使其工作。

这是我当前的PlayWavFile函数(其中资源整数是从0到25的随机生成的数字,强制转换为该函数以播放随机歌曲):

void PlayWavFile(int resource) {
    PlaySound(MAKEINTRESOURCE(resource), hInst, SND_RESOURCE | SND_ASYNC);
}

我希望能够使用我预先确定的整数值(例如持续时间)以某种方式播放歌曲。

3 个答案:

答案 0 :(得分:2)

  

我已经将mciSendString作为选项,但是我不知道如何   使用Windows API和C ++进行编码时,我可以设法使其正常工作。

您可以阅读Multimedia Command Strings之类的MSDN文档来了解命令的语法

从1秒钟读取wav文件的基本示例=>

int rc = mciSendString(L"open E:\\test.wav alias wav1", NULL, 0, 0);
if (rc == 0)
{
    rc = mciSendString(L"set wav1 time format ms", NULL, 0, 0);
    rc = mciSendString(L"seek wav1 to 1000", NULL, 0, 0);
    if (rc == 0)
    {
        rc = mciSendString(L"play wav1", NULL, 0, 0);
    }
    else
    {
        // handle error (like MCIERR_OUTOFRANGE for example)
    }
}
else
{
    // handle error
}

答案 1 :(得分:2)

要将MCI与mciSendCommand一起使用,您应该

  • 打开设备并获取设备ID。
  • 设置搜索。
  • 播放。

  • 打开设备并获取设备ID。
  • 直接从指定位置播放。

示例:

#include <windows.h>
#pragma comment(lib, "winmm.lib")
MCIDEVICEID MCIOpen(LPCTSTR strPath)
{
    MCI_OPEN_PARMS mciOP;
    DWORD opReturn;
    mciOP.lpstrDeviceType = NULL;
    mciOP.lpstrElementName = strPath;  //Set the .wav file name to open
    opReturn = mciSendCommand(NULL, MCI_OPEN, MCI_OPEN_ELEMENT, (DWORD)(LPVOID)&mciOP);
    if (!opReturn)
        return mciOP.wDeviceID;
    return -1;
}
DWORD MCISeek(MCIDEVICEID wDeviceID,int sec)
{
    MCI_SEEK_PARMS SeekParms;
    SeekParms.dwTo = (sec) * 1000;
    return mciSendCommand(wDeviceID, MCI_SEEK, MCI_TO, (DWORD)(LPVOID)&SeekParms);
}
DWORD MCIPlay(MCIDEVICEID wDeviceID)
{
    MCI_PLAY_PARMS mciPP;
    return mciSendCommand(wDeviceID, MCI_PLAY, MCI_NOTIFY| MCI_WAIT, (DWORD)&mciPP);
}
DWORD MCIPlayFrom(MCIDEVICEID wDeviceID,int sec)
{
    MCI_PLAY_PARMS play;
    play.dwFrom = sec*1000;//Play From sec*1000 ms
    return mciSendCommand(wDeviceID, MCI_PLAY, MCI_NOTIFY | MCI_FROM| MCI_WAIT, (DWORD)&play);
}

int main()
{
    //open device
    MCIDEVICEID wDeviceID = MCIOpen("test.wav");  //Save DeviceID
    DWORD opReturn;
    if (wDeviceID != -1)
    {
        //MCI_SET_PARMS mciSet;
        //mciSet.dwTimeFormat = MCI_FORMAT_MILLISECONDS;//set time format to milliseconds
        //opReturn = mciSendCommand(wDeviceID, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD)(LPVOID)&mciSet);

        ////set the position at 30s.
        //opReturn = MCISeek(wDeviceID, 30);

        ////play
        //opReturn = MCIPlay(wDeviceID);

        opReturn = MCIPlayFrom(wDeviceID,30);

    }
    return opReturn;
}

请注意,如果要获取声音,则在使用MCI_WAIT时需要MCI_PLAY标志。

编辑: 您无法听到声音,因为开始播放后运行时间很短。它将启动播放并立即将其关闭(可以在播放开始后通过添加Sleep()进行验证)。请参见related issue。如果您不想等待播放,则需要处理MCI_NOTIFY,设置回调窗口句柄,并在播放结束后处理MM_MCINOTIFY

#include <windows.h>
#pragma comment(lib, "winmm.lib")

HWND hwnd;
MCIDEVICEID MCIOpen(LPCTSTR strPath)
{
    MCI_OPEN_PARMS mciOP;
    DWORD opReturn;
    mciOP.lpstrDeviceType = NULL;
    mciOP.lpstrElementName = strPath;  //Set the .wav file name to open
    opReturn = mciSendCommand(NULL, MCI_OPEN, MCI_OPEN_ELEMENT, (DWORD)(LPVOID)&mciOP);
    if (!opReturn)
        return mciOP.wDeviceID;
    return -1;
}
DWORD MCISeek(MCIDEVICEID wDeviceID,int sec)
{
    MCI_SEEK_PARMS SeekParms;
    SeekParms.dwTo = (sec) * 1000;
    return mciSendCommand(wDeviceID, MCI_SEEK, MCI_TO, (DWORD)(LPVOID)&SeekParms);
}
DWORD MCIPlay(MCIDEVICEID wDeviceID)
{
    MCI_PLAY_PARMS mciPP;
    mciPP.dwCallback = (DWORD_PTR)hwnd;
    return mciSendCommand(wDeviceID, MCI_PLAY, MCI_NOTIFY, (DWORD)&mciPP);
}
DWORD MCIPlayFrom(MCIDEVICEID wDeviceID,int sec)
{
    MCI_PLAY_PARMS play;
    play.dwFrom = sec*1000;//Play From sec*1000 ms
    return mciSendCommand(wDeviceID, MCI_PLAY, MCI_NOTIFY | MCI_FROM, (DWORD)&play);
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case MM_MCINOTIFY:
    {
        if (MCI_NOTIFY_SUCCESSFUL == wParam) //MCI_NOTIFY_SUCCESSFUL means that the song has been played successfully. 
        {
            //To Do

        }
    }
    break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}
int main()
{
    static const char* class_name = "DUMMY_CLASS";
    WNDCLASSEX wx = {};
    wx.cbSize = sizeof(WNDCLASSEX);
    wx.lpfnWndProc = WndProc;        // function which will handle messages
    wx.hInstance = GetModuleHandleA(NULL);
    wx.lpszClassName = class_name;
    if (RegisterClassEx(&wx)) {
        hwnd = CreateWindowEx(0, class_name, "dummy_name", 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
    }

    //open device
    MCIDEVICEID wDeviceID = MCIOpen("test.wav");  //Save DeviceID
    DWORD opReturn;
    if (wDeviceID != -1)
    {
        //MCI_SET_PARMS mciSet;
        //mciSet.dwTimeFormat = MCI_FORMAT_MILLISECONDS;//set time format to milliseconds
        //opReturn = mciSendCommand(wDeviceID, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD)(LPVOID)&mciSet);

        ////set the position at 30s.
        //opReturn = MCISeek(wDeviceID, 30);

        ////play
        //opReturn = MCIPlay(wDeviceID);

        opReturn = MCIPlayFrom(wDeviceID,30);
    }
    HACCEL hAccelTable = LoadAccelerators(wx.hInstance, class_name);
    MSG msg;
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    return 0;
}

答案 2 :(得分:0)

有很多可以播放声音的库,但是如果您想继续使用PlaySound(),可以使用以下方法:

因此,尽管这可能不是最简单的方法,但是您可以打开原始声音,跳过前N秒(使用.wav文件非常容易,因为它们是恒定比特率并且没有关键帧),请存储该声音作为一种新资源,最后使用它。