簡體   English   中英

Win32 PlaySound重疊音頻

[英]Win32 PlaySound overlapping audio

我有一個C ++ Win32應用程序,該應用程序每次觸發特定事件時都需要能夠播放外部.wav文件。 我目前有這樣的代碼:

void CALLBACK timerCall(HWND hwnd, UINT msg, UINT timer, DWORD time)
{
    if(/*some condition is met*/)
    {
        std::cout << "Detected event" << std::endl;
        PlaySound("file.wav", NULL, SND_FILENAME | SND_ASYNC);
    }
}

問題是.wav文件的長度為幾秒鍾,我希望事件的每次調用都播放該聲音的新實例。 沒有SND_ASYNC ,它將不會觸發事件,直到聲音播放完畢。 這可以通過添加SND_ASYNC解決。 但是,現在,如果在聲音已經播放的情況下再次觸發事件,則會中斷播放,而只是重新開始而不是重疊聲音。

如何防止對PlaySound的新呼叫打斷先前的呼叫並迫使聲音重疊?

“ waveaudio”設備(由PlaySound使用)不支持同時播放多個文件。 嘗試使用.mp3文件。 下面的例子。

#include <Windows.h>
#include <stdio.h>
#include <stdexcept>

#ifdef _UNICODE
#define stprintf_s swprintf_s
#else
#define stprintf_s sprintf_s
#endif

class Player {
public:
    Player(LPCTSTR lpFileName) {
        MCI_OPEN_PARMS openp;
        MCI_SET_PARMS setp;
        openp.dwCallback = NULL;
        openp.lpstrDeviceType = reinterpret_cast<LPCTSTR>(MCI_ALL_DEVICE_ID);
        openp.lpstrElementName = lpFileName;
        TCHAR name[32];
        static int alias = 0;
        stprintf_s(name, TEXT("alias%08d"), alias++);
        openp.lpstrAlias = name;
        checkerror(mciSendCommand(0, MCI_OPEN, MCI_WAIT | MCI_OPEN_ELEMENT | MCI_OPEN_SHAREABLE | MCI_OPEN_ALIAS, reinterpret_cast<DWORD_PTR>(&openp)));
        _device = openp.wDeviceID;
        setp.dwCallback = NULL;
        setp.dwTimeFormat = 0;
        if (mciSendCommand(openp.wDeviceID, MCI_SET, MCI_SET_TIME_FORMAT, reinterpret_cast<DWORD_PTR>(&setp)) == DWORD(-1)) {
            close();
            throw std::runtime_error("Can't open MCI device");
        }
    }

    Player(Player const&) = delete;

    Player(Player&& other) {
        _device = other._device;
        other._device = 0;
    }

    ~Player() {
        if (_device != 0) {
            close();
        }
    }

    void play(HWND hWndNotify) {
        MCI_PLAY_PARMS params;
        params.dwCallback = reinterpret_cast<DWORD_PTR>(hWndNotify);
        params.dwFrom = NULL;
        params.dwTo = NULL;
        checkerror(mciSendCommand(_device, MCI_PLAY, (hWndNotify != 0) ? MCI_NOTIFY : 0, reinterpret_cast<DWORD_PTR>(&params)));
    }

    void rewind() {
        MCI_SEEK_PARMS params;
        checkerror(mciSendCommand(_device, MCI_SEEK, MCI_WAIT | MCI_SEEK_TO_START, reinterpret_cast<DWORD_PTR>(&params)));
    }

    void pause() {
        MCI_GENERIC_PARMS params;
        params.dwCallback = NULL;
        checkerror(mciSendCommand(_device, MCI_PAUSE, MCI_WAIT, reinterpret_cast<DWORD_PTR>(&params)));
    }

    void stop() {
        MCI_GENERIC_PARMS params;
        params.dwCallback = NULL;
        checkerror(mciSendCommand(_device, MCI_STOP, MCI_WAIT, reinterpret_cast<DWORD_PTR>(&params)));
    }

