2010-12-24

Windows Media Player Audio DSP Plug-in.

These are a changed DoProcessOutput and ValidateMediaType of a plug-in wizard sample code (see the Implementing DoProcessOutput article) with implementing of the Bauer stereophonic-to-binaural DSP (bs2b).



I have try to support a various media types. There are something have get success, but the main disappoint of my experience is untouched playback of WAV file with 32bit floating point samples by WMP.
There is no problem of passing to effect of 32bit floating point WAVs and of CD/HDCD on Windows7 with WMP12, but still no way to pass of 32bit float on Windows XP.

//////////////////////////////////////////////////
// CBs2bwmp::DoProcessOutput
//
// Convert the input buffer to the output buffer
//////////////////////////////////////////////////

HRESULT CBs2bwmp::DoProcessOutput(
  BYTE *pbOutputData,
  const BYTE *pbInputData,
  DWORD *cbBytesProcessed)
{
  // see if the plug-in has been disabled by the user
  if (!m_bEnabled)
  {
    memcpy(pbOutputData, pbInputData, *cbBytesProcessed);
    return S_OK;
  }

  WAVEFORMATEX *pWave = (WAVEFORMATEX *)m_mtInput.pbFormat;

  //DWORD dwSamplesToProcess = (*cbBytesProcessed / pWave->nBlockAlign) * pWave->nChannels;
  DWORD dwStereoSamplesToProcess = *cbBytesProcessed / pWave->nBlockAlign;

  bs2b.set_srate(pWave->nSamplesPerSec);

  // Note: for 8 and 16-bit samples, we assume the container is the same size as
  // the samples. For > 16-bit samples, we need to use the WAVEFORMATEXTENSIBLE
  // structure to determine the valid bits per sample.
  // ...this comment (and some other ones) is from Microsoft's Plug-in Wizard of
  // Windows Media Player (WMP) SDK which is part of
  // Microsoft Windows SDK for Windows 7 and .NET Framework 3.5 SP1.

  switch (pWave->wBitsPerSample)
  {
  case 8: // 8-bit sound is 0..255 with 128 == silence
    {
      // return no. bytes actually copied to output buffer
      //*cbBytesProcessed = dwSamplesToProcess * sizeof(BYTE);

      memcpy(pbOutputData, pbInputData, *cbBytesProcessed);
      bs2b.cross_feed((uint8_t *)pbOutputData, dwStereoSamplesToProcess);
    }
    break;

  case 16:
    {
      // return no. bytes actually copied to output buffer
      //*cbBytesProcessed = dwSamplesToProcess * sizeof(short);

      memcpy(pbOutputData, pbInputData, *cbBytesProcessed);
      bs2b.cross_feed((int16_t *)pbOutputData, dwStereoSamplesToProcess);
    }
    break;

  case 24:
    {
      // return no. bytes actually copied to output buffer
      //*cbBytesProcessed = dwSamplesToProcess * 3;

      WAVEFORMATEXTENSIBLE *pWaveXT = (WAVEFORMATEXTENSIBLE *)pWave;

      switch (pWaveXT->Samples.wValidBitsPerSample)
      {
      case 16: // not implemented yet (can't find a data to test)
        {
          *cbBytesProcessed = 0;
          return E_FAIL;
        }
        break;

      case 20: // wikipedia.org/wiki/High_Definition_Compatible_Digital#Windows_Media_Player
         // Tools menu: Tools/Options/Devices/Speakers/Properties/Performance
         // with 24bit=On - CDDA/HDCD playback hit here

        /* This commented code should be ok if the ValidBitsPerSample will be equal
         to a real bit depth of the data.
         The real bit depth is 24 bit, so, go to case of 24bit.
        {
          while (dwStereoSamplesToProcess-- > 0)
          {
            int i;
            double df[2];

            i = (char)pbInputData[2];
            i = (i << 8) | pbInputData[1];
            i = (i << 4) | (pbInputData[0] >> 4);
            df[0] = (double)i / 0x7FFFF; // normalize to [-1..1]

            i = (char)pbInputData[5];
            i = (i << 8) | pbInputData[4];
            i = (i << 4) | (pbInputData[3] >> 4);
            df[1] = (double)i / 0x7FFFF; // normalize to [-1..1]

            pbInputData += 6;

            bs2b.cross_feed(df);

            i = (int)(df[0] * 0x7FFFF);
            *pbOutputData++ = i & 0xFF;
            *pbOutputData++ = (i >> 8) & 0xFF;
            *pbOutputData++ = (i >> 16) & 0xFF;

            i = (int)(df[1] * 0x7FFFF);
            *pbOutputData++ = i & 0xFF;
            *pbOutputData++ = (i >> 8) & 0xFF;
            *pbOutputData++ = (i >> 16) & 0xFF;
          }
        }
        break;
        */

      case 24: // Can't find a test stream to hit here
      default: // ++ 24bit LPCM WAV playback hit here only (BitsPerSample!=16,20,24)
        {
          memcpy(pbOutputData, pbInputData, *cbBytesProcessed);
          bs2b.cross_feed((bs2b_int24_t *)pbOutputData, dwStereoSamplesToProcess);
        }
        break;
      }
    }
    break;

  case 32:
    {
      // return no. bytes actually copied to output buffer
      //*cbBytesProcessed = dwSamplesToProcess * 4;

      switch (pWave->wFormatTag)
      {
      case WAVE_FORMAT_IEEE_FLOAT: // 32bit float WAV playback not hit here
        {
          memcpy(pbOutputData, pbInputData, *cbBytesProcessed);
          bs2b.cross_feed((float *)pbOutputData, dwStereoSamplesToProcess);
        }
        break;

      case WAVE_FORMAT_PCM: // ++ 32bit LPCM WAV playback hit to here only
        {
          memcpy(pbOutputData, pbInputData, *cbBytesProcessed);
          bs2b.cross_feed((int32_t *)pbOutputData, dwStereoSamplesToProcess);
        }
        break;

      case WAVE_FORMAT_EXTENSIBLE:
        {
          WAVEFORMATEXTENSIBLE *pWaveXT = (WAVEFORMATEXTENSIBLE *)pWave;

          if (pWaveXT->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)
          {
            // 32bit float WAV playback not hit here
            memcpy(pbOutputData, pbInputData, *cbBytesProcessed);
            bs2b.cross_feed((float *)pbOutputData, dwStereoSamplesToProcess);
          }
          else if (pWaveXT->SubFormat == KSDATAFORMAT_SUBTYPE_PCM)
          {
            // 32bit LPCM WAV playback not hit here
            memcpy(pbOutputData, pbInputData, *cbBytesProcessed);
            bs2b.cross_feed((int32_t *)pbOutputData, dwStereoSamplesToProcess);
          }
        }
        break;

      default:
        // should never happen
        _ASSERT(false);
        *cbBytesProcessed = 0;
        return E_FAIL;
        break;
      }
    }
    break;

  default:
    // return no. bytes actually copied to output buffer
    *cbBytesProcessed = 0;
    return E_FAIL;
    break;
  }

  return S_OK;
}

