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

📄 core_sound.cpp

📁 3D游戏引擎 Programming Role-Playing Games with DirectX, 2nd Edition by Jim Adams
💻 CPP
📖 第 1 页 / 共 3 页
字号:

  return TRUE;
}

BOOL cSoundData::LoadWAV(char *Filename, FILE *fp)
{
  if(LoadWAVHeader(Filename, fp) == FALSE)
    return FALSE;

  if(Create() == FALSE)
    return FALSE;

  // open file, seek to position and read in data
  if(Filename != NULL) {
    if((fp=fopen(Filename, "rb"))==NULL)
      return FALSE;
  }
  fseek(fp, m_StartPos, SEEK_SET);
  fread(m_Buf, 1, m_Size, fp);
  m_StartPos = m_Pos = 0;

  // close up file
  if(Filename != NULL)
    fclose(fp);

  return TRUE;
}

BOOL cSoundData::LoadWAVHeader(char *Filename, FILE *fp)
{
  sWaveHeader Hdr;
  BOOL ReturnVal;
  long Pos;

  if(Filename == NULL && fp == NULL)
    return FALSE;

  if(Filename != NULL) {
    if((fp=fopen(Filename, "rb"))==NULL)
      return FALSE;
  }

  // Save position in file
  Pos = ftell(fp);

  // Read in header and parse
  ReturnVal = FALSE;
  fread(&Hdr, 1, sizeof(sWaveHeader), fp);
  if(!memcmp(Hdr.RiffSig, "RIFF", 4) && !memcmp(Hdr.WaveSig, "WAVE", 4) && !memcmp(Hdr.FormatSig, "fmt ", 4) && !memcmp(Hdr.DataSig, "data", 4)) {
    m_Frequency     = Hdr.SampleRate;
    m_Channels      = Hdr.Channels;
    m_BitsPerSample = Hdr.BitsPerSample;

    m_Size = m_Left = Hdr.DataSize;
    m_StartPos = m_Pos = ftell(fp);

    ReturnVal = TRUE;
  }

  // Close if we opened file
  // otherwise return to original position
  if(Filename != NULL)
    fclose(fp);
  else 
    fseek(fp, Pos, SEEK_SET);

  return ReturnVal;
}

BOOL cSoundData::Copy(cSoundData *Source)
{
  if(Source == NULL)
    return FALSE;
  
  m_Frequency     = Source->m_Frequency;
  m_Channels      = Source->m_Channels;
  m_BitsPerSample = Source->m_BitsPerSample;

  m_fp       = Source->m_fp;
  m_Ptr      = Source->m_Ptr;
  m_Size     = Source->m_Size;
  m_Left     = Source->m_Left;
  m_Pos      = Source->m_Pos;
  m_StartPos = Source->m_StartPos;

  return TRUE;
}

//////////////////////////////////////////////////////////////////////
//
// cSoundChannel Functions
//
//////////////////////////////////////////////////////////////////////
cSoundChannel::cSoundChannel()
{
  m_Sound     = NULL;
  m_pDSBuffer = NULL;
  m_pDSNotify = NULL;

  m_Event     = -1;

  m_Volume    = 0;
  m_Pan       = 0;
  m_Frequency = 0;
  m_Playing   = FALSE;
}

cSoundChannel::~cSoundChannel()
{
  Free();
}

IDirectSoundBuffer8 *cSoundChannel::GetSoundBufferCOM()
{
  return m_pDSBuffer;
}

IDirectSoundNotify8 *cSoundChannel::GetNotifyCOM()
{
  return m_pDSNotify;
}    

