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(¤tMin, ¤tMax); 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; }