bool WWPlayPcmGroup::AddPlayPcmData(int id, BYTE *data, int64_t bytes) { #ifdef _X86_ if (0x7fffffffL < bytes) { // cannot alloc 2GB buffer on 32bit build dprintf("E: %s(%d, %p, %lld) cannot alloc 2GB buffer on 32bit build\n", __FUNCTION__, id, data, bytes); return false; } #endif if (0 == bytes) { dprintf("E: %s(%d, %p, %lld) arg check failed\n", __FUNCTION__, id, data, bytes); return false; } WWPcmData pcmData; if (!pcmData.Init(id, m_pcmFormat.sampleFormat, m_pcmFormat.numChannels, bytes/m_pcmFormat.BytesPerFrame(), m_pcmFormat.BytesPerFrame(), WWPcmDataContentMusicData, m_pcmFormat.streamType)) { dprintf("E: %s(%d, %p, %lld) malloc failed\n", __FUNCTION__, id, data, bytes); return false; } if (nullptr != data) { CopyMemory(pcmData.stream, data, (bytes/m_pcmFormat.BytesPerFrame()) * m_pcmFormat.BytesPerFrame()); } m_playPcmDataList.push_back(pcmData); return true; }
WWPcmData * WWReadWavFile(const char *path, WWPcmDataStreamAllocType t) { unsigned char buff[12]; WWPcmData *result = nullptr; WaveFormatInfo wfi; memset(&wfi, 0, sizeof wfi); FILE *fp = nullptr; fopen_s(&fp, path, "rb"); if (nullptr == fp) { return nullptr; } size_t rv = fread(buff, 1, 12, fp); if (rv != 12) { printf("E: flie size is too small\n"); goto end; } if (0 != (strncmp("RIFF", (const char*)buff, 4))) { goto end; } if (0 != (strncmp("WAVE", (const char*)&buff[8], 4))) { printf("E: WAVE not found\n"); goto end; } for (;;) { if (!ReadWaveChunk(fp, wfi)) { break; } } if (wfi.data) { result = new WWPcmData(); if (nullptr == result) { goto end; } result->Init(t); result->bitsPerSample = wfi.bitsPerSample; result->validBitsPerSample = wfi.bitsPerSample; result->nChannels = wfi.nChannels; result->nSamplesPerSec = wfi.nSamplesPerSec; result->nFrames = wfi.nFrames; result->posFrame = 0; int64_t bytes = (int64_t)result->nFrames * result->nChannels * result->bitsPerSample / 8; if (!result->StoreStream(wfi.data, bytes)) { printf("memory allocation failed\n"); goto end; } } end: fclose(fp); return result; }
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; }
WWPcmData * WWReadDsfFile(const char *path, WWBitsPerSampleType bitsPerSampleType, WWPcmDataStreamAllocType allocType) { WWPcmData *pcmData = nullptr; char fourCC[4]; DsfDsdChunk dsdChunk; DsfFmtChunk fmtChunk; DsfDataChunk dataChunk; int64_t streamBytes; int64_t writePos; uint32_t blockNum; unsigned char *blockData = nullptr; unsigned char *stream = nullptr; int result = -1; if (bitsPerSampleType == WWBpsNone) { printf("E: device does not support DoP\n"); return nullptr; } FILE *fp = nullptr; fopen_s(&fp, path, "rb"); if (nullptr == fp) { return nullptr; } if (fread(fourCC, 1, 4, fp) < 4 || 0 != memcmp(fourCC, DSD_CHUNK_FOURCC, 4) || dsdChunk.ReadFromFile(fp) < 0) { goto end; } if (fread(fourCC, 1, 4, fp) < 4 || 0 != memcmp(fourCC, FMT_CHUNK_FOURCC, 4) || fmtChunk.ReadFromFile(fp) < 0) { goto end; } if (fread(fourCC, 1, 4, fp) < 4 || 0 != memcmp(fourCC, DATA_CHUNK_FOURCC, 4) || dataChunk.ReadFromFile(fp) < 0) { goto end; } pcmData = new WWPcmData(); if (nullptr == pcmData) { goto end; } pcmData->Init(allocType); pcmData->bitsPerSample = bitsPerSampleType == WWBps32v24 ? 32 : 24; pcmData->validBitsPerSample = 24; pcmData->nChannels = fmtChunk.channelNum; // DSD 16bit == 1 frame pcmData->nFrames = fmtChunk.sampleCount/16; pcmData->nSamplesPerSec = 176400; pcmData->posFrame = 0; streamBytes = (pcmData->bitsPerSample/8) * pcmData->nFrames * pcmData->nChannels; stream = new unsigned char[streamBytes]; if (nullptr == stream) { goto end; } memset(stream, 0, streamBytes); blockNum = (uint32_t)((dataChunk.chunkBytes-12)/fmtChunk.blockSizePerChannel); blockData = new unsigned char[fmtChunk.blockSizePerChannel * fmtChunk.channelNum]; if (nullptr == blockData) { goto end; } writePos = 0; for (uint32_t block = 0; block < blockNum; ++block) { // data is stored in following order: // L channel 4096bytes consecutive data, R channel 4096bytes consecutive data, L channel 4096bytes consecutive data, ... // // read 4096 x numChannels bytes. if (fread(blockData, fmtChunk.blockSizePerChannel * fmtChunk.channelNum, 1, fp) < 1) { goto end; } switch (bitsPerSampleType) { case WWBps32v24: for (uint32_t i=0; i<fmtChunk.blockSizePerChannel/2; ++i) { for (uint32_t ch=0; ch<fmtChunk.channelNum; ++ch) { stream[writePos+0] = 0; stream[writePos+1] = gBitReverse[blockData[i*2+1 + ch*fmtChunk.blockSizePerChannel]]; stream[writePos+2] = gBitReverse[blockData[i*2+0 + ch*fmtChunk.blockSizePerChannel]]; stream[writePos+3] = i & 1 ? 0xfa : 0x05; writePos += 4; if (streamBytes <= writePos) { // recorded sample is ended on part of the way of the block result = 0; goto end; } } } break; case WWBps24: for (uint32_t i=0; i<fmtChunk.blockSizePerChannel/2; ++i) { for (uint32_t ch=0; ch<fmtChunk.channelNum; ++ch) { stream[writePos+0] = gBitReverse[blockData[i*2+1 + ch*fmtChunk.blockSizePerChannel]]; stream[writePos+1] = gBitReverse[blockData[i*2+0 + ch*fmtChunk.blockSizePerChannel]]; stream[writePos+2] = i & 1 ? 0xfa : 0x05; writePos += 3; if (streamBytes <= writePos) { // recorded sample is ended on part of the way of the block result = 0; goto end; } } } break; } } // coincidentally block size == recorded sample size result = 0; end: if (result == 0) { // succeeded if (!pcmData->StoreStream(stream, streamBytes)) { printf("pcmData->StoreStream() failed\n"); result = -1; } } delete [] blockData; blockData = nullptr; delete [] stream; stream = nullptr; if (result < 0) { if (pcmData) { pcmData->Term(); delete pcmData; pcmData = nullptr; } } fclose(fp); return pcmData; }