#include "CAudioPlayerProc.h"

#include <QFile>
//#include <mmsystem.h>
#include <mmdeviceapi.h>
#include <endpointvolume.h>
#include <audioclient.h>
#include <QFileInfo>
#include "MediaInfoDLL.h"

#define DITHER_FLAG     (1) /**/

/* Select sample format. */
#if 0
#define PA_SAMPLE_TYPE  paFloat32
typedef float SAMPLE;
#define SAMPLE_SILENCE  (0.0f)
#define PRINTF_S_FORMAT "%.8f"
#elif 1
#define PA_SAMPLE_TYPE  paInt16
typedef short SAMPLE;
#define SAMPLE_SILENCE  (0)
#define PRINTF_S_FORMAT "%d"
#elif 2
#define PA_SAMPLE_TYPE  paInt8
typedef char SAMPLE;
#define SAMPLE_SILENCE  (0)
#define PRINTF_S_FORMAT "%d"
#else
#define PA_SAMPLE_TYPE  paUInt8
typedef unsigned char SAMPLE;
#define SAMPLE_SILENCE  (128)
#define PRINTF_S_FORMAT "%d"
#endif

int CAudioPlayerProc::SAMPLE_RATE = 16000;//采样率
int CAudioPlayerProc::NUM_CHANNELS = 1; //通道
int CAudioPlayerProc::FRAMES_PER_BUFFER = (SAMPLE_RATE/10);
CAudioPlayerProc* CAudioPlayerProc::pThis = NULL;
int CAudioPlayerProc::m_Vol = 0;
std::shared_ptr<CAudioPlayerProc> g_audioPalyerPtr = nullptr;

CAudioPlayerProc::CAudioPlayerProc(QObject *parent) : QObject(parent)
{
    std::string device = "default";
    m_mic.LinkToDevice(device);
    setHeadSetVol(100);
    setMicVol(100);
    m_Vol = 100;
    pThis = this;    
    m_pAudioPlayer = new QMediaPlayer(this);
}

CAudioPlayerProc::~CAudioPlayerProc()
{
    delete m_pAudioPlayer;
    m_pAudioPlayer = nullptr;
}


void CAudioPlayerProc::onMetaDataAvailableChanged(bool available)
{
    if (available)
    {
        foreach(QString str, m_pAudioPlayer->availableMetaData())
        {
            qDebug()<<str<<"   :"<<m_pAudioPlayer->metaData(str).toString().toUtf8().data();
        }
        qint64 time = m_pAudioPlayer->metaData("Duration").toInt();
        QString str = QString("%1").arg((time+999)/1000);
        int m_nAudioSecond = (time+999)/1000;
        emit durationChange(m_nAudioSecond);
    }
}

int CAudioPlayerProc::GetMediaDuration(QString sAudio)
{
    QFileInfo fileInfo(sAudio);
    MediaInfoDLL::MediaInfo MI;
    QString filepath = fileInfo.absoluteFilePath();
    MI.Open(filepath.toStdWString());
    int nDuration = (QString::fromStdWString((std::basic_string<wchar_t>)MI.Get(MediaInfoDLL::stream_t::Stream_Audio, 0, __T("Duration"))).toLong()+999)/1000;
    MI.Close();
    return nDuration;
}

void CAudioPlayerProc::setAudio(QString sAudio)
{
    m_pAudioPlayer->setMedia(QUrl::fromLocalFile(sAudio));
}

int CAudioPlayerProc::playAudio(QString sAudio, bool IsRepeat)
{
    if(IsRepeat)
    {
        m_playlist.clear();
        m_playlist.addMedia(QUrl(sAudio));
        m_playlist.setCurrentIndex(1);
        m_playlist.setPlaybackMode(QMediaPlaylist::CurrentItemInLoop);
        m_pAudioPlayer->setPlaylist(&m_playlist);
        m_pAudioPlayer->play();
    }
    else
    {
        m_pAudioPlayer->setMedia(QUrl::fromLocalFile(sAudio));
        m_pAudioPlayer->play();
    }
    qDebug()<<m_pAudioPlayer->duration();
    return (m_pAudioPlayer->duration()+999)/1000;
}

void CAudioPlayerProc::stopPlay()
{
    m_pAudioPlayer->stop();
}

void CAudioPlayerProc::pausePlay()
{
    m_pAudioPlayer->pause();
}

