⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 dsoundcardpmo.cpp

📁 FreeAMP(MP3播放)程序源代码-用来研究MP3解码
💻 CPP
📖 第 1 页 / 共 2 页
字号:
/*____________________________________________________________________________

  FreeAmp - The Free MP3 Player

  Portions Copyright (C) 1998-1999 EMusic.com
  Portions Copyright (C) 1998.Sylvain Rebaud (soothe@jps.net)

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

  $Id: dsoundcardpmo.cpp,v 1.24 2000/06/22 18:53:10 elrod Exp $
____________________________________________________________________________*/

/* system headers */
#include <windows.h>
#include <dsound.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>

/* project headers */
#include "config.h"
#include "dsoundcardpmo.h"
#include "eventdata.h"
#include "preferences.h"
#include "facontext.h"
#include "log.h"

#define DB Debug_v("%s:%d", __FILE__, __LINE__);

// XXX: This can and should be gotten rid of
static FAContext *g_Context = 0;

#define WRITESLEEPTIME        50      /*  50 msecs  */

extern "C"
{
  PhysicalMediaOutput* Initialize(FAContext *context)
  {
      g_Context = context;
      return new DSoundCardPMO(context);
  }
}

static
bool
CALLBACK
EnumThreadWndProc(  HWND hwnd,
                    LPARAM lParam )
{
  char  strName[128];
  int   nCount;

  DSoundCardPMO*  pDSoundCardPmo    = (DSoundCardPMO*)  lParam;
  pDSoundCardPmo->m_hMainWndHandle  = hwnd;

  nCount = GetWindowText(hwnd, strName, 128);
  // If we found our main window, stop the enumeration
  if (!_stricmp(strName, "FreeAmp"))
    return false;

  return true;
}

static
bool
CALLBACK
DSEnumCallback( LPGUID  lpGuid,
                LPSTR   strDescription,
                LPSTR   strModule,
                LPVOID  lpContext)
{
  DSDevice  *pDSDevice;
  HRESULT   hResult;
  size_t    size = 0;

  DSoundCardPMO*  pDSoundCardPmo = (DSoundCardPMO *)  lpContext;

  if (!pDSoundCardPmo)
    return false;

  if (pDSoundCardPmo->m_pDSDevices)
    size = _msize( pDSoundCardPmo->m_pDSDevices );

  pDSDevice = (DSDevice *)realloc(pDSoundCardPmo->m_pDSDevices, size + sizeof(DSDevice));

  if (pDSDevice == NULL)
  {
    g_Context->log->Log(LogOutput, "error reallocating DSDevices memory...\n");
    return false;
  }

  pDSoundCardPmo->m_pDSDevices = pDSDevice;
  pDSDevice = &pDSoundCardPmo->m_pDSDevices[pDSoundCardPmo->m_nNbDSDevices];

  if (lpGuid)
  {
    pDSDevice->pGuid = (GUID *)malloc(sizeof(GUID));
    memcpy(pDSDevice->pGuid, lpGuid, sizeof(GUID));
  }
  else
    pDSDevice->pGuid = NULL;

  pDSDevice->szName = strdup(strModule);
  pDSDevice->szDescription = strdup(strDescription);

  hResult = DirectSoundCreate(pDSDevice->pGuid, &pDSDevice->pDSObject, NULL);
  if (FAILED(hResult))
  {
    g_Context->log->Log(LogOutput, "error creating DirectSound Object...\n");
    if (lpGuid)
      free((void *) pDSDevice->pGuid);

    free((void *) pDSDevice->szName);
    free((void *) pDSDevice->szDescription);

    pDSDevice = (DSDevice *)realloc(pDSoundCardPmo->m_pDSDevices, size);

    if (pDSDevice != NULL)
      pDSoundCardPmo->m_pDSDevices = pDSDevice;

    return false;
  }

  pDSDevice->lCreated = 0;

  pDSoundCardPmo->m_nNbDSDevices++;

  return true;
}

