Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

output_ds.cpp

Go to the documentation of this file.
00001 /*
00002   Audiere DirectSound driver
00003 
00004   Terminology
00005 
00006   block 
00007   - set of bytes for the smallest block of audio in a stream
00008     (including all of the channels)
00009 
00010   buffer
00011   - a circular audio buffer which is constantly refilled with
00012     new data from the stream source
00013 
00014   segment
00015   - buffers are split into a set of segments data flows from
00016     the stream source into the buffer a segment at a time
00017 
00018 */
00019 
00020 
00021 #include <math.h>
00022 #include "output_ds.hpp"
00023 #include "debug.hpp"
00024 #include "input.hpp"
00025 
00026 
00027 static const int DS_DefaultBufferLength = 1000;  // one second
00028 
00029 
00030 // DirectSound treats volumes as decibels (exponential growth like the Richter
00031 // scale).  We want a linear ramp.  Do the conversion!
00032 inline int Volume_AudiereToDirectSound(int volume) {
00033   // I can't figure out the proper math, and this comes close enough...
00034   double fv = volume / 255.0;  // range: 0-1
00035   double attenuate = pow(1 - fv, 3);
00036   return int(-10000 * attenuate);
00037 }
00038 
00039 
00041 
00042 DSOutputContext::DSOutputContext()
00043 {
00044   m_DirectSound     = NULL;
00045   m_BufferLength    = DS_DefaultBufferLength;
00046   m_AnonymousWindow = NULL;
00047 }
00048 
00050 
00051 DSOutputContext::~DSOutputContext()
00052 {
00053   ADR_ASSERT(m_OpenStreams.size() == 0,
00054     "DirectSound output context should not die with open streams");
00055 
00056   // if the anonymous window is open, close it
00057   if (m_AnonymousWindow) {
00058     DestroyWindow(m_AnonymousWindow);
00059     m_AnonymousWindow = NULL;
00060   }
00061 
00062   // shut down DirectSound
00063   if (m_DirectSound) {
00064     m_DirectSound->Release();
00065     m_DirectSound = NULL;
00066   }
00067 }
00068 
00070 
00071 bool
00072 DSOutputContext::Initialize(const char* parameters)
00073 {
00074   ADR_GUARD("DSOutputContext::Initialize");
00075 
00076   // parse the parameter list
00077   ParameterList pl;
00078   ParseParameters(parameters, pl);
00079 
00080   ParameterList::iterator i = pl.begin();
00081   while (i != pl.end()) {
00082     
00083     if (i->first.c_str() == "buffer") {
00084       m_BufferLength = atoi(i->second.c_str());
00085       if (m_BufferLength == 0) {
00086         m_BufferLength = DS_DefaultBufferLength;
00087       }
00088     }
00089 
00090     ++i;
00091   }
00092 
00093   // initialize COM
00094   HRESULT rv = CoInitialize(NULL);
00095   if (FAILED(rv)) {
00096     return false;
00097   }
00098 
00099   ADR_LOG("COM initialized properly");
00100 
00101   // register anonymous window class
00102   // don't worry about failure, if it fails, the window creation will fail
00103   WNDCLASS wc;
00104   wc.style          = 0;
00105   wc.lpfnWndProc    = DefWindowProc;
00106   wc.cbClsExtra     = 0;
00107   wc.cbWndExtra     = 0;
00108   wc.hInstance      = GetModuleHandle(NULL);
00109   wc.hIcon          = NULL;
00110   wc.hCursor        = NULL;
00111   wc.hbrBackground  = NULL;
00112   wc.lpszMenuName   = NULL;
00113   wc.lpszClassName  = "AudiereHiddenWindow";
00114   RegisterClass(&wc);
00115 
00116   // create anonymous window
00117   m_AnonymousWindow = CreateWindow(
00118     "AudiereHiddenWindow", "", WS_POPUP,
00119     0, 0, 0, 0,
00120     NULL, NULL, GetModuleHandle(NULL), NULL);
00121   if (!m_AnonymousWindow) {
00122     return false;
00123   }
00124 
00125   ADR_LOG("Anonymous window created successfully");
00126 
00127   // create the DirectSound object
00128   rv = CoCreateInstance(
00129     GetCLSID(),
00130     NULL,
00131     CLSCTX_INPROC_SERVER,
00132     IID_IDirectSound,
00133     (void**)&m_DirectSound);
00134   if (FAILED(rv) || !m_DirectSound) {
00135     DestroyWindow(m_AnonymousWindow);
00136     m_AnonymousWindow = NULL;
00137     return false;
00138   }
00139 
00140   ADR_LOG("Created DS object");
00141 
00142   // initialize the DirectSound device
00143   rv = m_DirectSound->Initialize(NULL);
00144   if (FAILED(rv)) {
00145     DestroyWindow(m_AnonymousWindow);
00146     m_AnonymousWindow = NULL;
00147     m_DirectSound->Release();
00148     m_DirectSound = NULL;
00149     return false;
00150   }
00151 
00152   ADR_LOG("Initialized DS object");
00153 
00154   // set the cooperative level
00155   rv = m_DirectSound->SetCooperativeLevel(
00156     m_AnonymousWindow,
00157     GetCooperativeLevel());
00158   if (FAILED(rv)) {
00159     DestroyWindow(m_AnonymousWindow);
00160     m_AnonymousWindow = NULL;
00161     m_DirectSound->Release();
00162     m_DirectSound = NULL;
00163     return false;
00164   }
00165 
00166   ADR_LOG("Set cooperative level");
00167 
00168   if (!CreatePrimarySoundBuffer(m_DirectSound)) {
00169 
00170     ADR_LOG("CreatePrimarySoundBuffer failed");
00171 
00172     DestroyWindow(m_AnonymousWindow);
00173     m_AnonymousWindow = NULL;
00174     m_DirectSound->Release();
00175     m_DirectSound = NULL;
00176     return false;
00177   }
00178 
00179   ADR_LOG("Primary sound buffer created");
00180 
00181   return true;
00182 }
00183 
00185 
00186 void
00187 DSOutputContext::Update()
00188 {
00189   ADR_GUARD("DSOutputContext::Update");
00190 
00191   // enumerate all open streams
00192   StreamList::iterator i = m_OpenStreams.begin();
00193   while (i != m_OpenStreams.end()) {
00194     DSOutputStream* s = *i++;
00195     s->Update();
00196   }
00197 
00198   Sleep(50);
00199 }
00200 
00202 
00203 IOutputStream*
00204 DSOutputContext::OpenStream(ISampleSource* source)
00205 {
00206   ADR_GUARD("DSOutputContext::OpenStream");
00207 
00208   int channel_count, sample_rate, bits_per_sample;
00209   source->GetFormat(channel_count, sample_rate, bits_per_sample);
00210 
00211   int sample_size = channel_count * bits_per_sample / 8;
00212 
00213   // calculate an ideal buffer size
00214   int buffer_length = sample_rate * m_BufferLength / 1000;
00215 
00216   // define the wave format
00217   WAVEFORMATEX wfx;
00218   memset(&wfx, 0, sizeof(wfx));
00219   wfx.wFormatTag      = WAVE_FORMAT_PCM;
00220   wfx.nChannels       = channel_count;
00221   wfx.nSamplesPerSec  = sample_rate;
00222   wfx.nAvgBytesPerSec = sample_rate * sample_size;
00223   wfx.nBlockAlign     = sample_size;
00224   wfx.wBitsPerSample  = bits_per_sample;
00225   wfx.cbSize          = sizeof(wfx);
00226 
00227   // define the DirectSound buffer type
00228   #if DIRECTSOUND_VERSION >= 0x0700
00229     DSBUFFERDESC1 dsbd;
00230   #else
00231     DSBUFFERDESC dsbd;
00232   #endif
00233   memset(&dsbd, 0, sizeof(dsbd));
00234   dsbd.dwSize          = sizeof(dsbd);
00235   dsbd.dwFlags         = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLPAN |
00236                          DSBCAPS_CTRLVOLUME | DSBCAPS_GLOBALFOCUS;
00237   dsbd.dwBufferBytes   = sample_size * buffer_length;
00238   dsbd.lpwfxFormat     = &wfx;
00239 
00240   // create the DirectSound buffer
00241   IDirectSoundBuffer* buffer;
00242   HRESULT result = m_DirectSound->CreateSoundBuffer(
00243     (DSBUFFERDESC*)&dsbd,
00244     &buffer,
00245     NULL);
00246   if (FAILED(result) || !buffer) {
00247     return 0;
00248   }
00249 
00250   ADR_LOG("CreateSoundBuffer succeeded");
00251 
00252   DSOutputStream* stream = new DSOutputStream(
00253     this, buffer, sample_size, buffer_length, source);
00254 
00255   // add ourselves to the list of streams and return
00256   m_OpenStreams.push_back(stream);
00257   return stream;
00258 }
00259 
00261 
00262 void
00263 DSOutputContext::RemoveStream(DSOutputStream* stream)
00264 {
00265   m_OpenStreams.remove(stream);
00266 }
00267 
00269 
00270 DSOutputStream::DSOutputStream(
00271   DSOutputContext* context,
00272   IDirectSoundBuffer* buffer,
00273   int sample_size,
00274   int buffer_length,
00275   ISampleSource* source)
00276 {
00277   ADR_GUARD("DSOutputStream::DSOutputStream");
00278 
00279   m_Context = context;
00280   m_Buffer = buffer;
00281   m_NextRead = 0;
00282   m_BufferLength = buffer_length;
00283 
00284   m_Source = source;
00285 
00286   m_SampleSize = sample_size;
00287   m_LastSample = new BYTE[sample_size];
00288 
00289   SetVolume(ADR_VOLUME_MAX);
00290 
00291   // fill the buffer with data
00292   FillStream();
00293 }
00294 
00296 
00297 DSOutputStream::~DSOutputStream()
00298 {
00299   ADR_GUARD("DSOutputStream::~DSOutputStream");
00300 
00301   m_Context->RemoveStream(this);
00302 
00303   // destroy the sound buffer interface
00304   m_Buffer->Release();
00305   delete[] m_LastSample;
00306 }
00307 
00309 
00310 void
00311 DSOutputStream::FillStream()
00312 {
00313   ADR_GUARD("DSOutputStream::FillStream");
00314 
00315   // we know the stream is stopped, so just lock the buffer and fill it
00316 
00317   void* buffer = NULL;
00318   DWORD buffer_length = 0;
00319 
00320   // lock
00321   HRESULT result = m_Buffer->Lock(
00322     0,
00323     m_BufferLength * m_SampleSize,
00324     &buffer,
00325     &buffer_length,
00326     NULL,
00327     NULL,
00328     0);
00329   if (FAILED(result) || !buffer) {
00330     ADR_LOG("FillStream failed!");
00331     return;
00332   }
00333 
00334   ADR_IF_DEBUG {
00335     char str[80];
00336     sprintf(str, "Buffer Length = %d", buffer_length);
00337     ADR_LOG(str);
00338   }
00339 
00340   // fill
00341   int samples_to_read = buffer_length / m_SampleSize;
00342   int samples_read = StreamRead(samples_to_read, buffer);
00343   if (samples_read != samples_to_read) {
00344     m_NextRead = samples_read;
00345   } else {
00346     m_NextRead = 0;
00347   }
00348 
00349   // unlock
00350   m_Buffer->Unlock(buffer, buffer_length, NULL, 0);
00351 }
00352 
00354 
00355 void
00356 DSOutputStream::Update()
00357 {
00358   ADR_GUARD("DSOutputStream::Update");
00359 
00360   // if it's not playing, don't do anything
00361   if (!IsPlaying()) {
00362     return;
00363   }
00364 
00365   /* this method reads more PCM data into the stream if it is required */
00366 
00367   // read the stream's play and write cursors
00368   DWORD play;
00369   DWORD write;
00370   HRESULT result = m_Buffer->GetCurrentPosition(&play, &write);
00371   if (FAILED(result)) {
00372     ADR_LOG("GetCurrentPosition failed");
00373     return;
00374   }
00375 
00376   ADR_IF_DEBUG {
00377     char str[160];
00378     sprintf(str, "play: %d  write: %d", play, write);
00379     ADR_LOG(str);
00380   }
00381 
00382   // deal with them in samples, not bytes
00383   play  /= m_SampleSize;
00384   write /= m_SampleSize;
00385 
00386   // read from |m_NextRead| to |play|
00387   int read_length = play - m_NextRead;
00388   if (read_length < 0) {
00389     read_length += m_BufferLength;
00390   }
00391 
00392   if (read_length == 0) {
00393     return;
00394   }
00395 
00396   // lock the buffer
00397   void* buffer1;
00398   void* buffer2;
00399   DWORD buffer1_length;
00400   DWORD buffer2_length;
00401   result = m_Buffer->Lock(
00402     m_NextRead * m_SampleSize,
00403     read_length * m_SampleSize,
00404     &buffer1,
00405     &buffer1_length,
00406     &buffer2,
00407     &buffer2_length,
00408     0
00409   );
00410   if (FAILED(result)) {
00411     ADR_LOG("Lock() failed!");
00412     return;
00413   }
00414 
00415   ADR_IF_DEBUG {
00416     char str[160];
00417     sprintf(str, "buffer1: %d  buffer2: %d", buffer1_length, buffer2_length);
00418     ADR_LOG(str);
00419   }
00420 
00421   // now actually read samples
00422   int length1 = buffer1_length / m_SampleSize;
00423   int length2 = buffer2_length / m_SampleSize;
00424   int read = StreamRead(length1, buffer1);
00425   if (length1 == read) {
00426     read += StreamRead(length2, buffer2);
00427   }
00428 
00429   ADR_IF_DEBUG {
00430     char str[80];
00431     sprintf(str, "read: %d", read);
00432     ADR_LOG(str);
00433   }
00434 
00435   m_NextRead = (m_NextRead + read) % m_BufferLength;
00436 
00437   // unlock
00438   m_Buffer->Unlock(buffer1, buffer1_length, buffer2, buffer2_length);
00439 
00440   
00441   // Should we stop?  If we didn't read any data, and if the read cursor
00442   // is between the play and the write cursors, we should.
00443   if (read == 0 && IsBetween(m_NextRead, play, write)) {
00444 
00445     ADR_LOG("Stopping stream!");
00446 
00447     m_Buffer->Stop();
00448     m_Buffer->SetCurrentPosition(0);
00449 
00450     m_Source->Reset();
00451 
00452     m_NextRead = 0;
00453     FillStream();
00454 
00455     return;
00456   }
00457 }
00458 
00460 
00461 // read as much as possible from the stream source, fill the rest with 0
00462 int
00463 DSOutputStream::StreamRead(int sample_count, void* samples)
00464 {
00465   ADR_GUARD("StreamRead");
00466 
00467   // try to read from the stream
00468   int samples_read = m_Source->Read(sample_count, samples);
00469 
00470   // read the last sample
00471   if (samples_read > 0) {
00472     memcpy(
00473       m_LastSample,
00474       (BYTE*)samples + (samples_read - 1) * m_SampleSize,
00475       m_SampleSize);
00476   }
00477 
00478   // fill the rest with silence
00479   BYTE* out = (BYTE*)samples + m_SampleSize * samples_read;
00480   int c = sample_count - samples_read;
00481   while (c--) {
00482     memcpy(out, m_LastSample, m_SampleSize);
00483     out += m_SampleSize;
00484   }
00485 
00486   return samples_read;
00487 }
00488 
00490 
00491 bool
00492 DSOutputStream::IsBetween(int position, int start, int end)
00493 {
00494   if (start < end) {
00495     return (position >= start && position < end);
00496   } else {
00497     return (position >= start || position < end);
00498   }
00499 }
00500 
00502 
00503 void
00504 DSOutputStream::Play()
00505 {
00506   m_Buffer->Play(0, 0, DSBPLAY_LOOPING);
00507 }
00508 
00510 
00511 void
00512 DSOutputStream::Stop()
00513 {
00514   m_Buffer->Stop();
00515 }
00516 
00518 
00519 void
00520 DSOutputStream::Reset()
00521 {
00522   ADR_GUARD("DSOutputStream::Reset");
00523 
00524   // figure out if we're playing or not
00525   bool is_playing = IsPlaying();
00526 
00527   // if we're playing, stop
00528   if (is_playing) {
00529     m_Buffer->Stop();
00530   }
00531 
00532   m_Buffer->SetCurrentPosition(0);
00533   m_Source->Reset();
00534   m_NextRead = 0;
00535   FillStream();
00536 
00537   // if we were playing, restart
00538   if (is_playing) {
00539     m_Buffer->Play(0, 0, DSBPLAY_LOOPING);
00540   }
00541 }
00542 
00544 
00545 bool
00546 DSOutputStream::IsPlaying()
00547 {
00548   DWORD status;
00549   HRESULT rv = m_Buffer->GetStatus(&status);
00550   return (SUCCEEDED(rv) && status & DSBSTATUS_PLAYING);
00551 }
00552 
00554 
00555 void
00556 DSOutputStream::SetVolume(int volume)
00557 {
00558   m_Volume = volume;
00559   m_Buffer->SetVolume(Volume_AudiereToDirectSound(volume));
00560 }
00561 
00563 
00564 int
00565 DSOutputStream::GetVolume()
00566 {
00567   return m_Volume;
00568 }
00569 

Generated at Mon Jun 10 02:55:12 2002 for audiere by doxygen1.2.8.1 written by Dimitri van Heesch, © 1997-2001