doubango/plugins/pluginWinAudioDSP/plugin_audio_dsp_resampler.cxx

361 lines
12 KiB
C++
Executable File

/* Copyright (C) 2013 Mamadou DIOP
* Copyright (C) 2013 Doubango Telecom <http://www.doubango.org>
*
* This file is part of Open Source Doubango Framework.
*
* DOUBANGO 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 3 of the License, or
* (at your option) any later version.
*
* DOUBANGO 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 DOUBANGO.
*/
// MS Audio Resampler DSP: http://msdn.microsoft.com/en-us/library/windows/desktop/ff819070(v=vs.85).aspx
#include "plugin_audio_dsp_utils.h"
#include "tinymedia/tmedia_resampler.h"
#include "tsk_memory.h"
#include "tsk_debug.h"
#include <initguid.h>
#include <Wmcodecdsp.h>
#include <Mftransform.h>
#include <Mfapi.h>
#include <Mferror.h>
#if !defined(PLUGIN_AUDIO_DSP_RESAMPLER_MAX_QUALITY)
# define PLUGIN_AUDIO_DSP_RESAMPLER_MAX_QUALITY 60 /* [1 - 60]: http://msdn.microsoft.com/en-us/library/windows/desktop/ff819449(v=vs.85).aspx */
#endif
static const UINT32 g_nBitsPerSample = 16;
static HRESULT ProcessOutput(struct plugin_audio_dsp_resampler_s *resampler, IMFSample **ppSample);
typedef struct plugin_audio_dsp_resampler_s {
TMEDIA_DECLARE_RESAMPLER;
bool bOpened;
tsk_size_t in_size_samples;
tsk_size_t in_size_bytes;
tsk_size_t out_size_samples;
tsk_size_t out_size_bytes;
uint32_t in_channels;
uint32_t out_channels;
LONGLONG rtStart;
UINT64 rtDuration;
IMFTransform* pMFT;
IMFSample *pSampleIn;
IMFSample *pSampleOut;
}
plugin_audio_dsp_resampler_t;
// Doubango engine uses quality from [1 - 10].
static int plugin_audio_dsp_resampler_open(tmedia_resampler_t* self, uint32_t in_freq, uint32_t out_freq, uint32_t frame_duration, uint32_t in_channels, uint32_t out_channels, uint32_t quality)
{
plugin_audio_dsp_resampler_t *resampler = (plugin_audio_dsp_resampler_t *)self;
IMFMediaType* pTypeIn = NULL;
IMFMediaType* pTypeOut = NULL;
IWMResamplerProps* pProps = NULL;
HRESULT hr = S_OK;
if(in_channels != 1 && in_channels != 2) {
TSK_DEBUG_ERROR("%d not valid as input channel", in_channels);
CHECK_HR(hr = E_INVALIDARG);
}
if(out_channels != 1 && out_channels != 2) {
TSK_DEBUG_ERROR("%d not valid as output channel", out_channels);
CHECK_HR(hr = E_INVALIDARG);
}
if(resampler->bOpened) {
TSK_DEBUG_ERROR("Resampler already opened");
CHECK_HR(hr = E_FAIL);
}
resampler->in_size_samples = ((in_freq * frame_duration) / 1000) << (in_channels == 2 ? 1 : 0);
resampler->out_size_samples = ((out_freq * frame_duration) / 1000) << (out_channels == 2 ? 1 : 0);
resampler->in_channels = in_channels;
resampler->out_channels = out_channels;
resampler->in_size_bytes = (resampler->in_size_samples * (g_nBitsPerSample >> 3));
resampler->out_size_bytes = (resampler->out_size_samples * (g_nBitsPerSample >> 3));
resampler->rtStart = 0;
resampler->rtDuration = PLUGIN_AUDIO_DSP_MILLIS_TO_100NS(frame_duration); // milliseconds -> 100ns
CHECK_HR(hr = CoCreateInstance(CLSID_CResamplerMediaObject, NULL,
CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&resampler->pMFT)));
CHECK_HR(hr = AudioDSPUtils::CreatePCMAudioType(in_freq, g_nBitsPerSample, in_channels, &pTypeIn));
CHECK_HR(hr = AudioDSPUtils::CreatePCMAudioType(out_freq, g_nBitsPerSample, out_channels, &pTypeOut));
CHECK_HR(hr = resampler->pMFT->SetInputType(0, pTypeIn, 0));
CHECK_HR(hr = resampler->pMFT->SetOutputType(0, pTypeOut, 0));
CHECK_HR(hr = resampler->pMFT->QueryInterface(IID_PPV_ARGS(&pProps)));
CHECK_HR(hr = pProps->SetHalfFilterLength((quality * PLUGIN_AUDIO_DSP_RESAMPLER_MAX_QUALITY) / 10)); // [1 - 10] -> [1 - 60]
CHECK_HR(hr = resampler->pMFT->ProcessMessage(MFT_MESSAGE_COMMAND_FLUSH, NULL));
CHECK_HR(hr = resampler->pMFT->ProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, NULL));
CHECK_HR(hr = resampler->pMFT->ProcessMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, NULL));
bail:
resampler->bOpened = SUCCEEDED(hr);
if(!resampler->bOpened) {
SafeRelease(&resampler->pMFT);
}
SafeRelease(&pTypeIn);
SafeRelease(&pTypeOut);
SafeRelease(&pProps);
return resampler->bOpened ? 0 : -1;
}
static tsk_size_t plugin_audio_dsp_resampler_process(tmedia_resampler_t* self, const uint16_t* in_data, tsk_size_t in_size, uint16_t* out_data, tsk_size_t out_size)
{
plugin_audio_dsp_resampler_t *resampler = (plugin_audio_dsp_resampler_t *)self;
HRESULT hr = S_OK;
tsk_size_t retSize = 0;
if(!resampler || !out_data) {
CHECK_HR(hr = E_POINTER);
}
if(!resampler->bOpened) {
TSK_DEBUG_ERROR("Resampler not opened");
CHECK_HR(hr = E_FAIL);
}
if(in_size != resampler->in_size_samples) {
TSK_DEBUG_ERROR("Input data has wrong size");
CHECK_HR(hr = E_FAIL);
}
if(out_size < resampler->out_size_samples) {
TSK_DEBUG_ERROR("Output data is too short");
CHECK_HR(hr = E_FAIL);
}
IMFMediaBuffer* pBufferIn = NULL;
IMFMediaBuffer* pBufferOut = NULL;
IMFSample *pSampleOut = NULL;
BYTE* pBufferPtr = NULL;
if(!resampler->pSampleIn) {
CHECK_HR(hr = AudioDSPUtils::CreateMediaSample(resampler->in_size_bytes, &resampler->pSampleIn));
hr = resampler->pSampleIn->GetBufferByIndex(0, &pBufferIn);
if(FAILED(hr)) {
SafeRelease(&resampler->pSampleIn);
CHECK_HR(hr);
}
}
else {
DWORD dwMaxLength = 0;
CHECK_HR(hr = resampler->pSampleIn->GetBufferByIndex(0, &pBufferIn));
CHECK_HR(hr = pBufferIn->GetMaxLength(&dwMaxLength));
if(dwMaxLength < resampler->in_size_bytes) {
CHECK_HR(hr = resampler->pSampleIn->RemoveAllBuffers());
SafeRelease(&pBufferIn);
CHECK_HR(hr = MFCreateMemoryBuffer(resampler->in_size_bytes, &pBufferIn));
CHECK_HR(hr = resampler->pSampleIn->AddBuffer(pBufferIn));
}
}
CHECK_HR(hr = pBufferIn->Lock(&pBufferPtr, NULL, NULL));
memcpy(pBufferPtr, in_data, resampler->in_size_bytes);
CHECK_HR(hr = pBufferIn->Unlock());
CHECK_HR(hr = pBufferIn->SetCurrentLength(resampler->in_size_bytes));
CHECK_HR(hr = resampler->pSampleIn->SetSampleDuration(resampler->rtDuration));
CHECK_HR(hr = resampler->pSampleIn->SetSampleTime(resampler->rtStart));
// Process input
hr = resampler->pMFT->ProcessInput(0, resampler->pSampleIn, 0);
if(hr == MF_E_NOTACCEPTING) {
hr = S_OK;
}
CHECK_HR(hr);
resampler->rtStart += resampler->rtDuration;
// Process output
CHECK_HR(hr = ProcessOutput(resampler, &pSampleOut));
if(pSampleOut) {
CHECK_HR(hr = pSampleOut->GetBufferByIndex(0, &pBufferOut));
BYTE* pBufferOutPtr = NULL;
DWORD dwDataLength = 0;
CHECK_HR(hr = pBufferOut->GetCurrentLength(&dwDataLength));
//if(dwDataLength == resampler->out_size_bytes)
{
CHECK_HR(hr = pBufferOut->Lock(&pBufferOutPtr, NULL, NULL));
{
memcpy(out_data, pBufferOutPtr, TSK_MIN(dwDataLength, resampler->out_size_bytes));
if(dwDataLength < resampler->out_size_bytes) {
TSK_DEBUG_INFO("[MS Resampler DSP] Output too short filling with silence");
memset(&((uint8_t*)out_data)[dwDataLength], 0, (resampler->out_size_bytes - dwDataLength));
}
retSize = (tsk_size_t)resampler->out_size_bytes;
}
CHECK_HR(hr = pBufferOut->Unlock());
}
}
bail:
SafeRelease(&pBufferIn);
SafeRelease(&pBufferOut);
SafeRelease(&pSampleOut);
return retSize;
}
static int plugin_audio_dsp_resampler_close(tmedia_resampler_t* self)
{
plugin_audio_dsp_resampler_t *resampler = (plugin_audio_dsp_resampler_t *)self;
HRESULT hr = S_OK;
if(resampler->pMFT) {
hr = resampler->pMFT->ProcessMessage(MFT_MESSAGE_NOTIFY_END_OF_STREAM, NULL);
}
SafeRelease(&resampler->pMFT);
SafeRelease(&resampler->pSampleIn);
SafeRelease(&resampler->pSampleOut);
resampler->bOpened = false;
return 0;
}
static HRESULT ProcessOutput(plugin_audio_dsp_resampler_t *resampler, IMFSample **ppSample)
{
*ppSample = NULL;
IMFMediaBuffer* pBufferOut = NULL;
DWORD dwStatus;
HRESULT hr = S_OK;
MFT_OUTPUT_DATA_BUFFER mftOutputData = { 0 };
if(!resampler || !ppSample) {
CHECK_HR(hr = E_POINTER);
}
if(!resampler->pSampleOut) {
CHECK_HR(hr = AudioDSPUtils::CreateMediaSample(resampler->out_size_bytes, &resampler->pSampleOut));
hr = resampler->pSampleOut->GetBufferByIndex(0, &pBufferOut);
if(FAILED(hr)) {
SafeRelease(&resampler->pSampleOut);
CHECK_HR(hr);
}
}
else {
DWORD dwMaxLength = 0;
CHECK_HR(hr = resampler->pSampleOut->GetBufferByIndex(0, &pBufferOut));
CHECK_HR(hr = pBufferOut->GetMaxLength(&dwMaxLength));
if(dwMaxLength < resampler->out_size_bytes) {
CHECK_HR(hr = resampler->pSampleOut->RemoveAllBuffers());
SafeRelease(&pBufferOut);
CHECK_HR(hr = MFCreateMemoryBuffer(resampler->out_size_bytes, &pBufferOut));
CHECK_HR(hr = resampler->pSampleOut->AddBuffer(pBufferOut));
}
}
CHECK_HR(hr = pBufferOut->SetCurrentLength(0));
//Set the output sample
mftOutputData.pSample = resampler->pSampleOut;
//Set the output id
mftOutputData.dwStreamID = 0;
//Generate the output sample
hr = resampler->pMFT->ProcessOutput(0, 1, &mftOutputData, &dwStatus);
if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT) {
hr = S_OK;
goto bail;
}
// TODO: Handle MF_E_TRANSFORM_STREAM_CHANGE
if (FAILED(hr)) {
goto bail;
}
*ppSample = resampler->pSampleOut;
(*ppSample)->AddRef();
bail:
SafeRelease(&pBufferOut);
return hr;
}
//
// Speex resamplerr Plugin definition
//
/* constructor */
static tsk_object_t* plugin_audio_dsp_resampler_ctor(tsk_object_t * self, va_list * app)
{
plugin_audio_dsp_resampler_t *resampler = (plugin_audio_dsp_resampler_t *)self;
if(resampler) {
AudioDSPUtils::Startup();
/* init base */
tmedia_resampler_init(TMEDIA_RESAMPLER(resampler));
/* init self */
}
return self;
}
/* destructor */
static tsk_object_t* plugin_audio_dsp_resampler_dtor(tsk_object_t * self)
{
plugin_audio_dsp_resampler_t *resampler = (plugin_audio_dsp_resampler_t *)self;
if(resampler) {
/* deinit base */
tmedia_resampler_deinit(TMEDIA_RESAMPLER(resampler));
/* deinit self */
// tmedia_resampler_deinit() already closed the resampler and freed the resources...but do it again
SafeRelease(&resampler->pMFT);
SafeRelease(&resampler->pSampleIn);
SafeRelease(&resampler->pSampleOut);
TSK_DEBUG_INFO("*** MS Audio Resampler DSP (plugin) destroyed ***");
}
return self;
}
/* object definition */
static const tsk_object_def_t plugin_audio_dsp_resampler_def_s = {
sizeof(plugin_audio_dsp_resampler_t),
plugin_audio_dsp_resampler_ctor,
plugin_audio_dsp_resampler_dtor,
tsk_null,
};
/* plugin definition*/
static const tmedia_resampler_plugin_def_t plugin_audio_dsp_resampler_plugin_def_s = {
&plugin_audio_dsp_resampler_def_s,
"MS Audio Resampler DSP", /* http://msdn.microsoft.com/en-us/library/windows/desktop/ff819070(v=vs.85).aspx */
plugin_audio_dsp_resampler_open,
plugin_audio_dsp_resampler_process,
plugin_audio_dsp_resampler_close,
};
const tmedia_resampler_plugin_def_t *plugin_audio_dsp_resampler_plugin_def_t = &plugin_audio_dsp_resampler_plugin_def_s;