Skip to content

Commit

Permalink
WASAPI Input
Browse files Browse the repository at this point in the history
git-svn-id: https://mumble.svn.sourceforge.net/svnroot/mumble/trunk@1009 05730e5d-ab1b-0410-a4ac-84af385074fa
  • Loading branch information
thorvald committed Mar 19, 2008
1 parent ec619c7 commit 6bef650
Show file tree
Hide file tree
Showing 10 changed files with 224 additions and 84 deletions.
1 change: 1 addition & 0 deletions src/mumble/AudioInput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ AudioInputRegistrar::AudioInputRegistrar(const QString &n) : name(n) {
}

AudioInputRegistrar::~AudioInputRegistrar() {
qmNew->remove(name);
}

AudioInputPtr AudioInputRegistrar::newFromChoice(QString choice) {
Expand Down
1 change: 1 addition & 0 deletions src/mumble/AudioOutput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ AudioOutputRegistrar::AudioOutputRegistrar(const QString &n) : name(n) {
}

AudioOutputRegistrar::~AudioOutputRegistrar() {
qmNew->remove(name);
}

AudioOutputPtr AudioOutputRegistrar::newFromChoice(QString choice) {
Expand Down
1 change: 0 additions & 1 deletion src/mumble/AudioStats.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,6 @@ void AudioStats::on_Tick_timeout() {
spx_int32_t v;
speex_preprocess_ctl(ai->sppPreprocess, SPEEX_PREPROCESS_GET_AGC_GAIN, &v);
float fv = pow(10.0f, (v / 20.0f));
qWarning("dB %d %f", v, fv);
txt.sprintf("%03.0f%%",100.0f / fv);
qlMicVolume->setText(txt);

Expand Down
5 changes: 5 additions & 0 deletions src/mumble/ConfigDialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,14 @@ QMap<int, ConfigWidgetNew> *ConfigRegistrar::c_qmNew;
ConfigRegistrar::ConfigRegistrar(int priority, ConfigWidgetNew n) {
if (! c_qmNew)
c_qmNew = new QMap<int, ConfigWidgetNew>();
iPriority = priority;
c_qmNew->insert(priority,n);
}

ConfigRegistrar::~ConfigRegistrar() {
c_qmNew->remove(iPriority);
}

ConfigWidget::ConfigWidget(Settings &st) : s(st) {
}

Expand Down
2 changes: 2 additions & 0 deletions src/mumble/ConfigDialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,11 @@ typedef ConfigWidget *(*ConfigWidgetNew)(Settings &st);
class ConfigRegistrar {
friend class ConfigDialog;
protected:
int iPriority;
static QMap<int, ConfigWidgetNew> *c_qmNew;
public:
ConfigRegistrar(int priority, ConfigWidgetNew n);
~ConfigRegistrar();
};

class ConfigDialog : public QDialog, public Ui::ConfigDialog {
Expand Down
209 changes: 197 additions & 12 deletions src/mumble/WASAPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,25 +58,51 @@ static ConfigWidget *WASAPIConfigDialogNew(Settings &st) {
}

class WASAPIInit : public DeferInit {
ConfigRegistrar *confReg;
WASAPIInputRegistrar *wirReg;
WASAPIOutputRegistrar *worReg;
public:
void initialize();
void destroy();
};

static ConfigRegistrar registrar(23, WASAPIConfigDialogNew);

static WASAPIInputRegistrar airWASAPI;
static WASAPIOutputRegistrar aorWASAPI;
static WASAPIInit wasapiinit;

void WASAPIInit::initialize() {
qWarning("Input:");
WASAPISystem::getInputDevices();
qWarning("Output:");
WASAPISystem::getOutputDevices();
confReg = NULL;
wirReg = NULL;
worReg = NULL;

OSVERSIONINFOEXW ovi;
memset(&ovi, 0, sizeof(ovi));

ovi.dwOSVersionInfoSize=sizeof(ovi);
GetVersionEx(reinterpret_cast<OSVERSIONINFOW *>(&ovi));

if ((ovi.dwMajorVersion < 6) || (ovi.dwBuildNumber < 6001)) {
qWarning("WASAPIInit: Requires Vista SP1");
return;
}

HMODULE hLib = LoadLibrary(L"AVRT.DLL");
if (hLib == NULL) {
qWarning("WASAPIInit: Failed to load avrt.dll");
return;
}
FreeLibrary(hLib);

confReg = new ConfigRegistrar(23, WASAPIConfigDialogNew);
wirReg = new WASAPIInputRegistrar();
worReg = new WASAPIOutputRegistrar();
}

void WASAPIInit::destroy() {
if (confReg)
delete confReg;
if (wirReg)
delete wirReg;
if (worReg)
delete worReg;
}


Expand Down Expand Up @@ -149,15 +175,11 @@ const QHash<QString, QString> WASAPISystem::getDevices(EDataFlow dataflow) {
LPWSTR strid = NULL;
pDevice->GetId(&strid);

qWarning("Dev %ls\n", strid);

PROPVARIANT varName;
PropVariantInit(&varName);

pStore->GetValue(PKEY_Device_FriendlyName, &varName);

qWarning("Named %ls\n", varName.pwszVal);

devices.insert(QString::fromWCharArray(strid), QString::fromWCharArray(varName.pwszVal));

PropVariantClear(&varName);
Expand Down Expand Up @@ -257,6 +279,169 @@ WASAPIInput::~WASAPIInput() {
}

void WASAPIInput::run() {
HRESULT hr;
IMMDeviceEnumerator *pEnumerator = NULL;
IMMDevice *pDevice = NULL;
IAudioClient *pAudioClient = NULL;
IAudioCaptureClient *pCaptureClient = NULL;
WAVEFORMATEX *pwfx = NULL;
WAVEFORMATEXTENSIBLE *pwfxe = NULL;
UINT32 bufferFrameCount;
REFERENCE_TIME hnsRequestedDuration = 20 * 10000;
SpeexResamplerState *srs = NULL;
UINT32 numFramesAvailable;
UINT32 numFramesLeft;
UINT32 packetLength;
UINT32 wantLength;
HANDLE hEvent;
BYTE *pData;
DWORD flags;
int err = 0;
DWORD gotLength = 0;
DWORD dwTaskIndex = 0;
HANDLE hMmThread;

CoInitialize(NULL);

hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), reinterpret_cast<void **>(&pEnumerator));

if (! pEnumerator || FAILED(hr)) {
qWarning("WASAPIInput: Failed to instatiate enumerator");
return;
}

if (! g.s.qsWASAPIInput.isEmpty()) {
STACKVAR(wchar_t, devname, g.s.qsWASAPIInput.length());
g.s.qsWASAPIInput.toWCharArray(devname);
hr = pEnumerator->GetDevice(devname, &pDevice);
if (FAILED(hr)) {
qWarning("WASAPIInput: Failed to open selected device, falling back to default");
}
}

if (! pDevice) {
hr = pEnumerator->GetDefaultAudioEndpoint(eCapture, eCommunications, &pDevice);
if (FAILED(hr)) {
qWarning("WASAPIInput: Failed to open input device");
pEnumerator->Release();
return;
}
}

pEnumerator->Release();
pEnumerator = NULL;

hr = pDevice->Activate(__uuidof(IAudioClient), CLSCTX_ALL, NULL, (void **) &pAudioClient);
pDevice->Release();
pDevice = NULL;

if (FAILED(hr)) {
qWarning("WASAPIInput: Activate AudioClient failed");
return;
}

hr = pAudioClient->GetMixFormat(&pwfx);
pwfxe = reinterpret_cast<WAVEFORMATEXTENSIBLE *>(pwfx);

if ((pwfx->wBitsPerSample != (sizeof(float) * 8)) || (pwfxe->SubFormat != KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) {
qWarning("WASAPIInput: Subformat is not IEEE Float");
CoTaskMemFree(pwfx);
pAudioClient->Release();
return;
}

hr = pAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, hnsRequestedDuration, 0, pwfx, NULL);
if (FAILED(hr)) {
qWarning("WASAPIInput: Initialize failed");
CoTaskMemFree(pwfx);
pAudioClient->Release();
return;
}

hr = pAudioClient->GetBufferSize(&bufferFrameCount);
hr = pAudioClient->GetService(__uuidof(IAudioCaptureClient), (void**)&pCaptureClient);
if (FAILED(hr)) {
qWarning("WASAPIInput: GetService failed");
CoTaskMemFree(pwfx);
pAudioClient->Release();
return;
}

srs = speex_resampler_init(1, pwfx->nSamplesPerSec, 16000, 8, &err);

wantLength = (iFrameSize * pwfx->nSamplesPerSec) / 16000;

qWarning("WASAPIInput: %d %d %d", bufferFrameCount, pwfx->nSamplesPerSec, wantLength);

hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
pAudioClient->SetEventHandle(hEvent);
if ((hEvent == NULL) || (FAILED(hr))) {
qWarning("WASAPI: Input: Failed to set event");
}


hMmThread = AvSetMmThreadCharacteristics(L"Pro Audio", &dwTaskIndex);
if (hMmThread == NULL) {
qWarning("WASAPI: Input: Failed to set Pro Audio thread priority");
}

hr = pAudioClient->Start();

float mul = 32768.0f / pwfx->nChannels;

STACKVAR(float, inbuff, wantLength);
STACKVAR(float, outbuff, iFrameSize);

while (bRunning && ! FAILED(hr)) {
hr = pCaptureClient->GetNextPacketSize(&packetLength);
// qWarning("Avail: %d", packetLength);
while (! FAILED(hr) && (packetLength > 0)) {
hr = pCaptureClient->GetBuffer(&pData, &numFramesAvailable, &flags, NULL, NULL);
numFramesLeft = numFramesAvailable;
if (! FAILED(hr)) {
float *inData = reinterpret_cast<float *>(pData);
while (numFramesLeft) {
float v = 0.0f;
for(int i=0;i<pwfx->nChannels;i++) {
v+= *inData;
++inData;
}
inbuff[gotLength] = v;
++gotLength;

if (gotLength == wantLength) {
spx_uint32_t inlen = wantLength;
spx_uint32_t outlen = iFrameSize;
speex_resampler_process_float(srs, 0, inbuff, &inlen, outbuff, &outlen);
for(int i=0;i<iFrameSize;i++)
psMic[i] = static_cast<short>(outbuff[i] * mul);

encodeAudioFrame();
gotLength = 0;
}
--numFramesLeft;
}
hr = pCaptureClient->ReleaseBuffer(numFramesAvailable);
if (! FAILED(hr)) {
hr = pCaptureClient->GetNextPacketSize(&packetLength);
}
}
}
if (! FAILED(hr))
WaitForSingleObject(hEvent, 2000);
}

pAudioClient->Stop();

if (hMmThread != NULL)
AvRevertMmThreadCharacteristics(hMmThread);

speex_resampler_destroy(srs);
CoTaskMemFree(pwfx);
pCaptureClient->Release();
pAudioClient->Release();

CloseHandle(hEvent);
}

WASAPIOutput::WASAPIOutput() {
Expand Down
57 changes: 2 additions & 55 deletions src/mumble/WASAPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
#include <functiondiscoverykeys.h>
#include <propidl.h>
#include <initguid.h>
#include <delayimp.h>


class WASAPIConfig : public ConfigWidget, public Ui::WASAPIConfig {
Q_OBJECT
Expand All @@ -66,70 +68,15 @@ class WASAPISystem : public QObject {
static const QList<audioDevice> mapToDevice(const QHash<QString, QString>, const QString &);
};

class WASAPIInput;
class WASAPIOutput;

/*
class WASAPISystem : public QThread {
Q_OBJECT
public:
pa_context *pacContext;
pa_stream *pasInput, *pasOutput, *pasSpeaker;
pa_mainloop *pam;
pa_defer_event *pade;
bool bSourceDone, bSinkDone, bServerDone;
QString qsDefaultInput, qsDefaultOutput;
int iDelayCache;
QString qsOutputCache, qsInputCache, qsEchoCache;
QHash<QString, QString> qhInput;
QHash<QString, QString> qhOutput;
QHash<QString, QString> qhEchoMap;
QHash<QString, int> qhIndexMap;
short *psInput;
short *psEcho;
int iInputIdx;
int iEchoIdx;
int iEchoSeq;
JitterBuffer *jbJitter;
static void defer_event_callback(pa_mainloop_api *a, pa_defer_event *e, void *userdata);
static void context_state_callback(pa_context *c, void *userdata);
static void subscribe_callback(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata);
static void sink_callback(pa_context *c, const pa_sink_info *i, int eol, void *userdata);
static void source_callback(pa_context *c, const pa_source_info *i, int eol, void *userdata);
static void server_callback(pa_context *c, const pa_server_info *i, void *userdata);
static void stream_callback(pa_stream *s, void *userdata);
static void read_callback(pa_stream *s, size_t bytes, void *userdata);
static void write_callback(pa_stream *s, size_t bytes, void *userdata);
void contextCallback(pa_context *c);
void eventCallback(pa_mainloop_api *a, pa_defer_event *e);
void query();
WASAPISystem();
~WASAPISystem();
void run();
void wakeup();
};
*/

class WASAPIInput : public AudioInput {
Q_OBJECT
protected:
void release();
public:
WASAPIInput();
~WASAPIInput();
void run();
};

class WASAPIOutput : public AudioOutput {
friend class WASAPISystem;
Q_OBJECT
public:
WASAPIOutput();
Expand Down
Loading

0 comments on commit 6bef650

Please sign in to comment.