BOOL cSoundChannel::Create(cSound *Sound, long Frequency, short Channels, short BitsPerSample)
{
  DSBUFFERDESC dsbd;
  WAVEFORMATEX wfex;
  HANDLE       hEvent;
  DSBPOSITIONNOTIFY   dspn[4];
  IDirectSoundBuffer *pDSBuffer;

  // Free a prior channel
  Free();

  if((m_Sound = Sound) == NULL)
    return FALSE;
  if(m_Sound->GetDirectSoundCOM() == NULL)
    return FALSE;

  // Save playback format
  m_Frequency     = Frequency;
  m_BitsPerSample = BitsPerSample;
  m_Channels      = Channels;

  // Create a new sound buffer for this channel
  // Using specified format
  ZeroMemory(&wfex, sizeof(WAVEFORMATEX)); 
  wfex.wFormatTag      = WAVE_FORMAT_PCM; 
  wfex.nChannels       = (WORD)m_Channels;
  wfex.nSamplesPerSec  = m_Frequency;
  wfex.wBitsPerSample  = (WORD)m_BitsPerSample;
  wfex.nBlockAlign     = wfex.wBitsPerSample / 8 * wfex.nChannels;
  wfex.nAvgBytesPerSec = wfex.nSamplesPerSec * wfex.nBlockAlign;

  ZeroMemory(&dsbd, sizeof(DSBUFFERDESC));
  dsbd.dwSize        = sizeof(DSBUFFERDESC);
  dsbd.dwFlags       = DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLPAN | DSBCAPS_CTRLFREQUENCY | DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_LOCSOFTWARE;
  dsbd.dwBufferBytes = g_SoundBufferSize;
  dsbd.lpwfxFormat   = &wfex;
  if(FAILED(m_Sound->GetDirectSoundCOM()->CreateSoundBuffer(&dsbd, &pDSBuffer, NULL)))
    return FALSE;

  // query for newer interface
  if(FAILED(pDSBuffer->QueryInterface(IID_IDirectSoundBuffer8, (void**)&m_pDSBuffer))) {
    pDSBuffer->Release();
    return FALSE;
  }

  // Release old object - we have the newer one now
  pDSBuffer->Release();

  // Create the notification interface
  if(FAILED(m_pDSBuffer->QueryInterface(IID_IDirectSoundNotify8, (void**)&m_pDSNotify)))
  //if(FAILED(m_pDSBuffer->QueryInterface(IID_IDirectSoundNotify, (void**)&m_pDSNotify)))
    return FALSE;

  // Get an event for this
  if(m_Sound->AssignEvent(this, &m_Event, &hEvent) == FALSE)
    return FALSE;

  // Setup the 4 notification positions
  dspn[0].dwOffset = g_SoundBufferChunk - 1;
  dspn[0].hEventNotify = hEvent;
  dspn[1].dwOffset = g_SoundBufferChunk * 2 - 1;
  dspn[1].hEventNotify = hEvent;
  dspn[2].dwOffset = g_SoundBufferChunk * 3 - 1;
  dspn[2].hEventNotify = hEvent;
  dspn[3].dwOffset = g_SoundBufferSize - 1;
  dspn[3].hEventNotify = hEvent;

  if(FAILED(m_pDSNotify->SetNotificationPositions(4, dspn)))
    return FALSE;

  // set the pan and default volume
  SetVolume(100);
  SetPan(0);

  return TRUE;
}

BOOL cSoundChannel::Create(cSound *Sound, cSoundData *SoundDesc)
{
  return Create(Sound, SoundDesc->m_Frequency, SoundDesc->m_Channels, SoundDesc->m_BitsPerSample);
}

BOOL cSoundChannel::Free()
{
  // Stop any playback
  Stop();

  // Release the notification
  ReleaseCOM(m_pDSNotify);

  // Release the buffer
  ReleaseCOM(m_pDSBuffer);

  // Release event from parent cSound class
  m_Sound->ReleaseEvent(this, &m_Event);

  // Set to no parent sound
  m_Sound = NULL;

  return TRUE;
}

BOOL cSoundChannel::Play(cSoundData *Desc, long VolumePercent, long Loop)
{
  if(Desc == NULL)
    return FALSE;
  if(m_pDSBuffer == NULL)
    return FALSE;
  if(m_pDSNotify == NULL)
    return FALSE;

  // Stop any playback
  Stop();

  // Restore a lost buffer just in case
  m_pDSBuffer->Restore();

  // Setup playing information
  m_Desc.Copy(Desc);
  
  // Set looping data
  m_Loop = Loop;

  // Calculate stop section position
  if(!m_Loop)
    m_StopSection = -1;
  else
    m_StopSection = (short)(((m_Desc.m_Size * m_Loop) % g_SoundBufferSize) / g_SoundBufferChunk);

  // Buffer in data
  m_LoadSection = 0;
  BufferData();
  BufferData();
  BufferData();
  BufferData();

  // Set the volume
  SetVolume(VolumePercent);

  // Set position and begin play
  m_NextNotify = 0;
  if(FAILED(m_pDSBuffer->SetCurrentPosition(0)))
    return FALSE;
  if(FAILED(m_pDSBuffer->Play(0,0,DSBPLAY_LOOPING)))
    return FALSE;

  // Flag as playing
  m_Playing = TRUE;

  return TRUE;
}

BOOL cSoundChannel::Stop()
{
  if(m_pDSBuffer)
    m_pDSBuffer->Stop();

  m_Playing = FALSE;

  return TRUE;
}

long cSoundChannel::GetVolume()
{
  return m_Volume;
}

BOOL cSoundChannel::SetVolume(long Percent)
{
  long Volume;

  if(m_pDSBuffer == NULL)
    return FALSE;

  // calculate a usable volume level
  if(!Percent)
    Volume = DSBVOLUME_MIN;
  else 
    Volume = -20 * (100 - (Percent % 101));

  if(FAILED(m_pDSBuffer->SetVolume(Volume)))
    return FALSE;

  m_Volume = Percent % 101;

  return TRUE;
}

signed long cSoundChannel::GetPan()
{
  return m_Pan;
}

BOOL cSoundChannel::SetPan(signed long Level)
{
  signed long Pan;

  if(m_pDSBuffer == NULL)
    return FALSE;

  // calculate a usable setting
  if(Level < 0) 
    Pan = DSBPAN_LEFT / 100 * ((-Level) % 101);
  else
    Pan = DSBPAN_RIGHT / 100 * (Level % 101);

  if(FAILED(m_pDSBuffer->SetPan(Pan)))
    return FALSE;

  m_Pan = Level % 101;

  return TRUE;  
}

long cSoundChannel::GetFrequency()
{
  return m_Frequency;
}

BOOL cSoundChannel::SetFrequency(long Level)
{
  if(m_pDSBuffer == NULL)
    return FALSE;

  if(FAILED(m_pDSBuffer->SetFrequency(Level)))
    return FALSE;

  m_Frequency = Level;

  return TRUE;
}

BOOL cSoundChannel::IsPlaying()
{
  if(m_Sound == NULL || m_pDSBuffer == NULL || m_pDSNotify == NULL)
    return FALSE;

  return m_Playing;
}

BOOL cSoundChannel::BufferData()
{
  long Pos, Size;
  long ToLoad, LoadPos;
  char *Ptr;

  if(m_pDSBuffer == NULL)
    return FALSE;

  // Setup position to load in
  Pos  = (m_LoadSection % 4) * g_SoundBufferChunk;
  if(FAILED(m_pDSBuffer->Lock(Pos, g_SoundBufferChunk, (void**)&Ptr, (DWORD*)&Size, NULL, NULL, 0)))
    return FALSE;
    
  // Clear out buffer if nothing left to load
  if(!m_Desc.m_Left)
    ZeroMemory(Ptr, Size);
  else {
    // Load in the data - take looping into account
    ToLoad = Size;
    LoadPos = 0;
    for(;;) {
      if(m_Desc.m_Left > ToLoad) {
        if(m_Desc.m_fp != NULL) {
          fseek(m_Desc.m_fp, m_Desc.m_Pos, SEEK_SET);
          fread(&Ptr[LoadPos], 1, ToLoad, m_Desc.m_fp);
        } else 
          memcpy(&Ptr[LoadPos], &m_Desc.m_Ptr[m_Desc.m_Pos], ToLoad);
        m_Desc.m_Left -= ToLoad;
        m_Desc.m_Pos += ToLoad;
        break;
      } else {
        if(m_Desc.m_fp != NULL) {
          fseek(m_Desc.m_fp, m_Desc.m_Pos, SEEK_SET);
          fread(&Ptr[LoadPos], 1, m_Desc.m_Left, m_Desc.m_fp);
        } else 
          memcpy(&Ptr[LoadPos], &m_Desc.m_Ptr[m_Desc.m_Pos], m_Desc.m_Left);
        ToLoad -= m_Desc.m_Left;
        LoadPos += m_Desc.m_Left;

        // Check if we need to stop loop
        if(m_Loop >= 1) {
          m_Loop--;
          if(!m_Loop) {
            // Clear out remaining buffer space
            if(ToLoad)
              ZeroMemory(&Ptr[LoadPos], ToLoad);
            m_Desc.m_Left = 0L;
            break;
          }
        }

        m_Desc.m_Pos = m_Desc.m_StartPos;
        m_Desc.m_Left = m_Desc.m_Size;

        // See if we need to stop loading data
        if(!ToLoad)
          break;
      }
    }
  }

  // Unlock the buffer
  m_pDSBuffer->Unlock(Ptr, Size, NULL, 0);

  // Mark next section to load
  if((m_LoadSection+=1) > 3)
    m_LoadSection = 0;

  return TRUE;
}

BOOL cSoundChannel::Update()
{
  // Check for end of sound
  if(m_NextNotify == m_StopSection && !m_Desc.m_Left) {
    Stop();
  } else {
    // Buffer in more data
    BufferData();
    if((m_NextNotify+=1) > 3)
      m_NextNotify = 0;
  }

  return TRUE;
}

//////////////////////////////////////////////////////////////////////
//
// cMusicChannel Functions
//
//////////////////////////////////////////////////////////////////////
cMusicChannel::cMusicChannel()
{
  m_Sound      = NULL;
  m_pDMSegment = NULL;
  m_Volume     = 0;
}

cMusicChannel::~cMusicChannel()
{
  Free();
}

IDirectMusicSegment8 *cMusicChannel::GetSegmentCOM()

⌨️ 快捷键说明

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