How to play a .wav file from a particular point (e.g. 30 seconds in)

夙愿已清 提交于 2021-02-08 05:09:11

问题


I want to be able to begin playing a song at a particular time (for example, 30 seconds into the song) in my program which replicates a radio station, where when you change the channel and the song is already playing or almost finished.

Currently I use PlaySound and have my 26 songs saved as wav files and as resources within the program. I have seen mciSendString as an option however I do not understand how i could manage to get it to work when coding with Windows API and C++.

Here is my current PlayWavFile function (where the resource integer is a randomly generated number from 0 to 25 cast to this function to play a random song):

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

I would expect to be able to play the song a certain way through, using an integer value i have predetermined (like a duration).


回答1:


To use MCI with mciSendCommand, you should

  • Open the Device and get the deviceID.
  • Set the seek.
  • Play.

Or

  • Open the Device and get the deviceID.
  • Play from the specified position directly.

Sample:

#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;
}

Note that if you want to get the sound, MCI_WAIT flag is required when using MCI_PLAY.

EDIT: You can't hear the sound because you have little running time after start to play it. It will initiate the play and close it immediately(It can be verified by adding Sleep() after play starts). See the related issue. If you want play no wait, you need to handle the MCI_NOTIFY, set the callback window handle, and handle the MM_MCINOTIFY when the play has finish.

#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:


I have seen mciSendString as an option however I do not understand how i could manage to get it to work when coding with Windows API and C++.

You can read MSDN docs like Multimedia Command Strings for the syntax of commands

A basic sample reading a wav file from 1 second =>

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
}



回答3:


There are lots of libraries that can play sound, but if you want to continue using PlaySound(), here's an idea:

  • You're playing sound from resources, rather than directly from files.
  • You can create resources programmatically.

So while it may not be the easiest way, you could open your original sound, skip the first N seconds of it (easy enough with .wav files, since they are constant-bitrate and do not have keyframes), store that as a new resource, and finally play it.



来源:https://stackoverflow.com/questions/56822035/how-to-play-a-wav-file-from-a-particular-point-e-g-30-seconds-in

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!