device_ds.cpp

Go to the documentation of this file.
00001 #include <algorithm>
00002 #include <sstream>
00003 #include <math.h>
00004 #include "device_ds.h"
00005 #include "device_ds_stream.h"
00006 #include "device_ds_buffer.h"
00007 #include "debug.h"
00008 #include "utility.h"
00009 
00010 
00011 namespace audiere {
00012 
00013   static const int DEFAULT_BUFFER_LENGTH = 1000;  // one second
00014 
00015 
00016   DSAudioDevice*
00017   DSAudioDevice::create(const ParameterList& parameters) {
00018     ADR_GUARD("DSAudioDevice::create");
00019 
00020     // parse parameters
00021     int stream_buffer_length = parameters.getInt("buffer", 0);
00022     if (stream_buffer_length <= 0) {
00023       stream_buffer_length = DEFAULT_BUFFER_LENGTH;
00024     }
00025     int min_buffer_length = parameters.getInt("min_buffer_size", 0);
00026     min_buffer_length = std::max(1, min_buffer_length);
00027     bool global_focus = parameters.getBoolean("global", true);
00028 
00029     // initialize COM
00030     HRESULT rv = CoInitialize(NULL);
00031     if (FAILED(rv)) {
00032       return 0;
00033     }
00034 
00035     ADR_LOG("COM initialized properly");
00036 
00037     // register anonymous window class
00038     // don't worry about failure, if it fails, the window creation will fail
00039     WNDCLASS wc;
00040     wc.style          = 0;
00041     wc.lpfnWndProc    = DefWindowProc;
00042     wc.cbClsExtra     = 0;
00043     wc.cbWndExtra     = 0;
00044     wc.hInstance      = GetModuleHandle(NULL);
00045     wc.hIcon          = NULL;
00046     wc.hCursor        = NULL;
00047     wc.hbrBackground  = NULL;
00048     wc.lpszMenuName   = NULL;
00049     wc.lpszClassName  = "AudiereHiddenWindow";
00050     RegisterClass(&wc);
00051 
00052     // create anonymous window
00053     HWND anonymous_window = CreateWindow(
00054       "AudiereHiddenWindow", "", WS_POPUP,
00055       0, 0, 0, 0,
00056       NULL, NULL, GetModuleHandle(NULL), NULL);
00057     if (!anonymous_window) {
00058       return false;
00059     }
00060 
00061     ADR_LOG("Anonymous window created successfully");
00062 
00063     // create the DirectSound object
00064     IDirectSound* direct_sound;
00065     rv = CoCreateInstance(
00066       CLSID_DirectSound,
00067       NULL,
00068       CLSCTX_INPROC_SERVER,
00069       IID_IDirectSound,
00070       (void**)&direct_sound);
00071     if (FAILED(rv) || !direct_sound) {
00072       DestroyWindow(anonymous_window);
00073       return 0;
00074     }
00075 
00076     ADR_LOG("Created DS object");
00077 
00078     LPGUID guid = NULL;
00079     GUID stack_guid;  // so we can point 'guid' to an object that won't be destroyed
00080 
00081     std::string guid_string = parameters.getValue("device_guid", "");
00082     if (!guid_string.empty()) {
00083       if (UuidFromString((unsigned char*)guid_string.c_str(), &stack_guid) == RPC_S_OK) {
00084         guid = &stack_guid;
00085       }
00086     }
00087 
00088     // initialize the DirectSound device
00089     rv = direct_sound->Initialize(guid);
00090     if (FAILED(rv)) {
00091       DestroyWindow(anonymous_window);
00092       direct_sound->Release();
00093       return 0;
00094     }
00095 
00096     ADR_LOG("Initialized DS object");
00097 
00098     // set the cooperative level
00099     rv = direct_sound->SetCooperativeLevel(anonymous_window, DSSCL_NORMAL);
00100     if (FAILED(rv)) {
00101       DestroyWindow(anonymous_window);
00102       direct_sound->Release();
00103       return 0;
00104     }
00105 
00106     ADR_LOG("Set cooperative level");
00107 
00108     return new DSAudioDevice(
00109       global_focus, stream_buffer_length, min_buffer_length,
00110       anonymous_window, direct_sound);
00111   }
00112 
00113 
00114   DSAudioDevice::DSAudioDevice(
00115     bool global_focus,
00116     int stream_buffer_length,
00117     int min_buffer_length,
00118     HWND anonymous_window,
00119     IDirectSound* direct_sound)
00120   {
00121     m_global_focus      = global_focus;
00122     m_buffer_length     = stream_buffer_length;
00123     m_min_buffer_length = min_buffer_length;
00124     m_anonymous_window  = anonymous_window;
00125     m_direct_sound      = direct_sound;
00126   }
00127 
00128 
00129   DSAudioDevice::~DSAudioDevice() {
00130     ADR_ASSERT(m_open_streams.empty(),
00131       "DirectSound device should not die with open streams");
00132     ADR_ASSERT(m_open_buffers.empty(),
00133       "DirectSound device should not die with open buffers");
00134 
00135     // shut down DirectSound
00136     if (m_direct_sound) {
00137       m_direct_sound->Release();
00138       m_direct_sound = NULL;
00139     }
00140 
00141     // if the anonymous window is open, close it
00142     if (m_anonymous_window) {
00143       DestroyWindow(m_anonymous_window);
00144       m_anonymous_window = NULL;
00145     }
00146 
00147     CoUninitialize();
00148   }
00149 
00150 
00151   void
00152   DSAudioDevice::update() {
00153     ADR_GUARD("DSAudioDevice::update");
00154 
00155     {
00156       /* Put the critical section in its own scope so we don't hold the lock
00157          while sleeping. --MattC */
00158       SYNCHRONIZED(this);
00159 
00160       // enumerate all open streams
00161       StreamList::iterator i = m_open_streams.begin();
00162       while (i != m_open_streams.end()) {
00163         DSOutputStream* s = *i++;
00164         s->update();
00165       }
00166 
00167       // enumerate all open buffers
00168       BufferList::iterator j = m_open_buffers.begin();
00169       while (j != m_open_buffers.end()) {
00170         DSOutputBuffer* b = *j++;
00171         b->update();
00172       }
00173     }
00174 
00175     Sleep(50);
00176   }
00177 
00178 
00179   OutputStream*
00180   DSAudioDevice::openStream(SampleSource* source) {
00181     if (!source) {
00182       return 0;
00183     }
00184 
00185     ADR_GUARD("DSAudioDevice::openStream");
00186 
00187     int channel_count, sample_rate;
00188     SampleFormat sample_format;
00189     source->getFormat(channel_count, sample_rate, sample_format);
00190 
00191     const int frame_size = channel_count * GetSampleSize(sample_format);
00192 
00193     // calculate an ideal buffer size
00194     const int buffer_length = sample_rate * m_buffer_length / 1000;
00195 
00196     // define the wave format
00197     WAVEFORMATEX wfx;
00198     memset(&wfx, 0, sizeof(wfx));
00199     wfx.wFormatTag      = WAVE_FORMAT_PCM;
00200     wfx.nChannels       = channel_count;
00201     wfx.nSamplesPerSec  = sample_rate;
00202     wfx.nAvgBytesPerSec = sample_rate * frame_size;
00203     wfx.nBlockAlign     = frame_size;
00204     wfx.wBitsPerSample  = GetSampleSize(sample_format) * 8;
00205     wfx.cbSize          = sizeof(wfx);
00206 
00207     DSBUFFERDESC dsbd;
00208     memset(&dsbd, 0, sizeof(dsbd));
00209     dsbd.dwSize        = sizeof(dsbd);
00210     dsbd.dwFlags       = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLPAN |
00211                          DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY;
00212     if (m_global_focus) {
00213       dsbd.dwFlags |= DSBCAPS_GLOBALFOCUS;
00214     }
00215     dsbd.dwBufferBytes = frame_size * buffer_length;
00216     dsbd.lpwfxFormat   = &wfx;
00217 
00218     // create the DirectSound buffer
00219     IDirectSoundBuffer* buffer;
00220     HRESULT result = m_direct_sound->CreateSoundBuffer(&dsbd, &buffer, NULL);
00221     if (FAILED(result) || !buffer) {
00222       return 0;
00223     }
00224 
00225     ADR_LOG("CreateSoundBuffer succeeded");
00226 
00227     // now create the output stream
00228     DSOutputStream* stream = new DSOutputStream(
00229       this, buffer, buffer_length, source);
00230 
00231     // add it the list of streams and return
00232     SYNCHRONIZED(this);
00233     m_open_streams.push_back(stream);
00234     return stream;
00235   }
00236 
00237 
00238   OutputStream*
00239   DSAudioDevice::openBuffer(
00240     void* samples, int frame_count,
00241     int channel_count, int sample_rate, SampleFormat sample_format)
00242   {
00243     ADR_GUARD("DSAudioDevice::openBuffer");
00244 
00245     const int frame_size = channel_count * GetSampleSize(sample_format);
00246 
00247     WAVEFORMATEX wfx;
00248     memset(&wfx, 0, sizeof(wfx));
00249     wfx.wFormatTag      = WAVE_FORMAT_PCM;
00250     wfx.nChannels       = channel_count;
00251     wfx.nSamplesPerSec  = sample_rate;
00252     wfx.nAvgBytesPerSec = sample_rate * frame_size;
00253     wfx.nBlockAlign     = frame_size;
00254     wfx.wBitsPerSample  = GetSampleSize(sample_format) * 8;
00255     wfx.cbSize          = sizeof(wfx);
00256 
00257     DSBUFFERDESC dsbd;
00258     memset(&dsbd, 0, sizeof(dsbd));
00259     dsbd.dwSize  = sizeof(dsbd);
00260     dsbd.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLPAN |
00261                    DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY |
00262                    DSBCAPS_STATIC | DSBCAPS_CTRLPOSITIONNOTIFY;
00263     if (m_global_focus) {
00264       dsbd.dwFlags |= DSBCAPS_GLOBALFOCUS;
00265     }
00266 
00267     const int buffer_frame_count = std::max(m_min_buffer_length, frame_count);
00268     const int buffer_size = buffer_frame_count * frame_size;
00269     dsbd.dwBufferBytes = buffer_size;
00270     dsbd.lpwfxFormat   = &wfx;
00271 
00272     // create the DS buffer
00273     IDirectSoundBuffer* buffer;
00274     HRESULT result = m_direct_sound->CreateSoundBuffer(
00275       &dsbd, &buffer, NULL);
00276     if (FAILED(result) || !buffer) {
00277       return 0;
00278     }
00279 
00280     ADR_IF_DEBUG {
00281       DSBCAPS caps;
00282       caps.dwSize = sizeof(caps);
00283       result = buffer->GetCaps(&caps);
00284       if (FAILED(result)) {
00285         buffer->Release();
00286         return 0;
00287       } else {
00288         std::ostringstream ss;
00289         ss << "actual buffer size: " << caps.dwBufferBytes << std::endl
00290            << "buffer_size: " << buffer_size;
00291         ADR_LOG(ss.str().c_str());
00292       }
00293     }
00294 
00295     void* data;
00296     DWORD data_size;
00297     result = buffer->Lock(0, buffer_size, &data, &data_size, 0, 0, 0);
00298     if (FAILED(result)) {
00299       buffer->Release();
00300       return 0;
00301     }
00302 
00303     ADR_IF_DEBUG {
00304       std::ostringstream ss;
00305       ss << "buffer size: " << buffer_size << std::endl
00306          << "data size:   " << data_size << std::endl
00307          << "frame count: " << frame_count;
00308       ADR_LOG(ss.str().c_str());
00309     }
00310 
00311     const int actual_size = frame_count * frame_size;
00312     memcpy(data, samples, actual_size);
00313     memset((u8*)data + actual_size, 0, buffer_size - actual_size);
00314 
00315     buffer->Unlock(data, data_size, 0, 0);
00316 
00317     DSOutputBuffer* b = new DSOutputBuffer(
00318       this, buffer, buffer_frame_count, frame_size);
00319     SYNCHRONIZED(this);
00320     m_open_buffers.push_back(b);
00321     return b;
00322   }
00323 
00324 
00325   const char* ADR_CALL
00326   DSAudioDevice::getName() {
00327     return "directsound";
00328   }
00329 
00330 
00331   void
00332   DSAudioDevice::removeStream(DSOutputStream* stream) {
00333     SYNCHRONIZED(this);
00334     m_open_streams.remove(stream);
00335   }
00336 
00337 
00338   void
00339   DSAudioDevice::removeBuffer(DSOutputBuffer* buffer) {
00340     SYNCHRONIZED(this);
00341     m_open_buffers.remove(buffer);
00342   }
00343 
00344 
00345   int
00346   DSAudioDevice::Volume_AudiereToDirectSound(float volume) {
00347     if (volume == 0) {
00348       return -10000;
00349     } else {
00350       double attenuate = 1000 * log(1 / volume);
00351       return int(-attenuate);
00352     }
00353   }
00354 
00355 
00356   int
00357   DSAudioDevice::Pan_AudiereToDirectSound(float pan) {
00358     if (pan < 0) {
00359       return -Pan_AudiereToDirectSound(-pan);
00360     } else {
00361       return -Volume_AudiereToDirectSound(1 - pan);
00362     }
00363   }
00364 
00365 }

Generated on Mon Feb 13 23:07:46 2006 for audiere by  doxygen 1.4.6