Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

SoundManager.cc

Go to the documentation of this file.
00001 #include "Shared/Config.h"
00002 #include "SoundManager.h"
00003 #include "IPC/LockScope.h"
00004 #include "WAV.h"
00005 #include "Events/EventRouter.h"
00006 #include <sys/types.h>
00007 #include <sys/stat.h>
00008 #include <unistd.h>
00009 #include <fstream>
00010 #ifdef PLATFORM_APERIOS
00011 #  include <OPENR/OSubject.h>
00012 #  include <OPENR/ObjcommEvent.h>
00013 #endif
00014 
00015 using namespace std;
00016 
00017 SoundManager * sndman=NULL;
00018 
00019 //!for convenience when locking each of the functions
00020 typedef LockScope<ProcessID::NumProcesses> AutoLock;
00021 
00022 SoundManager::SoundManager()
00023 : mixerBuffer(0), mixerBufferSize(0), sndlist(),playlist(),chanlist(),mix_mode(Fast),queue_mode(Override),max_chan(4),lock(),sn(0)
00024 {}
00025 
00026 #ifdef PLATFORM_APERIOS
00027 void
00028 SoundManager::InitAccess(OSubject* subj) {
00029   subjs[ProcessID::getID()]=subj;
00030 }
00031 #else //PLATFORM_LOCAL
00032 void
00033 SoundManager::InitAccess(MessageQueueBase& sndbufq) {
00034   subjs[ProcessID::getID()]=&sndbufq;
00035 }
00036 #endif //PLATFORM-specific initialization
00037 
00038 SoundManager::~SoundManager() {
00039   StopPlay();
00040   if(!sndlist.empty())
00041     cerr << "Warning: SoundManager was deleted with active sound buffer references" << endl;
00042   while(!sndlist.empty()) {
00043     sndlist_t::index_t it=sndlist.begin();
00044     if(sndlist[it].rcr==NULL)
00045       cerr << sndlist[it].name << " was still inflight (IPC), with " << sndlist[it].ref << " sound references" << endl;
00046     else {
00047       cerr << sndlist[it].name << " was deleted, with " << sndlist[it].ref << " sound references and " << sndlist[it].rcr->NumberOfReference() << " region references (one will be removed)" << endl;
00048       sndlist[it].rcr->RemoveReference();
00049     }
00050     sndlist.erase(it);
00051   }
00052   delete[] mixerBuffer;
00053 }
00054 
00055 //!@todo this does one more copy than it really needs to
00056 SoundManager::Snd_ID
00057 SoundManager::LoadFile(std::string const &name) {
00058   AutoLock autolock(lock,ProcessID::getID());
00059   if (name.size() == 0) {
00060     cout << "SoundManager::LoadFile() null filename" << endl;
00061     return invalid_Snd_ID;
00062   };
00063   std::string path(config->sound.makePath(name));
00064   Snd_ID id=lookupPath(path);
00065   if(id!=invalid_Snd_ID) {
00066     //    cout << "add reference to pre-existing" << endl;
00067     sndlist[id].ref++;
00068   } else {
00069     //    cout << "load new file" << endl;
00070     struct stat buf;
00071     if(stat(path.c_str(),&buf)==-1) {
00072       cout << "SoundManager::LoadFile(): Sound file not found: " << path << endl;
00073       return invalid_Snd_ID;
00074     }
00075     byte * sndbuf=new byte[buf.st_size];
00076     std::ifstream file(path.c_str());
00077     file.read(reinterpret_cast<char*>(sndbuf),buf.st_size);
00078     WAV wav;
00079     WAVError error = wav.Set(sndbuf);
00080     if (error != WAV_SUCCESS) {
00081       printf("%s : %s %d: '%s'","SoundManager::LoadFile()","wav.Set() FAILED",error, path.c_str());
00082       return invalid_Snd_ID;
00083     }
00084     if(wav.GetSamplingRate()!=config->sound.sample_rate || wav.GetBitsPerSample()!=config->sound.sample_bits) {
00085       printf("%s : %s %d","SoundManager::LoadFile()","bad sample rate/bits", error);
00086       return invalid_Snd_ID;
00087     }
00088     //cout << "Loading " << name << endl;
00089     id=LoadBuffer(reinterpret_cast<char*>(wav.GetDataStart()),wav.GetDataEnd()-wav.GetDataStart());
00090     delete [] sndbuf;
00091     if(path.size()>MAX_NAME_LEN)
00092       strncpy(sndlist[id].name,path.substr(path.size()-MAX_NAME_LEN).c_str(),MAX_NAME_LEN);
00093     else
00094       strncpy(sndlist[id].name,path.c_str(),MAX_NAME_LEN);
00095   }
00096   return id;
00097 }
00098 
00099 SoundManager::Snd_ID
00100 SoundManager::LoadBuffer(const char buf[], unsigned int len) {
00101   cout << "SoundManager::LoadBuffer() of " << len << " bytes" << endl;
00102   if(buf==NULL || len==0)
00103     return invalid_Snd_ID;
00104   AutoLock autolock(lock,ProcessID::getID());
00105   //setup region
00106   RCRegion * region=initRegion(len+MSG_SIZE);
00107   //setup message
00108   SoundManagerMsg * msg=new (reinterpret_cast<SoundManagerMsg*>(region->Base())) SoundManagerMsg;
00109   Snd_ID msgid=sndlist.new_front();
00110   msg->setAdd(msgid,sn);
00111   //init sound structure
00112   sndlist[msg->getID()].rcr=NULL;  // set by SoundPlay upon reception
00113   sndlist[msg->getID()].data=NULL; // set by SoundPlay upon reception
00114   sndlist[msg->getID()].ref=1;
00115   sndlist[msg->getID()].len=len;
00116   sndlist[msg->getID()].sn=sn;
00117   //copy buffer, do any filtering needed here
00118   const byte* src=reinterpret_cast<const byte*>(buf);
00119   byte* dest=reinterpret_cast<byte*>(region->Base())+MSG_SIZE;
00120   byte* end=dest+len;
00121   if (config->sound.sample_bits==8)
00122     while (dest < end)
00123       *dest++ = *src++ ^ 0x80; // offset binary -> signed char 
00124   else
00125     while (dest < end)
00126       *dest++ = *src++;
00127   //cout << "SoundManager init region " << region->ID().key << endl;
00128   //send message
00129   if(ProcessID::getID()==ProcessID::SoundProcess) {
00130     //if SoundPlay is preloading files, don't need to do inter-object comm
00131     sndlist[msg->getID()].rcr=region;
00132     sndlist[msg->getID()].data=reinterpret_cast<byte*>(region->Base()+MSG_SIZE);    
00133   } else {
00134 #ifdef PLATFORM_APERIOS
00135     //cout << "Send new at " << get_time() << '-';
00136     subjs[ProcessID::getID()]->SetData(region);
00137     subjs[ProcessID::getID()]->NotifyObservers();
00138     //cout << get_time() << endl;
00139 #else
00140     subjs[ProcessID::getID()]->sendMessage(region);
00141     region->RemoveReference();
00142 #endif
00143   }
00144   return msgid;
00145 }
00146   
00147 void
00148 SoundManager::ReleaseFile(std::string const &name) {
00149   AutoLock autolock(lock,ProcessID::getID());
00150   Release(lookupPath(config->sound.makePath(name)));
00151 }
00152 
00153 void
00154 SoundManager::Release(Snd_ID id) {
00155   if(id==invalid_Snd_ID)
00156     return;
00157   if(sndlist[id].ref==0) {
00158     cerr << "SoundManager::Release() " << id << " extra release" << endl;
00159     return;
00160   }
00161   AutoLock autolock(lock,ProcessID::getID());
00162   sndlist[id].ref--;
00163   if(sndlist[id].ref==0) {
00164     if(sndlist[id].rcr!=NULL) {
00165       //The sound buffer is attached in the sound process, we need to detach it.
00166       //Note that if this was NULL, we just assume that's because the message for the
00167       //region is still in transit from the originating process, and erase the sndlist entry.
00168       //Then when that buffer does arrive, we'll discover the sndlist entry is invalid and ignore it
00169       if(ProcessID::getID()==ProcessID::SoundProcess) {
00170         //we're currently running in sound process -- don't need to send ourselves an IPC message
00171         sndlist[id].rcr->RemoveReference();
00172         sndlist[id].rcr=NULL;
00173       } else {
00174         //we're currently running in a foreign process -- have to send message to sound process to release
00175         //setup region
00176         RCRegion * region=initRegion(MSG_SIZE);
00177         //setup message
00178         SoundManagerMsg * msg=new (reinterpret_cast<SoundManagerMsg*>(region->Base())) SoundManagerMsg;
00179         msg->setDelete(sndlist[id].rcr);
00180         //cout << "Sending delete msg for " << sndlist[id].name << endl;
00181         //send message
00182 #ifdef PLATFORM_APERIOS
00183         //cout << "Send delete at " << get_time() << '-';
00184         subjs[ProcessID::getID()]->SetData(region);
00185         subjs[ProcessID::getID()]->NotifyObservers();
00186         //cout << get_time() << endl;
00187 #else
00188         subjs[ProcessID::getID()]->sendMessage(region);
00189         region->RemoveReference();
00190 #endif
00191       }
00192     }
00193     //clean up sound data structure
00194     sndlist[id].sn=0; // we use '1' for the first issued, so 0 marks it as invalid
00195     sndlist.erase(id);
00196   }
00197 }
00198 
00199 SoundManager::Play_ID
00200 SoundManager::PlayFile(std::string const &name) {
00201   if(playlist.size()>=playlist_t::MAX_ENTRIES)
00202     return invalid_Play_ID; 
00203   AutoLock autolock(lock,ProcessID::getID());
00204   Snd_ID sndid=LoadFile(name);
00205   if(sndid==invalid_Snd_ID)
00206     return invalid_Play_ID;
00207   sndlist[sndid].ref--;
00208   return Play(sndid);
00209 }
00210 
00211 SoundManager::Play_ID
00212 SoundManager::PlayBuffer(const char buf[], unsigned int len) {
00213   if(playlist.size()>=playlist_t::MAX_ENTRIES || buf==NULL || len==0)
00214     return invalid_Play_ID; 
00215   AutoLock autolock(lock,ProcessID::getID());
00216   Snd_ID sndid=LoadBuffer(buf,len);
00217   if(sndid==invalid_Snd_ID)
00218     return invalid_Play_ID;
00219   sndlist[sndid].ref--;
00220   return Play(sndid);
00221 }
00222   
00223 SoundManager::Play_ID
00224 SoundManager::Play(Snd_ID id) {
00225   //  cout << "Play " << id << endl;
00226   if(id==invalid_Snd_ID)
00227     return invalid_Play_ID;
00228   AutoLock autolock(lock,ProcessID::getID());
00229   Play_ID playid=playlist.new_front();
00230   if(playid!=invalid_Play_ID) {
00231     sndlist[id].ref++;
00232     playlist[playid].snd_id=id;
00233     playlist[playid].offset=0;
00234     //playlist.size() should be greater than or equal to chanlist.size
00235     //so if we got a playid, we can get a channel slot.
00236     chanlist.push_front(playid);
00237 
00238     //setup message to "wake-up" 
00239     //(only really need if chanlist was empty)
00240     //    if(chanlist.size()==1) { //commented out because sometimes doesn't wake up, thinks it's playing but isn't
00241     if(ProcessID::getID()!=ProcessID::SoundProcess) {
00242       RCRegion * region=initRegion(MSG_SIZE);
00243       ASSERT(region!=NULL,"initRegion returned NULL");
00244       SoundManagerMsg * msg=new (reinterpret_cast<SoundManagerMsg*>(region->Base())) SoundManagerMsg;
00245       msg->setWakeup();
00246 #ifdef PLATFORM_APERIOS
00247       //cout << "Send wakeup at " << get_time() << '-';
00248       subjs[ProcessID::getID()]->SetData(region);
00249       subjs[ProcessID::getID()]->NotifyObservers();
00250       //cout << get_time() << endl;
00251 #else
00252       subjs[ProcessID::getID()]->sendMessage(region);
00253       region->RemoveReference();
00254 #endif
00255     }
00256     //    }
00257     
00258     if(sndlist[id].rcr!=NULL) {
00259       const char * name=sndlist[playlist[playid].snd_id].name;
00260       if(name[0]!='\0')
00261         erouter->postEvent(EventBase::audioEGID,playid,EventBase::activateETID,0,name,1);
00262       else
00263         erouter->postEvent(EventBase::audioEGID,playid,EventBase::activateETID,0);
00264     }
00265   }
00266   return playid;
00267 }
00268   
00269 SoundManager::Play_ID
00270 SoundManager::ChainFile(Play_ID base, std::string const &next) {
00271        if(base==invalid_Play_ID)
00272     return base;
00273   Play_ID orig=base;
00274   while(playlist[base].next_id!=invalid_Play_ID)
00275     base=playlist[base].next_id;
00276   Play_ID nplay=playlist.new_front();
00277   if(nplay==invalid_Play_ID)
00278     return nplay;
00279   Snd_ID nsnd=LoadFile(next);
00280   if(nsnd==invalid_Snd_ID) {
00281     playlist.pop_front();
00282     return invalid_Play_ID;
00283   }
00284   playlist[nplay].snd_id=nsnd;
00285   playlist[base].next_id=nplay;
00286   return orig;
00287 }
00288 
00289 SoundManager::Play_ID
00290 SoundManager::ChainBuffer(Play_ID base, const char buf[], unsigned int len) {
00291   if(base==invalid_Play_ID || buf==NULL || len==0)
00292     return base;
00293   Play_ID orig=base;
00294   while(playlist[base].next_id!=invalid_Play_ID)
00295     base=playlist[base].next_id;
00296   Play_ID nplay=playlist.new_front();
00297   if(nplay==invalid_Play_ID)
00298     return nplay;
00299   Snd_ID nsnd=LoadBuffer(buf,len);
00300   if(nsnd==invalid_Snd_ID) {
00301     playlist.pop_front();
00302     return invalid_Play_ID;
00303   }
00304   playlist[nplay].snd_id=nsnd;
00305   playlist[base].next_id=nplay;
00306   return orig;
00307 }
00308 
00309 SoundManager::Play_ID
00310 SoundManager::Chain(Play_ID base, Snd_ID next) {
00311   if(base==invalid_Play_ID || next==invalid_Snd_ID)
00312     return base;
00313   Play_ID orig=base;
00314   while(playlist[base].next_id!=invalid_Play_ID)
00315     base=playlist[base].next_id;
00316   Play_ID nplay=playlist.new_front();
00317   if(nplay==invalid_Play_ID)
00318     return nplay;
00319   playlist[nplay].snd_id=next;
00320   playlist[base].next_id=nplay;
00321   return orig;
00322 }
00323 
00324 void
00325 SoundManager::StopPlay() {
00326   while(!playlist.empty())
00327     StopPlay(playlist.begin());
00328 }
00329 
00330 void
00331 SoundManager::StopPlay(Play_ID id) {
00332   if(id==invalid_Play_ID)
00333     return;
00334   AutoLock autolock(lock,ProcessID::getID());
00335   //cout << "Stopping sound " << id << ": " << sndlist[playlist[id].snd_id].name << endl;
00336   //we start at the back (oldest) since these are the most likely to be removed...
00337   for(chanlist_t::index_t it=chanlist.prev(chanlist.end()); it!=chanlist.end(); it=chanlist.prev(it))
00338     if(chanlist[it]==id) {
00339       std::string name=sndlist[playlist[id].snd_id].name;
00340       Release(playlist[id].snd_id);
00341       playlist.erase(id);
00342       chanlist.erase(it);
00343       playlist[id].cumulative+=playlist[id].offset;
00344       unsigned int ms=playlist[id].cumulative/(config->sound.sample_bits/8)/(config->sound.sample_rate/1000);
00345       if(name.size()>0)
00346         erouter->postEvent(EventBase::audioEGID,id,EventBase::deactivateETID,ms,name,0);
00347       else
00348         erouter->postEvent(EventBase::audioEGID,id,EventBase::deactivateETID,ms);
00349       return;
00350     }
00351   cerr << "SoundManager::StopPlay(): " << id << " does not seem to be playing" << endl;
00352 }
00353 
00354 void
00355 SoundManager::PausePlay(Play_ID id) {
00356   if(id==invalid_Play_ID)
00357     return;
00358   AutoLock autolock(lock,ProcessID::getID());
00359   for(chanlist_t::index_t it=chanlist.begin(); it!=chanlist.end(); it=chanlist.next(it))
00360     if(chanlist[it]==id) {
00361       chanlist.erase(it);
00362       return;
00363     }
00364 }
00365   
00366 void
00367 SoundManager::ResumePlay(Play_ID id) {
00368   if(id==invalid_Play_ID)
00369     return;
00370   AutoLock autolock(lock,ProcessID::getID());
00371   for(chanlist_t::index_t it=chanlist.begin(); it!=chanlist.end(); it=chanlist.next(it))
00372     if(chanlist[it]==id)
00373       return;
00374   chanlist.push_front(id);
00375 }
00376   
00377 void
00378 SoundManager::SetMode(unsigned int max_channels, MixMode_t mixer_mode, QueueMode_t queuing_mode) {
00379   AutoLock autolock(lock,ProcessID::getID());
00380   max_chan=max_channels;
00381   mix_mode=mixer_mode;
00382   queue_mode=queuing_mode;
00383 }
00384 
00385 unsigned int
00386 SoundManager::GetRemainTime(Play_ID id) const {
00387   AutoLock autolock(lock,ProcessID::getID());
00388   unsigned int t=0;
00389   while(id!=invalid_Play_ID) {
00390     t+=sndlist[playlist[id].snd_id].len-playlist[id].offset;
00391     id=playlist[id].next_id;
00392   }
00393   const unsigned int bytesPerMS=config->sound.sample_bits/8*config->sound.sample_rate/1000;
00394   return t/bytesPerMS;
00395 }
00396 
00397 void
00398 SoundManager::MixChannel(Play_ID channelId, void* buf, size_t destSize) {
00399   char *dest = (char*) buf;
00400   
00401   PlayState& channel = playlist[channelId];
00402   while (destSize > 0) {
00403     const SoundData& buffer = sndlist[channel.snd_id];
00404     const char* samples = ((char*) (buffer.data)) + channel.offset;
00405     const unsigned int samplesSize = buffer.len - channel.offset;
00406     if (samplesSize > destSize) {
00407       memcpy(dest, samples, destSize);
00408       channel.offset += destSize;
00409       dest += destSize;
00410       destSize = 0;
00411       return;
00412     } else {
00413       memcpy(dest, samples, samplesSize);
00414       channel.offset += samplesSize;
00415       dest += samplesSize;
00416       destSize -= samplesSize;
00417       if (endPlay(channelId)) {
00418         break;
00419       }
00420     }
00421   }
00422   if (destSize > 0) {
00423     memset(dest, 0, destSize);
00424   }
00425 }
00426 
00427 void
00428 SoundManager::MixChannelAdditively(Play_ID channelId, int bitsPerSample, MixMode_t mode,
00429                                    short scalingFactor, void* buf, size_t destSize)
00430 {
00431   PlayState& channel = playlist[channelId];
00432   while (destSize > 0) {
00433     const SoundData& buffer = sndlist[channel.snd_id];
00434     const unsigned int samplesSize = buffer.len - channel.offset;
00435     const unsigned int mixedSamplesSize =
00436       ((mode == Fast)
00437         ? ((samplesSize > destSize) ? destSize : samplesSize)
00438         : ((samplesSize > destSize / 2) ? destSize / 2 : samplesSize)); 
00439     
00440     if (bitsPerSample == 8) {
00441       //  8-bit mode
00442       const char* samples = (char*) (buffer.data + channel.offset);
00443       if (mode == Fast) {
00444         // 8-bit mixing
00445         char *dest = (char*) buf;
00446         for (size_t i = 0; i < mixedSamplesSize; i++) {
00447           *dest += samples[i] / scalingFactor;
00448           dest++;
00449         }
00450         destSize -= (char*) dest - (char*) buf;
00451         buf = dest;
00452       } else {
00453         // 16-bit mixing
00454         short* dest = (short*) buf;
00455         for (size_t i = 0; i < mixedSamplesSize; i++) {
00456           *dest += samples[i];
00457           *dest++;
00458         }
00459         destSize -= (char*) dest - (char*) buf;
00460         buf = dest;
00461       }
00462     } else {
00463       // 16-bit mode
00464       const short* samples = (short*) (buffer.data + channel.offset);
00465       if (mode == Fast) {
00466         // 16-bit mixing
00467         short* dest = (short*) buf;
00468         for (size_t i = 0; i < mixedSamplesSize / 2; i++) {
00469           *dest += samples[i] / scalingFactor;
00470           *dest++;
00471         }
00472         destSize -= (char*) dest - (char*) buf;
00473         buf = dest;
00474       } else {
00475         // 32-bit mixing
00476         int* dest = (int*) buf;
00477         for (size_t i = 0; i < mixedSamplesSize / 2; i++) {
00478           *dest += samples[i];
00479           *dest++;
00480         }
00481         destSize -= (char*) dest - (char*) buf;
00482         buf = dest;
00483       }
00484     }
00485     channel.offset += mixedSamplesSize;
00486     if (destSize == 0) {
00487       return;
00488     } else {
00489       if (endPlay(channelId)) {
00490         return;
00491       }
00492     }
00493   }
00494 }
00495 
00496 #ifdef PLATFORM_APERIOS
00497 unsigned int
00498 SoundManager::CopyTo(OSoundVectorData* data) {
00499   AutoLock autolock(lock,ProcessID::getID());
00500   return CopyTo(data->GetData(0), data->GetInfo(0)->dataSize);
00501 }
00502 
00503 void
00504 SoundManager::ReceivedMsg(const ONotifyEvent& event) {
00505   //cout << "Got msg at " << get_time() << endl;
00506   for(int x=0; x<event.NumOfData(); x++)
00507     ProcessMsg(event.RCData(x));
00508 }
00509 #endif
00510 
00511 unsigned int SoundManager::CopyTo(void * dest, size_t destSize) {
00512   AutoLock autolock(lock,ProcessID::getID());
00513 
00514   void * origdest=dest;
00515   size_t origDestSize=destSize;
00516   if(chanlist.size() == 0) {
00517     memset(dest, 0, destSize);
00518     return 0;
00519   }
00520   
00521   std::vector<Play_ID> channels;
00522   selectChannels(channels);
00523   
00524   if (channels.size() == 0) {
00525     // No channels to mix
00526     memset(dest, 0, destSize); 
00527   } else if (channels.size() == 1) {
00528     // One channel to mix
00529     MixChannel(channels.front(), dest, destSize);
00530   } else {
00531     // Several channels to mix  
00532     const MixMode_t mode = mix_mode;
00533     const int bitsPerSample = config->sound.sample_bits;
00534     if (mode == Quality) {
00535       // Quality mixing uses an intermediate buffer
00536       if ((mixerBuffer == 0) || (mixerBufferSize < destSize * 2)) {
00537         delete[] mixerBuffer;
00538         mixerBuffer = 0;
00539         mixerBufferSize = destSize * 2;
00540         mixerBuffer = new int[(mixerBufferSize / 4) + 1]; // makes sure it's int-aligned
00541       }
00542       memset(mixerBuffer, 0,  mixerBufferSize);
00543       dest = mixerBuffer;
00544       destSize *= 2;
00545     } else {
00546       // Fast mixing does not use the intermeridate buffer
00547       memset(dest, 0, destSize);
00548     }
00549     
00550     const int channelCount = channels.size();
00551     const short scalingFactor = (short) ((mode == Fast) ? channelCount : 1);  
00552     for(std::vector<Play_ID>::iterator i = channels.begin(); i != channels.end(); i++)
00553       MixChannelAdditively(*i, bitsPerSample, mode, scalingFactor, dest, destSize);
00554     
00555     if (mode == Quality) {
00556       // Quality mixing uses an intermediate buffer
00557       // Scale the buffer
00558       destSize /= 2;
00559       if (bitsPerSample == 8) {
00560         //  8-bit mode
00561         char* destChar = (char*) origdest;
00562         short* mixerBufferShort = (short*) mixerBuffer;
00563         for (size_t i = 0; i < destSize; i++) {
00564           destChar[i] = (char) (mixerBufferShort[i] / channelCount);
00565         } 
00566       } else {
00567         // 16-bit mode
00568         short* destShort = (short*) origdest;
00569         const size_t destSampleCount = destSize / 2; 
00570         for (size_t i = 0; i < destSampleCount; i++) {
00571           destShort[i] = (short) (mixerBuffer[i] / channelCount);
00572         }
00573       }
00574     }
00575   }
00576   
00577   updateChannels(channels, origDestSize);
00578   return channels.size(); 
00579 }
00580 
00581 void SoundManager::ProcessMsg(RCRegion * rcr) {
00582   SoundManagerMsg * msg = reinterpret_cast<SoundManagerMsg*>(rcr->Base());
00583   //cout << "Processing " << msg << ": " << rcr->ID().key << endl;
00584   switch(msg->type) {
00585     case SoundManagerMsg::add: {
00586       //cout << "it's an add of " << msg->id << ", sn=" << msg->sn << " (expecting " << sndlist[msg->id].sn << ", next sn is " << sn << ")" << endl;
00587       //first check msg->id's validity, in case it was deleted while in-flight
00588       /* //since Release marks deleted entries with serial number 0, we can get around this
00589       bool valid=false;
00590       for(Snd_ID it=sndlist.begin(); it!=sndlist.end(); it=sndlist.next(it)) {
00591         if(it==msg->id) {
00592           valid=true;
00593           break;
00594         }
00595       }
00596       if(!valid) {
00597         //was deleted while buffer was still in-flight, ignore this message
00598         break; //leaves switch statement, try next message if any
00599       }
00600       //now, even though the sound id is valid, verify the serial numbers match
00601       */
00602       if(sndlist[msg->id].sn!=msg->sn) {
00603         /*this means that the sound this message references was deleted while
00604         * in-flight, even if some crazy process (perhaps the same one) has
00605         * created and destroyed a number of sounds before we got this message */
00606         /* So although there is a sound for this id, it's a different sound than
00607         * the one this message refers to, and we should ignore this message */
00608         cerr << "Warning: serial numbers don't match... may be pathological sound usage (many load/releases very quickly)" << endl;
00609         break; //leaves switch statement, try next message if any
00610       }
00611       ASSERT(sndlist[msg->id].rcr==NULL,"The sndlist entry for an add message already has an attached region, I'm going to leak the old region")
00612       rcr->AddReference();
00613       sndlist[msg->id].rcr=rcr;
00614       sndlist[msg->id].data=reinterpret_cast<byte*>(rcr->Base()+MSG_SIZE);
00615       //look to see if there's any play's for the sound we just finished loading
00616       for(playlist_t::index_t it=playlist.begin();it!=playlist.end();it=playlist.next(it))
00617         if(playlist[it].snd_id==msg->id) {
00618           //send an event if there are
00619           const char * name=sndlist[playlist[it].snd_id].name;
00620           if(name[0]!='\0')
00621             erouter->postEvent(EventBase::audioEGID,it,EventBase::activateETID,0,name,1);
00622           else
00623             erouter->postEvent(EventBase::audioEGID,it,EventBase::activateETID,0);
00624         }
00625     } break;
00626       case SoundManagerMsg::del: {
00627         //cout << "it's an del" << endl;
00628         if(msg->region==NULL) {
00629           cerr << "SoundManager received a delete message for a NULL region" << endl;
00630         } else {
00631           msg->region->RemoveReference();
00632         }
00633       } break;
00634       case SoundManagerMsg::wakeup: {
00635         //cout << "it's a wakeup" << endl;
00636         //doesn't need to do anything, just causes SoundPlay process to check activity
00637       } break;
00638       default:
00639         printf("*** WARNING *** unknown SoundManager msg type received\n");
00640   }
00641 }
00642 
00643 
00644 //protected:
00645 
00646 RCRegion*
00647 SoundManager::initRegion(unsigned int size) {
00648   sn++; //so the first serial number handed out will be '1', leaving '0' as 'invalid'
00649 #ifdef PLATFORM_APERIOS
00650   unsigned int pagesize=4096;
00651   sError err=GetPageSize(&pagesize);
00652   ASSERT(err==sSUCCESS,"Error "<<err<<" getting page size");
00653   unsigned int pages=(size+pagesize-1)/pagesize;
00654   return new RCRegion(pages*pagesize);
00655 #else
00656   char name[RCRegion::MAX_NAME_LEN];
00657   snprintf(name,RCRegion::MAX_NAME_LEN,"SndMsg.%d.%d",ProcessID::getID(),sn);
00658   name[RCRegion::MAX_NAME_LEN-1]='\0';
00659   //cout << "Created " << name << endl;
00660   return new RCRegion(name,size);
00661 #endif
00662 }
00663 
00664 SoundManager::Snd_ID 
00665 SoundManager::lookupPath(std::string const &path) const {
00666   std::string clippedPath;
00667   const char* cpath=NULL;
00668   if(path.size()>MAX_NAME_LEN) {  //compare against the end of the string if it's too long -- the end is more likely to be unique
00669     clippedPath=path.substr(path.size()-MAX_NAME_LEN);
00670     cpath=clippedPath.c_str();
00671   } else
00672     cpath=path.c_str();
00673   for(sndlist_t::index_t it=sndlist.begin(); it!=sndlist.end(); it=sndlist.next(it)) {
00674     if(strncasecmp(cpath,sndlist[it].name,MAX_NAME_LEN)==0)
00675       return it;
00676   }
00677   return invalid_Snd_ID;
00678 }
00679 
00680 void
00681 SoundManager::selectChannels(std::vector<Play_ID>& mix) {
00682   unsigned int selected=0;
00683   switch(queue_mode) {
00684   case Enqueue: { //select the oldest channels
00685     for(chanlist_t::index_t it=chanlist.prev(chanlist.end());it!=chanlist.end();it=chanlist.prev(it)) {
00686       if(sndlist[playlist[chanlist[it]].snd_id].data!=NULL) {
00687         mix.push_back(chanlist[it]);
00688         selected++;
00689         if(selected==max_chan)
00690           return;
00691       }
00692     }
00693   } break;
00694   case Override:
00695   case Pause: { //select the youngest channels (difference between these two is in the final update)
00696     for(chanlist_t::index_t it=chanlist.begin(); it!=chanlist.end(); it=chanlist.next(it)) {
00697       if(sndlist[playlist[chanlist[it]].snd_id].data!=NULL) {
00698         mix.push_back(chanlist[it]);
00699         selected++;
00700         if(selected==max_chan)
00701           return;
00702       }
00703     }
00704   } break;
00705   case Stop: { //select the youngest, stop anything that remains
00706     unsigned int numkeep=0;
00707     chanlist_t::index_t it=chanlist.begin();
00708     for(;it!=chanlist.end(); it=chanlist.next(it), numkeep++) {
00709       if(sndlist[playlist[chanlist[it]].snd_id].data!=NULL) {
00710         mix.push_back(chanlist[it]);
00711         selected++;
00712         if(selected==max_chan) {
00713           for(unsigned int i=chanlist.size()-numkeep-1; i>0; i--)
00714             endPlay(chanlist.back());
00715           return;
00716         }
00717       }
00718     }
00719   } break;
00720   default:
00721     cout << "SoundManager::selectChannels(): Illegal queue mode" << endl;
00722   }
00723 }
00724 
00725 void
00726 SoundManager::updateChannels(const std::vector<Play_ID>& mixs,size_t used) {
00727   switch(queue_mode) {
00728   case Enqueue:
00729   case Pause:
00730   case Stop: 
00731     break;
00732   case Override: { //increase offset of everything that wasn't selected
00733     //assumes mode hasn't changed since the mix list was created... (so order is same as chanlist)
00734     chanlist_t::index_t it=chanlist.begin(); 
00735     std::vector<Play_ID>::const_iterator mixit=mixs.begin();
00736     for(;it!=chanlist.end(); it=chanlist.next(it)) {
00737       for(;mixit!=mixs.end(); mixit++) //some mixs may have been stopped during play
00738         if(*mixit==chanlist[it])
00739           break;
00740       if(mixit==mixs.end())
00741         break;
00742     }
00743     for(;it!=chanlist.end(); it=chanlist.next(it)) {
00744       const Play_ID channelId = chanlist[it];
00745       PlayState &channel = playlist[channelId];
00746       size_t skip = used;
00747       while (skip > 0) {
00748         SoundData &buffer = sndlist[channel.snd_id];
00749         // FIXME: Don't know why the buffer.data != 0 check is done 
00750         if (buffer.data != 0) {
00751           size_t remain = buffer.len - channel.offset;
00752           if (remain < skip) {
00753             channel.offset = buffer.len;
00754             skip -= buffer.len;
00755             if (endPlay(channelId)) {
00756               break;
00757             }
00758           } else {
00759             channel.offset += skip;
00760             skip = 0;
00761           }
00762         } else {
00763           break;
00764         }
00765       }
00766     }
00767   } break;
00768   default:
00769     cout << "SoundManager::updateChannels(): Illegal queue mode" << endl;
00770   }
00771 }
00772 
00773 bool
00774 SoundManager::endPlay(Play_ID id) {
00775   if(playlist[id].next_id==invalid_Play_ID) {
00776     StopPlay(id);
00777     return true;
00778   } else {
00779     //copies the next one into current so that the Play_ID consistently refers to the same "sound"
00780     Play_ID next=playlist[id].next_id;
00781     //    cout << "play " << id << " moving from " << playlist[id].snd_id << " to " << playlist[next].snd_id << endl;
00782     Release(playlist[id].snd_id);
00783     playlist[id].snd_id=playlist[next].snd_id;
00784     playlist[id].cumulative+=playlist[id].offset;
00785     playlist[id].offset=0;
00786     playlist[id].next_id=playlist[next].next_id;
00787     playlist.erase(next);
00788     unsigned int ms=playlist[id].cumulative/(config->sound.sample_bits/8)/(config->sound.sample_rate/1000);
00789     const char * name=sndlist[playlist[id].snd_id].name;
00790     if(name[0]!='\0')
00791       erouter->postEvent(EventBase::audioEGID,id,EventBase::statusETID,ms,name,1);
00792     else
00793       erouter->postEvent(EventBase::audioEGID,id,EventBase::statusETID,ms);
00794     return false;
00795   }
00796 }
00797 
00798 SoundManager::SoundData::SoundData()
00799   : rcr(NULL), data(NULL), len(0), ref(0), sn(0)
00800 {
00801   name[0]='\0';
00802 }
00803 
00804 SoundManager::PlayState::PlayState()
00805   : snd_id(invalid_Snd_ID), offset(0), cumulative(0), next_id(invalid_Play_ID)
00806 {}
00807 
00808 
00809 /*! @file
00810  * @brief Implements SoundManager, which provides sound effects and caching services, as well as mixing buffers for the SoundPlay process
00811  * @author ejt (Creator)
00812  *
00813  * $Author: ejt $
00814  * $Name: tekkotsu-2_4_1 $
00815  * $Revision: 1.2 $
00816  * $State: Exp $
00817  * $Date: 2005/06/01 05:47:53 $
00818  */
00819 
00820 
00821 
00822 
00823 //This is a faster mix algo using bit shifting, but it doesn't work with
00824 // non power of two number of channels, despite my efforts... eh, maybe
00825 // i'll fix it later...
00826     // NOT WORKING
00827     /*
00828     if(mixs.size()==2 || mix_mode==Fast) {
00829       unsigned int shift=0;
00830       unsigned int offset=0;
00831       unsigned int tmp=mixs.size();
00832       while(tmp>1) {
00833         tmp>>=1;
00834         shift++;
00835       }
00836       unsigned int mask;
00837       if(config->sound.sample_bits==8) {
00838         unsigned int c=(unsigned char)~0;
00839         c>>=shift;
00840         mask=(c<<24)|(c<<16)|(c<<8)|c;
00841         offset=(1<<7)|(1<<15)|(1<<23)|(1<<31);
00842       } else {
00843         unsigned int c=(unsigned short)~0;
00844         c>>=shift;
00845         mask=(c<<16)|c;
00846         offset=(1<<31)|(1<<15);
00847       }
00848       memset(dest,0,avail);
00849       for(unsigned int c=0; c<mixs.size(); c++) {
00850         if(ends[c]-srcs[c]>avail) {
00851           for(unsigned int * beg=(unsigned int*)dest;beg<(unsigned int*)end;beg++) {
00852             const unsigned int src=*(unsigned int*)srcs[c];
00853             if(beg==(unsigned int*)dest) {
00854               cout << src <<' '<< (void*)src << endl;
00855               unsigned int x=((src^offset)>>shift)&mask;
00856               cout << x <<' '<< (void*)x << endl;
00857               cout << "****" << endl;
00858             }
00859             *beg+=((src^offset)>>shift)&mask;
00860             if(beg==(unsigned int*)dest)
00861               cout << *beg <<' '<< (void*)*beg << endl << "########" << endl;
00862             srcs[c]+=sizeof(int);
00863           }
00864           playlist[mixs[c]].offset+=avail;
00865         } else {
00866           unsigned int * beg=(unsigned int*)dest;
00867           for(;srcs[c]<ends[c];srcs[c]+=sizeof(int)) {
00868             const unsigned int src=*(unsigned int*)srcs[c];
00869             *beg+=((src^offset)>>shift)&mask;
00870             beg++;
00871           }
00872           for(;beg<(unsigned int*)end; beg++)
00873             *beg+=offset>>shift;
00874           playlist[mixs[c]].offset=sndlist[playlist[mixs[c]].snd_id].len;
00875           StopPlay(mixs[c]);
00876         }
00877       }
00878       unsigned int leftover=(offset>>shift)*((1<<shift)-mixs.size());
00879       for(unsigned int * beg=(unsigned int*)dest;beg<(unsigned int*)end;beg++)
00880         *beg=*(beg+leftover)^offset;
00881       updateChannels(avail);
00882       return mixs.size();
00883       } else*/

Tekkotsu v2.4.1
Generated Tue Aug 16 16:32:49 2005 by Doxygen 1.4.4