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

📄 sound_directsound.cxx

📁 opal的ptlib c++源程序 可以从官方网站上下载
💻 CXX
📖 第 1 页 / 共 2 页
字号:
/*
 * sound_directsound.cxx
 *
 * DirectX Sound driver implementation.
 *
 * Portable Windows Library
 *
 * Copyright (c) 2006-2007 Novacom, a division of IT-Optics
 *
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 * the License for the specific language governing rights and limitations
 * under the License.
 *
 * The Original Code is Portable Windows Library.
 *
 * The Initial Developer of the Original DirectSound Code is 
 * Vincent Luba <vincent.luba@novacom.be>
 *
 * Contributor(s): /
 *
 * $Revision: 19307 $
 * $Author: rjongbloed $
 * $Date: 2008-01-22 11:59:32 +0000 (Tue, 22 Jan 2008) $
 */

#pragma implementation "sound_directsound.h"

#include <ptlib.h>

#if defined(P_DIRECTSOUND) && ! defined(P_DIRECTSOUND_WINCE)

#include <ptlib/msos/ptlib/sound_directsound.h>

#include <math.h>

//#include <dxerr9.h>  Doesn't seem to exist for me!
#define DXGetErrorString9(r) r


#pragma comment(lib, P_DIRECTSOUND_LIBRARY)


#define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }
#define SAFE_DELETE_ARRAY(p) { if (p) { delete[] (p);   (p)=NULL; } }

/* Instantiate the PWLIBsound plugin*/ 
PCREATE_SOUND_PLUGIN(DirectSound, PSoundChannelDirectSound)

/* Callback function used to collect data from enumerated DirectX devices */
INT_PTR CALLBACK DSoundEnumCallback( GUID* pGUID, LPSTR strDesc, LPSTR strDrvName,
				     VOID* devices );

///////////////////////////////////////////////////////////////////////////////

/*
 * DESC	: Default Constructor	
 * BEHAVIOUR :
 * RETURN :
 */
PSoundChannelDirectSound::PSoundChannelDirectSound()
{
  Construct();
}

/*
 * DESC	:	PSoundChannelDirectSound Constructor. Creates the object and Initialise the device 
 * BEHAVIOUR :
 * RETURN :
 */
PSoundChannelDirectSound::PSoundChannelDirectSound (const PString &device,
						    Directions dir,
						    unsigned numChannels,
						    unsigned sampleRate,
						    unsigned bitsPerSample)
{
  Construct();
  Open (device, dir, numChannels, sampleRate, bitsPerSample);
}

/*
 * DESC	:	DirectSound Object destructor	
 * BEHAVIOUR :  Close any opened channels
 * RETURN :
 */

PSoundChannelDirectSound::~PSoundChannelDirectSound()
{
  PTRACE (4, "dsound\t (" << ((mDirection == Player) ? "Playback" : "Recording") << " ~PSoundChannelDirectSound");
  Close();
}
	
/*
 * DESC	:	Initialize Object variable	
 * BEHAVIOUR :	Every member set to NULL
 * RETURN :
 */
void 
PSoundChannelDirectSound::Construct()
{

  mAudioPlaybackBuffer = NULL;
  mAudioPrimaryPlaybackBuffer = NULL;
  sAudioPlaybackDevice = NULL;

  sAudioCaptureDevice = NULL;
  mAudioCaptureBuffer = NULL;
  
  mDXBufferSize = 0;
  mStreaming = true;
  mOutburst = 0;
  mVolume = 0;
}

/*
 * DESC	:	Opens a device with format specifications
 * BEHAVIOUR : Fetch the requested device GUID, then initialize the Device.
 * RETURN :
 */

PBoolean 
PSoundChannelDirectSound::Open (const PString & _device,
				     Directions _dir,
				     unsigned _numChannels,
				     unsigned _sampleRate,
				     unsigned _bitsPerSample)
{
  mDirection = _dir;
  mNumChannels = _numChannels;
  mSampleRate = _sampleRate;
  mBitsPerSample = _bitsPerSample;
 
  GUID deviceGUID;

  if (!GetDeviceID (_device, &deviceGUID))
    return false;
  
  PTRACE (4, "dsound\tOpen " << ((mDirection == Player) ? "Playback" : "Recording") << " Channel\n"
          << "  --> mNumChannels " << mNumChannels << '\n'
          << "  --> mSampleRate" << mSampleRate << '\n'
          << "  --> mBitsPerSample" << mBitsPerSample);
  
  Close();

  SetFormat (_numChannels,
             _sampleRate,
             _bitsPerSample);

  PBoolean result = (mDirection == Recorder) ? 
    				InitCaptureDevice (&deviceGUID) : 
				InitPlaybackDevice (&deviceGUID);

  if (!result) {
    PTRACE(4, "dsound\tCould not open device " << ((mDirection == Player) ? "Playback" : "Recording") << " failed");
  }

  return result;
}

/*
 * DESC	:	Provides Default device names
 * BEHAVIOUR :  Defines 'Default' as the default interface's name
 * RETURN :	PString
 */
PString 
PSoundChannelDirectSound::GetDefaultDevice (Directions /*dir*/)
{
  return PString ("Default");
}

/*
 * DESC	:
 * BEHAVIOUR :
 * RETURN :
 */
DirectSoundDevices 
PSoundChannelDirectSound::DevicesEnumerators (Directions dir)
{

  DirectSoundDevices devices;
  if (dir == Recorder)
    DirectSoundCaptureEnumerate( (LPDSENUMCALLBACK)DSoundEnumCallback,
				 (void*) &devices);
  else
    DirectSoundEnumerate( (LPDSENUMCALLBACK)DSoundEnumCallback,
			  (void*) &devices);

  if (devices.names.GetSize () > 1)
    {
      GUID pTemp  = (dir == Player) ? DSDEVID_DefaultPlayback  : DSDEVID_DefaultCapture;
      devices.guids [devices.names.GetSize ()] = pTemp;
      devices.names += "Default";
    }
  
  return devices;
}

/*
 * DESC	:
 * BEHAVIOUR :
 * RETURN :
 */
PBoolean 
PSoundChannelDirectSound::GetDeviceID (PString deviceName, GUID *pGUID)
{

  PTRACE (4, "dsound\tGet " << ((mDirection == Player) ? "Playback" : "Recording") << " Device ID for " << deviceName);

  *pGUID = (mDirection == Player) ? DSDEVID_DefaultPlayback  : DSDEVID_DefaultCapture;
  DirectSoundDevices devices = DevicesEnumerators (mDirection);
  
  PINDEX idx = devices.names.GetStringsIndex (deviceName);
  
  if (idx != P_MAX_INDEX) 
    *pGUID = devices.guids[idx];
  
  return true;
}

/* 
 * DESC : Provides a list of detected devices human readable names
 * BEHAVIOUR : Returns the names array of enumerated devices
 * RETURN : Names as PStringArray
 */

PStringArray 
PSoundChannelDirectSound::GetDeviceNames (Directions dir)
{
 
  PTRACE (4, "dsound\tGetDeviceNames " << ((dir == Player) ? "Playback" : "Recording") << " device Name");
  DirectSoundDevices devices = DevicesEnumerators (dir);
  return devices.names; 
}

/*
 * DESC	:
 * BEHAVIOUR :
 * RETURN :
 */
INT_PTR CALLBACK 
DSoundEnumCallback( GUID* pGUID, LPSTR strDesc, LPSTR /*strDrvName*/,
				     void* device)
{
  DirectSoundDevices* devices_array = (DirectSoundDevices *) device;

  if( pGUID )
    {
  
      if (devices_array->names.GetSize () < 20)
	{
	  GUID *pTemp  = & (*devices_array).guids [devices_array->names.GetSize ()];
	  memcpy( pTemp, pGUID, sizeof(GUID) );
	  devices_array->names += strDesc;
	  PTRACE (4, "dsound\tDevice -->  " << strDesc );
	} else
	  return PTrue;

    }

  return PTrue;
}



/*
 * DESC	:
 * BEHAVIOUR :
 * RETURN :
 */
PBoolean 
PSoundChannelDirectSound::InitPlaybackDevice(GUID *pGUID) {

  HRESULT hr;

  PTRACE (4, "dsound\t" << ((mDirection == Player) ? "Playback" : "Recording") << " InitPlaybackDevice");
  hr = DirectSoundCreate8 (pGUID,
			   &sAudioPlaybackDevice,
			   NULL);
  if (FAILED (hr)) 
    {
      PTRACE (4, "dsound\tCould not create playback device " << DXGetErrorString9 (hr));
      return false;
    }

  HWND hWnd = GetForegroundWindow();
  
  if (hWnd == NULL)
      hWnd = GetDesktopWindow();
  
  hr = sAudioPlaybackDevice->SetCooperativeLevel (hWnd,
						  DSSCL_PRIORITY);

  if (FAILED (hr)) 
    {
      PTRACE (4, "dsound\tCould not set cooperative level " << DXGetErrorString9 (hr));
      return false;
    }
  
  DSBUFFERDESC dsbd;
  ZeroMemory(&dsbd, sizeof(dsbd));
  dsbd.dwSize = sizeof(DSBUFFERDESC);
  dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
  dsbd.dwBufferBytes = 0;
  dsbd.lpwfxFormat = NULL;

  if ( FAILED(sAudioPlaybackDevice->CreateSoundBuffer(&dsbd, 
						      &mAudioPrimaryPlaybackBuffer, 
						      NULL)) ) 
    {
      PTRACE (4, "dsound\tCould not create primary buffer " << DXGetErrorString9 (hr));
      return false;
    }
  return PTrue;
}

/*
 * DESC	:
 * BEHAVIOUR :
 * RETURN :
 */
PBoolean 
PSoundChannelDirectSound::InitCaptureDevice(GUID *pGUID) {

  HRESULT hr;
  hr = DirectSoundCaptureCreate8 (pGUID,
				  &sAudioCaptureDevice,
				  NULL);
  if (FAILED (hr)) 
    {
      PTRACE (4, "dsound\tCould not create Capture device " << DXGetErrorString9 (hr));
      return false;
    }
  return PTrue;
}

/*
 * DESC	:	
 * BEHAVIOUR :
 * RETURN :
 */
PBoolean 
PSoundChannelDirectSound::Setup()
{
  PTRACE (4, "dsound\t" << ((mDirection == Player) ? "Playback" : "Recording") << " Setup");
  PBoolean no_error = PTrue;

  return no_error;
}


/*
 * DESC	:	
 * BEHAVIOUR :
 * RETURN :
 */

PBoolean 
PSoundChannelDirectSound::Close()
{

  PTRACE (4, "dsound\tClosing " << ((mDirection == Player) ? "Playback" : "Recording") << ") Channel");
  
  PWaitAndSignal mutex(bufferMutex);
  switch (mDirection) 
    {

    case Player:
      if (mAudioPlaybackBuffer)
	mAudioPlaybackBuffer->Stop ();
      SAFE_RELEASE(mAudioPlaybackBuffer);
      SAFE_RELEASE(mAudioPrimaryPlaybackBuffer);
      SAFE_RELEASE(sAudioPlaybackDevice);
      break;

    case Recorder:
      if (mAudioCaptureBuffer)
	mAudioCaptureBuffer->Stop ();
      SAFE_RELEASE(mAudioCaptureBuffer);
      SAFE_RELEASE(sAudioCaptureDevice);
      break;
    }
  isInitialised = false;
  return PTrue;
}

/*
 * DESC	:	Compute the freeSpace in a DirectX Circular Buffer
 * BEHAVIOUR :
 * RETURN 	: the size
 */

DWORD 
PSoundChannelDirectSound::GetDXBufferFreeSpace ()
{
  DWORD dwCursor = 0;
  PINDEX freeSpace;

  if (mDirection == Player)
      mAudioPlaybackBuffer->GetCurrentPosition (&dwCursor, NULL);
  else
      mAudioCaptureBuffer->GetCurrentPosition (&dwCursor, NULL);
  
  freeSpace = mDXBufferSize - (mDXOffset - dwCursor);
  if (freeSpace > mDXBufferSize) 
    freeSpace -= mDXBufferSize; // write_offset < play_offset

  return freeSpace;
}

/* 
 * DESC : 	Write Method is called by the playback device
 * BEHAVIOUR: 	It writes (len) bytes of input data (*buf) into the device circular buffer.  
 * 		In case data to write are bigger than the free space left in the buffer, it writes them than sleep while directx cursor move forward to leave 
 * RETURN :	PTrue if successful and PFalse otherwise.	
 */

PBoolean 
PSoundChannelDirectSound::Write (const void *buf, 
				 PINDEX len)
{

  PINDEX to_write = len, written = 0;
  BYTE * input_buffer = (BYTE*) buf;

  if (!isInitialised)
    {
      PTRACE (4, "dsound\tWrite Failed: Device not initialised :");
      return false;
    }

  //Wait for Mutex signal
  PWaitAndSignal mutex(bufferMutex);

  lastWriteCount = 0;

  while (to_write > mOutburst)
    {

      /*
      //Adjust to blockalign
      if (mOutburst != 0)
      write_in_cycle = (write_in_cycle / mOutburst ) * mOutburst;
      */

      /* Write data from buf to circular buffer */
      written = WriteToDXBuffer (input_buffer, 
				 PMIN ((PINDEX)GetDXBufferFreeSpace (), to_write));

      if (HasPlayCompleted ()) 
	{
	  mAudioPlaybackBuffer->Play (0, 
				      0, 
				      mStreaming ? DSBPLAY_LOOPING : 0L);
	}

      //Move the cursor into the data buffer
      input_buffer += written;
      //Update the written buffer count for PWLIB usage
      lastWriteCount += written;
      //Set the count of buffers left to write
      to_write -= written;

      /* Wait as buffer is played */
      FlushBuffer ();
    }
  return true;

}

/* 
 * DESC : 	Read Method is called by the recording device
 * BEHAVIOUR: 	It reads (len) bytes from the device circular buffer to an input data (*buf).  
 * RETURN :	PTrue if successful and PFalse otherwise.	
 */

PBoolean 
PSoundChannelDirectSound::Read (void * buf, PINDEX len)
{

  PINDEX read = 0, to_read = len;
  BYTE * output_buffer = (BYTE*) buf;
  
  PWaitAndSignal mutex(bufferMutex);

  if (!isInitialised) 
    {
      PTRACE (4, "dsound\tRead : Device not initialised ");
      return false;
    }
 
  lastReadCount = 0;

  while (to_read > mOutburst) 
    {
     
      /* Will read from device buffer minimum between the data left to read, and the available space */
      read = ReadFromDXBuffer (output_buffer,
			       PMIN ((PINDEX)GetDXBufferFreeSpace (), to_read));
      to_read -= read;
      lastReadCount += read;
      output_buffer += read; /* Increment the buffer pointer */
      
      /* Wait as buffer is being played */
      FlushBuffer ();
    }

  return true;
}

/*
 * DESC:   Writes (len) bytes from the buffer (*buf) to DirectX sound device buffer
 * BEHAVIOUR :  Locks the buffer on the requested size; In case buffer was lost, tries to restore it.
 * 	  	Copies the data into the buffer
 * 	  	Unlock the buffer
 * RETURN    : Returns the size actually written
 */

PINDEX 
PSoundChannelDirectSound::WriteToDXBuffer (const void *buf, 
					   PINDEX len) 
{

  HRESULT hr;
  LPVOID lpvWrite1, lpvWrite2;
  DWORD dwLength1, dwLength2;
  PINDEX written = 0;

  /***  Lock the buffer   ***/
  hr = mAudioPlaybackBuffer->Lock (mDXOffset,
				   len,
				   &lpvWrite1,
				   &dwLength1,
				   &lpvWrite2,
				   &dwLength2,
				   0L);

  if (hr == DSERR_BUFFERLOST) 
    {
      //Buffer was lost, need to restore it
      PTRACE (4, "dsound\tPlayback buffer was lost, Need to restore.");
      mAudioPlaybackBuffer->Restore ();
      hr = mAudioPlaybackBuffer->Lock (mDXOffset,
				       len,
				       &lpvWrite1,
				       &dwLength1,
				       &lpvWrite2,
				       &dwLength2,
				       0L);
    }

  if (!FAILED (hr)) 
    {
      /***  Copy memory into buffer  ***/
      memcpy (lpvWrite1, buf, dwLength1);
      if (lpvWrite2 != NULL)
	memcpy (lpvWrite2, (BYTE *) buf + dwLength1, dwLength2);

      written = dwLength1 + dwLength2;
      
      /***  Unlock the buffer   ***/
      mAudioPlaybackBuffer->Unlock (lpvWrite1,
				    dwLength1,
				    lpvWrite2,
				    dwLength2);
      //Move write cursor
      mDXOffset += written;
      mDXOffset %= mDXBufferSize;
    } 

⌨️ 快捷键说明

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