void WWPlayPcmGroup::RemoveAt(int id) { assert(0 <= id && (uint32_t)id < m_playPcmDataList.size()); WWPcmData *pcmData = &m_playPcmDataList[id]; pcmData->Term(); m_playPcmDataList.erase(m_playPcmDataList.begin()+id); // 連続再生のリンクリストをつなげ直す。 SetPlayRepeat(m_repeat); }
int main(int argc, char *argv[]) { WWPcmData *pcmData = NULL; int deviceId = -1; int latencyInMillisec = LATENCY_MILLISEC_DEFAULT; char *filePath = 0; WWBitsPerSampleType bitsPerSampleType = WWBpsNone; if (argc != 3 && argc != 4 && argc != 6) { PrintUsage(); PrintDeviceList(); return 0; } if (0 != strcmp("-d", argv[1])) { PrintUsage(); return 1; } deviceId = atoi(argv[2]); if (argc == 3) { Test(deviceId); return 0; } if (argc == 6) { if (0 != strcmp("-l", argv[3])) { PrintUsage(); return 1; } latencyInMillisec = atoi(argv[4]); } bitsPerSampleType = InspectDeviceBitsPerSample(deviceId); filePath = argv[argc-1]; pcmData = WWReadWavFile(filePath); if (NULL == pcmData) { pcmData = WWReadDsfFile(filePath, bitsPerSampleType); if (NULL == pcmData) { pcmData = WWReadDsdiffFile(filePath, bitsPerSampleType); if (NULL == pcmData) { printf("E: read file failed %s\n", argv[3]); return 1; } } } HRESULT hr = Run(deviceId, latencyInMillisec, *pcmData); if (FAILED(hr)) { printf("E: Run failed (%08x)\n", hr); } if (NULL != pcmData) { pcmData->Term(); delete pcmData; pcmData = NULL; } #ifdef _DEBUG _CrtDumpMemoryLeaks(); #endif return 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(¤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; }