Autor Wątek: Streamowanie DirectSound w Delphi/FPC  (Przeczytany 2880 razy)

Offline Brucie

  • Użytkownik

# Czerwiec 03, 2012, 14:03:52
Witam
Potrzebuje pomocy w temacie streamowania w directsound. Wiem, że trzeba utworzyć jakieś zdarzenia które będą informować w którym miejscu aktualnie jest "czytany" bufor, (np. na początku i w środku bufora) i reagować na nie czyli zapełniać odpowiednią cześć bufora kolejną porcją danych.

Problem w tym, że mam problemy z rozumieniem kodu w C++(programuje w pascalu od dwóch lat, a tak na serio to od roku, od jakichś dwóch miesięcy w object pascalu więc jestem nowy w tym temacie), a tylko w tym języku są dobre przykłady(w C# samo się streamuje z tego co wiem, a VB nie ogarniam :P). Te dobre przykłady znalazłem tylko 2, nazywam je dobre bo lepszych nie znalazłem. Linków nie podaje bo nie wiem czy można, ale wystarczy w google wstukać "directsound streaming".

Na razie mam napisane tylko ładowanie całego pliku wav do bufora, z tym nie miałem większych problemów, korzystałem z przykładu w C++.

Dlaczego directsound? Działa na wszystkich windowsach(zależy mi na xp, vista, seven), nie trzeba dodatkowych plików dll(każdy system dzisiaj ma plik dsound.dll), no i jest przekonwertowany header do delphi/fpc. Użył bym xaudio2 ale do tej dllki nie ma headera.

Czy mógłby ktoś pomóc? Przykładów w delphi nie znalazłem :( Może ktoś napisał coś w delphi albo fpc? Nie trzeba mi dużo, prosty program który odtwarza plik wav ustawiony na sztywno używając streamowania.

Dokładnie to nie rozumiem np. czegoś takiego typedef HRESULT  (WINAPI *LPGETAUDIOSAMPLES_PROGRESS)(int iAudioChannel, LPBYTE lpDesBuf, const DWORD dwRequiredSamples,  DWORD &dwRetSamples, LPVOID lpData);
Wiem, że typedef służy do nadawania alternatywnych nazw, np.: typedef bardzo_skomplikowany_typ nasza_nazwawięc kompletnie nie rozumiem jak rozumieć ten kod z LPGETAUDIOSAMPLES_PROGRESS a co dopiero jak napisać go w pascalu.

Oczywiście to nie jedyny z problemów jakie mam, ale na razie więcej nie będe pisał, czekam na czyjąś pomoc.

Offline Mr. Spam

  • Miłośnik przetworów mięsnych

Offline Karol

  • Użytkownik

# Czerwiec 03, 2012, 14:11:03
Możesz zainteresować się biblioteką BASS (www.un4seen.com), dllka jest malutka, są headery do Delphi i obsługuje z dźwięku praktycznie wszystko co się da (razem z modułami w stylu protrackera etc). Tam możesz też strumieniować dane z pliku czy netu, albo samemu bufor zapychać.

Offline Brucie

  • Użytkownik

# Czerwiec 03, 2012, 14:25:44
Wiem o tej bibliotece, korzystałem z niej, ale chciałem coś co nie potrzebuje dołączania dodatkowych plików dll do programu. Musze pokonać tylko ten temat streamowania a później już pójdzie z górki. Przy okazji naucze się nowych rzeczy bo w bass to trzy linijki i plik się odtwarza ;)

Offline radsun

  • Użytkownik
    • CaRpg

# Czerwiec 03, 2012, 15:22:15
LPGETAUDIOSAMPLES_PROGRESS to wskaźnik na funkcję:
Kod: (cpp) [Zaznacz]
HRESULT WINAPI NazwaFuncji(int iAudioChannel, LPBYTE lpDesBuf, const DWORD dwRequiredSamples,  DWORD &dwRetSamples, LPVOID lpData);

Offline kubera

  • Użytkownik
    • Prywatna strona

# Czerwiec 03, 2012, 16:46:57
Witam!

Czy warto korzystać z DirectSound?
Ostatnim Windows-em, które wspiera sprzętowo wymienione API jest WinXP.
Może lepiej OpenAl lub XAudio2.
To drugie narzędzie potrafi wykorzystać w pełni DirectSound,
a w nowszych wersjach Windows, jego niższą i sprzętową warstwą będzie WASAPI.
Proponowałbym, ażeby korzystać z nowoczesnego API, co zapewni twojemu programowi długowieczność.

Pozdrawiam

Offline Brucie

  • Użytkownik

# Czerwiec 03, 2012, 17:49:26
LPGETAUDIOSAMPLES_PROGRESS to wskaźnik na funkcję:
Kod: (cpp) [Zaznacz]
HRESULT WINAPI NazwaFuncji(int iAudioChannel, LPBYTE lpDesBuf, const DWORD dwRequiredSamples,  DWORD &dwRetSamples, LPVOID lpData);

Dzięki na reszcie ktoś kto odpowiedział na pytanie :P

Tutaj jest tut: http://www.codeproject.com/Articles/8396/Using-DirectSound-to-Play-Audio-Stream-Data
tutaj kod:
MyDirectSound.cpp
Kod: (cpp) [Zaznacz]
#include "stdafx.h"   
#include "MyDirectSound.h"   
//#include "math.h"   
   
#ifdef _DEBUG   
#undef THIS_FILE   
static char THIS_FILE[]=__FILE__;   
#define new DEBUG_NEW   
#endif   
   
//<CALLBACK Function>   
void CALLBACK TimerProcess(UINT uTimerID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)   
{   
    CMyDirectSound *pDDS = (CMyDirectSound *)dwUser;   
    pDDS->TimerCallback();     
}   
//</TimerProc>   
   
CMyDirectSound::CMyDirectSound()   
{   
    //<DirectSound>   
    ZeroMemory(&m_WFE, sizeof(m_WFE));   
    m_lpDS = NULL;   
    m_lpDSB = NULL;   
    m_pHEvent[0] = CreateEvent(NULL, FALSE, FALSE, _T("Direct_Sound_Buffer_Notify_0"));   
    m_pHEvent[1] = CreateEvent(NULL, FALSE, FALSE, _T("Direct_Sound_Buffer_Notify_1"));   
    //</DirectSound>   
   
    //<Audio Buffer>   
    m_lpAudioBuf = NULL;   
    m_lpGETAUDIOSAMPLES = NULL;   
    m_lpData = NULL;   
    //</Audio Buffer>   
       
    //<Playing>   
    m_dwCircles1 = 0;   
    m_dwCircles2 = 0;   
    //</Playing>     
}   
//</CMyDirectSound>   
   
CMyDirectSound::~CMyDirectSound()   
{   
    if (NULL != m_lpAudioBuf) {   
        delete []m_lpAudioBuf;   
        m_lpAudioBuf = NULL;   
    }   
}   
//</~CMyDirectSound>   
   
void CMyDirectSound::SetFormat(WAVEFORMATEX WFE)   
{   
    m_WFE = WFE;       
   
    //Create DirectSound   
    if ( FAILED(DirectSoundCreate(NULL, &m_lpDS, NULL)) ) {   
        OutputDebugString(_T("Create DirectSound Failed!"));   
        m_strLastError = _T("MyDirectSound SetFormat Failed!");   
        return;   
    }   
   
    //Set Cooperative Level   
    HWND hWnd = GetForegroundWindow();   
    if (hWnd == NULL)   
    {   
        hWnd = GetDesktopWindow();   
    }   
       
    if ( FAILED(m_lpDS->SetCooperativeLevel(hWnd, DSSCL_PRIORITY)) ) {   
        OutputDebugString(_T("SetCooperativeLevel Failed"));   
        m_strLastError = _T("MyDirectSound SetFormat Failed!");   
        return;   
    }   
   
    //Create Primary Buffer   
    DSBUFFERDESC dsbd;   
    ZeroMemory(&dsbd, sizeof(dsbd));   
    dsbd.dwSize = sizeof(DSBUFFERDESC);   
    dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;   
    dsbd.dwBufferBytes = 0;   
    dsbd.lpwfxFormat = NULL;   
   
    LPDIRECTSOUNDBUFFER lpDSB = NULL;   
    if ( FAILED(m_lpDS->CreateSoundBuffer(&dsbd, &lpDSB, NULL)) ) {   
        OutputDebugString(_T("Create Primary Sound Buffer Failed!"));   
        m_strLastError = _T("MyDirectSound SetFormat Failed!");   
        return;   
    }   
       
    //Set Primary Buffer Format   
    if ( FAILED(lpDSB->SetFormat(&m_WFE)) ) {   
        OutputDebugString(_T("Set Primary Format Failed!"));   
        m_strLastError = _T("MyDirectSound SetFormat Failed!");   
        return;   
    }   
       
    //Create Second Sound Buffer   
    dsbd.dwFlags = DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GLOBALFOCUS;   
    dsbd.dwBufferBytes = 2*m_WFE.nAvgBytesPerSec; //2 Seconds Buffer   
    dsbd.lpwfxFormat = &m_WFE;   
   
    if ( FAILED(m_lpDS->CreateSoundBuffer(&dsbd, &m_lpDSB, NULL)) ) {   
        OutputDebugString(_T("Create Second Sound Buffer Failed!"));   
        m_strLastError = _T("MyDirectSound SetFormat Failed!");   
        return;   
    }   
   
    //Query DirectSoundNotify   
    LPDIRECTSOUNDNOTIFY lpDSBNotify;   
    if ( FAILED(m_lpDSB->QueryInterface(IID_IDirectSoundNotify, (LPVOID *)&lpDSBNotify)) ) {   
        OutputDebugString(_T("QueryInterface DirectSoundNotify Failed!"));   
        m_strLastError = _T("MyDirectSound SetFormat Failed!");   
        return;   
    }   
   
    //Set Direct Sound Buffer Notify Position   
    DSBPOSITIONNOTIFY pPosNotify[2];   
    pPosNotify[0].dwOffset = m_WFE.nAvgBytesPerSec/2 - 1;   
    pPosNotify[1].dwOffset = 3*m_WFE.nAvgBytesPerSec/2 - 1;       
    pPosNotify[0].hEventNotify = m_pHEvent[0];   
    pPosNotify[1].hEventNotify = m_pHEvent[1];     
   
    if ( FAILED(lpDSBNotify->SetNotificationPositions(2, pPosNotify)) ) {   
        OutputDebugString(_T("Set NotificationPosition Failed!"));   
        m_strLastError = _T("MyDirectSound SetFormat Failed!");   
        return;   
    }     
   
    //New audio buffer   
    if (NULL != m_lpAudioBuf) {   
        delete []m_lpAudioBuf;   
        m_lpAudioBuf = NULL;           
    }   
    m_lpAudioBuf = new BYTE[m_WFE.nAvgBytesPerSec];   
   
    //Init Audio Buffer   
    memset(m_lpAudioBuf, 0, m_WFE.nAvgBytesPerSec);   
}   
//</SetFormat>   
   
void CMyDirectSound::Play()   
{   
    //Check if the DirectSound was created successfully   
    if (NULL == m_lpDS) {   
        m_strLastError = _T("DirectSound was not created!");   
        OutputDebugString(m_strLastError);         
        return;   
    }   
   
    //Check if the callback function is valid   
    if (NULL == m_lpGETAUDIOSAMPLES) {   
        m_strLastError = _T("Callback Function is NULL!");   
        OutputDebugString(m_strLastError);         
        return;   
    }   
   
    //Check if SetFormat successfully   
    if ( !m_strLastError.CompareNoCase(_T("MyDirectSound SetFormat Failed!")) ) {   
        OutputDebugString(m_strLastError);   
        return;   
    }   
   
    if (0 == m_dwCircles1) {   
           
        //Get audio data by callback function   
        DWORD dwRetSamples = 0, dwRetBytes = 0;   
        m_lpGETAUDIOSAMPLES(m_lpAudioBuf, m_WFE.nSamplesPerSec, dwRetSamples, m_lpData);   
        dwRetBytes = dwRetSamples*m_WFE.nBlockAlign;   
           
        //Write the audio data to DirectSoundBuffer   
        LPVOID lpvAudio1 = NULL, lpvAudio2 = NULL;   
        DWORD dwBytesAudio1 = 0, dwBytesAudio2 = 0;   
           
        //Lock DirectSoundBuffer   
        HRESULT hr = m_lpDSB->Lock(0, m_WFE.nAvgBytesPerSec, &lpvAudio1, &dwBytesAudio1, &lpvAudio2, &dwBytesAudio2, 0);   
        if ( FAILED(hr) ) {   
            m_strLastError = _T("Lock DirectSoundBuffer Failed!");   
            OutputDebugString(m_strLastError);   
            return;   
        }   
           
        //Init lpvAudio1   
        if (NULL != lpvAudio1) {               
            memset(lpvAudio1, 0, dwBytesAudio1);               
        }   
           
        //Init lpvAudio2   
        if (NULL != lpvAudio2) {               
            memset(lpvAudio2, 0, dwBytesAudio2);               
        }   
   
        //Copy Audio Buffer to DirectSoundBuffer   
        if (NULL == lpvAudio2) {   
            memcpy(lpvAudio1, m_lpAudioBuf, dwRetBytes);   
        }   
        else {   
            memcpy(lpvAudio1, m_lpAudioBuf, dwBytesAudio1);   
            memcpy(lpvAudio2, m_lpAudioBuf + dwBytesAudio1, dwBytesAudio2);   
        }   
           
        //Unlock DirectSoundBuffer   
        m_lpDSB->Unlock(lpvAudio1, dwBytesAudio1, lpvAudio2, dwBytesAudio2);   
    }   
   
       
    //Beging Play   
    m_lpDSB->Play(0, 0, DSBPLAY_LOOPING);   
   
    //timeSetEvent   
    m_timerID = timeSetEvent(300, 100, TimerProcess, (DWORD)this, TIME_PERIODIC | TIME_CALLBACK_FUNCTION);   
}   
//</Play>   
   
void CMyDirectSound::Pause()   
{   
    if (NULL != m_lpDSB) {   
        m_lpDSB->Stop();   
        timeKillEvent(m_timerID);   
    }   
}   
//</Pause>   
   
void CMyDirectSound::Stop()   
{   
    if (NULL != m_lpDSB) {   
   
        m_lpDSB->Stop();   
        timeKillEvent(m_timerID);   
           
        //Empty the buffer   
        LPVOID lpvAudio1 = NULL;   
        DWORD dwBytesAudio1 = 0;   
        HRESULT hr = m_lpDSB->Lock(0, 0, &lpvAudio1, &dwBytesAudio1, NULL, NULL, DSBLOCK_ENTIREBUFFER);   
        if ( FAILED(hr) ) {   
            m_strLastError = _T("Lock entirebuffer failed! Stop Failed!");   
            OutputDebugString(m_strLastError);   
            return;   
        }   
        memset(lpvAudio1, 0, dwBytesAudio1);   
        m_lpDSB->Unlock(lpvAudio1, dwBytesAudio1, NULL, NULL);   
   
        //Move the current play position to begin   
        m_lpDSB->SetCurrentPosition(0);     
           
        //Reset Event   
        ResetEvent(m_pHEvent[0]);   
        ResetEvent(m_pHEvent[1]);   
   
        //Set Circles1 and Circles2 0   
        m_dwCircles1 = 0;   
        m_dwCircles2 = 0;   
    }     
}   
//</Stop>   
   
DWORD CMyDirectSound::GetSamplesPlayed()   
{   
    if (NULL == m_lpDSB) {   
        return 0;   
    }   
   
    //Get curren play position   
    DWORD dwCurPlayPos = 0, dwCurPlaySample = 0;   
    m_lpDSB->GetCurrentPosition(&dwCurPlayPos, NULL);   
    dwCurPlaySample = dwCurPlayPos/m_WFE.nBlockAlign;   
   
    //Caculate the samples played   
    DWORD dwSamplesPlayed = 0;   
    if (m_dwCircles2  1) {   
        return dwCurPlaySample;   
    }   
       
    dwSamplesPlayed = (m_dwCircles2-1)*2*m_WFE.nSamplesPerSec + 3*m_WFE.nSamplesPerSec/2;         
    if (dwCurPlaySample > (3*m_WFE.nSamplesPerSec/2)) {   
   
        if (m_dwCircles2  m_dwCircles1) {   
   
            dwSamplesPlayed = (m_dwCircles1-1)*2*m_WFE.nSamplesPerSec + 3*m_WFE.nSamplesPerSec/2;   
        }   
   
        dwSamplesPlayed += dwCurPlaySample - 3*m_WFE.nSamplesPerSec/2 + 1;                 
    }   
    else {   
           
        dwSamplesPlayed += dwCurPlaySample + m_WFE.nSamplesPerSec/2;   
    }   
   
    CString strSamplesPlayed;   
    strSamplesPlayed.Format(_T("Samples Played: %d \n"), dwSamplesPlayed);   
    OutputDebugString(strSamplesPlayed);   
   
    return dwSamplesPlayed;   
}   
//</GetSamplePlaying>   
   
void CMyDirectSound::SetCallback(LPGETAUDIOSAMPLES_PROGRESS Function_Callback, LPVOID lpData)   
{   
    m_lpGETAUDIOSAMPLES = Function_Callback;   
    m_lpData = lpData;   
}   
//</SetCallback>   
   
void CMyDirectSound::TimerCallback()   
{   
    LPVOID lpvAudio1 = NULL, lpvAudio2 = NULL;   
    DWORD dwBytesAudio1 = 0, dwBytesAudio2 = 0;   
    DWORD dwRetSamples = 0, dwRetBytes = 0;   
   
    HRESULT hr = WaitForMultipleObjects(2, m_pHEvent, FALSE, 0);   
    if(WAIT_OBJECT_0 == hr) {   
   
        m_dwCircles1++;   
   
        //Lock DirectSoundBuffer Second Part   
        HRESULT hr = m_lpDSB->Lock(m_WFE.nAvgBytesPerSec, m_WFE.nAvgBytesPerSec, &lpvAudio1, &dwBytesAudio1, &lpvAudio2, &dwBytesAudio2, 0);   
        if ( FAILED(hr) ) {   
            m_strLastError = _T("Lock DirectSoundBuffer Failed!");   
            OutputDebugString(m_strLastError);   
            return;   
        }         
    }   
    else if (WAIT_OBJECT_0 + 1 == hr) {       
   
        m_dwCircles2++;   
   
        //Lock DirectSoundBuffer First Part   
        HRESULT hr = m_lpDSB->Lock(0, m_WFE.nAvgBytesPerSec, &lpvAudio1, &dwBytesAudio1, &lpvAudio2, &dwBytesAudio2, 0);   
        if ( FAILED(hr) ) {   
            m_strLastError = _T("Lock DirectSoundBuffer Failed!");   
            OutputDebugString(m_strLastError);   
            return;   
        }         
    }   
    else {   
        return;   
    }   
   
    //Get 1 Second Audio Buffer   
    m_lpGETAUDIOSAMPLES(m_lpAudioBuf, m_WFE.nSamplesPerSec, dwRetSamples, m_lpData);   
    dwRetBytes = dwRetSamples*m_WFE.nBlockAlign;   
       
    //If near the end of the audio data   
    if (dwRetSamples  m_WFE.nSamplesPerSec) {   
        DWORD dwRetBytes = dwRetSamples*m_WFE.nBlockAlign;   
        memset(m_lpAudioBuf+dwRetBytes, 0, m_WFE.nAvgBytesPerSec - dwRetBytes);               
    }   
       
    //Copy AudioBuffer to DirectSoundBuffer   
    if (NULL == lpvAudio2) {   
        memcpy(lpvAudio1, m_lpAudioBuf, dwBytesAudio1);   
    }   
    else {   
        memcpy(lpvAudio1, m_lpAudioBuf, dwBytesAudio1);   
        memcpy(lpvAudio2, m_lpAudioBuf + dwBytesAudio1, dwBytesAudio2);   
    }   
       
    //Unlock DirectSoundBuffer   
    m_lpDSB->Unlock(lpvAudio1, dwBytesAudio1, lpvAudio2, dwBytesAudio2);   
}   
//</TimerCallback>

MyDirectSound.h:
Kod: (cpp) [Zaznacz]
#pragma once
 
#if _MSC_VER > 1000
#pragma once
#endif
 
typedef HRESULT (WINAPI *LPGETAUDIOSAMPLES_PROGRESS)(LPBYTE lpDesBuf, const DWORD dwRequiredSamples, DWORD &dwRetSamples, LPVOID lpData);
 
class CMyDirectSound   
{
public:

CMyDirectSound();
virtual ~CMyDirectSound();
 
void SetFormat(WAVEFORMATEX WFE);
void SetCallback(LPGETAUDIOSAMPLES_PROGRESS Function_Callback, LPVOID lpData);
void Play();
void Pause();
void Stop();
DWORD GetSamplesPlayed();
void TimerCallback();

private:

//<DirectSound>
WAVEFORMATEX m_WFE;
LPDIRECTSOUND m_lpDS;
LPDIRECTSOUNDBUFFER m_lpDSB;
HANDLE m_pHEvent[2];
//</DirectSound>
 
//<Audio Buffer>
LPBYTE m_lpAudioBuf;
LPGETAUDIOSAMPLES_PROGRESS m_lpGETAUDIOSAMPLES;
LPVOID m_lpData;
//</Audio Buffer>
 
//<Playing>
MMRESULT m_timerID;
DWORD m_dwCircles1;
DWORD m_dwCircles2;
int m_iDB;
//</Playing>
 
//<Error Information>
CString m_strLastError;
//</Error Information>
};
//</CMyDirectSound  >



Witam!

Czy warto korzystać z DirectSound?
Ostatnim Windows-em, które wspiera sprzętowo wymienione API jest WinXP.
Może lepiej OpenAl lub XAudio2.
To drugie narzędzie potrafi wykorzystać w pełni DirectSound,
a w nowszych wersjach Windows, jego niższą i sprzętową warstwą będzie WASAPI.
Proponowałbym, ażeby korzystać z nowoczesnego API, co zapewni twojemu programowi długowieczność.

Pozdrawiam

Nie wiem jakie to ma konsekwencje ale dla mnie ważne jest że działa. Z tego co patrzyłem streamowanie w openal też nie jest łatwe. XAudio2 odpada bo nie ma headerów do delphi/fpc.