Click to See Complete Forum and Search --> : Left or Right Audio out both speakers


yooper
December 1st, 2005, 06:44 AM
Hello.

I have a DirectX implementation using IBasicAudio, and I can control the balance just fine. What I would like to do is to select the Left or Right audio signal and drive it out both speakers. Can someone direct me to an existing implementation or give me some pointers? Thank you very much!

Best regards,
Dave

yooper
December 5th, 2005, 09:49 AM
Hello again,

I believe I know how to replicate the left channel sound on the right, or vice versa. But I still need some help. I need a working callback routine for the BufferCB interface of an ISampleGrabberCB, hooked into my DirectX audio filter. If anyone has a working SampleGrabber that matches this description, please be so kind as to share it with the forum (and me :-). Thanks!

Best regards,
Dave

yooper
December 16th, 2005, 03:21 PM
For those who may stumble onto this thread and need some assistance, here's how I finally got it to work:


IBaseFilter *pGF = NULL;
IBaseFilter *pSrc = NULL;
IBaseFilter *pDst = NULL;
ISampleGrabber *pSG = NULL;
AM_MEDIA_TYPE mt;

typedef enum
{
MUX_BOTH = 1,
MUX_LEFT,
MUX_RIGHT
} MuxType;

static MuxType g_lMux = MUX_BOTH;

class VidAudioSampleGrabber : public ISampleGrabberCB
{
public:

STDMETHODIMP_(ULONG) AddRef() { return 2; }
STDMETHODIMP_(ULONG) Release() { return 1; }

STDMETHODIMP QueryInterface(REFIID riid, void ** ppv);
STDMETHODIMP SampleCB( double SampleTime, IMediaSample * pSample );
STDMETHODIMP BufferCB( double SampleTime, BYTE * pBuffer, long BufferLen );
};

VidAudioSampleGrabber pVASG;

void MyFreeMediaType(AM_MEDIA_TYPE& mt)
{
if (mt.cbFormat != 0)
{
CoTaskMemFree((PVOID)mt.pbFormat);
mt.cbFormat = 0;
mt.pbFormat = NULL;
}
if (mt.pUnk != NULL)
{
// Unecessary because pUnk should not be used, but safest.
mt.pUnk->Release();
mt.pUnk = NULL;
}
}

HRESULT PlayMovieInWindow(LPTSTR szFile)
{
.
.
.
// Convert filename to wide character string
wcsncpy(wFile, T2W(szFile), NUMELMS(wFile)-1);
wFile[MAX_PATH-1] = 0;

// Get the interface for DirectShow's GraphBuilder
HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
IID_IGraphBuilder, (void **)&pGB));

if(SUCCEEDED(hr))
{
// Add a Sample Grabber to the Filter Graph.
hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER,
IID_IBaseFilter, (void**)&pGF));
hr = pGB->AddFilter(pGF, L"Sample Grabber"));
hr = pGF->QueryInterface(IID_ISampleGrabber, (void**)&pSG));

// Set the media type of the Sample Grabber to Audio.
ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE));
mt.majortype = MEDIATYPE_Audio;
mt.subtype = MEDIASUBTYPE_PCM;
mt.formattype = FORMAT_WaveFormatEx;
hr = pSG->SetMediaType(&mt));

// Have the graph builder construct the appropriate graph automatically
hr = pGB->RenderFile(wFile, NULL));

// QueryInterface for DirectShow interfaces
hr = pGB->QueryInterface(IID_IMediaControl, (void **)&pMC));
hr = pGB->QueryInterface(IID_IMediaEventEx, (void **)&pME));
hr = pGB->QueryInterface(IID_IMediaPosition, (void **)&pMP));
hr = pGB->QueryInterface(IID_IMediaSeeking, (void **)&pMS));

// Query for video interfaces, which may not be relevant for audio files
hr = pGB->QueryInterface(IID_IVideoWindow, (void **)&pVW));
hr = pGB->QueryInterface(IID_IBasicVideo, (void **)&pBV));

// Query for audio interfaces, which may not be relevant for video-only files
hr = pGB->QueryInterface(IID_IBasicAudio, (void **)&pBA));

// Have the graph signal event via window callbacks for performance
hr = pME->SetNotifyWindow((OAHWND)ghApp, WM_GRAPHNOTIFY, 0));

// Set up the callback function to MUX the audio when required.
hr = pSG->SetOneShot(FALSE));
hr = pSG->SetBufferSamples(TRUE));
hr = pSG->SetCallback(&pVASG, 0));

// Run the graph to play the media file *********************
hr = pMC->Run());
}

return hr;
}

void VideoSetAudioChannel(ZMUX_MODE button)
{
if (button == MUX_both)
g_lMux = MUX_BOTH;
else if (button == MUX_mute_right)
g_lMux = MUX_LEFT;
else if (button == MUX_mute_left)
g_lMux = MUX_RIGHT;
//else
// Invalid input. Leave the MUX alone.
}

STDMETHODIMP VidAudioSampleGrabber::QueryInterface(REFIID riid, void ** ppv)
{
if (riid == IID_ISampleGrabberCB || riid == IID_IUnknown)
{
*ppv = (void *) static_cast<ISampleGrabberCB *>(this);
return S_OK;
}
return E_NOINTERFACE;
}

STDMETHODIMP VidAudioSampleGrabber::SampleCB( double SampleTime, IMediaSample * pSample )
{
// Only process the audio stream if we're MUXing
if (g_lMux != MUX_BOTH)
{
unsigned char * pData;
LONG lSamples = pSample->GetActualDataLength();
HRESULT hr = pSample->GetPointer(&pData);

if (SUCCEEDED(hr))
{
while (lSamples > 0)
{
if (g_lMux == MUX_RIGHT)
{
// Place the left channel onto the right
pData[0] = pData[2];
pData[1] = pData[3];
}
else if (g_lMux == MUX_LEFT)
{
// Place the right channel onto the left
pData[2] = pData[0];
pData[3] = pData[1];
}

pData += 4;
lSamples -= 4;
}
}
}

return S_OK;
}

STDMETHODIMP VidAudioSampleGrabber::BufferCB( double SampleTime, BYTE * pBuffer, long bufferLen )
{
return E_NOTIMPL;
}


I've left out a lot of the boring (and possibly confidential) stuff, but this should give you the basics to create a working SampleGrabber. I was trying to Connect filters, find PinIn and PinOut, etc. But what I finally got to work turned out to be rather simple.

I hope you benefit from this.

Best regards,
Dave

lkm985
September 12th, 2006, 12:01 PM
Want to know something from you!
Can you make the left or right audio out both speakers??

Can you teach me how to make it? i also have to same problem.

Thanks

yooper
September 12th, 2006, 12:34 PM
Hi,

Did you check out my code segment above? It is pretty much all you need to know to get this to happen.

Basically, you need an ISampleGrabberCB class where you define methods for: AddRef(), Release(), QueryInterface(), SampleCB(), and BufferCB(). I chose to implement the channel swap in the SampleCB, as that seemed to be the easiest and have the best performance.

In my example, you can see which bytes of the quadruple need to be overlaid with which other bytes. You can do other types of audio manipulation here, like key changing, amplitude filtering, etc., but those require a lot more computation than simply swapping bytes.

Good luck!

Dave

P.S. If you like my post, please feel free to rate me. :-)

lkm985
September 16th, 2006, 07:51 AM
Sorry, i'm new with it and dont really understand it.
Can you show some code how to define for AddRef(), Release(), QueryInterface() and etc.?

I dont mind you email to me!

yooper
September 16th, 2006, 11:04 AM
It's all up there. The class defines AddRef() to return an unsigned long value of 2. It also defines release to return an unsigned long value of 1. QueryInterface() is farther down, and looks like this:STDMETHODIMP VidAudioSampleGrabber::QueryInterface(REFIID riid, void ** ppv)
{
if (riid == IID_ISampleGrabberCB || riid == IID_IUnknown)
{
*ppv = (void *) static_cast<ISampleGrabberCB *>(this);
return S_OK;
}
return E_NOINTERFACE;
}Everything you need should be defined right at the top of this thread.

lkm985
September 16th, 2006, 11:31 PM
Finally, i found out what happen to my code. Missed one part. lolx.
but another thing is after i change the audio channel to left or right then cannot change back to both channel audio. What wrong??

Other, want to know something from you. Is that everytime will get different of BYTE when trying to pSample->GetPointer(&pData); ?

Thank's Man!!

lkm985
September 17th, 2006, 04:12 AM
i use the keyboard key to call the VideoSetAudio Channel function but it does not work if i call the both channel function. Any idea?
Here is my code:


IBaseFilter *pGF = NULL;
IBaseFilter *pSrc = NULL;
IBaseFilter *pDst = NULL;
ISampleGrabber *pSG = NULL;
AM_MEDIA_TYPE mty;

typedef enum
{
MUX_BOTH = 1,
MUX_LEFT,
MUX_RIGHT
} MuxType;

MuxType g_lMux=MUX_RIGHT;

class VidAudioSampleGrabber : public ISampleGrabberCB
{
public:
STDMETHODIMP_(ULONG) AddRef() { return 2; }
STDMETHODIMP_(ULONG) Release() { return 1; }

STDMETHODIMP QueryInterface(REFIID riid, void ** ppv);
STDMETHODIMP SampleCB( double SampleTime, IMediaSample * pSample );
STDMETHODIMP BufferCB( double SampleTime, BYTE * pBuffer, long BufferLen );

};

VidAudioSampleGrabber pVASG;

void MyFreeMediaType(AM_MEDIA_TYPE& mty)
{
if (mty.cbFormat != 0)
{
CoTaskMemFree((PVOID)mty.pbFormat);
mty.cbFormat = 0;
mty.pbFormat = NULL;
}
if (mt.pUnk != NULL)
{
// Unecessary because pUnk should not be used, but safest.
mty.pUnk->Release();
mty.pUnk = NULL;
}
}

HRESULT PlayMovieInWindow(LPTSTR szFile)
{
LONG lMode;
USES_CONVERSION;
WCHAR wFile[MAX_PATH];
HRESULT hr;
lMode = OATRUE;

if (!szFile)
return E_POINTER;

// Clear open dialog remnants before calling RenderFile()
UpdateWindow(ghApp);

// Convert filename to wide character string
wcsncpy(wFile, T2W(szFile), NUMELMS(wFile)-1);
wFile[MAX_PATH-1] = 0;

// Get the interface for DirectShow's GraphBuilder
JIF(CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
IID_IGraphBuilder, (void **)&pGB));

if(SUCCEEDED(hr))
{
// Add a Sample Grabber to the Filter Graph.
JIF(CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER,
IID_IBaseFilter, (void**)&pGF));
JIF(pGB->AddFilter(pGF, L"Sample Grabber"));
JIF(pGF->QueryInterface(IID_ISampleGrabber, (void**)&pSG));

// Set the media type of the Sample Grabber to Audio.
ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE));
mty.majortype = MEDIATYPE_Audio;
mty.subtype = MEDIASUBTYPE_PCM;
mty.formattype = FORMAT_WaveFormatEx;
JIF(pSG->SetMediaType(&mty));


// Have the graph builder construct its the appropriate graph automatically
JIF(pGB->RenderFile(wFile, NULL));

// QueryInterface for DirectShow interfaces
JIF(pGB->QueryInterface(IID_IMediaControl, (void **)&pMC));
JIF(pGB->QueryInterface(IID_IMediaEventEx, (void **)&pME));
JIF(pGB->QueryInterface(IID_IMediaSeeking, (void **)&pMS));
JIF(pGB->QueryInterface(IID_IMediaPosition, (void **)&pMP));

// Query for video interfaces, which may not be relevant for audio files
JIF(pGB->QueryInterface(IID_IVideoWindow, (void **)&pVW));
JIF(pGB->QueryInterface(IID_IBasicVideo, (void **)&pBV));