//////////////////////////////////////////////////
// CBs2bwmp::ValidateMediaType
//
// Validate that the media type is acceptable
//////////////////////////////////////////////////

HRESULT CBs2bwmp::ValidateMediaType(
  const DMO_MEDIA_TYPE *pmtTarget,
  const DMO_MEDIA_TYPE *pmtPartner)
{
  // make sure the target media type has the fields we require
  if( ( MEDIATYPE_Audio != pmtTarget->majortype ) ||
    ( FORMAT_WaveFormatEx != pmtTarget->formattype ) ||
    ( pmtTarget->cbFormat < sizeof( WAVEFORMATEX )) ||
    ( NULL == pmtTarget->pbFormat) )
  {
    return DMO_E_TYPE_NOT_ACCEPTED;
  }

  // make sure the wave header has the fields we require
  WAVEFORMATEX *pWave = (WAVEFORMATEX *) pmtTarget->pbFormat;

  if ((2 != pWave->nChannels) || // stereo only for bs2b (was: 0 == pWave->nChannels)
    (0 == pWave->nSamplesPerSec) ||
    (0 == pWave->nAvgBytesPerSec) ||
    (0 == pWave->nBlockAlign) ||
    (0 == pWave->wBitsPerSample))
  {
    return DMO_E_TYPE_NOT_ACCEPTED;
  }

  // make sure this is a supported container size
  if ((8 != pWave->wBitsPerSample) &&
    (16 != pWave->wBitsPerSample) &&
    (24 != pWave->wBitsPerSample) &&
    (32 != pWave->wBitsPerSample))
  {
    return DMO_E_TYPE_NOT_ACCEPTED;
  }

  // make sure the wave format is acceptable
  switch (pWave->wFormatTag)
  {
  case WAVE_FORMAT_PCM:

    // make sure sample size is 8 or 16-bit
    if ((8 != pWave->wBitsPerSample) &&
      (16 != pWave->wBitsPerSample) &&
      (24 != pWave->wBitsPerSample) &&  // ++ 24bit LPCM WAV
      (32 != pWave->wBitsPerSample))    // ++ 32bit LPCM WAV
    {
      return DMO_E_TYPE_NOT_ACCEPTED;
    }
    break;

  case WAVE_FORMAT_IEEE_FLOAT:

    // make sure the input is sane
    if (32 != pWave->wBitsPerSample)
    {
      return DMO_E_TYPE_NOT_ACCEPTED;
    }
    break;

  case WAVE_FORMAT_EXTENSIBLE:
    {
      WAVEFORMATEXTENSIBLE *pWaveXT = (WAVEFORMATEXTENSIBLE *) pWave;

      // make sure the wave format extensible has the fields we require
      if ((KSDATAFORMAT_SUBTYPE_PCM != pWaveXT->SubFormat &&
        KSDATAFORMAT_SUBTYPE_IEEE_FLOAT != pWaveXT->SubFormat) ||
        (0 == pWaveXT->Samples.wSamplesPerBlock) ||
        (pWaveXT->Samples.wValidBitsPerSample > pWave->wBitsPerSample))
      {
        return DMO_E_TYPE_NOT_ACCEPTED;
      }

      // for 8 or 16-bit, the container and sample size must match
      if ((8 == pWave->wBitsPerSample) ||
        (16 == pWave->wBitsPerSample))
      {
        if (pWave->wBitsPerSample != pWaveXT->Samples.wValidBitsPerSample)
        {
          return DMO_E_TYPE_NOT_ACCEPTED;
        }
      }
      else 
      {
        // for any other container size, make sure the valid
        // bits per sample is a value we support
        if (//(16 != pWaveXT->Samples.wValidBitsPerSample) && // not implemented yet
          (20 != pWaveXT->Samples.wValidBitsPerSample) &&
          (24 != pWaveXT->Samples.wValidBitsPerSample) &&
          (32 != pWaveXT->Samples.wValidBitsPerSample))
        {
          return DMO_E_TYPE_NOT_ACCEPTED;
        }
      }
    }
    break;

  default:
    return DMO_E_TYPE_NOT_ACCEPTED;
    break;
  }

  // if the partner media type is configured, make sure it matches the target.
  // this is done because this plug-in requires the same input and output types
  if (GUID_NULL != pmtPartner->majortype)
  {
    if ((pmtTarget->majortype != pmtPartner->majortype) ||
      (pmtTarget->subtype != pmtPartner->subtype))
    {
      return DMO_E_TYPE_NOT_ACCEPTED;
    }

    // make sure the wave headers for the target and the partner match
    WAVEFORMATEX *pPartnerWave = (WAVEFORMATEX *) pmtPartner->pbFormat;

    if ((pWave->nChannels != pPartnerWave->nChannels) ||
      (pWave->nSamplesPerSec != pPartnerWave->nSamplesPerSec) ||
      (pWave->nAvgBytesPerSec != pPartnerWave->nAvgBytesPerSec) ||
      (pWave->nBlockAlign != pPartnerWave->nBlockAlign) ||
      (pWave->wBitsPerSample != pPartnerWave->wBitsPerSample) ||
      (pWave->wFormatTag != pPartnerWave->wFormatTag))
    {
      return DMO_E_TYPE_NOT_ACCEPTED;
    }

    // make sure the waveformatextensible types are the same
    if (pWave->wFormatTag == WAVE_FORMAT_EXTENSIBLE) 
    {
      WAVEFORMATEXTENSIBLE *pWaveXT = (WAVEFORMATEXTENSIBLE *) pWave;
      WAVEFORMATEXTENSIBLE *pPartnerWaveXT = (WAVEFORMATEXTENSIBLE *) pPartnerWave;
      if (pWaveXT->SubFormat != pPartnerWaveXT->SubFormat)
      {
        return DMO_E_TYPE_NOT_ACCEPTED;
      }
    }
  }

  // media type is valid
  return S_OK;
}
//////////////////////////////////////////////////