DSoundCardPMO::
DSoundCardPMO(FAContext *context):
     PhysicalMediaOutput(context)
{
  HRESULT hResult;
  MMRESULT mmresult = 0;
  Int32PropValue *pProp;
  HWND hWnd;

  m_samples_per_second  = 0;
  m_data_size           = 0;
  m_wfex                = NULL;
  m_initialized         = false;
  m_pBufferThread       = NULL;
  m_iLastFrame          = -1;
  m_iTotalBytesWritten  = 0;

  m_nNbDSDevices        = 0;
  m_pDSDevices          = NULL;
  m_nCurrentDevice      = -1;
  m_bDSEnumFailed       = false;
  m_hMainWndHandle      = NULL;
  m_pDSWriteSem         = new Semaphore(1);
  m_pDSBufferSem        = new Semaphore(0);
  m_DSBufferManager.pDSSecondaryBuffer  = NULL;
  m_bIsBufferEmptyNow   = true;
  m_bLMCsaidToPlay      = false;

  // Get a list of windows handle
  BOOL breturn = EnumWindows((WNDENUMPROC) EnumThreadWndProc, (LPARAM) this);
  // get a list of devices
  hResult = DirectSoundEnumerate((LPDSENUMCALLBACK) DSEnumCallback, this);

  if (FAILED(hResult))
  {
    m_bDSEnumFailed = true;
  }

  if (!m_pBufferThread)
  {
    m_pBufferThread = Thread::CreateThread();
    assert(m_pBufferThread);
    m_pBufferThread->Create(DSoundCardPMO::StartWorkerThread, this);
  }
  
  if (IsError(m_pContext->props->GetProperty("MainWindow", 
             (PropValue **)&pProp)))
     return;        
  else
     hWnd = (HWND)pProp->GetInt32();
  
  mmresult = mixerOpen(&m_hmixer, 0, (DWORD)hWnd, NULL, 
                        MIXER_OBJECTF_MIXER | CALLBACK_WINDOW);
  if (mmresult != MMSYSERR_NOERROR)
  {
      m_hmixer = NULL;
      m_pContext->log->Error("Cannot open Mixer device.");
  }    
  SetupVolumeControl();
}

DSoundCardPMO::
~DSoundCardPMO()
{
  int i;

  m_bExit = true;
  m_pSleepSem->Signal();
  m_pPauseSem->Signal();
  m_pDSBufferSem->Signal();
  m_pDSWriteSem->Signal();

  mixerClose(m_hmixer);

  if (m_pBufferThread)
  {
    m_pBufferThread->Join();
    delete m_pBufferThread;
    m_pBufferThread = NULL;
  }

  delete m_pDSBufferSem;
  delete m_pDSWriteSem;

  if(m_initialized)
  {
    delete m_wfex;

    // delete the direct sound secondary buffer
    if (m_DSBufferManager.pDSSecondaryBuffer)
    {
      m_DSBufferManager.pDSSecondaryBuffer->Stop();
      m_DSBufferManager.pDSSecondaryBuffer->Release();
    }

    // Do I need to release primary as well ?
  }

  if (m_pDSDevices != NULL)
  {
    for (i=0;i<m_nNbDSDevices;i++)
    {
      if (&m_pDSDevices[i] != NULL)
      {
        // Should I destroy the DirectSound object as well ?

        if ((&m_pDSDevices[i])->pGuid)
        {
          free((void *) (&m_pDSDevices[i])->pGuid);
          (&m_pDSDevices[i])->pGuid = NULL;
        }

        free((void *) (&m_pDSDevices[i])->szName);
        (&m_pDSDevices[i])->szName = NULL;
        free((void *) (&m_pDSDevices[i])->szDescription);
        (&m_pDSDevices[i])->szDescription = NULL;
      }
    }
    if (m_pDSDevices)
    {
      free((void *) (m_pDSDevices));
      m_pDSDevices = NULL;
    }
  }
}

bool DSoundCardPMO::SetupVolumeControl(void)
{
    MIXERLINE mxl;
    MIXERCONTROL mxc;
    MIXERLINECONTROLS mxlc;
    
    m_oDstLineName = "";
    m_oVolumeControlName = "";

    if (m_hmixer == NULL)
        return FALSE;

    mxl.cbStruct = sizeof(MIXERLINE);
    mxl.dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT;
    if (mixerGetLineInfo((HMIXEROBJ)m_hmixer,
                         &mxl,
                         MIXER_OBJECTF_HMIXER |
                         MIXER_GETLINEINFOF_COMPONENTTYPE)
        != MMSYSERR_NOERROR)
        return false;

    mxlc.cbStruct = sizeof(MIXERLINECONTROLS);
    mxlc.dwLineID = mxl.dwLineID;
    mxlc.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
    mxlc.cControls = 1;
    mxlc.cbmxctrl = sizeof(MIXERCONTROL);
    mxlc.pamxctrl = &mxc;

    if (mixerGetLineControls((HMIXEROBJ)m_hmixer,
                             &mxlc,
                             MIXER_OBJECTF_HMIXER |
                             MIXER_GETLINECONTROLSF_ONEBYTYPE)
        != MMSYSERR_NOERROR)
        return false;

    // record dwControlID
    m_oDstLineName = mxl.szName;
    m_oVolumeControlName = mxc.szName;
    m_dwMinimum = mxc.Bounds.dwMinimum;
    m_dwMaximum = mxc.Bounds.dwMaximum;
    m_dwVolumeControlID = mxc.dwControlID;

    return TRUE;
}