// Query for audio interfaces, which may not be relevant for video-only files
JIF(pGB->QueryInterface(IID_IBasicAudio, (void **)&pBA));

// Is this an audio-only file (no video component)?
CheckVisibility();

// Have the graph signal event via window callbacks for performance
JIF(pME->SetNotifyWindow((OAHWND)ghApp, WM_GRAPHNOTIFY, 0));

if (!g_bAudioOnly)
{
// Setup the video window
JIF(pVW->put_Owner((OAHWND)ghApp));
JIF(pVW->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN));

JIF(InitVideoWindow(1, 1));


}
else
{
// Initialize the default player size and enable playback menu items
JIF(InitPlayerWindow());
}

// Set up the callback function to MUX the audio when required.
JIF(pSG->SetOneShot(FALSE));
JIF(pSG->SetBufferSamples(TRUE));
JIF(pSG->SetCallback(&pVASG, 0));


#ifdef REGISTER_FILTERGRAPH
hr = AddGraphToRot(pGB, &g_dwGraphRegister);
if (FAILED(hr))
{
Msg(TEXT("Failed to register filter graph with ROT! hr=0x%x"), hr);
g_dwGraphRegister = 0;
}
#endif

// Complete window initialization
ShowWindow(ghApp, SW_SHOWMAXIMIZED);
SetForegroundWindow(ghApp);
g_bFullscreen = FALSE;
g_PlaybackRate = 1.0;

// Run the graph to play the media file
JIF(pMC->Run());

g_psCurrent=Running;
SetFocus(ghApp);
}

return hr;
}
void VideoSetAudioLeftChannel()
{
.
.
.
.
.
g_lMux=MUX_RIGHT;
}

void VideoSetAudioRightChannel()
{
.
.
.
.
.
.

g_lMux=MUX_LEFT;
}

void VideoSetAudioBothChannel()
{
.
.
.
.
.
g_lMux=MUX_BOTH;
}

STDMETHODIMP VidAudioSampleGrabber::QueryInterface(REFIID riid, void ** ppv)
{
if (riid == IID_ISampleGrabberCB || riid == IID_IUnknown)
{
*ppv = (void *) static_cast<ISampleGrabberCB *>(this);
return S_OK;
}
return E_NOINTERFACE;
}


STDMETHODIMP VidAudioSampleGrabber::SampleCB( double SampleTime, IMediaSample * pSample )
{
//Only process the audio stream if we're MUXing
if (g_lMux != MUX_BOTH)
{
unsigned char * pData;
LONG lSamples = pSample->GetActualDataLength();
HRESULT hr = pSample->GetPointer(&pData);

if (SUCCEEDED(hr))
{

while (lSamples > 0)
{
if (g_lMux == MUX_RIGHT)
{
// Place the left channel onto the right
pData[0] = pData[2];
pData[1] = pData[3];
}

else if (g_lMux == MUX_LEFT)
{
// Place the right channel onto the left
pData[2] = pData[0];
pData[3] = pData[1];
}
pData += 4;
lSamples -= 4;
}
}
}
return S_OK;
}

STDMETHODIMP VidAudioSampleGrabber::BufferCB( double SampleTime, BYTE * pBuffer, long bufferLen )
{
return E_NOTIMPL;
}


LRESULT CALLBACK WndMainProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message)
{
.
.
.
.
.

case WM_KEYDOWN:

switch(toupper((int) wParam))
{
case '2':
VideoSetAudioBothChannel();
case '3':
VideoSetAudioLeftChannel();
case '4':
VideoSetAudioRightChannel();
.............

// Pass this message to the video window for notification of system changes
if (pVW)
pVW->NotifyOwnerMessage((LONG_PTR) hWnd, message, wParam, lParam);

return DefWindowProc(hWnd, message, wParam, lParam);
}

lkm985
September 17th, 2006, 11:35 AM
HHAHAHAHAH!! Very funny. i really do until stuck already. something i miss it. Now is work. thanks man.

I rate for you. many forum i post thread but never got people tell me how, even mircosoft forum.

Whatever. thanks for your thread.