void CAudioPlayerProc::play()
{
	m_pAudioPlayer->play();
}

void CAudioPlayerProc::setVol(int vol)
{
    if(vol > 100)
    {
        vol = 100;
    }
    if(vol < 0)
    {
        vol= 0;
    }
    m_pAudioPlayer->setVolume(vol);
    m_Vol = vol;
}

//设置系统麦克风音量
void CAudioPlayerProc::setMicVol(int nVol)
{
    m_mic.SetVolume(nVol/100.0);
}

//设置系统音量
void CAudioPlayerProc::setHeadSetVol(int nVol)
{
    HRESULT hr;
    IMMDeviceEnumerator* pDeviceEnumerator=0;
    IMMDevice* pDevice=0;
    IAudioEndpointVolume* pAudioEndpointVolume=0;
    IAudioClient* pAudioClient=0;


    try
    {
        hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),NULL,CLSCTX_ALL,__uuidof(IMMDeviceEnumerator),(void**)&pDeviceEnumerator);
        if(FAILED(hr)) throw "CoCreateInstance";
        hr = pDeviceEnumerator->GetDefaultAudioEndpoint(eRender,eMultimedia,&pDevice);
        if(FAILED(hr)) throw "GetDefaultAudioEndpoint";
        hr = pDevice->Activate(__uuidof(IAudioEndpointVolume),CLSCTX_ALL,NULL,(void**)&pAudioEndpointVolume);
        if(FAILED(hr)) throw "pDevice->Active";
        hr = pDevice->Activate(__uuidof(IAudioClient),CLSCTX_ALL,NULL,(void**)&pAudioClient);
        if(FAILED(hr)) throw "pDevice->Active";


        float fVolume;
        fVolume = nVol/100.0f;
        hr = pAudioEndpointVolume->SetMasterVolumeLevelScalar(fVolume,&GUID_NULL);
        if(FAILED(hr)) throw "SetMasterVolumeLevelScalar";


        pAudioClient->Release();
        pAudioEndpointVolume->Release();
        pDevice->Release();
        pDeviceEnumerator->Release();
    }
    catch(...)
    {
        if(pAudioClient) pAudioClient->Release();
        if(pAudioEndpointVolume) pAudioEndpointVolume->Release();
        if(pDevice) pDevice->Release();
        if(pDeviceEnumerator) pDeviceEnumerator->Release();
    }
}

bool CAudioPlayerProc::getDeviceinfo(std::string &sName)
{
    PaError err = Pa_Initialize();
    if( err != paNoError )
    {
        Pa_Terminate();
        m_sErrMsg = QString::fromLocal8Bit(PaGetErrorText(err));
        fclose(m_RawFile);
        return false;
    }

    m_inputParameters.device = Pa_GetDefaultInputDevice(); /* default input device */
    if (m_inputParameters.device == paNoDevice)
    {
        Pa_Terminate();
        m_sErrMsg = QString::fromLocal8Bit("系统无默认录音设备,请检查耳机是否插好！");
        return false;
    }
    const PaDeviceInfo *painfo = Pa_GetDeviceInfo(m_inputParameters.device );
    sName = painfo->name;
    Pa_Terminate();
    return true;
}