void
DSoundCardPMO::
GetVolume(int32 &left, int32 &right)
{
    MIXERCONTROLDETAILS_UNSIGNED mxcdVolume[2];
    MIXERCONTROLDETAILS mxcd;
    int                 ret;
    
    mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
    mxcd.dwControlID = m_dwVolumeControlID;
    mxcd.cChannels = 2;
    mxcd.cMultipleItems = 0;
    mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
    mxcd.paDetails = &mxcdVolume;
    ret = mixerGetControlDetails((HMIXEROBJ)m_hmixer,
                                 &mxcd,
                                 MIXER_OBJECTF_HMIXER |
                                 MIXER_GETCONTROLDETAILSF_VALUE);
    if (ret != MMSYSERR_NOERROR)
        return;

    left = (int)(((float)((mxcdVolume[0].dwValue - m_dwMinimum) * 100) /  
                  (float)(m_dwMaximum - m_dwMinimum)) + 0.5); 
    right = (int)(((float)((mxcdVolume[1].dwValue - m_dwMinimum) * 100) /  
                   (float)(m_dwMaximum - m_dwMinimum)) + 0.5); 
}

void
DSoundCardPMO::
SetVolume(int32 left, int32 right)
{
    DWORD dwLeft, dwRight;
    
    dwLeft = (left * (m_dwMaximum - m_dwMinimum) / 100);
    dwRight = (right * (m_dwMaximum - m_dwMinimum) / 100);

    MIXERCONTROLDETAILS_UNSIGNED mxcdVolume[2];
    MIXERCONTROLDETAILS mxcd;
    
    memcpy(&mxcdVolume[0], &dwLeft, sizeof(MIXERCONTROLDETAILS_UNSIGNED));
    memcpy(&mxcdVolume[1], &dwRight, sizeof(MIXERCONTROLDETAILS_UNSIGNED));
    
    mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
    mxcd.dwControlID = m_dwVolumeControlID;
    mxcd.cChannels = 2;
    mxcd.cMultipleItems = 0;
    mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
    mxcd.paDetails = &mxcdVolume;
    
    mixerSetControlDetails((HMIXEROBJ)m_hmixer, &mxcd, 
                           MIXER_OBJECTF_HMIXER | 
                           MIXER_SETCONTROLDETAILSF_VALUE);
}

Error
DSoundCardPMO::
Init(OutputInfo* info)
{
  HRESULT       hResult;
  DSBUFFERDESC  DSBufferInfo;
  Error         result  = kError_UnknownErr;
  int32         iNewSize = iDefaultBufferSize;
  PropValue     *pProp;

  if ((m_bDSEnumFailed) || (m_nNbDSDevices == 0) || (m_pDSDevices == NULL))
    return result;

  m_samples_per_second  = info->samples_per_second;
  m_data_size           = info->max_buffer_size;

  m_pPropManager->GetProperty("MainHwnd", &pProp);
  if (pProp)
  {
    m_hMainWndHandle = (HWND) atoi(((StringPropValue *)pProp)->GetString());
  }

  m_iBytesPerSample = info->number_of_channels * (info->bits_per_sample >> 3);

  m_DSBufferManager.state             = UNDERFLOW;
  // Use Primary Sound Driver
  m_nCurrentDevice                    = 0;
  m_DSBufferManager.pDSDevice         = &m_pDSDevices[m_nCurrentDevice];
  m_DSBufferManager.pDSSecondaryBuffer= NULL;

  // What if the Callback hasn't finished yet here ?
  // Do it only if it didn't happen already
  if (InterlockedExchange(&m_DSBufferManager.pDSDevice->lCreated,1) == 0)
  {
    // the device was not initialized
    // create primary buffer

    // Should set cooperative level here to set primary buffer format
    // but we need a handle to a window ... need to talk to Mark about that
    hResult = m_DSBufferManager.pDSDevice->pDSObject->SetCooperativeLevel( m_hMainWndHandle,
                                                                                                                                                        DSSCL_PRIORITY);
    if (FAILED(hResult))
    {
      m_pContext->log->Log(LogOutput, "error cannot set DirectSound DSSCL_PRIORITY Cooperative Level...\n");
      return result;
    }

    memset(&DSBufferInfo, 0, sizeof(DSBUFFERDESC));
    DSBufferInfo.dwSize   = sizeof(DSBUFFERDESC);
    DSBufferInfo.dwFlags  = DSBCAPS_PRIMARYBUFFER;

    hResult = m_DSBufferManager.pDSDevice->pDSObject->CreateSoundBuffer(
                                              &DSBufferInfo,
                                              &m_DSBufferManager.pDSPrimaryBuffer,
                                              NULL);
    if (FAILED(hResult))
    {
      m_pContext->log->Log(LogOutput, "error Creating DirectSound Primary Buffer...\n");
      return result;
    }

    // try to set the primary buffer to 44.1 stereo 16 bits
    if (SUCCEEDED(hResult))
    {
      WAVEFORMATEX format;

      format.wBitsPerSample   = 16;
      format.wFormatTag       = WAVE_FORMAT_PCM;
      format.nChannels        = 2;
      format.nSamplesPerSec   = 44100;
      format.nAvgBytesPerSec  = 2*2*44100;
      format.nBlockAlign      = 2*2;
      format.cbSize           = 0;

      hResult = m_DSBufferManager.pDSPrimaryBuffer->SetFormat(&format);
      if (FAILED(hResult))
      {
        m_pContext->log->Log(LogOutput, "Cannot Set DirectSound Primary Buffer Format...\n");
        return result;
      }
    }
  }

  memset(&DSBufferInfo, 0, sizeof(DSBUFFERDESC));
  m_wfex              = new WAVEFORMATEX;

  // compute the levels for monitoring
  m_DSBufferManager.dwBufferSize  = 32*m_data_size;
  m_DSBufferManager.dwOverflow    = m_DSBufferManager.dwBufferSize - m_data_size;
  m_DSBufferManager.dwUnderflow   = m_data_size;
  m_DSBufferManager.dwTrigger     = 3*m_data_size;
  m_DSBufferManager.dwZerofill    = 4*m_data_size;

  m_wfex->wBitsPerSample          = info->bits_per_sample;
  m_wfex->wFormatTag              = WAVE_FORMAT_PCM;
  m_wfex->nChannels               = (WORD) info->number_of_channels;
  m_wfex->nSamplesPerSec          = info->samples_per_second;
  m_wfex->nAvgBytesPerSec         = info->number_of_channels * info->samples_per_second *
                                      (info->bits_per_sample >> 3);
  m_wfex->nBlockAlign             = (WORD) (info->number_of_channels * (info->bits_per_sample >> 3));
  m_wfex->cbSize                  = 0;

  DSBufferInfo.dwSize             = sizeof(DSBUFFERDESC);
  DSBufferInfo.dwFlags            =   DSBCAPS_CTRLFREQUENCY       |
                                      DSBCAPS_CTRLPAN             |
                                      DSBCAPS_CTRLVOLUME          |
                                      DSBCAPS_GETCURRENTPOSITION2 |
                                      DSBCAPS_GLOBALFOCUS;
  DSBufferInfo.dwBufferBytes      = m_DSBufferManager.dwBufferSize;
  DSBufferInfo.lpwfxFormat        = m_wfex;

  // Create Secondary Buffer
  hResult = m_DSBufferManager.pDSDevice->pDSObject->CreateSoundBuffer(
                                            &DSBufferInfo,
                                            &m_DSBufferManager.pDSSecondaryBuffer,
                                            NULL);
  if (FAILED(hResult))
  {
    m_pContext->log->Log(LogOutput, "error Creating DirectSound Secondary Buffer...\n");
    m_DSBufferManager.pDSSecondaryBuffer = NULL;
    return result;
  }

  m_initialized = true;

  // Clear the new buffer
  DSClear();

  // Release Semaphore so that the DSMonitorBufferThread can start polling buffer state
  m_pDSBufferSem->Signal();

  return kError_NoErr;
}

void 
DSoundCardPMO::
Pause()
{
  DSReset();
  m_bLMCsaidToPlay = false;
  m_bIsBufferEmptyNow = false;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -