2005-05-13 00:22:45 +00:00
|
|
|
/**
|
|
|
|
* dsoundchan.cpp
|
|
|
|
* This file is part of the YATE Project http://YATE.null.ro
|
|
|
|
*
|
|
|
|
* DirectSound channel driver for Windows.
|
|
|
|
*
|
|
|
|
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
2006-05-27 15:08:43 +00:00
|
|
|
* Copyright (C) 2004-2006 Null Team
|
2005-05-13 00:22:45 +00:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program 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 General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
2006-05-27 15:08:43 +00:00
|
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
2005-05-13 00:22:45 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
// define DCOM before including windows.h so advanced COM functions can be compiled
|
|
|
|
#define _WIN32_DCOM
|
|
|
|
|
|
|
|
#include <yatephone.h>
|
|
|
|
|
2005-05-14 14:41:45 +00:00
|
|
|
#ifndef _WINDOWS
|
|
|
|
#error This module is only for Windows
|
|
|
|
#else
|
|
|
|
|
2005-05-13 00:22:45 +00:00
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
// initialize the GUIDs so we don't need to link against dsound.lib
|
|
|
|
#include <initguid.h>
|
|
|
|
#include <dsound.h>
|
|
|
|
|
|
|
|
using namespace TelEngine;
|
2006-05-27 14:53:18 +00:00
|
|
|
namespace { // anonymous
|
2005-05-13 00:22:45 +00:00
|
|
|
|
2005-07-09 18:27:49 +00:00
|
|
|
// we should use the primary sound buffer else we will lose sound while we have no input focus
|
2006-01-26 17:43:02 +00:00
|
|
|
static bool s_primary = true;
|
2005-07-09 18:27:49 +00:00
|
|
|
|
2010-03-31 13:53:44 +00:00
|
|
|
// default sampling rate
|
|
|
|
static int s_rate = 8000;
|
2005-05-14 01:38:58 +00:00
|
|
|
|
|
|
|
class DSoundSource : public DataSource
|
2005-05-13 00:22:45 +00:00
|
|
|
{
|
2005-05-14 01:38:58 +00:00
|
|
|
friend class DSoundRec;
|
2005-05-13 00:22:45 +00:00
|
|
|
public:
|
2010-03-31 13:53:44 +00:00
|
|
|
DSoundSource(int rate = 8000);
|
2005-05-14 01:38:58 +00:00
|
|
|
~DSoundSource();
|
2008-08-04 02:06:00 +00:00
|
|
|
bool control(NamedList& msg);
|
2005-05-13 00:22:45 +00:00
|
|
|
private:
|
2005-05-14 01:38:58 +00:00
|
|
|
DSoundRec* m_dsound;
|
2005-05-13 00:22:45 +00:00
|
|
|
};
|
|
|
|
|
2005-05-14 01:38:58 +00:00
|
|
|
class DSoundConsumer : public DataConsumer
|
2005-05-13 00:22:45 +00:00
|
|
|
{
|
2005-05-14 01:38:58 +00:00
|
|
|
friend class DSoundPlay;
|
2005-05-13 00:22:45 +00:00
|
|
|
public:
|
2010-03-31 13:53:44 +00:00
|
|
|
DSoundConsumer(int rate = 8000, bool stereo = false);
|
2005-05-14 01:38:58 +00:00
|
|
|
~DSoundConsumer();
|
2009-07-02 09:24:33 +00:00
|
|
|
virtual unsigned long Consume(const DataBlock &data, unsigned long tStamp, unsigned long flags);
|
2008-08-04 02:06:00 +00:00
|
|
|
bool control(NamedList& msg);
|
2005-05-13 00:22:45 +00:00
|
|
|
private:
|
2005-05-14 01:38:58 +00:00
|
|
|
DSoundPlay* m_dsound;
|
2009-06-22 16:10:35 +00:00
|
|
|
bool m_stereo;
|
2005-05-13 00:22:45 +00:00
|
|
|
};
|
|
|
|
|
2005-05-14 01:38:58 +00:00
|
|
|
// all DirectSound play related objects are created in this thread's apartment
|
|
|
|
class DSoundPlay : public Thread, public Mutex
|
2005-05-13 00:22:45 +00:00
|
|
|
{
|
|
|
|
public:
|
2010-03-31 13:53:44 +00:00
|
|
|
DSoundPlay(DSoundConsumer* owner, DWORD rate, LPGUID device = 0);
|
2005-05-14 01:38:58 +00:00
|
|
|
virtual ~DSoundPlay();
|
|
|
|
virtual void run();
|
|
|
|
virtual void cleanup();
|
|
|
|
bool init();
|
|
|
|
inline void terminate()
|
|
|
|
{ m_owner = 0; }
|
|
|
|
inline LPDIRECTSOUND dsound() const
|
|
|
|
{ return m_ds; }
|
|
|
|
inline LPDIRECTSOUNDBUFFER buffer() const
|
|
|
|
{ return m_dsb; }
|
|
|
|
void put(const DataBlock& data);
|
2008-08-04 02:06:00 +00:00
|
|
|
bool control(NamedList& msg);
|
2005-05-14 01:38:58 +00:00
|
|
|
private:
|
|
|
|
DSoundConsumer* m_owner;
|
2010-03-31 13:53:44 +00:00
|
|
|
DWORD m_rate;
|
2005-05-14 01:38:58 +00:00
|
|
|
LPGUID m_device;
|
|
|
|
LPDIRECTSOUND m_ds;
|
|
|
|
LPDIRECTSOUNDBUFFER m_dsb;
|
2005-07-09 18:27:49 +00:00
|
|
|
DWORD m_buffSize;
|
2010-03-31 13:53:44 +00:00
|
|
|
DWORD m_chunk;
|
2005-05-14 01:38:58 +00:00
|
|
|
DataBlock m_buf;
|
2006-01-31 16:11:45 +00:00
|
|
|
u_int64_t m_start;
|
|
|
|
u_int64_t m_total;
|
2005-05-14 01:38:58 +00:00
|
|
|
};
|
2005-05-13 00:22:45 +00:00
|
|
|
|
2005-05-14 01:38:58 +00:00
|
|
|
// all DirectSound record related objects are created in this thread's apartment
|
|
|
|
class DSoundRec : public Thread
|
|
|
|
{
|
|
|
|
public:
|
2010-03-31 13:53:44 +00:00
|
|
|
DSoundRec(DSoundSource* owner, DWORD rate, LPGUID device = 0);
|
2005-05-14 01:38:58 +00:00
|
|
|
virtual ~DSoundRec();
|
|
|
|
virtual void run();
|
|
|
|
virtual void cleanup();
|
|
|
|
bool init();
|
|
|
|
inline void terminate()
|
|
|
|
{ m_owner = 0; Thread::msleep(10); }
|
|
|
|
inline LPDIRECTSOUNDCAPTURE dsound() const
|
|
|
|
{ return m_ds; }
|
|
|
|
inline LPDIRECTSOUNDCAPTUREBUFFER buffer() const
|
|
|
|
{ return m_dsb; }
|
2008-08-04 02:06:00 +00:00
|
|
|
bool control(NamedList& msg);
|
2005-05-13 00:22:45 +00:00
|
|
|
private:
|
2005-05-14 01:38:58 +00:00
|
|
|
DSoundSource* m_owner;
|
2010-03-31 13:53:44 +00:00
|
|
|
DWORD m_rate;
|
2005-05-14 01:38:58 +00:00
|
|
|
LPGUID m_device;
|
|
|
|
LPDIRECTSOUNDCAPTURE m_ds;
|
|
|
|
LPDIRECTSOUNDCAPTUREBUFFER m_dsb;
|
2005-07-09 18:27:49 +00:00
|
|
|
DWORD m_buffSize;
|
|
|
|
DWORD m_readPos;
|
2006-01-31 16:11:45 +00:00
|
|
|
u_int64_t m_start;
|
|
|
|
u_int64_t m_total;
|
2008-08-04 02:06:00 +00:00
|
|
|
int m_rshift;
|
2005-05-13 00:22:45 +00:00
|
|
|
};
|
|
|
|
|
2005-05-14 01:38:58 +00:00
|
|
|
class DSoundChan : public Channel
|
2005-05-13 00:22:45 +00:00
|
|
|
{
|
|
|
|
public:
|
2010-03-31 13:53:44 +00:00
|
|
|
DSoundChan(int rate = 8000);
|
2005-05-14 01:38:58 +00:00
|
|
|
virtual ~DSoundChan();
|
2005-05-13 00:22:45 +00:00
|
|
|
};
|
|
|
|
|
2012-06-12 23:47:01 +00:00
|
|
|
class AttachHandler;
|
2005-05-13 00:22:45 +00:00
|
|
|
|
|
|
|
class SoundDriver : public Driver
|
|
|
|
{
|
2005-05-14 01:38:58 +00:00
|
|
|
friend class DSoundPlay;
|
|
|
|
friend class DSoundRec;
|
2005-05-13 00:22:45 +00:00
|
|
|
public:
|
|
|
|
SoundDriver();
|
|
|
|
~SoundDriver();
|
|
|
|
virtual void initialize();
|
|
|
|
virtual bool msgExecute(Message& msg, String& dest);
|
|
|
|
private:
|
|
|
|
AttachHandler* m_handler;
|
|
|
|
};
|
|
|
|
|
2008-08-04 02:06:00 +00:00
|
|
|
|
2005-05-13 00:22:45 +00:00
|
|
|
INIT_PLUGIN(SoundDriver);
|
|
|
|
|
2012-06-12 23:47:01 +00:00
|
|
|
class AttachHandler : public MessageHandler
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
AttachHandler()
|
|
|
|
: MessageHandler("chan.attach",100,__plugin.name())
|
|
|
|
{ }
|
|
|
|
virtual bool received(Message &msg);
|
|
|
|
};
|
|
|
|
|
2008-08-04 02:06:00 +00:00
|
|
|
|
2010-03-31 13:53:44 +00:00
|
|
|
DSoundPlay::DSoundPlay(DSoundConsumer* owner, DWORD rate, LPGUID device)
|
2009-05-05 14:06:39 +00:00
|
|
|
: Thread("DirectSound Play",High), Mutex(false,"DSoundPlay"),
|
2010-03-31 13:53:44 +00:00
|
|
|
m_owner(0), m_rate(rate), m_device(device), m_ds(0), m_dsb(0),
|
|
|
|
m_buffSize(0), m_chunk(320), m_start(0), m_total(0)
|
2005-05-13 00:22:45 +00:00
|
|
|
{
|
2009-01-30 08:37:21 +00:00
|
|
|
if (owner && owner->ref())
|
|
|
|
m_owner = owner;
|
2005-05-13 00:22:45 +00:00
|
|
|
}
|
|
|
|
|
2005-05-14 01:38:58 +00:00
|
|
|
DSoundPlay::~DSoundPlay()
|
2005-05-13 00:22:45 +00:00
|
|
|
{
|
2006-01-31 16:11:45 +00:00
|
|
|
if (m_start && m_total) {
|
|
|
|
unsigned int rate = (unsigned int)(m_total * 1000000 / (Time::now() - m_start));
|
|
|
|
Debug(&__plugin,DebugInfo,"DSoundPlay transferred %u bytes/s, total " FMT64U,rate,m_total);
|
|
|
|
}
|
2005-05-13 00:22:45 +00:00
|
|
|
}
|
|
|
|
|
2005-05-14 01:38:58 +00:00
|
|
|
bool DSoundPlay::init()
|
2005-05-13 00:22:45 +00:00
|
|
|
{
|
|
|
|
HRESULT hr;
|
|
|
|
if (FAILED(hr = ::CoInitializeEx(NULL,COINIT_MULTITHREADED))) {
|
|
|
|
Debug(DebugGoOn,"Could not initialize the COM library, code 0x%X",hr);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (FAILED(hr = ::CoCreateInstance(CLSID_DirectSound, NULL, CLSCTX_INPROC_SERVER,
|
|
|
|
IID_IDirectSound, (void**)&m_ds)) || !m_ds) {
|
|
|
|
Debug(DebugGoOn,"Could not create the DirectSound object, code 0x%X",hr);
|
|
|
|
return false;
|
|
|
|
}
|
2005-05-14 01:38:58 +00:00
|
|
|
if (FAILED(hr = m_ds->Initialize(m_device))) {
|
2005-05-13 00:22:45 +00:00
|
|
|
Debug(DebugGoOn,"Could not initialize the DirectSound object, code 0x%X",hr);
|
|
|
|
return false;
|
|
|
|
}
|
2005-05-14 01:38:58 +00:00
|
|
|
HWND wnd = GetForegroundWindow();
|
|
|
|
if (!wnd)
|
|
|
|
wnd = GetDesktopWindow();
|
2006-01-26 17:43:02 +00:00
|
|
|
if (FAILED(hr = m_ds->SetCooperativeLevel(wnd,s_primary ? DSSCL_WRITEPRIMARY : DSSCL_EXCLUSIVE))) {
|
2005-05-14 01:38:58 +00:00
|
|
|
Debug(DebugGoOn,"Could not set the DirectSound cooperative level, code 0x%X",hr);
|
|
|
|
return false;
|
|
|
|
}
|
2009-06-22 16:10:35 +00:00
|
|
|
|
|
|
|
// Set channel number depending data
|
|
|
|
WORD nChannels = 1;
|
2010-03-31 13:53:44 +00:00
|
|
|
DWORD nAvgBytesPerSec = 2 * m_rate; // nSamplesPerSec * nBlockAlign.
|
2009-06-22 16:10:35 +00:00
|
|
|
WORD nBlockAlign = 2; // nChannels * wBitsPerSample / 8
|
|
|
|
if (m_owner && m_owner->m_stereo) {
|
|
|
|
nChannels = 2;
|
2010-03-31 13:53:44 +00:00
|
|
|
nAvgBytesPerSec = 4 * m_rate;
|
2009-06-22 16:10:35 +00:00
|
|
|
nBlockAlign = 4;
|
|
|
|
}
|
2010-03-31 13:53:44 +00:00
|
|
|
m_chunk = nChannels * m_rate / 25; // 20ms of 2-byte samples
|
2009-06-22 16:10:35 +00:00
|
|
|
|
2005-05-14 01:38:58 +00:00
|
|
|
WAVEFORMATEX fmt;
|
|
|
|
fmt.wFormatTag = WAVE_FORMAT_PCM;
|
2009-06-22 16:10:35 +00:00
|
|
|
fmt.nChannels = nChannels;
|
2010-03-31 13:53:44 +00:00
|
|
|
fmt.nSamplesPerSec = m_rate;
|
2009-06-22 16:10:35 +00:00
|
|
|
fmt.nAvgBytesPerSec = nAvgBytesPerSec;
|
|
|
|
fmt.nBlockAlign = nBlockAlign;
|
2005-05-14 01:38:58 +00:00
|
|
|
fmt.wBitsPerSample = 16;
|
|
|
|
fmt.cbSize = 0;
|
2005-07-09 18:27:49 +00:00
|
|
|
DSBUFFERDESC bdesc;
|
|
|
|
ZeroMemory(&bdesc, sizeof(bdesc));
|
|
|
|
bdesc.dwSize = sizeof(bdesc);
|
2008-08-04 02:06:00 +00:00
|
|
|
bdesc.dwFlags = DSBCAPS_CTRLVOLUME;
|
2006-01-26 17:43:02 +00:00
|
|
|
if (s_primary)
|
2008-08-04 02:06:00 +00:00
|
|
|
bdesc.dwFlags |= DSBCAPS_PRIMARYBUFFER | DSBCAPS_STICKYFOCUS;
|
2006-01-26 17:43:02 +00:00
|
|
|
else {
|
2008-08-04 02:06:00 +00:00
|
|
|
bdesc.dwFlags |= DSBCAPS_GLOBALFOCUS;
|
2006-01-26 17:43:02 +00:00
|
|
|
// we have to set format when creating secondary buffers
|
2010-03-31 13:53:44 +00:00
|
|
|
bdesc.dwBufferBytes = 4*m_chunk;
|
2006-01-26 17:43:02 +00:00
|
|
|
bdesc.lpwfxFormat = &fmt;
|
|
|
|
}
|
2005-07-09 18:27:49 +00:00
|
|
|
if (FAILED(hr = m_ds->CreateSoundBuffer(&bdesc, &m_dsb, NULL)) || !m_dsb) {
|
|
|
|
Debug(DebugGoOn,"Could not create the DirectSound buffer, code 0x%X",hr);
|
|
|
|
return false;
|
|
|
|
}
|
2006-01-26 17:43:02 +00:00
|
|
|
// format can be changed only for primary buffers
|
|
|
|
if (s_primary && FAILED(hr = m_dsb->SetFormat(&fmt))) {
|
2005-05-14 01:38:58 +00:00
|
|
|
Debug(DebugGoOn,"Could not set the DirectSound buffer format, code 0x%X",hr);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (FAILED(hr = m_dsb->GetFormat(&fmt,sizeof(fmt),0))) {
|
|
|
|
Debug(DebugGoOn,"Could not get the DirectSound buffer format, code 0x%X",hr);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if ((fmt.wFormatTag != WAVE_FORMAT_PCM) ||
|
2009-06-22 16:10:35 +00:00
|
|
|
(fmt.nChannels != nChannels) ||
|
2010-03-31 13:53:44 +00:00
|
|
|
(fmt.nSamplesPerSec != m_rate) ||
|
2005-05-14 01:38:58 +00:00
|
|
|
(fmt.wBitsPerSample != 16)) {
|
2010-03-31 13:53:44 +00:00
|
|
|
Debug(DebugGoOn,"DirectSound does not support %dHz 16bit %s PCM format, "
|
|
|
|
"got fmt=%u, chans=%d samp=%d size=%u",m_rate,nChannels == 1 ? "mono" : "stereo",
|
2005-05-14 01:38:58 +00:00
|
|
|
fmt.wFormatTag,fmt.nChannels,fmt.nSamplesPerSec,fmt.wBitsPerSample);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
DSBCAPS caps;
|
|
|
|
caps.dwSize = sizeof(caps);
|
2005-07-09 18:27:49 +00:00
|
|
|
if (FAILED(hr = m_dsb->GetCaps(&caps))) {
|
|
|
|
Debug(DebugGoOn,"Could not get the DirectSound buffer capabilities, code 0x%X",hr);
|
2005-05-14 01:38:58 +00:00
|
|
|
return false;
|
|
|
|
}
|
2005-07-09 18:27:49 +00:00
|
|
|
m_buffSize = caps.dwBufferBytes;
|
|
|
|
Debug(&__plugin,DebugInfo,"DirectSound buffer size %u",m_buffSize);
|
|
|
|
if (FAILED(hr = m_dsb->Play(0,0,DSBPLAY_LOOPING))) {
|
|
|
|
if ((hr != DSERR_BUFFERLOST) || FAILED(hr = m_dsb->Restore())) {
|
|
|
|
Debug(DebugGoOn,"Could not play the DirectSound buffer, code 0x%X",hr);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
m_dsb->Play(0,0,DSBPLAY_LOOPING);
|
|
|
|
}
|
2005-05-14 01:38:58 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void DSoundPlay::run()
|
|
|
|
{
|
|
|
|
if (!init())
|
|
|
|
return;
|
2009-01-30 08:37:21 +00:00
|
|
|
Debug(&__plugin,DebugInfo,"DSoundPlay is initialized and running");
|
2005-05-14 01:38:58 +00:00
|
|
|
if (m_owner)
|
|
|
|
m_owner->m_dsound = this;
|
2009-01-30 08:37:21 +00:00
|
|
|
else
|
|
|
|
return;
|
2006-01-31 19:34:07 +00:00
|
|
|
DWORD writeOffs = 0;
|
2010-03-31 13:53:44 +00:00
|
|
|
DWORD margin = m_chunk/4;
|
2005-07-09 18:27:49 +00:00
|
|
|
bool first = true;
|
2009-01-30 08:37:21 +00:00
|
|
|
while (m_owner->refcount() > 1) {
|
2005-05-14 01:38:58 +00:00
|
|
|
msleep(1,true);
|
2005-07-09 18:27:49 +00:00
|
|
|
if (first) {
|
2010-03-31 13:53:44 +00:00
|
|
|
if ((m_buf.length() < 2*m_chunk) || !m_dsb)
|
2005-07-09 18:27:49 +00:00
|
|
|
continue;
|
|
|
|
first = false;
|
2006-01-31 19:34:07 +00:00
|
|
|
m_dsb->GetCurrentPosition(NULL,&writeOffs);
|
2008-09-29 16:54:39 +00:00
|
|
|
writeOffs = (margin + writeOffs) % m_buffSize;
|
2005-07-09 18:27:49 +00:00
|
|
|
Debug(&__plugin,DebugAll,"DSoundPlay has %u in buffer and starts playing at %u",
|
2006-01-31 19:34:07 +00:00
|
|
|
m_buf.length(),writeOffs);
|
2006-01-31 16:11:45 +00:00
|
|
|
m_start = Time::now();
|
2005-07-09 18:27:49 +00:00
|
|
|
}
|
2008-09-29 16:54:39 +00:00
|
|
|
while (m_dsb) {
|
2006-01-31 19:34:07 +00:00
|
|
|
DWORD playPos = 0;
|
|
|
|
DWORD writePos = 0;
|
|
|
|
bool adjust = false;
|
|
|
|
// check if we slipped behind and advance our pointer if so
|
|
|
|
if (SUCCEEDED(m_dsb->GetCurrentPosition(&playPos,&writePos))) {
|
|
|
|
if (playPos < writePos)
|
|
|
|
// not wrapped - have to adjust if our pointer falls between play and write
|
|
|
|
adjust = (playPos < writeOffs) && (writeOffs < writePos);
|
|
|
|
else
|
|
|
|
// only write offset has wrapped - adjust if we are outside
|
|
|
|
adjust = (writeOffs < writePos) || (playPos <= writeOffs) ;
|
|
|
|
}
|
|
|
|
if (adjust) {
|
2008-09-29 16:54:39 +00:00
|
|
|
DWORD adjOffs = (margin + writePos) % m_buffSize;
|
|
|
|
Debug(&__plugin,DebugNote,"Slip detected, changing write offs from %u to %u, p=%u w=%u",
|
2006-01-31 19:34:07 +00:00
|
|
|
writeOffs,adjOffs,playPos,writePos);
|
|
|
|
writeOffs = adjOffs;
|
|
|
|
}
|
2010-03-31 13:53:44 +00:00
|
|
|
bool hasData = (m_buf.length() >= m_chunk);
|
2008-09-29 16:54:39 +00:00
|
|
|
if (!(adjust || hasData)) {
|
|
|
|
// don't fill the buffer if we still have at least one chunk until underflow
|
2010-03-31 13:53:44 +00:00
|
|
|
if ((m_buffSize + writeOffs - writePos) % m_buffSize >= m_chunk)
|
2008-09-29 16:54:39 +00:00
|
|
|
break;
|
|
|
|
}
|
2005-05-14 01:38:58 +00:00
|
|
|
void* buf = 0;
|
|
|
|
void* buf2 = 0;
|
|
|
|
DWORD len = 0;
|
|
|
|
DWORD len2 = 0;
|
2006-01-31 19:34:07 +00:00
|
|
|
// locking will prevent us to skip ahead and overwrite the play position
|
2010-03-31 13:53:44 +00:00
|
|
|
HRESULT hr = m_dsb->Lock(writeOffs,m_chunk,&buf,&len,&buf2,&len2,0);
|
2005-05-14 01:38:58 +00:00
|
|
|
if (FAILED(hr)) {
|
2006-01-31 19:34:07 +00:00
|
|
|
writeOffs = 0;
|
2005-05-14 01:38:58 +00:00
|
|
|
if ((hr == DSERR_BUFFERLOST) && SUCCEEDED(m_dsb->Restore())) {
|
|
|
|
m_dsb->Play(0,0,DSBPLAY_LOOPING);
|
2006-01-31 19:34:07 +00:00
|
|
|
m_dsb->GetCurrentPosition(NULL,&writeOffs);
|
2010-03-31 13:53:44 +00:00
|
|
|
writeOffs = (margin + writeOffs) % m_buffSize;
|
2005-07-09 18:27:49 +00:00
|
|
|
Debug(&__plugin,DebugAll,"DirectSound buffer lost and restored, playing at %u",
|
2006-01-31 19:34:07 +00:00
|
|
|
writeOffs);
|
2005-07-09 18:27:49 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
lock();
|
|
|
|
m_buf.clear();
|
|
|
|
unlock();
|
2005-05-14 01:38:58 +00:00
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
lock();
|
2008-09-29 16:54:39 +00:00
|
|
|
if (hasData) {
|
|
|
|
::memcpy(buf,m_buf.data(),len);
|
|
|
|
if (buf2)
|
|
|
|
::memcpy(buf2,((const char*)m_buf.data())+len,len2);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
::memset(buf,0,len);
|
|
|
|
if (buf2)
|
|
|
|
::memset(buf2,0,len2);
|
|
|
|
}
|
2005-07-09 18:27:49 +00:00
|
|
|
m_dsb->Unlock(buf,len,buf2,len2);
|
2010-03-31 13:53:44 +00:00
|
|
|
m_total += m_chunk;
|
|
|
|
m_buf.cut(-(int)m_chunk);
|
2008-08-04 02:06:00 +00:00
|
|
|
unlock();
|
2008-09-29 16:54:39 +00:00
|
|
|
#ifdef DEBUG
|
|
|
|
if (!hasData)
|
|
|
|
Debug(&__plugin,DebugInfo,"Underflow, filled %u bytes at %u, p=%u w=%u",
|
2010-03-31 13:53:44 +00:00
|
|
|
m_chunk,writeOffs,playPos,writePos);
|
2008-09-29 16:54:39 +00:00
|
|
|
#endif
|
2010-03-31 13:53:44 +00:00
|
|
|
writeOffs += m_chunk;
|
2008-09-29 16:54:39 +00:00
|
|
|
if (writeOffs >= m_buffSize)
|
|
|
|
writeOffs -= m_buffSize;
|
2005-07-09 18:27:49 +00:00
|
|
|
XDebug(&__plugin,DebugAll,"Locked %p,%d %p,%d",buf,len,buf2,len2);
|
2005-05-14 01:38:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-08-04 02:06:00 +00:00
|
|
|
bool DSoundPlay::control(NamedList& msg)
|
|
|
|
{
|
|
|
|
bool ok = false;
|
|
|
|
LONG val = 0;
|
|
|
|
int outValue = msg.getIntValue("out_volume",-1);
|
|
|
|
HRESULT hr; // we need it for debugging
|
|
|
|
if ((outValue >= 0) && (outValue <= 100)) {
|
|
|
|
// convert 0...100 to 0...-50.00 dB
|
|
|
|
val = (outValue - 100) * 50;
|
|
|
|
ok = ((hr = m_dsb->SetVolume(val)) == DS_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((hr = m_dsb->GetVolume(&val)) == DS_OK) {
|
|
|
|
// convert back 0...-50.0 dB to 0...100, watch out for values up to -100.00 dB
|
|
|
|
outValue = (5000 + val) / 50;
|
|
|
|
if (outValue < 0)
|
|
|
|
outValue = 0;
|
|
|
|
msg.setParam("out_volume", String(outValue));
|
|
|
|
}
|
2013-02-14 14:48:07 +00:00
|
|
|
return TelEngine::controlReturn(&msg,ok);
|
2008-08-04 02:06:00 +00:00
|
|
|
}
|
|
|
|
|
2005-05-14 01:38:58 +00:00
|
|
|
void DSoundPlay::cleanup()
|
|
|
|
{
|
|
|
|
Debug(DebugInfo,"DSoundPlay cleaning up");
|
2009-01-30 08:37:21 +00:00
|
|
|
if (m_owner) {
|
|
|
|
m_owner->m_dsound = 0;
|
|
|
|
if (m_owner->refcount() > 1)
|
|
|
|
Debug(&__plugin,DebugWarn,"DSoundPlay destroyed while consumer is still active");
|
|
|
|
TelEngine::destruct(m_owner);
|
|
|
|
}
|
2005-05-14 01:38:58 +00:00
|
|
|
if (m_dsb) {
|
|
|
|
m_dsb->Stop();
|
|
|
|
m_dsb->Release();
|
|
|
|
m_dsb = 0;
|
|
|
|
}
|
|
|
|
if (m_ds) {
|
|
|
|
m_ds->Release();
|
|
|
|
m_ds = 0;
|
|
|
|
}
|
|
|
|
::CoUninitialize();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DSoundPlay::put(const DataBlock& data)
|
|
|
|
{
|
|
|
|
if (!m_dsb)
|
|
|
|
return;
|
|
|
|
lock();
|
2010-03-31 13:53:44 +00:00
|
|
|
if (m_buf.length() + data.length() <= m_buffSize + m_chunk)
|
2005-05-14 01:38:58 +00:00
|
|
|
m_buf += data;
|
|
|
|
else
|
2005-07-09 18:27:49 +00:00
|
|
|
Debug(&__plugin,DebugMild,"DSoundPlay skipped %u bytes, buffer is full",data.length());
|
2005-05-14 01:38:58 +00:00
|
|
|
unlock();
|
|
|
|
}
|
|
|
|
|
2008-08-04 02:06:00 +00:00
|
|
|
|
2010-03-31 13:53:44 +00:00
|
|
|
DSoundRec::DSoundRec(DSoundSource* owner, DWORD rate, LPGUID device)
|
2006-01-26 17:43:02 +00:00
|
|
|
: Thread("DirectSound Rec",High),
|
2010-03-31 13:53:44 +00:00
|
|
|
m_owner(0), m_rate(rate), m_device(device), m_ds(0), m_dsb(0),
|
2008-08-04 02:06:00 +00:00
|
|
|
m_buffSize(0), m_readPos(0), m_start(0), m_total(0), m_rshift(0)
|
2005-05-14 01:38:58 +00:00
|
|
|
{
|
2009-01-30 08:37:21 +00:00
|
|
|
if (owner && owner->ref())
|
|
|
|
m_owner = owner;
|
2005-05-14 01:38:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
DSoundRec::~DSoundRec()
|
|
|
|
{
|
2006-01-31 16:11:45 +00:00
|
|
|
if (m_start && m_total) {
|
|
|
|
unsigned int rate = (unsigned int)(m_total * 1000000 / (Time::now() - m_start));
|
|
|
|
Debug(&__plugin,DebugInfo,"DSoundRec transferred %u bytes/s, total " FMT64U,rate,m_total);
|
|
|
|
}
|
2005-05-14 01:38:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool DSoundRec::init()
|
|
|
|
{
|
|
|
|
HRESULT hr;
|
|
|
|
if (FAILED(hr = ::CoInitializeEx(NULL,COINIT_MULTITHREADED))) {
|
|
|
|
Debug(DebugGoOn,"Could not initialize the COM library, code 0x%X",hr);
|
|
|
|
return false;
|
|
|
|
}
|
2005-05-13 00:22:45 +00:00
|
|
|
if (FAILED(hr = ::CoCreateInstance(CLSID_DirectSoundCapture, NULL, CLSCTX_INPROC_SERVER,
|
2005-05-14 01:38:58 +00:00
|
|
|
IID_IDirectSoundCapture, (void**)&m_ds)) || !m_ds) {
|
2005-05-13 00:22:45 +00:00
|
|
|
Debug(DebugGoOn,"Could not create the DirectSoundCapture object, code 0x%X",hr);
|
|
|
|
return false;
|
|
|
|
}
|
2005-05-14 01:38:58 +00:00
|
|
|
if (FAILED(hr = m_ds->Initialize(m_device))) {
|
2005-05-13 00:22:45 +00:00
|
|
|
Debug(DebugGoOn,"Could not initialize the DirectSoundCapture object, code 0x%X",hr);
|
|
|
|
return false;
|
|
|
|
}
|
2005-05-14 01:38:58 +00:00
|
|
|
WAVEFORMATEX fmt;
|
|
|
|
fmt.wFormatTag = WAVE_FORMAT_PCM;
|
|
|
|
fmt.nChannels = 1;
|
2010-03-31 13:53:44 +00:00
|
|
|
fmt.nSamplesPerSec = m_rate;
|
|
|
|
fmt.nAvgBytesPerSec = 2 * m_rate;
|
2005-05-14 01:38:58 +00:00
|
|
|
fmt.nBlockAlign = 2;
|
|
|
|
fmt.wBitsPerSample = 16;
|
|
|
|
fmt.cbSize = 0;
|
|
|
|
DSCBUFFERDESC bdesc;
|
|
|
|
ZeroMemory(&bdesc, sizeof(bdesc));
|
|
|
|
bdesc.dwSize = sizeof(bdesc);
|
|
|
|
bdesc.dwFlags = DSCBCAPS_WAVEMAPPED;
|
2010-03-31 13:53:44 +00:00
|
|
|
bdesc.dwBufferBytes = 4 * m_rate / 25;
|
2005-05-14 01:38:58 +00:00
|
|
|
bdesc.lpwfxFormat = &fmt;
|
|
|
|
if (FAILED(hr = m_ds->CreateCaptureBuffer(&bdesc, &m_dsb, NULL)) || !m_dsb) {
|
|
|
|
Debug(DebugGoOn,"Could not create the DirectSoundCapture buffer, code 0x%X",hr);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (FAILED(hr = m_dsb->GetFormat(&fmt,sizeof(fmt),0))) {
|
|
|
|
Debug(DebugGoOn,"Could not get the DirectSoundCapture buffer format, code 0x%X",hr);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if ((fmt.wFormatTag != WAVE_FORMAT_PCM) ||
|
|
|
|
(fmt.nChannels != 1) ||
|
2010-03-31 13:53:44 +00:00
|
|
|
(fmt.nSamplesPerSec != m_rate) ||
|
2005-05-14 01:38:58 +00:00
|
|
|
(fmt.wBitsPerSample != 16)) {
|
2010-03-31 13:53:44 +00:00
|
|
|
Debug(DebugGoOn,"DirectSoundCapture does not support %dHz 16bit mono PCM format, "
|
|
|
|
"got fmt=%u, chans=%d samp=%d size=%u",m_rate,
|
2005-05-14 01:38:58 +00:00
|
|
|
fmt.wFormatTag,fmt.nChannels,fmt.nSamplesPerSec,fmt.wBitsPerSample);
|
|
|
|
return false;
|
|
|
|
}
|
2005-07-09 18:27:49 +00:00
|
|
|
DSCBCAPS caps;
|
|
|
|
caps.dwSize = sizeof(caps);
|
|
|
|
if (FAILED(hr = m_dsb->GetCaps(&caps))) {
|
|
|
|
Debug(DebugGoOn,"Could not get the DirectSoundCapture buffer capabilities, code 0x%X",hr);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
m_buffSize = caps.dwBufferBytes;
|
|
|
|
Debug(&__plugin,DebugInfo,"DirectSoundCapture buffer size %u",m_buffSize);
|
2005-05-14 01:38:58 +00:00
|
|
|
if (FAILED(hr = m_dsb->Start(DSCBSTART_LOOPING))) {
|
|
|
|
Debug(DebugGoOn,"Could not record to the DirectSoundCapture buffer, code 0x%X",hr);
|
|
|
|
return false;
|
|
|
|
}
|
2005-05-13 00:22:45 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2005-05-14 01:38:58 +00:00
|
|
|
void DSoundRec::run()
|
2005-05-13 00:22:45 +00:00
|
|
|
{
|
|
|
|
if (!init())
|
|
|
|
return;
|
2005-07-09 18:27:49 +00:00
|
|
|
Debug(&__plugin,DebugInfo,"DSoundRec is initialized and running");
|
2010-03-31 13:53:44 +00:00
|
|
|
DWORD chunk = m_rate / 25; // 20ms of 2-byte samples
|
2006-01-31 16:11:45 +00:00
|
|
|
m_start = Time::now();
|
2009-01-30 08:37:21 +00:00
|
|
|
if (m_owner)
|
|
|
|
m_owner->m_dsound = this;
|
|
|
|
else
|
|
|
|
return;
|
|
|
|
while (m_owner->refcount() > 1) {
|
2005-05-13 00:22:45 +00:00
|
|
|
msleep(1,true);
|
2005-05-14 01:38:58 +00:00
|
|
|
if (m_dsb) {
|
2005-07-09 18:27:49 +00:00
|
|
|
DWORD pos = 0;
|
|
|
|
if (FAILED(m_dsb->GetCurrentPosition(0,&pos)))
|
|
|
|
continue;
|
|
|
|
if (pos < m_readPos)
|
|
|
|
pos += m_buffSize;
|
|
|
|
pos -= m_readPos;
|
2010-03-31 13:53:44 +00:00
|
|
|
if (pos < chunk)
|
2005-07-09 18:27:49 +00:00
|
|
|
continue;
|
2005-05-14 01:38:58 +00:00
|
|
|
void* buf = 0;
|
2005-07-09 18:27:49 +00:00
|
|
|
void* buf2 = 0;
|
2005-05-14 01:38:58 +00:00
|
|
|
DWORD len = 0;
|
2005-07-09 18:27:49 +00:00
|
|
|
DWORD len2 = 0;
|
2010-03-31 13:53:44 +00:00
|
|
|
if (FAILED(m_dsb->Lock(m_readPos,chunk,&buf,&len,&buf2,&len2,0)))
|
2005-05-14 01:38:58 +00:00
|
|
|
continue;
|
2005-07-09 18:27:49 +00:00
|
|
|
DataBlock data(0,len+len2);
|
|
|
|
::memcpy(data.data(),buf,len);
|
|
|
|
if (buf2)
|
|
|
|
::memcpy(((char*)data.data())+len,buf2,len2);
|
|
|
|
m_dsb->Unlock(buf,len,buf2,len2);
|
2006-01-31 16:11:45 +00:00
|
|
|
m_total += (len+len2);
|
2005-07-09 18:27:49 +00:00
|
|
|
m_readPos += (len+len2);
|
|
|
|
if (m_readPos >= m_buffSize)
|
|
|
|
m_readPos -= m_buffSize;
|
2008-08-04 02:06:00 +00:00
|
|
|
if (m_rshift) {
|
|
|
|
// apply volume attenuation
|
|
|
|
signed short* s = (signed short*)data.data();
|
|
|
|
for (unsigned int n = data.length() / 2; n--; s++)
|
|
|
|
*s >>= m_rshift;
|
|
|
|
}
|
2005-05-14 01:38:58 +00:00
|
|
|
if (m_owner)
|
|
|
|
m_owner->Forward(data);
|
|
|
|
}
|
2005-05-13 00:22:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-05-14 01:38:58 +00:00
|
|
|
void DSoundRec::cleanup()
|
2005-05-13 00:22:45 +00:00
|
|
|
{
|
2005-07-09 18:27:49 +00:00
|
|
|
Debug(&__plugin,DebugInfo,"DSoundRec cleaning up");
|
2009-01-30 08:37:21 +00:00
|
|
|
if (m_owner) {
|
|
|
|
m_owner->m_dsound = 0;
|
|
|
|
if (m_owner->refcount() > 1)
|
|
|
|
Debug(&__plugin,DebugWarn,"DSoundRec destroyed while source is still active");
|
|
|
|
TelEngine::destruct(m_owner);
|
|
|
|
}
|
2005-05-14 01:38:58 +00:00
|
|
|
if (m_dsb) {
|
|
|
|
m_dsb->Stop();
|
|
|
|
m_dsb->Release();
|
|
|
|
m_dsb = 0;
|
2005-05-13 00:22:45 +00:00
|
|
|
}
|
|
|
|
if (m_ds) {
|
|
|
|
m_ds->Release();
|
|
|
|
m_ds = 0;
|
|
|
|
}
|
|
|
|
::CoUninitialize();
|
|
|
|
}
|
|
|
|
|
2008-08-04 02:06:00 +00:00
|
|
|
bool DSoundRec::control(TelEngine::NamedList &msg)
|
|
|
|
{
|
|
|
|
bool ok = false;
|
|
|
|
int inValue = msg.getIntValue("in_volume",-1);
|
|
|
|
if ((inValue >= 0) && (inValue <= 100)) {
|
|
|
|
// convert 0...100 to a 10...0 right shift count
|
|
|
|
m_rshift = (105 - inValue) / 10;
|
|
|
|
ok = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
inValue = (10 - m_rshift) * 10;
|
|
|
|
msg.setParam("in_volume", String(inValue));
|
2013-02-14 14:48:07 +00:00
|
|
|
return TelEngine::controlReturn(&msg,ok);
|
2008-08-04 02:06:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-03-31 13:53:44 +00:00
|
|
|
DSoundSource::DSoundSource(int rate)
|
2005-05-14 01:38:58 +00:00
|
|
|
: m_dsound(0)
|
|
|
|
{
|
2010-03-31 13:53:44 +00:00
|
|
|
if (rate != 8000)
|
|
|
|
m_format << "/" << rate;
|
|
|
|
DSoundRec* ds = new DSoundRec(this,rate);
|
2005-05-14 01:38:58 +00:00
|
|
|
ds->startup();
|
|
|
|
}
|
|
|
|
|
|
|
|
DSoundSource::~DSoundSource()
|
|
|
|
{
|
|
|
|
if (m_dsound)
|
|
|
|
m_dsound->terminate();
|
|
|
|
}
|
|
|
|
|
2008-08-04 02:06:00 +00:00
|
|
|
bool DSoundSource::control(NamedList& msg)
|
|
|
|
{
|
|
|
|
if (m_dsound)
|
|
|
|
return m_dsound->control(msg);
|
2013-02-14 14:48:07 +00:00
|
|
|
return TelEngine::controlReturn(&msg,false);
|
2008-08-04 02:06:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-03-31 13:53:44 +00:00
|
|
|
DSoundConsumer::DSoundConsumer(int rate, bool stereo)
|
2009-06-22 16:10:35 +00:00
|
|
|
: DataConsumer(stereo ? "2*slin" : "slin"),
|
|
|
|
m_dsound(0), m_stereo(stereo)
|
2005-05-14 01:38:58 +00:00
|
|
|
{
|
2010-03-31 13:53:44 +00:00
|
|
|
if (rate != 8000)
|
|
|
|
m_format << "/" << rate;
|
|
|
|
DSoundPlay* ds = new DSoundPlay(this,rate);
|
2005-05-14 01:38:58 +00:00
|
|
|
ds->startup();
|
|
|
|
}
|
|
|
|
|
|
|
|
DSoundConsumer::~DSoundConsumer()
|
|
|
|
{
|
|
|
|
if (m_dsound)
|
|
|
|
m_dsound->terminate();
|
|
|
|
}
|
|
|
|
|
2009-07-02 09:24:33 +00:00
|
|
|
unsigned long DSoundConsumer::Consume(const DataBlock &data, unsigned long tStamp, unsigned long flags)
|
2005-05-14 01:38:58 +00:00
|
|
|
{
|
2009-07-02 09:24:33 +00:00
|
|
|
if (m_dsound) {
|
2005-05-14 01:38:58 +00:00
|
|
|
m_dsound->put(data);
|
2009-07-02 09:24:33 +00:00
|
|
|
return invalidStamp();
|
|
|
|
}
|
|
|
|
return 0;
|
2005-05-14 01:38:58 +00:00
|
|
|
}
|
|
|
|
|
2008-08-04 02:06:00 +00:00
|
|
|
bool DSoundConsumer::control(NamedList& msg)
|
|
|
|
{
|
|
|
|
if (m_dsound)
|
|
|
|
return m_dsound->control(msg);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-03-31 13:53:44 +00:00
|
|
|
DSoundChan::DSoundChan(int rate)
|
2005-05-13 00:22:45 +00:00
|
|
|
: Channel(__plugin)
|
|
|
|
{
|
2010-03-31 13:53:44 +00:00
|
|
|
Debug(this,DebugAll,"DSoundChan::DSoundChan(%d) [%p]",rate,this);
|
2005-05-14 01:38:58 +00:00
|
|
|
|
2010-03-31 13:53:44 +00:00
|
|
|
setConsumer(new DSoundConsumer(rate));
|
2005-05-14 01:38:58 +00:00
|
|
|
getConsumer()->deref();
|
|
|
|
Thread::msleep(50);
|
2010-03-31 13:53:44 +00:00
|
|
|
setSource(new DSoundSource(rate));
|
2005-07-09 18:27:49 +00:00
|
|
|
getSource()->deref();
|
|
|
|
Thread::msleep(50);
|
2005-05-13 00:22:45 +00:00
|
|
|
}
|
|
|
|
|
2005-05-14 01:38:58 +00:00
|
|
|
DSoundChan::~DSoundChan()
|
2005-05-13 00:22:45 +00:00
|
|
|
{
|
2005-07-09 18:27:49 +00:00
|
|
|
Debug(this,DebugAll,"DSoundChan::~DSoundChan() [%p]",this);
|
2005-05-13 00:22:45 +00:00
|
|
|
}
|
|
|
|
|
2008-08-04 02:06:00 +00:00
|
|
|
|
2005-05-13 00:22:45 +00:00
|
|
|
bool AttachHandler::received(Message &msg)
|
|
|
|
{
|
2005-07-08 14:51:06 +00:00
|
|
|
int more = 2;
|
|
|
|
|
|
|
|
String src(msg.getValue("source"));
|
|
|
|
if (src.null())
|
|
|
|
more--;
|
|
|
|
else if (!src.startSkip("dsound/",false))
|
|
|
|
src = "";
|
|
|
|
|
|
|
|
String cons(msg.getValue("consumer"));
|
|
|
|
if (cons.null())
|
|
|
|
more--;
|
|
|
|
else if (!cons.startSkip("dsound/",false))
|
|
|
|
cons = "";
|
|
|
|
|
|
|
|
if (src.null() && cons.null())
|
|
|
|
return false;
|
|
|
|
|
2013-04-12 13:19:14 +00:00
|
|
|
DataEndpoint *dd = static_cast<DataEndpoint*>(msg.userObject(YATOM("DataEndpoint")));
|
2005-07-08 14:51:06 +00:00
|
|
|
if (!dd) {
|
2013-04-12 13:19:14 +00:00
|
|
|
CallEndpoint *ch = static_cast<CallEndpoint*>(msg.userObject(YATOM("CallEndpoint")));
|
2005-07-08 14:51:06 +00:00
|
|
|
if (ch)
|
|
|
|
dd = ch->setEndpoint();
|
|
|
|
}
|
|
|
|
if (!dd) {
|
2005-07-09 18:27:49 +00:00
|
|
|
Debug(&__plugin,DebugWarn,"DSound attach request with no control or data channel!");
|
2005-07-08 14:51:06 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2010-03-31 13:53:44 +00:00
|
|
|
int rate = msg.getIntValue("rate",s_rate);
|
|
|
|
|
2005-07-08 14:51:06 +00:00
|
|
|
if (cons) {
|
2010-03-31 13:53:44 +00:00
|
|
|
DSoundConsumer* c = new DSoundConsumer(rate,msg.getBoolValue("stereo"));
|
2005-07-08 14:51:06 +00:00
|
|
|
dd->setConsumer(c);
|
|
|
|
c->deref();
|
2005-07-09 18:27:49 +00:00
|
|
|
Thread::msleep(50);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (src) {
|
2010-03-31 13:53:44 +00:00
|
|
|
DSoundSource* s = new DSoundSource(rate);
|
2005-07-09 18:27:49 +00:00
|
|
|
dd->setSource(s);
|
|
|
|
s->deref();
|
|
|
|
Thread::msleep(50);
|
2005-07-08 14:51:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Stop dispatching if we handled all requested
|
|
|
|
return !more;
|
2005-05-13 00:22:45 +00:00
|
|
|
}
|
|
|
|
|
2008-08-04 02:06:00 +00:00
|
|
|
|
2005-05-13 00:22:45 +00:00
|
|
|
bool SoundDriver::msgExecute(Message& msg, String& dest)
|
|
|
|
{
|
2005-05-14 01:38:58 +00:00
|
|
|
CallEndpoint* ch = static_cast<CallEndpoint*>(msg.userData());
|
|
|
|
if (ch) {
|
2010-03-31 13:53:44 +00:00
|
|
|
DSoundChan *ds = new DSoundChan(msg.getIntValue("rate",s_rate));
|
2005-09-06 02:51:09 +00:00
|
|
|
if (ch->connect(ds,msg.getValue("reason"))) {
|
2005-06-14 12:36:03 +00:00
|
|
|
msg.setParam("peerid",ds->id());
|
2005-05-14 01:38:58 +00:00
|
|
|
ds->deref();
|
2005-06-14 12:36:03 +00:00
|
|
|
}
|
2005-05-13 00:22:45 +00:00
|
|
|
else {
|
2005-05-14 01:38:58 +00:00
|
|
|
ds->destruct();
|
2005-05-13 00:22:45 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
Message m("call.route");
|
2005-05-14 01:38:58 +00:00
|
|
|
m.addParam("module",name());
|
|
|
|
String callto(msg.getValue("direct"));
|
|
|
|
if (callto.null()) {
|
|
|
|
const char *targ = msg.getValue("target");
|
|
|
|
if (!targ) {
|
2005-07-09 18:27:49 +00:00
|
|
|
Debug(&__plugin,DebugWarn,"DSound outgoing call with no target!");
|
2005-05-14 01:38:58 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
callto = msg.getValue("caller");
|
|
|
|
if (callto.null())
|
|
|
|
callto << prefix() << dest;
|
|
|
|
m.addParam("called",targ);
|
|
|
|
m.addParam("caller",callto);
|
|
|
|
if (!Engine::dispatch(m)) {
|
2005-07-09 18:27:49 +00:00
|
|
|
Debug(&__plugin,DebugWarn,"DSound outgoing call but no route!");
|
2005-05-14 01:38:58 +00:00
|
|
|
return false;
|
2005-05-13 00:22:45 +00:00
|
|
|
}
|
2005-05-14 01:38:58 +00:00
|
|
|
callto = m.retValue();
|
|
|
|
m.retValue().clear();
|
2005-05-13 00:22:45 +00:00
|
|
|
}
|
2005-05-14 01:38:58 +00:00
|
|
|
m = "call.execute";
|
|
|
|
m.addParam("callto",callto);
|
2010-03-31 13:53:44 +00:00
|
|
|
DSoundChan *ds = new DSoundChan(msg.getIntValue("rate",8000));
|
2005-05-14 01:38:58 +00:00
|
|
|
m.setParam("targetid",ds->id());
|
|
|
|
m.userData(ds);
|
|
|
|
if (Engine::dispatch(m)) {
|
|
|
|
ds->deref();
|
|
|
|
return true;
|
|
|
|
}
|
2005-07-09 18:27:49 +00:00
|
|
|
Debug(&__plugin,DebugWarn,"DSound outgoing call not accepted!");
|
2005-05-14 01:38:58 +00:00
|
|
|
ds->destruct();
|
2005-05-13 00:22:45 +00:00
|
|
|
return false;
|
2005-05-14 01:38:58 +00:00
|
|
|
}
|
2005-05-13 00:22:45 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
SoundDriver::SoundDriver()
|
2005-05-14 01:38:58 +00:00
|
|
|
: Driver("dsound","misc"),
|
|
|
|
m_handler(0)
|
2005-05-13 00:22:45 +00:00
|
|
|
{
|
|
|
|
Output("Loaded module DirectSound");
|
|
|
|
}
|
|
|
|
|
|
|
|
SoundDriver::~SoundDriver()
|
|
|
|
{
|
|
|
|
Output("Unloading module DirectSound");
|
|
|
|
channels().clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void SoundDriver::initialize()
|
|
|
|
{
|
|
|
|
Output("Initializing module DirectSound");
|
|
|
|
setup(0,true); // no need to install notifications
|
|
|
|
Driver::initialize();
|
2010-03-31 13:53:44 +00:00
|
|
|
Configuration cfg(Engine::configFile("dsoundchan"));
|
|
|
|
s_rate = cfg.getIntValue("general","rate",8000);
|
|
|
|
// prefer primary buffer as we try to retain control of audio board
|
|
|
|
s_primary = cfg.getBoolValue("general","primary",true);
|
2005-05-13 00:22:45 +00:00
|
|
|
if (!m_handler) {
|
|
|
|
m_handler = new AttachHandler;
|
|
|
|
Engine::install(m_handler);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-05-27 14:53:18 +00:00
|
|
|
}; // anonymous namespace
|
|
|
|
|
2005-05-14 14:41:45 +00:00
|
|
|
#endif /* _WINDOWS */
|
|
|
|
|
2005-05-13 00:22:45 +00:00
|
|
|
/* vi: set ts=8 sw=4 sts=4 noet: */
|