Jump to content
  • Advertisement
Sign in to follow this  
cloud007

help for anyone with sound problems

This topic is 4911 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello, everyone. I just thought that I'd post this for the heck of it, since I just found out a great way to play WAV and MP3 files without having sound gaps or large memory allocations. I found code for doing this in the documentation for VC++ 2003, added a few modifications, and converted it to MFC: Header file:
#pragma once

#include <afxmt.h> // synchronization classes
#include <amstream.h> // DirectSound interfaces

#pragma comment(lib, "strmiids.lib")

class WaveBuffer {
 public:
  ~WaveBuffer();
  bool init(HWAVEOUT hWo, int size); // initialization procedure
  bool write(PBYTE pData, int nBytes, int &bytesWritten); // write wave data to buffer
  void flush(); // flush buffer to device
 private:
  WAVEHDR m_hdr; // structure for writing
  HWAVEOUT m_hWo; // device handle
  int m_nBytes; // bytes written so far
};

class WaveOut {
 public:
  WaveOut(LPCWAVEFORMATEX format, int nBuffers, int bufferSize);
  ~WaveOut();
  void write(PBYTE pData, int nBytes); // write wave data to buffers
  void flush(); // flush buffers to device
  void wait();
  void reset(); // reset device
 private:
  CSemaphore *m_cSem; // resource semaphore
  int m_nBuffers; // number of buffers
  int m_currentBuffer; // current buffer
  bool m_noBuffer; // true if no buffer is available at the moment
  WaveBuffer *m_hdrs; // buffers
  HWAVEOUT m_hWo; // device handle
  bool m_playing; // true if currently playing
};

class WaveFile {
 public:
  WaveFile(LPCWSTR fileName);
  ~WaveFile();
  CWinThread *playFile(bool loop); // returns a thread object for synchronization
  void stopFile();
  HRESULT renderStreamToDevice(); // thread calls this function
 private:
  HRESULT renderFileToMmstream(LPCWSTR fileName); // initialization
  // DirectX interfaces
  IMultiMediaStream *m_pMmstream;
  IAudioData *m_pAudioData;
  IAudioStreamSample *m_pSample;
  PBYTE m_pBuffer; // audio buffer
  WaveOut *m_wo; // device class
  bool m_playing;
  bool m_shouldLoop;
};

Implementation file:
#include "stdafx.h" // standard includes
#include "wavefile.h" // class definitions

#pragma warning(disable:4311 4312) // size conversion warnings

#define WAVEFILE_DATA_SIZE 5000

void CALLBACK waveCallback(HWAVEOUT hWo, UINT msg, DWORD user, DWORD par1, DWORD par2);
UINT playProc(LPVOID vParam);

WaveBuffer::~WaveBuffer() {
  if(m_hdr.lpData) {
    m_hdr.dwFlags = 0; // reset flags
    // uninitialize and delete allocated memory
    ::waveOutUnprepareHeader(m_hWo, &m_hdr, sizeof(WAVEHDR));
    ::LocalFree((HLOCAL)m_hdr.lpData);
  }
}

bool WaveBuffer::init(HWAVEOUT hWo, int size) {
  m_hWo = hWo;
  m_nBytes = 0;
  // initialize WAVEHDR structure
  memset(&m_hdr, 0, sizeof(WAVEHDR));
  m_hdr.lpData = (LPSTR)::LocalAlloc(LMEM_FIXED, size);
  if(!m_hdr.lpData)
    return false;
  m_hdr.dwBufferLength = size;
  ::waveOutPrepareHeader(m_hWo, &m_hdr, sizeof(WAVEHDR));
  return true;
}

// returns true if data was written to device
bool WaveBuffer::write(PBYTE pData, int nBytes, int &bytesWritten) {
  bytesWritten = min((int)m_hdr.dwBufferLength - m_nBytes, nBytes);
  memcpy(m_hdr.lpData + m_nBytes, pData, bytesWritten);
  m_nBytes += bytesWritten;
  if(m_nBytes == (int)m_hdr.dwBufferLength) {
    flush(); // write to device
    return true;
  }
  return false;
}

void WaveBuffer::flush() {
  m_nBytes = 0; // reset pointer
  ::waveOutWrite(m_hWo, &m_hdr, sizeof(WAVEHDR)); // write to device
}

void CALLBACK waveCallback(HWAVEOUT hWo, UINT msg, DWORD user, DWORD par1, DWORD par2) {
  if(msg == WOM_DONE)
    ((CSemaphore *)user)->Unlock(1); // unlock one buffer
}

WaveOut::WaveOut(LPCWAVEFORMATEX format, int nBuffers, int bufferSize) {
  m_nBuffers = nBuffers;
  m_currentBuffer = 0;
  m_noBuffer = true;
  m_cSem = new CSemaphore(m_nBuffers, m_nBuffers);
  m_hdrs = new WaveBuffer[m_nBuffers];
  m_playing = false;
  ::waveOutOpen(&m_hWo, WAVE_MAPPER, format, (DWORD)waveCallback, (DWORD)m_cSem, CALLBACK_FUNCTION);
  for(int i = 0; i < m_nBuffers; ++i)
    m_hdrs.init(m_hWo, bufferSize);
}

WaveOut::~WaveOut() {
  ::waveOutReset(m_hWo); // purge any playing sounds
  delete[] m_hdrs;
  ::waveOutClose(m_hWo);
  delete m_cSem;
}

void WaveOut::write(PBYTE pData, int nBytes) {
  m_playing = true;
  // do until either the user stops or we run out of data
  while(m_playing && nBytes) {
    if(m_noBuffer) {
      // wait for an available buffer
      ::WaitForSingleObject(*m_cSem, INFINITE);
      m_noBuffer = false;
    }
    int nWritten;
    if(m_hdrs[m_currentBuffer].write(pData, nBytes, nWritten)) {
      m_noBuffer = true;
      m_currentBuffer = (m_currentBuffer + 1) % m_nBuffers;
      nBytes -= nWritten;
      pData += nWritten;
    }
    else
      break;
  }
}

void WaveOut::flush() {
  if(!m_noBuffer) {
    m_hdrs[m_currentBuffer].flush();
    m_noBuffer = true;
    m_currentBuffer = (m_currentBuffer + 1) % m_nBuffers;
  }
}

void WaveOut::wait() {
  flush();
  for(int i = 0; i < m_nBuffers; ++i)
    ::WaitForSingleObject(*m_hSem, INFINITE); // wait for buffers
  m_hSem->Unlock(m_nBuffers); // release buffer locks
}

void WaveOut::reset() {
  m_playing = false; // stop playing
  ::waveOutReset(m_hWo); // stop sounds
}

WaveFile::WaveFile(LPCWSTR fileName) {
  m_wo = NULL;
  m_playing = false;
  m_shouldLoop = false;
  renderFileToMmstream(fileName);
}

WaveFile::~WaveFile() {
  m_wo->reset();
  m_playing = false;
  delete m_wo;
  m_pSample->Release();
  m_pAudioData->Release();
  m_pMmstream->Release();
  ::LocalFree((HLOCAL)m_pBuffer);
}

CWinThread *WaveFile::playFile(bool loop) {
  m_shouldLoop = loop;
  m_playing = true;
  return AfxBeginThread(playProc, (void *)this);
}

void WaveFile::stopFile() {
  if(!m_playing)
    return; // don't bother with it if we aren't currently playing
  m_shouldLoop = false;
  m_playing = false;
  m_pMmstream->Seek(0); // reset file pointer
}

HRESULT WaveFile::renderStreamToDevice() {
  HRESULT hr;
  CEvent ev;
  do {
    while(m_playing) {
      hr = m_pSample->Update(0, ev, NULL, 0); // update file buffer
      if(FAILED(hr) || (hr == MS_S_ENDOFSTREAM))
        break;
      ::WaitForSingleObject(ev, INFINITE); // wait for reading to finish
      DWORD len;
      m_pAudioData->GetInfo(NULL, NULL, &len); // get data length
      m_wo->write(m_pBuffer, len); // write to device
    }
    m_pMmstream->Seek(0); // reset file pointer
  } while(m_shouldLoop);
  m_wo->reset();
  return S_OK;
}

// internal initialization
HRESULT WaveFile::renderFileToMmstream(LPCWSTR fileName) {
  // This section is pretty much taken whole-cloth from documentation.
  if(strlen(fileName) > MAX_PATH)
    return E_INVALIDARG;
  IAMMultiMediaStream *pAmstream;
  HRESULT hr = ::CoCreateInstance(CLSID_AMMultiMediaStream, NULL, CLSCTX_INPROC_SERVER, IID_IAMMultiMediaStream, (void **)&pAmstream);
  if(FAILED(hr))
    return hr;
  pAmstream->Initialize(STREAMTYPE_READ, AMMSF_NOGRAPHTHREAD, NULL);
  pAmstream->AddMediaStream(NULL, &MSPID_PrimaryAudio, 0, NULL);
  hr = pAmstream->OpenFile(fileName, AMMSF_RUN);
  delete[] wFileName;
  if(FAILED(hr)) {
    pAmstream->Release();
    return hr;
  }
  hr = pAmstream->QueryInterface(IID_IMultiMediaStream, (void **)&m_pMmstream);
  pAmstream->Release();
  if(FAILED(hr))
    return hr;
  WAVEFORMATEX wfx;
  IMediaStream *pStream = NULL;
  IAudioMediaStream *pAudioStream = NULL;
  hr = m_pMmstream->GetMediaStream(MSPID_PrimaryAudio, &pStream);
  if(FAILED(hr))
    return hr;
  pStream->QueryInterface(IID_IAudioMediaStream, (void **)&pAudioStream);
  pStream->Release();
  hr = ::CoCreateInstance(CLSID_AMAudioData, NULL, CLSCTX_INPROC_SERVER, IID_IAudioData, (void **)&m_pAudioData);
  if(FAILED(hr)) {
    pAudioStream->Release();
    return hr;
  }
  m_pBuffer = (PBYTE)::LocalAlloc(LMEM_FIXED, WAVEFILE_DATA_SIZE);
  if(!m_pBuffer) {
    pAudioStream->Release();
    return E_OUTOFMEMORY;
  }
  pAudioStream->GetFormat(&wfx);
  m_pAudioData->SetBuffer(WAVEFILE_DATA_SIZE, m_pBuffer, 0);
  m_pAudioData->SetFormat(&wfx);
  hr = pAudioStream->CreateSample(m_pAudioData, 0, &m_pSample);
  pAudioStream->Release();
  if(FAILED(hr))
    return hr;
  m_wo = new WaveOut(&wfx, 4, (wfx.nSamplesPerSec / 11025) * 1024);
  return S_OK;
}

UINT playProc(LPVOID vParam) {
  ((WaveFile *)vParam)->renderStreamToDevice();
  return 0;
}

#pragma warning(default:4311 4312) // reset warning flags

Hope this helps!

Share this post


Link to post
Share on other sites
Advertisement
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!