bool CAudioPlayerProc::startRecord(std::string sFile)
{
    QFile file(sFile.c_str());
    if(file.exists())
    {
        file.remove();
    }
    m_RawFile = fopen(sFile.c_str(), "wb");
    if( m_RawFile == NULL )
    {
        m_sErrMsg = QString::fromLocal8Bit("创建录音文件失败！,");
        return false;
    }
    fseek(m_RawFile, sizeof(HEADER) + sizeof(FMT) +sizeof(DATA) ,SEEK_SET);

    PaError err = Pa_Initialize();
    if( err != paNoError )
    {
        Pa_Terminate();
        m_sErrMsg = QString::fromLocal8Bit(PaGetErrorText( err ));
        fclose(m_RawFile);
        return false;
    }

    m_inputParameters.device = Pa_GetDefaultInputDevice(); /* default input device */
    if (m_inputParameters.device == paNoDevice)
    {
        Pa_Terminate();
        fclose(m_RawFile);
        m_sErrMsg = QString::fromLocal8Bit("系统无默认录音设备,请检查耳机是否插好！");
        return false;
    }

    m_inputParameters.channelCount = NUM_CHANNELS;
    m_inputParameters.sampleFormat = PA_SAMPLE_TYPE;
    m_inputParameters.suggestedLatency = Pa_GetDeviceInfo( m_inputParameters.device )->defaultLowInputLatency;
    m_inputParameters.hostApiSpecificStreamInfo = NULL;

    err = Pa_OpenStream(
        &m_stream,
        &m_inputParameters,
        NULL,                  /* &outputParameters, */
        SAMPLE_RATE,
        FRAMES_PER_BUFFER,
        paClipOff,      /* we won't output out of range samples so don't bother clipping them */
        recordCb, /* no callback, use blocking API */
        NULL ); /* no callback, so no callback userData */
    if( err != paNoError )
    {
        Pa_Terminate();
        fclose(m_RawFile);
        m_sErrMsg = QString::fromLocal8Bit(PaGetErrorText( err ));
        return false;
    }

    err = Pa_StartStream( m_stream );
    if( err != paNoError )
    {
        Pa_Terminate();
        fclose(m_RawFile);
        m_sErrMsg = QString::fromLocal8Bit(PaGetErrorText( err ));
        return false;
    }
    return true;
}

void CAudioPlayerProc::setWavHead()
{
    HEADER   pcmHEADER;
    FMT   pcmFMT;
    DATA   pcmDATA;
    memset(&pcmHEADER, 0, sizeof(HEADER));
    memset(&pcmFMT, 0, sizeof(FMT));
    memset(&pcmDATA, 0, sizeof(DATA));

    memcpy(pcmHEADER.fccID,"RIFF", 4);
    memcpy(pcmHEADER.fccType,"WAVE", 4);
    fseek(m_RawFile, 0,SEEK_END);
    int nsize = ftell(m_RawFile);
    pcmHEADER.dwSize = nsize - 8;
    fseek(m_RawFile, 0, SEEK_SET);
    fwrite(&pcmHEADER,sizeof(HEADER),1,m_RawFile);

    //以下是创建wav头的FMT;
    pcmFMT.dwSamplesPerSec = SAMPLE_RATE;
    pcmFMT.wChannels = NUM_CHANNELS;
    pcmFMT.uiBitsPerSample = sizeof(SAMPLE)*8;
    pcmFMT.dwAvgBytesPerSec =
        (pcmFMT.uiBitsPerSample*pcmFMT.wChannels*pcmFMT.dwSamplesPerSec)/8;
    memcpy(pcmFMT.fccID,"fmt ", 4);
    pcmFMT.dwSize= 16;
    pcmFMT.wBlockAlign = (pcmFMT.uiBitsPerSample*pcmFMT.wChannels)/8;
    pcmFMT.wFormatTag = 1;
    //以上是创建wav头的FMT;
    fwrite(&pcmFMT,sizeof(FMT),1,m_RawFile); //将FMT写入.wav文件;

    //以下是创建wav头的DATA;   但由于DATA.dwsize未知所以不能写入.wav文件
    memcpy(pcmDATA.fccID,"data", 4);
    pcmDATA.dwSize = nsize - 44; //给pcmDATA.dwsize   0以便于下面给它赋值
    fwrite(&pcmDATA,sizeof(DATA),1,m_RawFile);
}

int CAudioPlayerProc::recordCb(
    const void *input, void *output,
    unsigned long frameCount,
    const PaStreamCallbackTimeInfo* timeInfo,
    PaStreamCallbackFlags statusFlags,
    void *userData )
{
    /* Write recorded data to a file. */
    short *pData = new short[frameCount];
    memcpy(pData, input, 2*frameCount);
    short *pTemp = pData;
    float db=96*m_Vol/100-96;
    float multiplier = pow(10,db/20);

    for(unsigned long i = 0; i < frameCount; i++)
    {
//		*pTemp = (*pTemp)*pow(10,m_Vol/256.0);
        *pTemp = (*pTemp)*multiplier;
        if (*pTemp > 32767) {
            *pTemp = 32767;
        }else if (*pTemp < -32768) {
            *pTemp = -32768;
        }
        pTemp++;
    }
    fwrite( (char*)pData, NUM_CHANNELS * sizeof(SAMPLE), frameCount, pThis->m_RawFile);

    pThis->calcVol(pData, frameCount);
    return paContinue;
}

void CAudioPlayerProc::calcVol(short *pData, int nsize)
{
    __int64 nTotalVol = 0;
    short *pTemp = pData;
    for(int i = 0; i < nsize; i++)
    {
        nTotalVol += (*pTemp)*(*pTemp);
        pTemp++;
    }
    //int nAvgVol = nTotalVol/nsize;
    int nAvgVol = nTotalVol/nsize;
    int ndb = 0;
    if(nAvgVol != 0)
    {
        ndb = (int)(10.0*log10((double)nAvgVol ));
    }
    else
    {
        ndb = 0;
    }
    delete [] pData;
    emit audioLoud(ndb);
}

bool  CAudioPlayerProc::stopRecord()
{
    Pa_StopStream(m_stream);
    PaError err = Pa_CloseStream( m_stream );
    if( err != paNoError )
    {
        Pa_Terminate();
        fclose(m_RawFile);
        m_sErrMsg = QString::fromLocal8Bit(PaGetErrorText(err));
        return false;
    }
    Pa_Terminate();

    setWavHead();

    fclose(m_RawFile);
    return true;
}

bool  CAudioPlayerProc::isRecording()
{
    PaError err;
    if( ( err = Pa_IsStreamActive(m_stream)) == 1 )
    {
        return true;
    }
    else
    {
        const PaHostErrorInfo *perr = Pa_GetLastHostErrorInfo() ;
        m_sErrMsg = QString::fromLocal8Bit(perr->errorText);
        if(perr->errorCode == 6)
        {
            m_sErrMsg = QString::fromLocal8Bit("无录音设备，请检查耳机是否插好！");
        }
        Pa_Terminate();
        fclose(m_RawFile);
        return false;
    }
}

QString  CAudioPlayerProc::getErrMsg()
{
    return m_sErrMsg;
}

const char* CAudioPlayerProc::PaGetErrorText( int errorCode )
{
    const char *result;

    switch( errorCode )
    {
    case paNoError:                  result = "成功"; break;
    case paNotInitialized:           result = "音频SDK没有初始化"; break;
    /** @todo could catenate the last host error text to result in the case of paUnanticipatedHostError */
    case paUnanticipatedHostError:   result = "未知的主机错误"; break;
    case paInvalidChannelCount:      result = "无效的频道"; break;
    case paInvalidSampleRate:        result = "无效的采样率"; break;
    case paInvalidDevice:            result = "无录音设备，请检查耳机是否插好！"; break;
    case paInvalidFlag:              result = "无效标志"; break;
    case paSampleFormatNotSupported: result = "采样格式不支持"; break;
    case paBadIODeviceCombination:   result = "输入/输出设备的非法组合"; break;
    case paInsufficientMemory:       result = "内存不足"; break;
    case paBufferTooBig:             result = "缓冲太大"; break;
    case paBufferTooSmall:           result = "缓冲太小"; break;
    case paNullCallback:             result = "未指定回调例程"; break;
    case paBadStreamPtr:             result = "无效的流指针"; break;
    case paTimedOut:                 result = "等待超时"; break;
    case paInternalError:            result = "音频SDK内部错误"; break;
    case paDeviceUnavailable:        result = "设备不可用"; break;
    case paIncompatibleHostApiSpecificStreamInfo:   result = "主机API特定流信息不兼容"; break;
    case paStreamIsStopped:          result = "数据流停止了"; break;
    case paStreamIsNotStopped:       result = "数据流未停止"; break;
    case paInputOverflowed:          result = "输入溢出"; break;
    case paOutputUnderflowed:        result = "输出不足"; break;
    case paHostApiNotFound:          result = "没有发现主机API"; break;
    case paInvalidHostApi:           result = "主机API无效"; break;
    case paCanNotReadFromACallbackStream:       result = "无法从回调流中读取"; break;
    case paCanNotWriteToACallbackStream:        result = "无法向回调流中写入"; break;
    case paCanNotReadFromAnOutputOnlyStream:    result = "无法从仅输出的流中读取"; break;
    case paCanNotWriteToAnInputOnlyStream:      result = "无法向仅输入流写入"; break;
    default:
        if( errorCode > 0 )
            result = "无效的错误代码(值大于零)";
        else
            result = "无效的错误代码";
        break;
    }
    return result;
}
