Ejemplo n.º 1
0
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;
}
Ejemplo n.º 2
0
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;
}
Ejemplo n.º 3
0
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);
}
Ejemplo n.º 4
0
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;
}
Ejemplo n.º 5
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;
}
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;
}