2004-08-24 04:28:35 +02:00
|
|
|
/*
|
|
|
|
* Direct Sound Audio Renderer
|
|
|
|
*
|
|
|
|
* Copyright 2004 Christian Costa
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
2006-05-18 14:49:52 +02:00
|
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
2004-08-24 04:28:35 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "quartz_private.h"
|
|
|
|
|
|
|
|
#include "uuids.h"
|
|
|
|
#include "vfwmsgs.h"
|
|
|
|
#include "windef.h"
|
|
|
|
#include "winbase.h"
|
|
|
|
#include "dshow.h"
|
|
|
|
#include "evcode.h"
|
|
|
|
#include "strmif.h"
|
|
|
|
#include "dsound.h"
|
2010-02-07 21:17:59 +01:00
|
|
|
#include "amaudio.h"
|
2004-08-24 04:28:35 +02:00
|
|
|
|
|
|
|
#include "wine/debug.h"
|
|
|
|
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(quartz);
|
|
|
|
|
2010-11-09 23:42:51 +01:00
|
|
|
/* NOTE: buffer can still be filled completely,
|
|
|
|
* but we start waiting until only this amount is buffered
|
|
|
|
*/
|
|
|
|
static const REFERENCE_TIME DSoundRenderer_Max_Fill = 150 * 10000;
|
|
|
|
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render
|
2004-08-24 04:28:35 +02:00
|
|
|
{
|
2019-10-24 03:04:23 +02:00
|
|
|
struct strmbase_renderer renderer;
|
2010-10-07 21:47:33 +02:00
|
|
|
|
2019-06-07 01:49:24 +02:00
|
|
|
IBasicAudio IBasicAudio_iface;
|
2012-04-03 21:28:37 +02:00
|
|
|
IAMDirectSound IAMDirectSound_iface;
|
quartz/dsoundrender: Delegate IReferenceClock to the system clock.
For several reasons.
Firstly, the reference clock should still function when the filter is not
running.
Secondly, IDirectSoundBuffer::GetPositions() in practice returns very coarse
positions, both on Windows and on Wine. On my hardware, the resolution is
about 10ms, which, while suitable for the DirectSound renderer and probably
also any video renderers, is nevertheless actually coarser than
GetTickCount().
Thirdly, testing supports that the native DirectSound renderer returns a
timestamp from IReferenceClock::GetTime() that is more accurate than
IDirectSoundBuffer::GetPositions(). In fact, after dumping a large number of
different clock sources, I came to the conclusion that it is probably using
timeGetTime() as a source. On Wine that's identical to GetTickCount(), so we
may as well just delegate directly to the system clock.
Signed-off-by: Zebediah Figura <z.figura12@gmail.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2019-11-29 00:35:40 +01:00
|
|
|
IUnknown *system_clock;
|
2004-08-24 04:28:35 +02:00
|
|
|
|
2008-06-10 17:00:40 +02:00
|
|
|
IDirectSound8 *dsound;
|
2004-08-24 04:28:35 +02:00
|
|
|
LPDIRECTSOUNDBUFFER dsbuffer;
|
2007-04-01 14:50:44 +02:00
|
|
|
DWORD buf_size;
|
2010-05-30 19:17:26 +02:00
|
|
|
DWORD in_loop;
|
2010-11-09 23:42:51 +01:00
|
|
|
DWORD last_playpos, writepos;
|
2008-04-09 01:48:00 +02:00
|
|
|
|
2007-04-06 12:48:03 +02:00
|
|
|
REFERENCE_TIME play_time;
|
|
|
|
|
2010-05-20 01:14:09 +02:00
|
|
|
LONG volume;
|
|
|
|
LONG pan;
|
2020-04-01 06:48:19 +02:00
|
|
|
};
|
2004-08-24 04:28:35 +02:00
|
|
|
|
2020-04-01 06:48:19 +02:00
|
|
|
static struct dsound_render *impl_from_strmbase_renderer(struct strmbase_renderer *iface)
|
2012-03-31 03:08:16 +02:00
|
|
|
{
|
2020-04-01 06:48:19 +02:00
|
|
|
return CONTAINING_RECORD(iface, struct dsound_render, renderer);
|
2012-03-31 03:08:16 +02:00
|
|
|
}
|
|
|
|
|
2020-04-01 06:48:19 +02:00
|
|
|
static struct dsound_render *impl_from_IBasicAudio(IBasicAudio *iface)
|
2012-04-03 21:28:37 +02:00
|
|
|
{
|
2020-04-01 06:48:19 +02:00
|
|
|
return CONTAINING_RECORD(iface, struct dsound_render, IBasicAudio_iface);
|
2012-04-03 21:28:37 +02:00
|
|
|
}
|
|
|
|
|
2020-04-01 06:48:19 +02:00
|
|
|
static struct dsound_render *impl_from_IAMDirectSound(IAMDirectSound *iface)
|
2012-04-03 21:28:37 +02:00
|
|
|
{
|
2020-04-01 06:48:19 +02:00
|
|
|
return CONTAINING_RECORD(iface, struct dsound_render, IAMDirectSound_iface);
|
2012-04-03 21:28:37 +02:00
|
|
|
}
|
|
|
|
|
2020-04-01 06:48:19 +02:00
|
|
|
static REFERENCE_TIME time_from_pos(struct dsound_render *This, DWORD pos)
|
|
|
|
{
|
2019-11-19 01:51:20 +01:00
|
|
|
WAVEFORMATEX *wfx = (WAVEFORMATEX *)This->renderer.sink.pin.mt.pbFormat;
|
2010-11-09 23:42:51 +01:00
|
|
|
REFERENCE_TIME ret = 10000000;
|
|
|
|
ret = ret * pos / wfx->nAvgBytesPerSec;
|
|
|
|
return ret;
|
|
|
|
}
|
2008-04-05 01:27:29 +02:00
|
|
|
|
2020-04-01 06:48:19 +02:00
|
|
|
static DWORD pos_from_time(struct dsound_render *This, REFERENCE_TIME time)
|
|
|
|
{
|
2019-11-19 01:51:20 +01:00
|
|
|
WAVEFORMATEX *wfx = (WAVEFORMATEX *)This->renderer.sink.pin.mt.pbFormat;
|
2010-11-09 23:42:51 +01:00
|
|
|
REFERENCE_TIME ret = time;
|
2017-06-19 21:15:32 +02:00
|
|
|
ret *= wfx->nAvgBytesPerSec;
|
2010-11-09 23:42:51 +01:00
|
|
|
ret /= 10000000;
|
2017-06-19 21:15:32 +02:00
|
|
|
ret -= ret % wfx->nBlockAlign;
|
2010-11-09 23:42:51 +01:00
|
|
|
return ret;
|
|
|
|
}
|
2008-04-05 01:27:29 +02:00
|
|
|
|
2020-04-01 06:48:19 +02:00
|
|
|
static void DSoundRender_UpdatePositions(struct dsound_render *This, DWORD *seqwritepos, DWORD *minwritepos)
|
|
|
|
{
|
2019-11-19 01:51:20 +01:00
|
|
|
WAVEFORMATEX *wfx = (WAVEFORMATEX *)This->renderer.sink.pin.mt.pbFormat;
|
2010-11-09 23:42:51 +01:00
|
|
|
BYTE *buf1, *buf2;
|
|
|
|
DWORD size1, size2, playpos, writepos, old_writepos, old_playpos, adv;
|
|
|
|
BOOL writepos_set = This->writepos < This->buf_size;
|
|
|
|
|
|
|
|
/* Update position and zero */
|
|
|
|
old_writepos = This->writepos;
|
|
|
|
old_playpos = This->last_playpos;
|
|
|
|
if (old_writepos <= old_playpos)
|
|
|
|
old_writepos += This->buf_size;
|
|
|
|
|
|
|
|
IDirectSoundBuffer_GetCurrentPosition(This->dsbuffer, &playpos, &writepos);
|
|
|
|
if (old_playpos > playpos) {
|
|
|
|
adv = This->buf_size + playpos - old_playpos;
|
|
|
|
This->play_time += time_from_pos(This, This->buf_size);
|
|
|
|
} else
|
|
|
|
adv = playpos - old_playpos;
|
|
|
|
This->last_playpos = playpos;
|
|
|
|
if (adv) {
|
|
|
|
TRACE("Moving from %u to %u: clearing %u bytes\n", old_playpos, playpos, adv);
|
|
|
|
IDirectSoundBuffer_Lock(This->dsbuffer, old_playpos, adv, (void**)&buf1, &size1, (void**)&buf2, &size2, 0);
|
|
|
|
memset(buf1, wfx->wBitsPerSample == 8 ? 128 : 0, size1);
|
|
|
|
memset(buf2, wfx->wBitsPerSample == 8 ? 128 : 0, size2);
|
|
|
|
IDirectSoundBuffer_Unlock(This->dsbuffer, buf1, size1, buf2, size2);
|
|
|
|
}
|
|
|
|
*minwritepos = writepos;
|
|
|
|
if (!writepos_set || old_writepos < writepos) {
|
|
|
|
if (writepos_set) {
|
|
|
|
This->writepos = This->buf_size;
|
2010-12-11 14:25:28 +01:00
|
|
|
FIXME("Underrun of data occurred!\n");
|
2008-04-05 01:27:29 +02:00
|
|
|
}
|
2010-11-09 23:42:51 +01:00
|
|
|
*seqwritepos = writepos;
|
|
|
|
} else
|
|
|
|
*seqwritepos = This->writepos;
|
|
|
|
}
|
2008-04-09 01:48:00 +02:00
|
|
|
|
2020-04-01 06:48:19 +02:00
|
|
|
static HRESULT DSoundRender_GetWritePos(struct dsound_render *This,
|
|
|
|
DWORD *ret_writepos, REFERENCE_TIME write_at, DWORD *pfree, DWORD *skip)
|
2010-11-09 23:42:51 +01:00
|
|
|
{
|
2019-11-19 01:51:20 +01:00
|
|
|
WAVEFORMATEX *wfx = (WAVEFORMATEX *)This->renderer.sink.pin.mt.pbFormat;
|
2010-11-09 23:42:51 +01:00
|
|
|
DWORD writepos, min_writepos, playpos;
|
|
|
|
REFERENCE_TIME max_lag = 50 * 10000;
|
|
|
|
REFERENCE_TIME cur, writepos_t, delta_t;
|
|
|
|
|
|
|
|
DSoundRender_UpdatePositions(This, &writepos, &min_writepos);
|
|
|
|
playpos = This->last_playpos;
|
2020-01-31 02:05:19 +01:00
|
|
|
if (This->renderer.filter.clock)
|
quartz/dsoundrender: Delegate IReferenceClock to the system clock.
For several reasons.
Firstly, the reference clock should still function when the filter is not
running.
Secondly, IDirectSoundBuffer::GetPositions() in practice returns very coarse
positions, both on Windows and on Wine. On my hardware, the resolution is
about 10ms, which, while suitable for the DirectSound renderer and probably
also any video renderers, is nevertheless actually coarser than
GetTickCount().
Thirdly, testing supports that the native DirectSound renderer returns a
timestamp from IReferenceClock::GetTime() that is more accurate than
IDirectSoundBuffer::GetPositions(). In fact, after dumping a large number of
different clock sources, I came to the conclusion that it is probably using
timeGetTime() as a source. On Wine that's identical to GetTickCount(), so we
may as well just delegate directly to the system clock.
Signed-off-by: Zebediah Figura <z.figura12@gmail.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2019-11-29 00:35:40 +01:00
|
|
|
{
|
2020-01-31 02:05:19 +01:00
|
|
|
IReferenceClock_GetTime(This->renderer.filter.clock, &cur);
|
2019-10-04 17:02:45 +02:00
|
|
|
cur -= This->renderer.stream_start;
|
2010-11-09 23:42:51 +01:00
|
|
|
} else
|
2010-12-01 23:17:29 +01:00
|
|
|
write_at = -1;
|
2010-11-09 23:42:51 +01:00
|
|
|
|
|
|
|
if (writepos == min_writepos)
|
2010-12-01 13:14:58 +01:00
|
|
|
max_lag = 0;
|
2010-11-09 23:42:51 +01:00
|
|
|
|
|
|
|
*skip = 0;
|
2010-12-01 23:17:29 +01:00
|
|
|
if (write_at < 0) {
|
2010-11-09 23:42:51 +01:00
|
|
|
*ret_writepos = writepos;
|
|
|
|
goto end;
|
|
|
|
}
|
2007-04-06 12:48:03 +02:00
|
|
|
|
2010-11-09 23:42:51 +01:00
|
|
|
if (writepos >= playpos)
|
|
|
|
writepos_t = cur + time_from_pos(This, writepos - playpos);
|
|
|
|
else
|
|
|
|
writepos_t = cur + time_from_pos(This, This->buf_size + writepos - playpos);
|
|
|
|
|
|
|
|
/* write_at: Starting time of sample */
|
|
|
|
/* cur: current time of play position */
|
|
|
|
/* writepos_t: current time of our pointer play position */
|
|
|
|
delta_t = write_at - writepos_t;
|
|
|
|
if (delta_t >= -max_lag && delta_t <= max_lag) {
|
|
|
|
TRACE("Continuing from old position\n");
|
|
|
|
*ret_writepos = writepos;
|
|
|
|
} else if (delta_t < 0) {
|
|
|
|
REFERENCE_TIME past, min_writepos_t;
|
2020-01-31 02:05:17 +01:00
|
|
|
WARN("Delta too big %s/%s, overwriting old data or even skipping\n", debugstr_time(delta_t), debugstr_time(max_lag));
|
2010-11-09 23:42:51 +01:00
|
|
|
if (min_writepos >= playpos)
|
|
|
|
min_writepos_t = cur + time_from_pos(This, min_writepos - playpos);
|
|
|
|
else
|
|
|
|
min_writepos_t = cur + time_from_pos(This, This->buf_size - playpos + min_writepos);
|
|
|
|
past = min_writepos_t - write_at;
|
|
|
|
if (past >= 0) {
|
|
|
|
DWORD skipbytes = pos_from_time(This, past);
|
2010-11-29 10:44:14 +01:00
|
|
|
WARN("Skipping %u bytes\n", skipbytes);
|
2010-11-09 23:42:51 +01:00
|
|
|
*skip = skipbytes;
|
|
|
|
*ret_writepos = min_writepos;
|
|
|
|
} else {
|
|
|
|
DWORD aheadbytes = pos_from_time(This, -past);
|
2010-11-29 10:44:14 +01:00
|
|
|
WARN("Advancing %u bytes\n", aheadbytes);
|
2010-11-09 23:42:51 +01:00
|
|
|
*ret_writepos = (min_writepos + aheadbytes) % This->buf_size;
|
2007-04-06 12:48:03 +02:00
|
|
|
}
|
2010-11-09 23:42:51 +01:00
|
|
|
} else /* delta_t > 0 */ {
|
|
|
|
DWORD aheadbytes;
|
2020-01-31 02:05:17 +01:00
|
|
|
WARN("Delta too big %s/%s, too far ahead\n", debugstr_time(delta_t), debugstr_time(max_lag));
|
2010-11-09 23:42:51 +01:00
|
|
|
aheadbytes = pos_from_time(This, delta_t);
|
2010-11-29 10:44:14 +01:00
|
|
|
WARN("Advancing %u bytes\n", aheadbytes);
|
2010-11-09 23:42:51 +01:00
|
|
|
if (delta_t >= DSoundRenderer_Max_Fill)
|
|
|
|
return S_FALSE;
|
|
|
|
*ret_writepos = (min_writepos + aheadbytes) % This->buf_size;
|
2007-04-06 12:48:03 +02:00
|
|
|
}
|
2010-11-09 23:42:51 +01:00
|
|
|
end:
|
|
|
|
if (playpos > *ret_writepos)
|
|
|
|
*pfree = playpos - *ret_writepos;
|
|
|
|
else if (playpos == *ret_writepos)
|
|
|
|
*pfree = This->buf_size - wfx->nBlockAlign;
|
|
|
|
else
|
|
|
|
*pfree = This->buf_size + playpos - *ret_writepos;
|
|
|
|
if (time_from_pos(This, This->buf_size - *pfree) >= DSoundRenderer_Max_Fill) {
|
2020-01-31 02:05:17 +01:00
|
|
|
TRACE("Blocked: too full %s / %s\n", debugstr_time(time_from_pos(This, This->buf_size - *pfree)),
|
|
|
|
debugstr_time(DSoundRenderer_Max_Fill));
|
2010-11-09 23:42:51 +01:00
|
|
|
return S_FALSE;
|
|
|
|
}
|
|
|
|
return S_OK;
|
2007-04-06 12:48:03 +02:00
|
|
|
}
|
|
|
|
|
2020-04-01 06:48:19 +02:00
|
|
|
static HRESULT DSoundRender_HandleEndOfStream(struct dsound_render *This)
|
2010-12-10 16:06:57 +01:00
|
|
|
{
|
2014-07-18 18:57:13 +02:00
|
|
|
while (This->renderer.filter.state == State_Running)
|
2010-12-10 16:06:57 +01:00
|
|
|
{
|
|
|
|
DWORD pos1, pos2;
|
|
|
|
DSoundRender_UpdatePositions(This, &pos1, &pos2);
|
|
|
|
if (pos1 == pos2)
|
|
|
|
break;
|
|
|
|
|
|
|
|
This->in_loop = 1;
|
2012-03-31 03:08:16 +02:00
|
|
|
LeaveCriticalSection(&This->renderer.csRenderLock);
|
2019-07-03 05:25:43 +02:00
|
|
|
WaitForSingleObject(This->renderer.flush_event, 10);
|
2012-03-31 03:08:16 +02:00
|
|
|
EnterCriticalSection(&This->renderer.csRenderLock);
|
2010-12-10 16:06:57 +01:00
|
|
|
This->in_loop = 0;
|
|
|
|
}
|
|
|
|
|
2012-05-11 20:20:18 +02:00
|
|
|
return S_OK;
|
2010-12-10 16:06:57 +01:00
|
|
|
}
|
|
|
|
|
2020-04-01 06:48:19 +02:00
|
|
|
static HRESULT DSoundRender_SendSampleData(struct dsound_render *This,
|
|
|
|
REFERENCE_TIME tStart, REFERENCE_TIME tStop, const BYTE *data, DWORD size)
|
2004-08-24 04:28:35 +02:00
|
|
|
{
|
2010-11-09 23:42:51 +01:00
|
|
|
HRESULT hr;
|
2004-08-24 04:28:35 +02:00
|
|
|
|
2012-03-31 03:08:16 +02:00
|
|
|
while (size && This->renderer.filter.state != State_Stopped) {
|
2010-11-09 23:42:51 +01:00
|
|
|
DWORD writepos, skip = 0, free, size1, size2, ret;
|
|
|
|
BYTE *buf1, *buf2;
|
2008-04-05 01:27:29 +02:00
|
|
|
|
2012-03-31 03:08:16 +02:00
|
|
|
if (This->renderer.filter.state == State_Running)
|
2010-12-01 23:17:28 +01:00
|
|
|
hr = DSoundRender_GetWritePos(This, &writepos, tStart, &free, &skip);
|
|
|
|
else
|
|
|
|
hr = S_FALSE;
|
2004-08-24 04:28:35 +02:00
|
|
|
|
2010-11-09 23:42:51 +01:00
|
|
|
if (hr != S_OK) {
|
2010-05-30 19:17:26 +02:00
|
|
|
This->in_loop = 1;
|
2012-03-31 03:08:16 +02:00
|
|
|
LeaveCriticalSection(&This->renderer.csRenderLock);
|
2019-07-03 05:25:43 +02:00
|
|
|
ret = WaitForSingleObject(This->renderer.flush_event, 10);
|
2012-03-31 03:08:16 +02:00
|
|
|
EnterCriticalSection(&This->renderer.csRenderLock);
|
2010-05-30 19:17:26 +02:00
|
|
|
This->in_loop = 0;
|
2019-06-21 03:13:19 +02:00
|
|
|
if (This->renderer.sink.flushing || This->renderer.filter.state == State_Stopped)
|
2012-03-31 03:08:16 +02:00
|
|
|
return This->renderer.filter.state == State_Paused ? S_OK : VFW_E_WRONG_STATE;
|
2010-11-09 23:42:51 +01:00
|
|
|
if (ret != WAIT_TIMEOUT)
|
|
|
|
ERR("%x\n", ret);
|
2006-08-18 18:38:38 +02:00
|
|
|
continue;
|
|
|
|
}
|
2010-11-09 23:42:51 +01:00
|
|
|
tStart = -1;
|
|
|
|
|
|
|
|
if (skip)
|
|
|
|
FIXME("Sample dropped %u of %u bytes\n", skip, size);
|
|
|
|
if (skip >= size)
|
2010-12-01 23:17:28 +01:00
|
|
|
return S_OK;
|
2010-11-09 23:42:51 +01:00
|
|
|
data += skip;
|
|
|
|
size -= skip;
|
2006-08-18 18:38:38 +02:00
|
|
|
|
2010-11-09 23:42:51 +01:00
|
|
|
hr = IDirectSoundBuffer_Lock(This->dsbuffer, writepos, min(free, size), (void**)&buf1, &size1, (void**)&buf2, &size2, 0);
|
2005-04-25 12:49:22 +02:00
|
|
|
if (hr != DS_OK) {
|
2006-10-12 20:57:23 +02:00
|
|
|
ERR("Unable to lock sound buffer! (%x)\n", hr);
|
2005-04-25 12:49:22 +02:00
|
|
|
break;
|
2004-08-24 04:28:35 +02:00
|
|
|
}
|
2010-11-09 23:42:51 +01:00
|
|
|
memcpy(buf1, data, size1);
|
|
|
|
if (size2)
|
|
|
|
memcpy(buf2, data+size1, size2);
|
|
|
|
IDirectSoundBuffer_Unlock(This->dsbuffer, buf1, size1, buf2, size2);
|
|
|
|
This->writepos = (writepos + size1 + size2) % This->buf_size;
|
|
|
|
TRACE("Wrote %u bytes at %u, next at %u - (%u/%u)\n", size1+size2, writepos, This->writepos, free, size);
|
|
|
|
data += size1 + size2;
|
|
|
|
size -= size1 + size2;
|
|
|
|
}
|
|
|
|
return S_OK;
|
2004-08-24 04:28:35 +02:00
|
|
|
}
|
|
|
|
|
2019-10-24 03:04:23 +02:00
|
|
|
static HRESULT WINAPI DSoundRender_ShouldDrawSampleNow(struct strmbase_renderer *iface,
|
|
|
|
IMediaSample *sample, REFERENCE_TIME *start, REFERENCE_TIME *end)
|
2012-04-02 14:52:08 +02:00
|
|
|
{
|
|
|
|
/* We time ourselves do not use the base renderers timing */
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-10-24 03:04:23 +02:00
|
|
|
static HRESULT WINAPI DSoundRender_PrepareReceive(struct strmbase_renderer *iface, IMediaSample *pSample)
|
2004-08-24 04:28:35 +02:00
|
|
|
{
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *This = impl_from_strmbase_renderer(iface);
|
2004-08-24 04:28:35 +02:00
|
|
|
HRESULT hr;
|
2008-10-21 12:50:36 +02:00
|
|
|
AM_MEDIA_TYPE *amt;
|
2004-08-24 04:28:35 +02:00
|
|
|
|
2008-10-21 12:50:36 +02:00
|
|
|
if (IMediaSample_GetMediaType(pSample, &amt) == S_OK)
|
|
|
|
{
|
2019-11-19 01:51:20 +01:00
|
|
|
AM_MEDIA_TYPE *orig = &This->renderer.sink.pin.mt;
|
2008-10-21 12:50:36 +02:00
|
|
|
WAVEFORMATEX *origfmt = (WAVEFORMATEX *)orig->pbFormat;
|
|
|
|
WAVEFORMATEX *newfmt = (WAVEFORMATEX *)amt->pbFormat;
|
|
|
|
|
|
|
|
if (origfmt->wFormatTag == newfmt->wFormatTag &&
|
|
|
|
origfmt->nChannels == newfmt->nChannels &&
|
|
|
|
origfmt->nBlockAlign == newfmt->nBlockAlign &&
|
|
|
|
origfmt->wBitsPerSample == newfmt->wBitsPerSample &&
|
|
|
|
origfmt->cbSize == newfmt->cbSize)
|
|
|
|
{
|
|
|
|
if (origfmt->nSamplesPerSec != newfmt->nSamplesPerSec)
|
|
|
|
{
|
|
|
|
hr = IDirectSoundBuffer_SetFrequency(This->dsbuffer,
|
|
|
|
newfmt->nSamplesPerSec);
|
|
|
|
if (FAILED(hr))
|
|
|
|
return VFW_E_TYPE_NOT_ACCEPTED;
|
|
|
|
FreeMediaType(orig);
|
|
|
|
CopyMediaType(orig, amt);
|
|
|
|
IMediaSample_SetMediaType(pSample, NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return VFW_E_TYPE_NOT_ACCEPTED;
|
|
|
|
}
|
2012-03-31 03:08:16 +02:00
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2019-10-24 03:04:23 +02:00
|
|
|
static HRESULT WINAPI DSoundRender_DoRenderSample(struct strmbase_renderer *iface, IMediaSample *pSample)
|
2012-03-31 03:08:16 +02:00
|
|
|
{
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *This = impl_from_strmbase_renderer(iface);
|
2012-03-31 03:08:16 +02:00
|
|
|
LPBYTE pbSrcStream = NULL;
|
|
|
|
LONG cbSrcStream = 0;
|
|
|
|
REFERENCE_TIME tStart, tStop;
|
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
TRACE("%p %p\n", iface, pSample);
|
|
|
|
|
|
|
|
/* Slightly incorrect, Pause completes when a frame is received so we should signal
|
|
|
|
* pause completion here, but for sound playing a single frame doesn't make sense
|
|
|
|
*/
|
2008-10-21 12:50:36 +02:00
|
|
|
|
2004-08-24 04:28:35 +02:00
|
|
|
hr = IMediaSample_GetPointer(pSample, &pbSrcStream);
|
|
|
|
if (FAILED(hr))
|
|
|
|
{
|
2006-10-12 20:57:23 +02:00
|
|
|
ERR("Cannot get pointer to sample data (%x)\n", hr);
|
2008-04-16 23:27:06 +02:00
|
|
|
return hr;
|
2004-08-24 04:28:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
hr = IMediaSample_GetTime(pSample, &tStart, &tStop);
|
2010-11-29 10:44:13 +01:00
|
|
|
if (FAILED(hr)) {
|
2006-10-12 20:57:23 +02:00
|
|
|
ERR("Cannot get sample time (%x)\n", hr);
|
2010-11-29 10:44:13 +01:00
|
|
|
tStart = tStop = -1;
|
|
|
|
}
|
2004-08-24 04:28:35 +02:00
|
|
|
|
2010-11-09 23:42:51 +01:00
|
|
|
IMediaSample_IsDiscontinuity(pSample);
|
2008-04-16 23:27:06 +02:00
|
|
|
|
2008-04-19 07:04:55 +02:00
|
|
|
if (IMediaSample_IsPreroll(pSample) == S_OK)
|
|
|
|
{
|
|
|
|
TRACE("Preroll!\n");
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2004-08-24 04:28:35 +02:00
|
|
|
cbSrcStream = IMediaSample_GetActualDataLength(pSample);
|
2010-05-20 01:14:09 +02:00
|
|
|
TRACE("Sample data ptr = %p, size = %d\n", pbSrcStream, cbSrcStream);
|
2004-08-24 04:28:35 +02:00
|
|
|
|
2010-11-09 23:42:51 +01:00
|
|
|
hr = DSoundRender_SendSampleData(This, tStart, tStop, pbSrcStream, cbSrcStream);
|
2020-01-31 02:05:19 +01:00
|
|
|
if (This->renderer.filter.state == State_Running && This->renderer.filter.clock && tStart >= 0) {
|
2010-12-01 23:17:30 +01:00
|
|
|
REFERENCE_TIME jitter, now = 0;
|
|
|
|
Quality q;
|
2020-01-31 02:05:19 +01:00
|
|
|
IReferenceClock_GetTime(This->renderer.filter.clock, &now);
|
2019-10-04 17:02:45 +02:00
|
|
|
jitter = now - This->renderer.stream_start - tStart;
|
2010-12-01 23:17:30 +01:00
|
|
|
if (jitter <= -DSoundRenderer_Max_Fill)
|
|
|
|
jitter += DSoundRenderer_Max_Fill;
|
|
|
|
else if (jitter < 0)
|
|
|
|
jitter = 0;
|
|
|
|
q.Type = (jitter > 0 ? Famine : Flood);
|
2016-05-17 20:43:32 +02:00
|
|
|
q.Proportion = 1000;
|
2010-12-01 23:17:30 +01:00
|
|
|
q.Late = jitter;
|
|
|
|
q.TimeStamp = tStart;
|
2019-09-02 12:18:35 +02:00
|
|
|
IQualityControl_Notify((IQualityControl *)This->renderer.qcimpl, &This->renderer.filter.IBaseFilter_iface, q);
|
2010-12-01 23:17:30 +01:00
|
|
|
}
|
2008-07-05 01:59:22 +02:00
|
|
|
return hr;
|
2004-08-24 04:28:35 +02:00
|
|
|
}
|
|
|
|
|
2019-10-24 03:04:23 +02:00
|
|
|
static HRESULT WINAPI DSoundRender_CheckMediaType(struct strmbase_renderer *iface, const AM_MEDIA_TYPE * pmt)
|
2004-08-24 04:28:35 +02:00
|
|
|
{
|
2008-06-10 17:00:40 +02:00
|
|
|
if (!IsEqualIID(&pmt->majortype, &MEDIATYPE_Audio))
|
|
|
|
return S_FALSE;
|
|
|
|
|
|
|
|
if (!IsEqualIID(&pmt->subtype, &MEDIASUBTYPE_PCM))
|
|
|
|
return S_FALSE;
|
|
|
|
|
|
|
|
return S_OK;
|
2004-08-24 04:28:35 +02:00
|
|
|
}
|
|
|
|
|
2019-10-24 03:04:23 +02:00
|
|
|
static void dsound_render_stop_stream(struct strmbase_renderer *iface)
|
2010-10-07 21:48:01 +02:00
|
|
|
{
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *This = impl_from_strmbase_renderer(iface);
|
2010-10-07 21:48:01 +02:00
|
|
|
|
2012-03-31 03:08:16 +02:00
|
|
|
TRACE("(%p/%p)->()\n", This, iface);
|
2010-10-07 21:48:01 +02:00
|
|
|
|
2012-03-31 03:08:16 +02:00
|
|
|
IDirectSoundBuffer_Stop(This->dsbuffer);
|
|
|
|
This->writepos = This->buf_size;
|
2010-10-07 21:48:01 +02:00
|
|
|
}
|
|
|
|
|
2019-10-24 03:04:23 +02:00
|
|
|
static void dsound_render_start_stream(struct strmbase_renderer *iface)
|
2010-10-07 21:48:01 +02:00
|
|
|
{
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *This = impl_from_strmbase_renderer(iface);
|
2012-03-31 03:08:16 +02:00
|
|
|
|
|
|
|
TRACE("(%p)\n", This);
|
|
|
|
|
2019-09-27 04:40:53 +02:00
|
|
|
if (This->renderer.sink.pin.peer)
|
2012-03-31 03:08:16 +02:00
|
|
|
{
|
|
|
|
IDirectSoundBuffer_Play(This->dsbuffer, 0, 0, DSBPLAY_LOOPING);
|
|
|
|
}
|
2010-10-07 21:48:01 +02:00
|
|
|
}
|
|
|
|
|
2019-12-10 17:35:12 +01:00
|
|
|
static HRESULT dsound_render_connect(struct strmbase_renderer *iface, const AM_MEDIA_TYPE *mt)
|
2012-03-31 03:08:16 +02:00
|
|
|
{
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *This = impl_from_strmbase_renderer(iface);
|
2019-12-10 17:35:12 +01:00
|
|
|
const WAVEFORMATEX *format = (WAVEFORMATEX *)mt->pbFormat;
|
2012-03-31 03:08:16 +02:00
|
|
|
HRESULT hr = S_OK;
|
|
|
|
DSBUFFERDESC buf_desc;
|
|
|
|
|
|
|
|
This->buf_size = format->nAvgBytesPerSec;
|
2010-10-13 18:02:01 +02:00
|
|
|
|
2012-03-31 03:08:16 +02:00
|
|
|
memset(&buf_desc,0,sizeof(DSBUFFERDESC));
|
|
|
|
buf_desc.dwSize = sizeof(DSBUFFERDESC);
|
|
|
|
buf_desc.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPAN |
|
|
|
|
DSBCAPS_CTRLFREQUENCY | DSBCAPS_GLOBALFOCUS |
|
|
|
|
DSBCAPS_GETCURRENTPOSITION2;
|
|
|
|
buf_desc.dwBufferBytes = This->buf_size;
|
2019-12-10 17:35:12 +01:00
|
|
|
buf_desc.lpwfxFormat = (WAVEFORMATEX *)format;
|
2018-03-30 06:04:46 +02:00
|
|
|
hr = IDirectSound8_CreateSoundBuffer(This->dsound, &buf_desc, &This->dsbuffer, NULL);
|
2012-03-31 03:08:16 +02:00
|
|
|
This->writepos = This->buf_size;
|
|
|
|
if (FAILED(hr))
|
|
|
|
ERR("Can't create sound buffer (%x)\n", hr);
|
|
|
|
|
|
|
|
if (SUCCEEDED(hr))
|
|
|
|
{
|
|
|
|
hr = IDirectSoundBuffer_SetVolume(This->dsbuffer, This->volume);
|
|
|
|
if (FAILED(hr))
|
|
|
|
ERR("Can't set volume to %d (%x)\n", This->volume, hr);
|
|
|
|
|
|
|
|
hr = IDirectSoundBuffer_SetPan(This->dsbuffer, This->pan);
|
|
|
|
if (FAILED(hr))
|
|
|
|
ERR("Can't set pan to %d (%x)\n", This->pan, hr);
|
|
|
|
hr = S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (FAILED(hr) && hr != VFW_E_ALREADY_CONNECTED)
|
|
|
|
{
|
|
|
|
if (This->dsbuffer)
|
|
|
|
IDirectSoundBuffer_Release(This->dsbuffer);
|
|
|
|
This->dsbuffer = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
2019-10-24 03:04:23 +02:00
|
|
|
static HRESULT WINAPI DSoundRender_BreakConnect(struct strmbase_renderer *iface)
|
2012-03-31 03:08:16 +02:00
|
|
|
{
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *This = impl_from_strmbase_renderer(iface);
|
2012-03-31 03:08:16 +02:00
|
|
|
|
|
|
|
TRACE("(%p)->()\n", iface);
|
|
|
|
|
|
|
|
if (This->dsbuffer)
|
|
|
|
IDirectSoundBuffer_Release(This->dsbuffer);
|
|
|
|
This->dsbuffer = NULL;
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2019-10-24 03:04:23 +02:00
|
|
|
static HRESULT WINAPI DSoundRender_EndOfStream(struct strmbase_renderer *iface)
|
2012-03-31 03:08:16 +02:00
|
|
|
{
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *This = impl_from_strmbase_renderer(iface);
|
2019-11-28 02:27:17 +01:00
|
|
|
return DSoundRender_HandleEndOfStream(This);
|
2012-03-31 03:08:16 +02:00
|
|
|
}
|
|
|
|
|
2019-10-24 03:04:23 +02:00
|
|
|
static HRESULT WINAPI DSoundRender_EndFlush(struct strmbase_renderer *iface)
|
2012-03-31 03:08:16 +02:00
|
|
|
{
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *This = impl_from_strmbase_renderer(iface);
|
2012-03-31 03:08:16 +02:00
|
|
|
|
|
|
|
if (This->dsbuffer)
|
|
|
|
{
|
|
|
|
LPBYTE buffer;
|
|
|
|
DWORD size;
|
|
|
|
|
|
|
|
/* Force a reset */
|
|
|
|
IDirectSoundBuffer_Lock(This->dsbuffer, 0, 0, (LPVOID *)&buffer, &size, NULL, NULL, DSBLOCK_ENTIREBUFFER);
|
|
|
|
memset(buffer, 0, size);
|
|
|
|
IDirectSoundBuffer_Unlock(This->dsbuffer, buffer, size, NULL, 0);
|
|
|
|
This->writepos = This->buf_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2019-10-24 03:04:23 +02:00
|
|
|
static void dsound_render_destroy(struct strmbase_renderer *iface)
|
2019-05-28 06:09:56 +02:00
|
|
|
{
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *filter = impl_from_strmbase_renderer(iface);
|
2019-05-28 06:09:56 +02:00
|
|
|
|
|
|
|
if (filter->dsbuffer)
|
|
|
|
IDirectSoundBuffer_Release(filter->dsbuffer);
|
|
|
|
filter->dsbuffer = NULL;
|
|
|
|
if (filter->dsound)
|
|
|
|
IDirectSound8_Release(filter->dsound);
|
|
|
|
filter->dsound = NULL;
|
|
|
|
|
|
|
|
strmbase_renderer_cleanup(&filter->renderer);
|
2020-04-01 06:48:18 +02:00
|
|
|
free(filter);
|
2020-03-13 03:34:12 +01:00
|
|
|
|
|
|
|
InterlockedDecrement(&object_locks);
|
2019-05-28 06:09:56 +02:00
|
|
|
}
|
|
|
|
|
2019-10-24 03:04:23 +02:00
|
|
|
static HRESULT dsound_render_query_interface(struct strmbase_renderer *iface, REFIID iid, void **out)
|
2019-05-31 05:59:37 +02:00
|
|
|
{
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *filter = impl_from_strmbase_renderer(iface);
|
2019-05-31 05:59:37 +02:00
|
|
|
|
|
|
|
if (IsEqualGUID(iid, &IID_IBasicAudio))
|
2019-06-07 01:49:24 +02:00
|
|
|
*out = &filter->IBasicAudio_iface;
|
2019-05-31 05:59:37 +02:00
|
|
|
else if (IsEqualGUID(iid, &IID_IReferenceClock))
|
quartz/dsoundrender: Delegate IReferenceClock to the system clock.
For several reasons.
Firstly, the reference clock should still function when the filter is not
running.
Secondly, IDirectSoundBuffer::GetPositions() in practice returns very coarse
positions, both on Windows and on Wine. On my hardware, the resolution is
about 10ms, which, while suitable for the DirectSound renderer and probably
also any video renderers, is nevertheless actually coarser than
GetTickCount().
Thirdly, testing supports that the native DirectSound renderer returns a
timestamp from IReferenceClock::GetTime() that is more accurate than
IDirectSoundBuffer::GetPositions(). In fact, after dumping a large number of
different clock sources, I came to the conclusion that it is probably using
timeGetTime() as a source. On Wine that's identical to GetTickCount(), so we
may as well just delegate directly to the system clock.
Signed-off-by: Zebediah Figura <z.figura12@gmail.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2019-11-29 00:35:40 +01:00
|
|
|
return IUnknown_QueryInterface(filter->system_clock, iid, out);
|
2019-05-31 05:59:37 +02:00
|
|
|
else if (IsEqualGUID(iid, &IID_IAMDirectSound))
|
|
|
|
*out = &filter->IAMDirectSound_iface;
|
|
|
|
else
|
|
|
|
return E_NOINTERFACE;
|
|
|
|
|
|
|
|
IUnknown_AddRef((IUnknown *)*out);
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
2019-10-24 03:04:24 +02:00
|
|
|
static const struct strmbase_renderer_ops renderer_ops =
|
2019-07-03 05:25:45 +02:00
|
|
|
{
|
|
|
|
.pfnCheckMediaType = DSoundRender_CheckMediaType,
|
|
|
|
.pfnDoRenderSample = DSoundRender_DoRenderSample,
|
2019-07-03 05:25:46 +02:00
|
|
|
.renderer_start_stream = dsound_render_start_stream,
|
2019-07-03 05:25:47 +02:00
|
|
|
.renderer_stop_stream = dsound_render_stop_stream,
|
2019-07-03 05:25:45 +02:00
|
|
|
.pfnShouldDrawSampleNow = DSoundRender_ShouldDrawSampleNow,
|
|
|
|
.pfnPrepareReceive = DSoundRender_PrepareReceive,
|
2019-12-10 17:35:12 +01:00
|
|
|
.renderer_connect = dsound_render_connect,
|
2019-07-03 05:25:45 +02:00
|
|
|
.pfnBreakConnect = DSoundRender_BreakConnect,
|
|
|
|
.pfnEndOfStream = DSoundRender_EndOfStream,
|
|
|
|
.pfnEndFlush = DSoundRender_EndFlush,
|
|
|
|
.renderer_destroy = dsound_render_destroy,
|
|
|
|
.renderer_query_interface = dsound_render_query_interface,
|
2010-10-13 18:02:01 +02:00
|
|
|
};
|
|
|
|
|
2004-08-24 04:28:35 +02:00
|
|
|
/*** IUnknown methods ***/
|
|
|
|
static HRESULT WINAPI Basicaudio_QueryInterface(IBasicAudio *iface,
|
|
|
|
REFIID riid,
|
|
|
|
LPVOID*ppvObj) {
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *This = impl_from_IBasicAudio(iface);
|
2004-08-24 04:28:35 +02:00
|
|
|
|
2016-08-10 13:03:39 +02:00
|
|
|
TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppvObj);
|
2004-08-24 04:28:35 +02:00
|
|
|
|
2019-12-05 06:03:31 +01:00
|
|
|
return IUnknown_QueryInterface(This->renderer.filter.outer_unk, riid, ppvObj);
|
2004-08-24 04:28:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI Basicaudio_AddRef(IBasicAudio *iface) {
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *This = impl_from_IBasicAudio(iface);
|
2004-08-24 04:28:35 +02:00
|
|
|
|
|
|
|
TRACE("(%p/%p)->()\n", This, iface);
|
|
|
|
|
2019-12-05 06:03:31 +01:00
|
|
|
return IUnknown_AddRef(This->renderer.filter.outer_unk);
|
2004-08-24 04:28:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI Basicaudio_Release(IBasicAudio *iface) {
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *This = impl_from_IBasicAudio(iface);
|
2004-08-24 04:28:35 +02:00
|
|
|
|
|
|
|
TRACE("(%p/%p)->()\n", This, iface);
|
|
|
|
|
2019-12-05 06:03:31 +01:00
|
|
|
return IUnknown_Release(This->renderer.filter.outer_unk);
|
2004-08-24 04:28:35 +02:00
|
|
|
}
|
|
|
|
|
2019-06-07 01:49:24 +02:00
|
|
|
HRESULT WINAPI basic_audio_GetTypeInfoCount(IBasicAudio *iface, UINT *count)
|
|
|
|
{
|
|
|
|
TRACE("iface %p, count %p.\n", iface, count);
|
|
|
|
*count = 1;
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT WINAPI basic_audio_GetTypeInfo(IBasicAudio *iface, UINT index,
|
|
|
|
LCID lcid, ITypeInfo **typeinfo)
|
|
|
|
{
|
|
|
|
TRACE("iface %p, index %u, lcid %#x, typeinfo %p.\n", iface, index, lcid, typeinfo);
|
|
|
|
return strmbase_get_typeinfo(IBasicAudio_tid, typeinfo);
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT WINAPI basic_audio_GetIDsOfNames(IBasicAudio *iface, REFIID iid,
|
|
|
|
LPOLESTR *names, UINT count, LCID lcid, DISPID *ids)
|
|
|
|
{
|
|
|
|
ITypeInfo *typeinfo;
|
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
TRACE("iface %p, iid %s, names %p, count %u, lcid %#x, ids %p.\n",
|
|
|
|
iface, debugstr_guid(iid), names, count, lcid, ids);
|
|
|
|
|
|
|
|
if (SUCCEEDED(hr = strmbase_get_typeinfo(IBasicAudio_tid, &typeinfo)))
|
|
|
|
{
|
|
|
|
hr = ITypeInfo_GetIDsOfNames(typeinfo, names, count, ids);
|
|
|
|
ITypeInfo_Release(typeinfo);
|
|
|
|
}
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI basic_audio_Invoke(IBasicAudio *iface, DISPID id, REFIID iid, LCID lcid,
|
|
|
|
WORD flags, DISPPARAMS *params, VARIANT *result, EXCEPINFO *excepinfo, UINT *error_arg)
|
|
|
|
{
|
|
|
|
ITypeInfo *typeinfo;
|
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
TRACE("iface %p, id %d, iid %s, lcid %#x, flags %#x, params %p, result %p, excepinfo %p, error_arg %p.\n",
|
|
|
|
iface, id, debugstr_guid(iid), lcid, flags, params, result, excepinfo, error_arg);
|
|
|
|
|
|
|
|
if (SUCCEEDED(hr = strmbase_get_typeinfo(IBasicAudio_tid, &typeinfo)))
|
|
|
|
{
|
|
|
|
hr = ITypeInfo_Invoke(typeinfo, iface, id, flags, params, result, excepinfo, error_arg);
|
|
|
|
ITypeInfo_Release(typeinfo);
|
|
|
|
}
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
2004-08-24 04:28:35 +02:00
|
|
|
static HRESULT WINAPI Basicaudio_put_Volume(IBasicAudio *iface,
|
2009-03-11 00:31:26 +01:00
|
|
|
LONG lVolume) {
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *This = impl_from_IBasicAudio(iface);
|
2004-08-24 04:28:35 +02:00
|
|
|
|
2009-03-11 00:31:26 +01:00
|
|
|
TRACE("(%p/%p)->(%d)\n", This, iface, lVolume);
|
2007-03-26 05:13:53 +02:00
|
|
|
|
|
|
|
if (lVolume > DSBVOLUME_MAX || lVolume < DSBVOLUME_MIN)
|
|
|
|
return E_INVALIDARG;
|
|
|
|
|
|
|
|
if (This->dsbuffer) {
|
|
|
|
if (FAILED(IDirectSoundBuffer_SetVolume(This->dsbuffer, lVolume)))
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
2004-08-24 04:28:35 +02:00
|
|
|
|
2007-03-26 05:13:53 +02:00
|
|
|
This->volume = lVolume;
|
2004-08-24 04:28:35 +02:00
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI Basicaudio_get_Volume(IBasicAudio *iface,
|
2009-03-11 00:31:26 +01:00
|
|
|
LONG *plVolume) {
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *This = impl_from_IBasicAudio(iface);
|
2004-08-24 04:28:35 +02:00
|
|
|
|
2007-03-26 05:13:53 +02:00
|
|
|
TRACE("(%p/%p)->(%p)\n", This, iface, plVolume);
|
2004-08-24 04:28:35 +02:00
|
|
|
|
2007-03-26 05:13:53 +02:00
|
|
|
if (!plVolume)
|
|
|
|
return E_POINTER;
|
|
|
|
|
|
|
|
*plVolume = This->volume;
|
2004-08-24 04:28:35 +02:00
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI Basicaudio_put_Balance(IBasicAudio *iface,
|
2009-03-11 00:31:26 +01:00
|
|
|
LONG lBalance) {
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *This = impl_from_IBasicAudio(iface);
|
2004-08-24 04:28:35 +02:00
|
|
|
|
2009-03-11 00:31:26 +01:00
|
|
|
TRACE("(%p/%p)->(%d)\n", This, iface, lBalance);
|
2007-03-26 05:13:53 +02:00
|
|
|
|
|
|
|
if (lBalance < DSBPAN_LEFT || lBalance > DSBPAN_RIGHT)
|
|
|
|
return E_INVALIDARG;
|
|
|
|
|
|
|
|
if (This->dsbuffer) {
|
|
|
|
if (FAILED(IDirectSoundBuffer_SetPan(This->dsbuffer, lBalance)))
|
|
|
|
return E_FAIL;
|
|
|
|
}
|
2004-08-24 04:28:35 +02:00
|
|
|
|
2007-03-26 05:13:53 +02:00
|
|
|
This->pan = lBalance;
|
2004-08-24 04:28:35 +02:00
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI Basicaudio_get_Balance(IBasicAudio *iface,
|
2009-03-11 00:31:26 +01:00
|
|
|
LONG *plBalance) {
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *This = impl_from_IBasicAudio(iface);
|
2004-08-24 04:28:35 +02:00
|
|
|
|
2007-03-26 05:13:53 +02:00
|
|
|
TRACE("(%p/%p)->(%p)\n", This, iface, plBalance);
|
|
|
|
|
|
|
|
if (!plBalance)
|
|
|
|
return E_POINTER;
|
2004-08-24 04:28:35 +02:00
|
|
|
|
2007-03-26 05:13:53 +02:00
|
|
|
*plBalance = This->pan;
|
2004-08-24 04:28:35 +02:00
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const IBasicAudioVtbl IBasicAudio_Vtbl =
|
|
|
|
{
|
|
|
|
Basicaudio_QueryInterface,
|
|
|
|
Basicaudio_AddRef,
|
|
|
|
Basicaudio_Release,
|
2019-06-07 01:49:24 +02:00
|
|
|
basic_audio_GetTypeInfoCount,
|
|
|
|
basic_audio_GetTypeInfo,
|
|
|
|
basic_audio_GetIDsOfNames,
|
|
|
|
basic_audio_Invoke,
|
2004-08-24 04:28:35 +02:00
|
|
|
Basicaudio_put_Volume,
|
|
|
|
Basicaudio_get_Volume,
|
|
|
|
Basicaudio_put_Balance,
|
|
|
|
Basicaudio_get_Balance
|
|
|
|
};
|
2007-04-06 12:48:03 +02:00
|
|
|
|
2010-02-07 21:17:59 +01:00
|
|
|
/*** IUnknown methods ***/
|
|
|
|
static HRESULT WINAPI AMDirectSound_QueryInterface(IAMDirectSound *iface,
|
|
|
|
REFIID riid,
|
|
|
|
LPVOID*ppvObj)
|
|
|
|
{
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *This = impl_from_IAMDirectSound(iface);
|
2010-02-07 21:17:59 +01:00
|
|
|
|
2016-08-10 13:03:39 +02:00
|
|
|
TRACE("(%p/%p)->(%s, %p)\n", This, iface, debugstr_guid(riid), ppvObj);
|
2010-02-07 21:17:59 +01:00
|
|
|
|
2019-12-05 06:03:31 +01:00
|
|
|
return IUnknown_QueryInterface(This->renderer.filter.outer_unk, riid, ppvObj);
|
2010-02-07 21:17:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI AMDirectSound_AddRef(IAMDirectSound *iface)
|
|
|
|
{
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *This = impl_from_IAMDirectSound(iface);
|
2010-02-07 21:17:59 +01:00
|
|
|
|
|
|
|
TRACE("(%p/%p)->()\n", This, iface);
|
|
|
|
|
2019-12-05 06:03:31 +01:00
|
|
|
return IUnknown_AddRef(This->renderer.filter.outer_unk);
|
2010-02-07 21:17:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI AMDirectSound_Release(IAMDirectSound *iface)
|
|
|
|
{
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *This = impl_from_IAMDirectSound(iface);
|
2010-02-07 21:17:59 +01:00
|
|
|
|
|
|
|
TRACE("(%p/%p)->()\n", This, iface);
|
|
|
|
|
2019-12-05 06:03:31 +01:00
|
|
|
return IUnknown_Release(This->renderer.filter.outer_unk);
|
2010-02-07 21:17:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*** IAMDirectSound methods ***/
|
|
|
|
static HRESULT WINAPI AMDirectSound_GetDirectSoundInterface(IAMDirectSound *iface, IDirectSound **ds)
|
|
|
|
{
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *This = impl_from_IAMDirectSound(iface);
|
2010-02-07 21:17:59 +01:00
|
|
|
|
|
|
|
FIXME("(%p/%p)->(%p): stub\n", This, iface, ds);
|
|
|
|
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI AMDirectSound_GetPrimaryBufferInterface(IAMDirectSound *iface, IDirectSoundBuffer **buf)
|
|
|
|
{
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *This = impl_from_IAMDirectSound(iface);
|
2010-02-07 21:17:59 +01:00
|
|
|
|
|
|
|
FIXME("(%p/%p)->(%p): stub\n", This, iface, buf);
|
|
|
|
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI AMDirectSound_GetSecondaryBufferInterface(IAMDirectSound *iface, IDirectSoundBuffer **buf)
|
|
|
|
{
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *This = impl_from_IAMDirectSound(iface);
|
2010-02-07 21:17:59 +01:00
|
|
|
|
|
|
|
FIXME("(%p/%p)->(%p): stub\n", This, iface, buf);
|
|
|
|
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI AMDirectSound_ReleaseDirectSoundInterface(IAMDirectSound *iface, IDirectSound *ds)
|
|
|
|
{
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *This = impl_from_IAMDirectSound(iface);
|
2010-02-07 21:17:59 +01:00
|
|
|
|
|
|
|
FIXME("(%p/%p)->(%p): stub\n", This, iface, ds);
|
|
|
|
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI AMDirectSound_ReleasePrimaryBufferInterface(IAMDirectSound *iface, IDirectSoundBuffer *buf)
|
|
|
|
{
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *This = impl_from_IAMDirectSound(iface);
|
2010-02-07 21:17:59 +01:00
|
|
|
|
|
|
|
FIXME("(%p/%p)->(%p): stub\n", This, iface, buf);
|
|
|
|
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI AMDirectSound_ReleaseSecondaryBufferInterface(IAMDirectSound *iface, IDirectSoundBuffer *buf)
|
|
|
|
{
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *This = impl_from_IAMDirectSound(iface);
|
2010-02-07 21:17:59 +01:00
|
|
|
|
|
|
|
FIXME("(%p/%p)->(%p): stub\n", This, iface, buf);
|
|
|
|
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
2013-07-25 20:26:49 +02:00
|
|
|
static HRESULT WINAPI AMDirectSound_SetFocusWindow(IAMDirectSound *iface, HWND hwnd, BOOL bgaudible)
|
2010-02-07 21:17:59 +01:00
|
|
|
{
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *This = impl_from_IAMDirectSound(iface);
|
2010-02-07 21:17:59 +01:00
|
|
|
|
2013-07-25 20:26:49 +02:00
|
|
|
FIXME("(%p/%p)->(%p,%d): stub\n", This, iface, hwnd, bgaudible);
|
2010-02-07 21:17:59 +01:00
|
|
|
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
2013-07-25 20:26:49 +02:00
|
|
|
static HRESULT WINAPI AMDirectSound_GetFocusWindow(IAMDirectSound *iface, HWND *hwnd, BOOL *bgaudible)
|
2010-02-07 21:17:59 +01:00
|
|
|
{
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *This = impl_from_IAMDirectSound(iface);
|
2010-02-07 21:17:59 +01:00
|
|
|
|
2013-07-25 20:26:49 +02:00
|
|
|
FIXME("(%p/%p)->(%p,%p): stub\n", This, iface, hwnd, bgaudible);
|
2010-02-07 21:17:59 +01:00
|
|
|
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const IAMDirectSoundVtbl IAMDirectSound_Vtbl =
|
|
|
|
{
|
|
|
|
AMDirectSound_QueryInterface,
|
|
|
|
AMDirectSound_AddRef,
|
|
|
|
AMDirectSound_Release,
|
|
|
|
AMDirectSound_GetDirectSoundInterface,
|
|
|
|
AMDirectSound_GetPrimaryBufferInterface,
|
|
|
|
AMDirectSound_GetSecondaryBufferInterface,
|
|
|
|
AMDirectSound_ReleaseDirectSoundInterface,
|
|
|
|
AMDirectSound_ReleasePrimaryBufferInterface,
|
|
|
|
AMDirectSound_ReleaseSecondaryBufferInterface,
|
|
|
|
AMDirectSound_SetFocusWindow,
|
|
|
|
AMDirectSound_GetFocusWindow
|
|
|
|
};
|
2019-11-29 00:35:39 +01:00
|
|
|
|
2020-03-12 03:10:10 +01:00
|
|
|
HRESULT dsound_render_create(IUnknown *outer, IUnknown **out)
|
2019-11-29 00:35:39 +01:00
|
|
|
{
|
|
|
|
static const DSBUFFERDESC buffer_desc = {
|
|
|
|
.dwSize = sizeof(DSBUFFERDESC),
|
|
|
|
.dwFlags = DSBCAPS_PRIMARYBUFFER,
|
|
|
|
};
|
|
|
|
|
2020-04-01 06:48:19 +02:00
|
|
|
struct dsound_render *object;
|
2019-11-29 00:35:39 +01:00
|
|
|
IDirectSoundBuffer *buffer;
|
|
|
|
HRESULT hr;
|
|
|
|
|
2020-04-01 06:48:18 +02:00
|
|
|
if (!(object = calloc(1, sizeof(*object))))
|
2019-11-29 00:35:39 +01:00
|
|
|
return E_OUTOFMEMORY;
|
|
|
|
|
2020-03-28 18:02:07 +01:00
|
|
|
strmbase_renderer_init(&object->renderer, outer,
|
|
|
|
&CLSID_DSoundRender, L"Audio Input pin (rendered)", &renderer_ops);
|
2019-11-29 00:35:39 +01:00
|
|
|
|
2020-03-12 03:10:10 +01:00
|
|
|
if (FAILED(hr = system_clock_create(&object->renderer.filter.IUnknown_inner, &object->system_clock)))
|
quartz/dsoundrender: Delegate IReferenceClock to the system clock.
For several reasons.
Firstly, the reference clock should still function when the filter is not
running.
Secondly, IDirectSoundBuffer::GetPositions() in practice returns very coarse
positions, both on Windows and on Wine. On my hardware, the resolution is
about 10ms, which, while suitable for the DirectSound renderer and probably
also any video renderers, is nevertheless actually coarser than
GetTickCount().
Thirdly, testing supports that the native DirectSound renderer returns a
timestamp from IReferenceClock::GetTime() that is more accurate than
IDirectSoundBuffer::GetPositions(). In fact, after dumping a large number of
different clock sources, I came to the conclusion that it is probably using
timeGetTime() as a source. On Wine that's identical to GetTickCount(), so we
may as well just delegate directly to the system clock.
Signed-off-by: Zebediah Figura <z.figura12@gmail.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2019-11-29 00:35:40 +01:00
|
|
|
{
|
|
|
|
strmbase_renderer_cleanup(&object->renderer);
|
2020-04-01 06:48:18 +02:00
|
|
|
free(object);
|
quartz/dsoundrender: Delegate IReferenceClock to the system clock.
For several reasons.
Firstly, the reference clock should still function when the filter is not
running.
Secondly, IDirectSoundBuffer::GetPositions() in practice returns very coarse
positions, both on Windows and on Wine. On my hardware, the resolution is
about 10ms, which, while suitable for the DirectSound renderer and probably
also any video renderers, is nevertheless actually coarser than
GetTickCount().
Thirdly, testing supports that the native DirectSound renderer returns a
timestamp from IReferenceClock::GetTime() that is more accurate than
IDirectSoundBuffer::GetPositions(). In fact, after dumping a large number of
different clock sources, I came to the conclusion that it is probably using
timeGetTime() as a source. On Wine that's identical to GetTickCount(), so we
may as well just delegate directly to the system clock.
Signed-off-by: Zebediah Figura <z.figura12@gmail.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2019-11-29 00:35:40 +01:00
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
2019-11-29 00:35:39 +01:00
|
|
|
object->IBasicAudio_iface.lpVtbl = &IBasicAudio_Vtbl;
|
|
|
|
object->IAMDirectSound_iface.lpVtbl = &IAMDirectSound_Vtbl;
|
|
|
|
|
|
|
|
if (FAILED(hr = DirectSoundCreate8(NULL, &object->dsound, NULL)))
|
|
|
|
{
|
quartz/dsoundrender: Delegate IReferenceClock to the system clock.
For several reasons.
Firstly, the reference clock should still function when the filter is not
running.
Secondly, IDirectSoundBuffer::GetPositions() in practice returns very coarse
positions, both on Windows and on Wine. On my hardware, the resolution is
about 10ms, which, while suitable for the DirectSound renderer and probably
also any video renderers, is nevertheless actually coarser than
GetTickCount().
Thirdly, testing supports that the native DirectSound renderer returns a
timestamp from IReferenceClock::GetTime() that is more accurate than
IDirectSoundBuffer::GetPositions(). In fact, after dumping a large number of
different clock sources, I came to the conclusion that it is probably using
timeGetTime() as a source. On Wine that's identical to GetTickCount(), so we
may as well just delegate directly to the system clock.
Signed-off-by: Zebediah Figura <z.figura12@gmail.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2019-11-29 00:35:40 +01:00
|
|
|
IUnknown_Release(object->system_clock);
|
2019-11-29 00:35:39 +01:00
|
|
|
strmbase_renderer_cleanup(&object->renderer);
|
2020-04-01 06:48:18 +02:00
|
|
|
free(object);
|
2019-11-29 00:35:39 +01:00
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (FAILED(hr = IDirectSound8_SetCooperativeLevel(object->dsound,
|
|
|
|
GetDesktopWindow(), DSSCL_PRIORITY)))
|
|
|
|
{
|
|
|
|
IDirectSound8_Release(object->dsound);
|
quartz/dsoundrender: Delegate IReferenceClock to the system clock.
For several reasons.
Firstly, the reference clock should still function when the filter is not
running.
Secondly, IDirectSoundBuffer::GetPositions() in practice returns very coarse
positions, both on Windows and on Wine. On my hardware, the resolution is
about 10ms, which, while suitable for the DirectSound renderer and probably
also any video renderers, is nevertheless actually coarser than
GetTickCount().
Thirdly, testing supports that the native DirectSound renderer returns a
timestamp from IReferenceClock::GetTime() that is more accurate than
IDirectSoundBuffer::GetPositions(). In fact, after dumping a large number of
different clock sources, I came to the conclusion that it is probably using
timeGetTime() as a source. On Wine that's identical to GetTickCount(), so we
may as well just delegate directly to the system clock.
Signed-off-by: Zebediah Figura <z.figura12@gmail.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
2019-11-29 00:35:40 +01:00
|
|
|
IUnknown_Release(object->system_clock);
|
2019-11-29 00:35:39 +01:00
|
|
|
strmbase_renderer_cleanup(&object->renderer);
|
2020-04-01 06:48:18 +02:00
|
|
|
free(object);
|
2019-11-29 00:35:39 +01:00
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (SUCCEEDED(hr = IDirectSound8_CreateSoundBuffer(object->dsound,
|
|
|
|
&buffer_desc, &buffer, NULL)))
|
|
|
|
{
|
|
|
|
IDirectSoundBuffer_Play(buffer, 0, 0, DSBPLAY_LOOPING);
|
|
|
|
IDirectSoundBuffer_Release(buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
TRACE("Created DirectSound renderer %p.\n", object);
|
|
|
|
*out = &object->renderer.filter.IUnknown_inner;
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|