    MCIDEVICEID device() const { return _device; }

private:
    MCIDEVICEID _device;

    static void checkerror(MCIERROR code) {
        if (code != 0) {
            char buffer[260];
            mciGetErrorStringA(code, buffer, sizeof(buffer) - 1);
            throw std::runtime_error(buffer);
        }
    }

    void close() {
        MCI_GENERIC_PARMS params;
        params.dwCallback = NULL;
        checkerror(mciSendCommand(_device, MCI_CLOSE, MCI_WAIT, reinterpret_cast<DWORD_PTR>(&params)));
    }

};


#include <map>
#include <string>
#include <mutex>

#ifdef _UNICODE
typedef std::wstring tstring;
#else
typedef std::string tstring;
#endif

class Repeater {
public:

    Repeater(LPCTSTR fn) : fn(fn) {
        hWnd = CreateWindowEx(0, TEXT("STATIC"), NULL, 0, 0, 0, 0, 0, HWND_DESKTOP, NULL, GetModuleHandle(NULL), 0);
        if (hWnd == NULL)
            throw std::runtime_error("Can't create window");
        oldproc = reinterpret_cast<WNDPROC>(GetWindowLongPtr(hWnd, GWLP_WNDPROC));
        SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG>(this));
        SetWindowLongPtr(hWnd, GWLP_WNDPROC, reinterpret_cast<LONG>(&myproc));
    }

    ~Repeater() {
        DestroyWindow(hWnd);
    }

    void play() {
        Player player(fn.c_str());
        std::lock_guard<std::recursive_mutex> lock(devmap_mutex);
        player.play(hWnd);
        devmap.insert(decltype(devmap)::value_type(player.device(), std::move(player)));
    }

    HWND wnd() const { return hWnd; }

    void stop() {
        std::lock_guard<std::recursive_mutex> lock(devmap_mutex);
        devmap.clear();
    }


private:
    HWND hWnd;
    tstring fn;
    std::recursive_mutex devmap_mutex;
    std::map<MCIDEVICEID, Player> devmap;
    WNDPROC oldproc;

    static LRESULT CALLBACK myproc(_In_ HWND hWnd, _In_ UINT Msg, _In_ WPARAM wParam, _In_ LPARAM lParam) {
        auto self = reinterpret_cast<Repeater*>(GetWindowLong(hWnd, GWLP_USERDATA));
        switch (Msg) {
        case MM_MCINOTIFY: {
            // see https://msdn.microsoft.com/ru-ru/library/windows/desktop/dd757358(v=vs.85).aspx
            std::lock_guard<std::recursive_mutex> lock(self->devmap_mutex);
            self->devmap.erase(static_cast<MCIDEVICEID>(lParam));
            return 0;
        }
        case WM_DESTROY: {
            SetWindowLongPtr(hWnd, GWLP_WNDPROC, reinterpret_cast<LONG>(self->oldproc));
        }
        default:
            break;
        }
        return CallWindowProc(self->oldproc, hWnd, Msg, wParam, lParam);
    }

};

int main() {
    // USE MP3. Forget about WAV.
    LPCTSTR filename = TEXT("c:\\Users\\Vyacheslav\\Music\\Ori\\soundtrack\\Racing the Lava.mp3");

#if 0
    // without notifications
    Player dev1(filename), dev2(filename);
    dev1.play();
    Sleep(1000);
    dev2.play();
    Sleep(10000);
#else
    // with notifications
    {
        Repeater rep(filename);

        std::thread thread([&rep] {
            for (int i = 0; i < 5; ++i) {
                rep.play();
                Sleep(1000);
            }
            Sleep(1000);
            rep.stop(); // .stop() MUST be called from the same thread as ALL .play() !!!
            PostMessage(rep.wnd(), WM_QUIT, 0, 0); // interrupt message processing queue
        });

        MSG msg;
        while (GetMessage(&msg, 0, 0, 0)) {
            if (msg.message == WM_QUIT)
                break;
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        thread.join();
    }
    Sleep(10000); // silence
#endif

    return 0;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM