Ejemplo n.º 1
0
HRESULT
WWPlayPcmGroup::DoResample(WWPcmFormat &targetFmt, int conversionQuality)
{
    HRESULT hr = S_OK;
    WWMFResampler resampler;
    size_t n = m_playPcmDataList.size();
    const int PROCESS_FRAMES = 128 * 1024;
    BYTE *buff = new BYTE[PROCESS_FRAMES * m_pcmFormat.BytesPerFrame()];
    std::list<size_t> toPcmDataIdxList;
    size_t numConvertedPcmData = 0;
    assert(1 <= conversionQuality && conversionQuality <= 60);

    if (nullptr == buff) {
        hr = E_OUTOFMEMORY;
        goto end;
    }

    // 共有モードのサンプルレート変更。
    HRG(resampler.Initialize(
        WWMFPcmFormat(
            (WWMFBitFormatType)WWPcmDataSampleFormatTypeIsFloat(m_pcmFormat.sampleFormat),
            (WORD)m_pcmFormat.numChannels,
            (WORD)WWPcmDataSampleFormatTypeToBitsPerSample(m_pcmFormat.sampleFormat),
            m_pcmFormat.sampleRate,
            0, //< TODO: target dwChannelMask
            (WORD)WWPcmDataSampleFormatTypeToValidBitsPerSample(m_pcmFormat.sampleFormat)),
        WWMFPcmFormat(
            WWMFBitFormatFloat,
            (WORD)targetFmt.numChannels,
            32,
            targetFmt.sampleRate,
            0, //< TODO: target dwChannelMask
            32),
        conversionQuality));

    for (size_t i=0; i<n; ++i) {
        WWPcmData *pFrom = &m_playPcmDataList[i];
        WWPcmData pcmDataTo;

        if (!pcmDataTo.Init(pFrom->id, targetFmt.sampleFormat, targetFmt.numChannels,
                (int64_t)(((double)targetFmt.sampleRate / m_pcmFormat.sampleRate) * pFrom->nFrames),
                targetFmt.numChannels * WWPcmDataSampleFormatTypeToBitsPerSample(targetFmt.sampleFormat)/8, WWPcmDataContentMusicData, m_pcmFormat.streamType)) {
            dprintf("E: %s malloc failed. pcm id=%d\n", __FUNCTION__, pFrom->id);
            hr = E_OUTOFMEMORY;
            goto end;
        }
        m_playPcmDataList.push_back(pcmDataTo);
        pFrom = &m_playPcmDataList[i];

        toPcmDataIdxList.push_back(n+i);

        dprintf("D: pFrom stream=%p nFrames=%lld\n", pFrom->stream, pFrom->nFrames);

        for (size_t posFrames=0; ; posFrames += PROCESS_FRAMES) {
            WWMFSampleData mfSampleData;
            DWORD consumedBytes = 0;

            int buffBytes = pFrom->GetBufferData(posFrames * m_pcmFormat.BytesPerFrame(), PROCESS_FRAMES * m_pcmFormat.BytesPerFrame(), buff);
            dprintf("D: pFrom->GetBufferData posBytes=%Iu bytes=%d rv=%d\n",
                    posFrames * m_pcmFormat.BytesPerFrame(), PROCESS_FRAMES * m_pcmFormat.BytesPerFrame(), buffBytes);
            if (0 == buffBytes) {
                break;
            }

            HRG(resampler.Resample(buff, buffBytes, &mfSampleData));
            dprintf("D: resampler.Resample mfSampleData.bytes=%u\n",
                    mfSampleData.bytes);
            consumedBytes = 0;
            while (0 < toPcmDataIdxList.size() && consumedBytes < mfSampleData.bytes) {
                size_t toIdx = toPcmDataIdxList.front();
                WWPcmData *pTo = &m_playPcmDataList[toIdx];
                assert(pTo);
                int rv = pTo->FillBufferAddData(&mfSampleData.data[consumedBytes], mfSampleData.bytes - consumedBytes);
                dprintf("D: consumedBytes=%d/%d FillBufferAddData() pTo->stream=%p pTo->nFrames=%lld rv=%d\n",
                        consumedBytes, mfSampleData.bytes, pTo->stream, pTo->nFrames, rv);
                consumedBytes += rv;
                if (0 == rv) {
                    pTo->FillBufferEnd();
                    ++numConvertedPcmData;
                    toPcmDataIdxList.pop_front();
                }
            }
            mfSampleData.Release();
        }
        pFrom->Term();
    }

    {
        WWMFSampleData mfSampleData;
        DWORD consumedBytes = 0;

        HRG(resampler.Drain(PROCESS_FRAMES * m_pcmFormat.BytesPerFrame(), &mfSampleData));
        consumedBytes = 0;
        while (0 < toPcmDataIdxList.size() && consumedBytes < mfSampleData.bytes) {
            size_t toIdx = toPcmDataIdxList.front();
            WWPcmData *pTo = &m_playPcmDataList[toIdx];
            assert(pTo);
            int rv = pTo->FillBufferAddData(&mfSampleData.data[consumedBytes], mfSampleData.bytes - consumedBytes);
            consumedBytes += rv;
            if (0 == rv) {
                pTo->FillBufferEnd();
                ++numConvertedPcmData;
                toPcmDataIdxList.pop_front();
            }
        }
        mfSampleData.Release();
    }

    while (0 < toPcmDataIdxList.size()) {
        size_t toIdx = toPcmDataIdxList.front();
        WWPcmData *pTo = &m_playPcmDataList[toIdx];
        assert(pTo);

        pTo->FillBufferEnd();
        if (0 == pTo->nFrames) {
            hr = E_FAIL;
            goto end;
        }
        ++numConvertedPcmData;
        toPcmDataIdxList.pop_front();
    }

    assert(n == numConvertedPcmData);

    for (size_t i=0; i<n; ++i) {
        m_playPcmDataList[i] = m_playPcmDataList[n+i];
        m_playPcmDataList[n+i].Forget();
    }

    m_playPcmDataList.resize(numConvertedPcmData);

    // update pcm format info
    m_pcmFormat.sampleFormat  = targetFmt.sampleFormat;
    m_pcmFormat.sampleRate    = targetFmt.sampleRate;
    m_pcmFormat.numChannels   = targetFmt.numChannels;
    m_pcmFormat.dwChannelMask = targetFmt.dwChannelMask;

    // reduce volume level when out of range sample value is found
    {
        float maxV = 0.0f;
        float minV = 0.0f;
        const float  SAMPLE_VALUE_MAX_FLOAT  =  1.0f;
        const float  SAMPLE_VALUE_MIN_FLOAT  = -1.0f;

        for (size_t i=0; i<n; ++i) {
            float currentMax = 0.0f;
            float currentMin = 0.0f;
            m_playPcmDataList[i].FindSampleValueMinMax(&currentMin, &currentMax);
            if (currentMin < minV) {
                minV = currentMin;
            }
            if (maxV < currentMax) {
                maxV = currentMax;
            }
        }

        float scale = 1.0f;
        if (SAMPLE_VALUE_MAX_FLOAT < maxV) {
            scale = SAMPLE_VALUE_MAX_FLOAT / maxV;
        }
        if (minV < SAMPLE_VALUE_MIN_FLOAT && SAMPLE_VALUE_MIN_FLOAT / minV < scale) {
            scale = SAMPLE_VALUE_MIN_FLOAT / minV;
        }
        if (scale < 1.0f) {
            for (size_t i=0; i<n; ++i) {
                m_playPcmDataList[i].ScaleSampleValue(scale);
            }
        }
    }

end:
    resampler.Finalize();
    delete [] buff;
    buff = nullptr;
    return hr;
}