Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

SoundManager.cc

Go to the documentation of this file.
00001 #include "Shared/Config.h"
00002 #include "Shared/debuget.h"
00003 #include "Shared/MarkScope.h"
00004 #include "Events/EventRouter.h"
00005 
00006 #include "SoundManager.h"
00007 #include "WAV.h"
00008 
00009 #include <sys/types.h>
00010 #include <sys/stat.h>
00011 #include <fcntl.h>
00012 #include <unistd.h>
00013 #include <stdio.h>
00014 #include <fstream>
00015 #include <stdexcept>
00016 
00017 #ifdef PLATFORM_APERIOS
00018 #  include <OPENR/OSubject.h>
00019 #  include <OPENR/ObjcommEvent.h>
00020 #else
00021 #  ifndef __APPLE__
00022 #    include "MaryClient.h"
00023 #  else
00024 #    include <ApplicationServices/ApplicationServices.h>
00025 //! deletes the MacSpeechState stored in the playlist entry's sound entry's data field, and then removes these entries
00026 void speechDoneCleanup(long playid) { MarkScope autolock(sndman->lock); sndman->endPlay(playid); }
00027 
00028 //! Callback function registered with the OS speech manager.
00029 /*! Passes on to speechDoneCleanup2() which is a friend of SoundManager without having to forward declare SpeechChannel */
00030 static void speechDoneCleanupCallback(SpeechChannel, long playid) { speechDoneCleanup(playid); }
00031 
00032 //! holds some OS specific context for speaking
00033 struct MacSpeechState {
00034   //! constructor, creates speech channel and configures speechDoneCleanup() as a callback
00035   MacSpeechState(const std::string& toSpeak) : chan(), text(toSpeak), id(SoundManager::invalid_Play_ID) {
00036     if(noErr != NewSpeechChannel(NULL,&chan))
00037       throw std::runtime_error("could not open speech channel");
00038     if(noErr != SetSpeechInfo(chan, soSpeechDoneCallBack, (void*)speechDoneCleanupCallback))
00039       throw std::runtime_error("could not set completion callback for speech channel");
00040   }
00041   //! deallocate the speech channel and text data
00042   ~MacSpeechState() {
00043     if(noErr != DisposeSpeechChannel(chan)) {
00044       std::cerr << "SoundManager::speechDoneCleanup, error in DisposeSpeechChannel" << std::endl;
00045     }
00046   }
00047   //! sets the context in the speech channel to the playid so we can clean up later in speechDoneCleanup() callback
00048   void setPlayID(SoundManager::Play_ID playid) {
00049     id=playid;
00050     if(noErr != SetSpeechInfo(chan, soRefCon, (void*)playid))
00051       throw std::runtime_error("could not set playid context for speech channel");
00052   }
00053   SpeechChannel chan; //!< speech channel from OS X ApplicationServices
00054   const std::string text; //!< the text being spoken; this is not buffered by OS, we have to retain this memory until speech is done
00055   SoundManager::Play_ID id; //!< the playlist id
00056 private:
00057   MacSpeechState(MacSpeechState&); //!< shouldn't be copying
00058   MacSpeechState& operator=(MacSpeechState&); //!< shouldn't be assigning
00059 };
00060 #  endif
00061 #endif
00062 
00063 using namespace std;
00064 
00065 SoundManager * sndman=NULL;
00066 
00067 //!for convenience when locking each of the functions
00068 typedef MarkScope AutoLock;
00069 
00070 SoundManager::SoundManager() :
00071   mixerBuffer(0), mixerBufferSize(0), sndlist(),playlist(),chanlist(),
00072   mix_mode(Fast),queue_mode(Override),max_chan(4),lock(),sn(0)
00073 { }
00074 
00075 #ifdef PLATFORM_APERIOS
00076 void
00077 SoundManager::InitAccess(OSubject* subj) {
00078   subjs[ProcessID::getID()]=subj;
00079 }
00080 #else //PLATFORM_LOCAL
00081 void
00082 SoundManager::InitAccess(MessageQueueBase& sndbufq) {
00083   subjs[ProcessID::getID()]=&sndbufq;
00084 }
00085 #endif //PLATFORM-specific initialization
00086 
00087 SoundManager::~SoundManager() {
00088   stopPlay();
00089   if(!sndlist.empty())
00090     cerr << "Warning: SoundManager was deleted with active sound buffer references" << endl;
00091   while(!sndlist.empty()) {
00092     sndlist_t::index_t it=sndlist.begin();
00093     if(sndlist[it].rcr==NULL)
00094       cerr << sndlist[it].name << " was still inflight (IPC), with " << sndlist[it].ref << " sound references" << endl;
00095     else {
00096       cerr << sndlist[it].name << " was deleted, with " << sndlist[it].ref << " sound references and " << sndlist[it].rcr->NumberOfReference() << " region references (one will be removed)" << endl;
00097       sndlist[it].rcr->RemoveReference();
00098     }
00099     sndlist.erase(it);
00100   }
00101   delete[] mixerBuffer;
00102 }
00103 
00104 //!@todo this does one more copy than it really needs to
00105 SoundManager::Snd_ID
00106 SoundManager::loadFile(std::string const &name) {
00107   AutoLock autolock(lock);
00108   if (name.size() == 0) {
00109     cerr << "SoundManager::loadFile() null filename" << endl;
00110     return invalid_Snd_ID;
00111   };
00112   std::string path(config->sound.makePath(name));
00113   Snd_ID id=lookupPath(path);
00114   if(id!=invalid_Snd_ID) {
00115     //    cout << "add reference to pre-existing" << endl;
00116     sndlist[id].ref++;
00117   } else {
00118     //    cout << "load new file" << endl;
00119     struct stat buf;
00120     if(stat(path.c_str(),&buf)==-1) {
00121       cerr << "SoundManager::loadFile(): Sound file not found: " << path << endl;
00122       return invalid_Snd_ID;
00123     }
00124     byte * sndbuf=new byte[buf.st_size];
00125     std::ifstream file(path.c_str());
00126     file.read(reinterpret_cast<char*>(sndbuf),(std::streamsize)buf.st_size);
00127     WAV wav;
00128     WAVError error = wav.Set(sndbuf);
00129     if (error != WAV_SUCCESS) {
00130       printf("%s : %s %d: '%s'","SoundManager::loadFile()","wav.Set() FAILED",error, path.c_str());
00131       return invalid_Snd_ID;
00132     }
00133     if(wav.GetSamplingRate()!=config->sound.sample_rate || wav.GetBitsPerSample()!=config->sound.sample_bits) {
00134       printf("%s : %s %d","SoundManager::loadFile()","bad sample rate/bits", error);
00135       return invalid_Snd_ID;
00136     }
00137     if(config->sound.verbose>=3)
00138       cout << "Loading " << name << endl;
00139     id=loadBuffer(reinterpret_cast<char*>(wav.GetDataStart()),wav.GetDataEnd()-wav.GetDataStart());
00140     delete [] sndbuf;
00141     if(path.size()>=MAX_NAME_LEN)
00142       strncpy(sndlist[id].name,path.substr(path.size()-MAX_NAME_LEN+1).c_str(),MAX_NAME_LEN);
00143     else
00144       strncpy(sndlist[id].name,path.c_str(),MAX_NAME_LEN);
00145   }
00146   return id;
00147 }
00148 
00149 SoundManager::Snd_ID
00150 SoundManager::loadBuffer(const char buf[], unsigned int len) {
00151   // cout << "SoundManager::loadBuffer() of " << len << " bytes" << endl;
00152   if(buf==NULL || len==0)
00153     return invalid_Snd_ID;
00154   AutoLock autolock(lock);
00155   //setup region
00156   RCRegion * region=initRegion(len+MSG_SIZE);
00157   //setup message
00158   SoundManagerMsg * msg=new (reinterpret_cast<SoundManagerMsg*>(region->Base())) SoundManagerMsg;
00159   Snd_ID msgid=sndlist.new_front();
00160   msg->setAdd(msgid,sn);
00161   //init sound structure
00162   sndlist[msg->getID()].rcr=NULL;  // set by SoundPlay upon reception
00163   sndlist[msg->getID()].data=NULL; // set by SoundPlay upon reception
00164   sndlist[msg->getID()].ref=1;
00165   sndlist[msg->getID()].len=len;
00166   sndlist[msg->getID()].sn=sn;
00167   //copy buffer, do any filtering needed here
00168   const byte* src=reinterpret_cast<const byte*>(buf);
00169   byte* dest=reinterpret_cast<byte*>(region->Base())+MSG_SIZE;
00170   byte* end=dest+len;
00171   if (config->sound.sample_bits==8u)
00172     while (dest < end)
00173       *dest++ = *src++ ^ 0x80; // offset binary -> signed char 
00174   else
00175     while (dest < end)
00176       *dest++ = *src++;
00177   //cout << "SoundManager init region " << region->ID().key << endl;
00178   //send message
00179   if(ProcessID::getID()==ProcessID::SoundProcess) {
00180     //if SoundPlay is preloading files, don't need to do inter-object comm
00181     sndlist[msg->getID()].rcr=region;
00182     sndlist[msg->getID()].data=reinterpret_cast<byte*>(region->Base()+MSG_SIZE);    
00183   } else {
00184 #ifdef PLATFORM_APERIOS
00185     //cout << "Send new at " << get_time() << '-';
00186     subjs[ProcessID::getID()]->SetData(region);
00187     subjs[ProcessID::getID()]->NotifyObservers();
00188     //cout << get_time() << endl;
00189 #else
00190     subjs[ProcessID::getID()]->sendMessage(region);
00191     region->RemoveReference();
00192 #endif
00193   }
00194   return msgid;
00195 }
00196   
00197 void
00198 SoundManager::releaseFile(std::string const &name) {
00199   AutoLock autolock(lock);
00200   release(lookupPath(config->sound.makePath(name)));
00201 }
00202 
00203 void
00204 SoundManager::release(Snd_ID id) {
00205   if(id==invalid_Snd_ID)
00206     return;
00207   if(sndlist[id].ref==0) {
00208     cerr << "SoundManager::release() " << id << " extra release" << endl;
00209     return;
00210   }
00211   AutoLock autolock(lock);
00212   sndlist[id].ref--;
00213   if(sndlist[id].ref==0) {
00214     if(config->sound.verbose>=3 && sndlist[id].name[0]!='\0' && sndlist[id].sn!=0) {
00215       char * name = strrchr(sndlist[id].name, '/');
00216       name = (name==NULL) ? sndlist[id].name : name+1;
00217       cout << "Releasing " << name << endl;
00218     }
00219     if(sndlist[id].rcr!=NULL) {
00220       //The sound buffer is attached in the sound process, we need to detach it.
00221       //Note that if this was NULL, we just assume that's because the message for the
00222       //region is still in transit from the originating process, and erase the sndlist entry.
00223       //Then when that buffer does arrive, we'll discover the sndlist entry is invalid and ignore it
00224       if(ProcessID::getID()==ProcessID::SoundProcess) {
00225         //we're currently running in sound process -- don't need to send ourselves an IPC message
00226         sndlist[id].rcr->RemoveReference();
00227         sndlist[id].rcr=NULL;
00228       } else {
00229         //we're currently running in a foreign process -- have to send message to sound process to release
00230         //setup region
00231         RCRegion * region=initRegion(MSG_SIZE);
00232         //setup message
00233         SoundManagerMsg * msg=new (reinterpret_cast<SoundManagerMsg*>(region->Base())) SoundManagerMsg;
00234         msg->setDelete(sndlist[id].rcr);
00235         //cout << "Sending delete msg for " << sndlist[id].name << endl;
00236         //send message
00237 #ifdef PLATFORM_APERIOS
00238         //cout << "Send delete at " << get_time() << '-';
00239         subjs[ProcessID::getID()]->SetData(region);
00240         subjs[ProcessID::getID()]->NotifyObservers();
00241         //cout << get_time() << endl;
00242 #else
00243         subjs[ProcessID::getID()]->sendMessage(region);
00244         region->RemoveReference();
00245 #endif
00246       }
00247     }
00248     //clean up sound data structure
00249     sndlist[id].sn=0; // we use '1' for the first issued, so 0 marks it as invalid
00250 #ifdef __APPLE__
00251     if(sndlist[id].macSpeech)
00252       lock.completed(sndlist[id].macSpeech);
00253 #endif
00254     sndlist.erase(id);
00255   }
00256 }
00257 
00258 SoundManager::Play_ID
00259 SoundManager::playFile(std::string const &name) {
00260   if(playlist.size()>=playlist_t::MAX_ENTRIES)
00261     return invalid_Play_ID;
00262   AutoLock autolock(lock);
00263   Snd_ID sndid=loadFile(name);
00264   if(sndid==invalid_Snd_ID)
00265     return invalid_Play_ID;
00266   sndlist[sndid].ref--;
00267   if(config->sound.verbose>=1)
00268     cout << "Playing " << name /*<< " from process " << ProcessID::getID()*/ << endl;
00269   return play(sndid);
00270 }
00271 
00272 SoundManager::Play_ID
00273 SoundManager::playBuffer(const char buf[], unsigned int len) {
00274   if(playlist.size()>=playlist_t::MAX_ENTRIES || buf==NULL || len==0)
00275     return invalid_Play_ID; 
00276   AutoLock autolock(lock);
00277   Snd_ID sndid=loadBuffer(buf,len);
00278   if(sndid==invalid_Snd_ID)
00279     return invalid_Play_ID;
00280   sndlist[sndid].ref--;
00281   return play(sndid);
00282 }
00283   
00284 SoundManager::Play_ID
00285 SoundManager::play(Snd_ID id) {
00286   // cout << "Play " << id << endl;
00287   if(id==invalid_Snd_ID)
00288     return invalid_Play_ID;
00289   AutoLock autolock(lock);
00290   Play_ID playid=playlist.new_front();
00291   if(playid==invalid_Play_ID)
00292     return playid;
00293 
00294   sndlist[id].ref++;
00295   playlist[playid].snd_id=id;
00296   playlist[playid].offset=0;
00297 
00298 #ifdef __APPLE__
00299   if(MacSpeechState* mss = sndlist[id].macSpeech) {
00300     try {
00301       mss->setPlayID(playid);
00302       lock.initiated(mss);
00303     } catch(const std::exception& ex) {
00304       std::cerr << "ERROR SoundManager: " << ex.what() << std::endl;
00305       playlist.erase(playid);
00306       release(id);
00307       return SoundManager::invalid_Play_ID;
00308     }
00309     const char * name=sndlist[playlist[playid].snd_id].name;
00310     if(name[0]!='\0')
00311       erouter->postEvent(EventBase::audioEGID,playid,EventBase::activateETID,0,name,1);
00312     else
00313       erouter->postEvent(EventBase::audioEGID,playid,EventBase::activateETID,0);
00314     return playid; // we don't have samples, don't add it to a channel
00315   }
00316 #endif
00317   
00318   //playlist.size() should be greater than or equal to chanlist.size
00319   //so if we got a playid, we can get a channel slot.
00320   chanlist.push_front(playid);
00321 
00322   //setup message to "wake-up" 
00323   //(only really need if chanlist was empty)
00324   //    if(chanlist.size()==1) { //commented out because sometimes doesn't wake up, thinks it's playing but isn't
00325   if(ProcessID::getID()!=ProcessID::SoundProcess) {
00326     RCRegion * region=initRegion(MSG_SIZE);
00327     ASSERT(region!=NULL,"initRegion returned NULL");
00328     SoundManagerMsg * msg=new (reinterpret_cast<SoundManagerMsg*>(region->Base())) SoundManagerMsg;
00329     msg->setWakeup();
00330 #ifdef PLATFORM_APERIOS
00331     //cout << "Send wakeup at " << get_time() << '-';
00332     subjs[ProcessID::getID()]->SetData(region);
00333     subjs[ProcessID::getID()]->NotifyObservers();
00334     //cout << get_time() << endl;
00335 #else
00336     subjs[ProcessID::getID()]->sendMessage(region);
00337     region->RemoveReference();
00338 #endif
00339   }
00340   //    }
00341   
00342   if(sndlist[id].rcr!=NULL) {
00343     const char * name=sndlist[playlist[playid].snd_id].name;
00344     if(name[0]!='\0')
00345       erouter->postEvent(EventBase::audioEGID,playid,EventBase::activateETID,0,name,1);
00346     else
00347       erouter->postEvent(EventBase::audioEGID,playid,EventBase::activateETID,0);
00348   }
00349   return playid;
00350 }
00351   
00352 SoundManager::Play_ID
00353 SoundManager::chainFile(Play_ID base, std::string const &next) {
00354   if(base==invalid_Play_ID)
00355     return playFile(next);
00356   if(playlist[base].snd_id==invalid_Snd_ID) {
00357     std::cerr << "WARNING SoundManager: chaining on play " << base << " already stopped" << std::endl;
00358     return invalid_Play_ID;
00359   }
00360   Play_ID orig=base;
00361   while(playlist[base].next_id!=invalid_Play_ID)
00362     base=playlist[base].next_id;
00363   Play_ID nplay=playlist.new_front();
00364   if(nplay==invalid_Play_ID)
00365     return nplay;
00366   Snd_ID nsnd=loadFile(next);
00367   if(nsnd==invalid_Snd_ID) {
00368     playlist.pop_front();
00369     return invalid_Play_ID;
00370   }
00371   playlist[nplay].snd_id=nsnd;
00372   playlist[base].next_id=nplay;
00373   return orig;
00374 }
00375 
00376 SoundManager::Play_ID
00377 SoundManager::chainBuffer(Play_ID base, const char buf[], unsigned int len) {
00378   if(base==invalid_Play_ID || buf==NULL || len==0)
00379     return playBuffer(buf,len);
00380   if(playlist[base].snd_id==invalid_Snd_ID) {
00381     std::cerr << "WARNING SoundManager: chaining on play " << base << " already stopped" << std::endl;
00382     return invalid_Play_ID;
00383   }
00384   Play_ID orig=base;
00385   while(playlist[base].next_id!=invalid_Play_ID)
00386     base=playlist[base].next_id;
00387   Play_ID nplay=playlist.new_front();
00388   if(nplay==invalid_Play_ID)
00389     return nplay;
00390   Snd_ID nsnd=loadBuffer(buf,len);
00391   if(nsnd==invalid_Snd_ID) {
00392     playlist.pop_front();
00393     return invalid_Play_ID;
00394   }
00395   playlist[nplay].snd_id=nsnd;
00396   playlist[base].next_id=nplay;
00397   return orig;
00398 }
00399 
00400 SoundManager::Play_ID
00401 SoundManager::chain(Play_ID base, Snd_ID next) {
00402   if(base==invalid_Play_ID || next==invalid_Snd_ID)
00403     return play(next);
00404   if(playlist[base].snd_id==invalid_Snd_ID) {
00405     std::cerr << "WARNING SoundManager: chaining on play " << base << " already stopped" << std::endl;
00406     return invalid_Play_ID;
00407   }
00408   Play_ID orig=base;
00409   while(playlist[base].next_id!=invalid_Play_ID)
00410     base=playlist[base].next_id;
00411   Play_ID nplay=playlist.new_front();
00412   if(nplay==invalid_Play_ID)
00413     return nplay;
00414   playlist[nplay].snd_id=next;
00415   playlist[base].next_id=nplay;
00416   return orig;
00417 }
00418 
00419 #ifdef PLATFORM_APERIOS
00420 
00421 SoundManager::Play_ID
00422 SoundManager::speak(const std::string& text, bool showText, const std::string& voice) {
00423   sout->printf("Speak: %s\n",text.c_str());
00424   return SoundManager::invalid_Play_ID;
00425 }
00426 
00427 
00428 #elif defined(__APPLE__)
00429 
00430 SoundManager::Play_ID
00431 SoundManager:: speak(const std::string& text, bool showText, const std::string& /*voice*/) {
00432   // we could try to pick a voice, but easier just to stick with default and ignore the voice parameter
00433   
00434   if ( showText )
00435     std::cout << "Speak: \"" << text << "\"" << std::endl;
00436   
00437   MacSpeechState * mss;
00438   try {
00439     mss = new MacSpeechState(text);
00440   } catch(const std::exception& ex) {
00441     std::cerr << "ERROR SoundManager::speak " << ex.what() << std::endl;
00442     return invalid_Play_ID;
00443   }
00444   
00445   AutoLock autolock(lock);
00446   Snd_ID sndid=sndlist.new_front();
00447   sndlist[sndid].rcr=NULL;
00448   sndlist[sndid].data=NULL;
00449   sndlist[sndid].ref=0;
00450   sndlist[sndid].len=0;
00451   sndlist[sndid].sn=++sn;
00452   sndlist[sndid].macSpeech=mss;
00453   std::string name = "Speech:"+text;
00454   strncpy(sndlist[sndid].name,name.c_str(),MAX_NAME_LEN-1);
00455   sndlist[sndid].name[MAX_NAME_LEN-1]='\0';
00456   
00457   return play(sndid); // if the play fails, it will remove the sndid
00458 }
00459 
00460 #else // linux and friends
00461 
00462 SoundManager::Play_ID
00463 SoundManager::speak(const std::string& text, bool showText, const std::string& voice) {
00464   if ( showText )
00465     std::cout << "Speak: \"" << text << "\"" << std::endl;
00466 
00467   string result;
00468   maryQuery(result, text, voice);
00469 
00470   if (result.length() == 0) {
00471     std::cout << "SoundManager::speak(): Error, no response from mary server." << std::endl;
00472     return SoundManager::invalid_Play_ID;
00473   }
00474 
00475   // open a random /tmp/ file
00476   std::stringstream ss("");
00477   int randomTag = rand();
00478   ss << "/tmp/" << randomTag;
00479   
00480   std::string basename = ss.str();
00481   std::string filename = basename + ".wav";
00482   std::ofstream ofs(config->sound.makePath(filename).c_str(), std::ofstream::out);
00483   ofs << result;
00484   ofs.flush();
00485   ofs.close();
00486 
00487   // increase volume
00488   std::string loudname = basename + "-loud.wav";
00489   std::string command = "sox -v 3.0 " + config->sound.makePath(filename) + " " + config->sound.makePath(loudname);
00490   system(command.c_str());
00491 
00492   // play it
00493   SoundManager::Play_ID id = playFile(loudname);
00494 
00495   // remove files
00496   remove(config->sound.makePath(filename).c_str());
00497   remove(config->sound.makePath(loudname).c_str());
00498   
00499   return id;
00500 }
00501 
00502 #endif
00503 
00504 void
00505 SoundManager::stopPlay() {
00506   AutoLock autolock(lock);
00507   while(!playlist.empty())
00508     stopPlay(playlist.begin());
00509 }
00510 
00511 void
00512 SoundManager::stopPlay(Play_ID id) {
00513   if(id==invalid_Play_ID)
00514     return;
00515   if(playlist[id].snd_id==invalid_Snd_ID) {
00516     std::cerr << "WARNING SoundManager: Stopping play " << id << " already stopped" << std::endl;
00517     return;
00518   }
00519   AutoLock autolock(lock);
00520   //cout << "Stopping sound " << id << ": " << sndlist[playlist[id].snd_id].name << endl;
00521   //we start at the back (oldest) since these are the most likely to be removed...
00522   chanlist_t::index_t it;
00523   for(it=chanlist.prev(chanlist.end()); it!=chanlist.end(); it=chanlist.prev(it))
00524     if(chanlist[it]==id)
00525       break;
00526   
00527 #ifdef __APPLE__
00528   ASSERTRET(playlist[id].snd_id!=invalid_Snd_ID,"playlist entry has invalid sound id");
00529   ASSERT(!sndlist[playlist[id].snd_id].macSpeech || it==chanlist.end(),"speech play id was found in channel list");
00530 #else
00531   ASSERT(it!=chanlist.end(),"SoundManager::stopPlay: play id does not seem to be playing");
00532 #endif
00533   
00534   if(config->sound.verbose>=2) {
00535     char * cname = strrchr(sndlist[playlist[id].snd_id].name, '/');
00536     cname = (cname==NULL) ? sndlist[id].name : cname+1;
00537     cout << "End play " << cname /*<< " from process " << ProcessID::getID()*/ << endl;
00538   }
00539   std::string name=sndlist[playlist[id].snd_id].name;
00540   playlist[id].cumulative+=playlist[id].offset;
00541   unsigned int ms=playlist[id].cumulative/(config->sound.sample_bits/8)/(config->sound.sample_rate/1000);
00542   release(playlist[id].snd_id);
00543   playlist[id].snd_id=invalid_Snd_ID;
00544   playlist.erase(id);
00545   if(it!=chanlist.end())
00546     chanlist.erase(it);
00547   if(name.size()>0)
00548     erouter->postEvent(EventBase::audioEGID,id,EventBase::deactivateETID,ms,name,0);
00549   else
00550     erouter->postEvent(EventBase::audioEGID,id,EventBase::deactivateETID,ms);
00551 }
00552 
00553 void
00554 SoundManager::pausePlay(Play_ID id) {
00555   if(id==invalid_Play_ID)
00556     return;
00557   if(playlist[id].snd_id==invalid_Snd_ID) {
00558     std::cerr << "WARNING SoundManager: pausing play " << id << " already stopped" << std::endl;
00559     return;
00560   }
00561   AutoLock autolock(lock);
00562   
00563 #ifdef __APPLE__
00564   ASSERTRET(playlist[id].snd_id!=invalid_Snd_ID,"playlist entry has invalid sound id");
00565   if(MacSpeechState* mss = sndlist[playlist[id].snd_id].macSpeech) {
00566     if(noErr != PauseSpeechAt(mss->chan, kEndOfWord)) // or kImmediate or kEndOfSentence
00567       std::cerr << "ERROR SoundManager could not pause speech" << std::endl;
00568     return; // don't put it in a channel, system will handle mixing with any other sounds
00569   }
00570 #endif
00571   
00572   for(chanlist_t::index_t it=chanlist.begin(); it!=chanlist.end(); it=chanlist.next(it)) {
00573     if(chanlist[it]==id) {
00574       chanlist.erase(it);
00575       return;
00576     }
00577   }
00578 }
00579 
00580 void
00581 SoundManager::resumePlay(Play_ID id) {
00582   if(id==invalid_Play_ID)
00583     return;
00584   if(playlist[id].snd_id==invalid_Snd_ID) {
00585     std::cerr << "WARNING SoundManager: resuming play " << id << " already stopped" << std::endl;
00586     return;
00587   }
00588   AutoLock autolock(lock);
00589   
00590 #ifdef __APPLE__
00591   ASSERTRET(playlist[id].snd_id!=invalid_Snd_ID,"playlist entry has invalid sound id");
00592   if(MacSpeechState* mss = sndlist[playlist[id].snd_id].macSpeech) {
00593     if(noErr != ContinueSpeech(mss->chan))
00594       std::cerr << "ERROR SoundManager could not continue speech" << std::endl;
00595     return; // don't put it in a channel, system will handle mixing with any other sounds
00596   }
00597 #endif
00598   
00599   for(chanlist_t::index_t it=chanlist.begin(); it!=chanlist.end(); it=chanlist.next(it))
00600     if(chanlist[it]==id)
00601       return;
00602   chanlist.push_front(id);
00603   if(chanlist.size()==1) {
00604     if(ProcessID::getID()!=ProcessID::SoundProcess) {
00605       RCRegion * region=initRegion(MSG_SIZE);
00606       ASSERT(region!=NULL,"initRegion returned NULL");
00607       SoundManagerMsg * msg=new (reinterpret_cast<SoundManagerMsg*>(region->Base())) SoundManagerMsg;
00608       msg->setWakeup();
00609 #ifdef PLATFORM_APERIOS
00610       //cout << "Send wakeup at " << get_time() << '-';
00611       subjs[ProcessID::getID()]->SetData(region);
00612       subjs[ProcessID::getID()]->NotifyObservers();
00613       //cout << get_time() << endl;
00614 #else
00615       subjs[ProcessID::getID()]->sendMessage(region);
00616       region->RemoveReference();
00617 #endif
00618     }
00619   }
00620 }
00621 
00622 void
00623 SoundManager::setMode(unsigned int max_channels, MixMode_t mixer_mode, QueueMode_t queuing_mode) {
00624   AutoLock autolock(lock);
00625   max_chan=max_channels;
00626   mix_mode=mixer_mode;
00627   queue_mode=queuing_mode;
00628 }
00629 
00630 unsigned int
00631 SoundManager::getRemainTime(Play_ID id) const {
00632   if(playlist[id].snd_id==invalid_Snd_ID)
00633     return 0;
00634   AutoLock autolock(lock);
00635   unsigned int t=0;
00636   while(id!=invalid_Play_ID) {
00637     t+=sndlist[playlist[id].snd_id].len-playlist[id].offset;
00638     id=playlist[id].next_id;
00639   }
00640   const unsigned int bytesPerMS=config->sound.sample_bits/8*config->sound.sample_rate/1000;
00641   return t/bytesPerMS;
00642 }
00643 
00644 void
00645 SoundManager::mixChannel(Play_ID channelId, void* buf, size_t destSize) {
00646   char *dest = (char*) buf;
00647   
00648   PlayState& channel = playlist[channelId];
00649   while (destSize > 0) {
00650     const SoundData& buffer = sndlist[channel.snd_id];
00651     const char* samples = ((char*) (buffer.data)) + channel.offset;
00652     const unsigned int samplesSize = buffer.len - channel.offset;
00653     if (samplesSize > destSize) {
00654       memcpy(dest, samples, destSize);
00655       channel.offset += destSize;
00656       dest += destSize;
00657       destSize = 0;
00658       return;
00659     } else {
00660       memcpy(dest, samples, samplesSize);
00661       channel.offset += samplesSize;
00662       dest += samplesSize;
00663       destSize -= samplesSize;
00664       if (endPlay(channelId)) {
00665         break;
00666       }
00667     }
00668   }
00669   if (destSize > 0) {
00670     memset(dest, 0, destSize);
00671   }
00672 }
00673 
00674 void
00675 SoundManager::mixChannelAdditively(Play_ID channelId, int bitsPerSample, MixMode_t mode,
00676                    short scalingFactor, void* buf, size_t destSize)
00677 {
00678   PlayState& channel = playlist[channelId];
00679   while (destSize > 0) {
00680     const SoundData& buffer = sndlist[channel.snd_id];
00681     const unsigned int samplesSize = buffer.len - channel.offset;
00682     const unsigned int mixedSamplesSize =
00683     ((mode == Fast)
00684      ? ((samplesSize > destSize) ? destSize : samplesSize)
00685      : ((samplesSize > destSize / 2) ? destSize / 2 : samplesSize)); 
00686     
00687     if (bitsPerSample == 8) {
00688       //  8-bit mode
00689       const char* samples = (char*) (buffer.data + channel.offset);
00690       if (mode == Fast) {
00691         // 8-bit mixing
00692         char *dest = (char*) buf;
00693         for (size_t i = 0; i < mixedSamplesSize; i++) {
00694           *dest += samples[i] / scalingFactor;
00695           ++dest;
00696         }
00697         destSize -= (char*) dest - (char*) buf;
00698         buf = dest;
00699       } else {
00700         // 16-bit mixing
00701         short* dest = (short*) buf;
00702         for (size_t i = 0; i < mixedSamplesSize; i++) {
00703           *dest += samples[i];
00704           ++dest;
00705         }
00706         destSize -= (char*) dest - (char*) buf;
00707         buf = dest;
00708       }
00709     } else {
00710       // 16-bit mode
00711       const short* samples = (short*) (buffer.data + channel.offset);
00712       if (mode == Fast) {
00713         // 16-bit mixing
00714         short* dest = (short*) buf;
00715         for (size_t i = 0; i < mixedSamplesSize / 2; i++) {
00716           *dest += samples[i] / scalingFactor;
00717           ++dest;
00718         }
00719         destSize -= (char*) dest - (char*) buf;
00720         buf = dest;
00721       } else {
00722         // 32-bit mixing
00723         int* dest = (int*) buf;
00724         for (size_t i = 0; i < mixedSamplesSize / 2; i++) {
00725           *dest += samples[i];
00726           ++dest;
00727         }
00728         destSize -= (char*) dest - (char*) buf;
00729         buf = dest;
00730       }
00731     }
00732     channel.offset += mixedSamplesSize;
00733     if (destSize == 0) {
00734       return;
00735     } else {
00736       if (endPlay(channelId)) {
00737         return;
00738       }
00739     }
00740   }
00741 }
00742 
00743 #ifdef PLATFORM_APERIOS
00744 unsigned int
00745 SoundManager::CopyTo(OSoundVectorData* data) {
00746   AutoLock autolock(lock);
00747   return CopyTo(data->GetData(0), data->GetInfo(0)->dataSize);
00748 }
00749 
00750 void
00751 SoundManager::ReceivedMsg(const ONotifyEvent& event) {
00752   //cout << "Got msg at " << get_time() << endl;
00753   for(int x=0; x<event.NumOfData(); x++)
00754     ProcessMsg(event.RCData(x));
00755 }
00756 #endif
00757 
00758 unsigned int
00759 SoundManager::CopyTo(void * dest, size_t destSize) {
00760   AutoLock autolock(lock);
00761   
00762   void * origdest=dest;
00763   size_t origDestSize=destSize;
00764   if(chanlist.size() == 0) {
00765     memset(dest, 0, destSize);
00766     return 0;
00767   }
00768   
00769   std::vector<Play_ID> channels;
00770   selectChannels(channels);
00771   
00772   if (channels.size() == 0) {
00773     // No channels to mix
00774     memset(dest, 0, destSize); 
00775   } else if (channels.size() == 1) {
00776     // One channel to mix
00777     mixChannel(channels.front(), dest, destSize);
00778   } else {
00779     // Several channels to mix  
00780     const MixMode_t mode = mix_mode;
00781     const int bitsPerSample = config->sound.sample_bits;
00782     if (mode == Quality) {
00783       // Quality mixing uses an intermediate buffer
00784       if ((mixerBuffer == 0) || (mixerBufferSize < destSize * 2)) {
00785         delete[] mixerBuffer;
00786         mixerBuffer = 0;
00787         mixerBufferSize = destSize * 2;
00788         mixerBuffer = new int[(mixerBufferSize / 4) + 1]; // makes sure it's int-aligned
00789       }
00790       memset(mixerBuffer, 0,  mixerBufferSize);
00791       dest = mixerBuffer;
00792       destSize *= 2;
00793     } else {
00794       // Fast mixing does not use the intermediate buffer
00795       memset(dest, 0, destSize);
00796     }
00797     
00798     const int channelCount = channels.size();
00799     const short scalingFactor = (short) ((mode == Fast) ? channelCount : 1);  
00800     for(std::vector<Play_ID>::iterator i = channels.begin(); i != channels.end(); i++)
00801       mixChannelAdditively(*i, bitsPerSample, mode, scalingFactor, dest, destSize);
00802     
00803     if (mode == Quality) {
00804       // Quality mixing uses an intermediate buffer
00805       // Scale the buffer
00806       destSize /= 2;
00807       if (bitsPerSample == 8) {
00808         //  8-bit mode
00809         char* destChar = (char*) origdest;
00810         short* mixerBufferShort = (short*) mixerBuffer;
00811         for (size_t i = 0; i < destSize; i++) {
00812           destChar[i] = (char) (mixerBufferShort[i] / channelCount);
00813         } 
00814       } else {
00815         // 16-bit mode
00816         short* destShort = (short*) origdest;
00817         const size_t destSampleCount = destSize / 2; 
00818         for (size_t i = 0; i < destSampleCount; i++) {
00819           destShort[i] = (short) (mixerBuffer[i] / channelCount);
00820         }
00821       }
00822     }
00823   }
00824   
00825   updateChannels(channels, origDestSize);
00826 #ifndef PLATFORM_APERIOS
00827   // on platform aperios, we make a system call to adjust global volume setting
00828   // on platform local, we adjust the volume manually:
00829   if(config->sound.volume==Config::sound_config::MUTE)
00830     memset(dest, 0, origDestSize); 
00831 #endif
00832   return channels.size(); 
00833 }
00834 
00835 void SoundManager::ProcessMsg(RCRegion * rcr) {
00836   SoundManagerMsg * msg = reinterpret_cast<SoundManagerMsg*>(rcr->Base());
00837   //cout << "Processing " << msg << ": " << rcr->ID().key << endl;
00838   switch(msg->type) {
00839     case SoundManagerMsg::add: {
00840       //cout << "it's an add of " << msg->id << ", sn=" << msg->sn << " (expecting " << sndlist[msg->id].sn << ", next sn is " << sn << ")" << endl;
00841       //first check msg->id's validity, in case it was deleted while in-flight
00842       /* //since Release marks deleted entries with serial number 0, we can get around this
00843        bool valid=false;
00844        for(Snd_ID it=sndlist.begin(); it!=sndlist.end(); it=sndlist.next(it)) {
00845        if(it==msg->id) {
00846        valid=true;
00847        break;
00848        }
00849        }
00850        if(!valid) {
00851        //was deleted while buffer was still in-flight, ignore this message
00852        break; //leaves switch statement, try next message if any
00853        }
00854        //now, even though the sound id is valid, verify the serial numbers match
00855        */
00856       if(sndlist[msg->id].sn!=msg->sn) {
00857         /*this means that the sound this message references was deleted while
00858          * in-flight, even if some crazy process (perhaps the same one) has
00859          * created and destroyed a number of sounds before we got this message */
00860         /* So although there is a sound for this id, it's a different sound than
00861          * the one this message refers to, and we should ignore this message */
00862         cerr << "Warning: serial numbers don't match... may be pathological sound usage (many load/releases very quickly)" << endl;
00863         break; //leaves switch statement, try next message if any
00864       }
00865       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")
00866       rcr->AddReference();
00867       sndlist[msg->id].rcr=rcr;
00868       sndlist[msg->id].data=reinterpret_cast<byte*>(rcr->Base()+MSG_SIZE);
00869       //look to see if there's any play's for the sound we just finished loading
00870       for(playlist_t::index_t it=playlist.begin();it!=playlist.end();it=playlist.next(it))
00871         if(playlist[it].snd_id==msg->id) {
00872           //send an event if there are
00873           const char * name=sndlist[playlist[it].snd_id].name;
00874           if(name[0]!='\0')
00875             erouter->postEvent(EventBase::audioEGID,it,EventBase::activateETID,0,name,1);
00876           else
00877             erouter->postEvent(EventBase::audioEGID,it,EventBase::activateETID,0);
00878         }
00879     } break;
00880     case SoundManagerMsg::del: {
00881       //cout << "it's an del" << endl;
00882       if(msg->region==NULL) {
00883         cerr << "SoundManager received a delete message for a NULL region" << endl;
00884       } else {
00885         msg->region->RemoveReference();
00886       }
00887     } break;
00888     case SoundManagerMsg::wakeup: {
00889       //cout << "it's a wakeup" << endl;
00890       //doesn't need to do anything, just causes SoundPlay process to check activity
00891     } break;
00892     default:
00893       printf("*** WARNING *** unknown SoundManager msg type received\n");
00894   }
00895 }
00896 
00897 
00898 //protected:
00899 
00900 RCRegion*
00901 SoundManager::initRegion(unsigned int size) {
00902   sn++; //so the first serial number handed out will be '1', leaving '0' as 'invalid'
00903 #ifdef PLATFORM_APERIOS
00904   unsigned int pagesize=4096;
00905   sError err=GetPageSize(&pagesize);
00906   if(err!=sSUCCESS)
00907     cerr << "Error "<<err<<" getting page size " << pagesize << endl;
00908   unsigned int pages=(size+pagesize-1)/pagesize;
00909   return new RCRegion(pages*pagesize);
00910 #else
00911   char name[RCRegion::MAX_NAME_LEN];
00912   snprintf(name,RCRegion::MAX_NAME_LEN,"SndMsg.%d.%d",ProcessID::getID(),sn);
00913   name[RCRegion::MAX_NAME_LEN-1]='\0';
00914   //cout << "Created " << name << endl;
00915   return new RCRegion(name,size);
00916 #endif
00917 }
00918 
00919 SoundManager::Snd_ID 
00920 SoundManager::lookupPath(std::string const &path) const {
00921   std::string clippedPath;
00922   const char* cpath=NULL;
00923   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
00924     clippedPath=path.substr(path.size()-MAX_NAME_LEN);
00925     cpath=clippedPath.c_str();
00926   } else
00927     cpath=path.c_str();
00928   for(sndlist_t::index_t it=sndlist.begin(); it!=sndlist.end(); it=sndlist.next(it)) {
00929     if(strncasecmp(cpath,sndlist[it].name,MAX_NAME_LEN)==0)
00930       return it;
00931   }
00932   return invalid_Snd_ID;
00933 }
00934 
00935 void
00936 SoundManager::selectChannels(std::vector<Play_ID>& mix) {
00937   unsigned int selected=0;
00938   switch(queue_mode) {
00939     case Enqueue: { //select the oldest channels
00940       for(chanlist_t::index_t it=chanlist.prev(chanlist.end());it!=chanlist.end();it=chanlist.prev(it)) {
00941         if(sndlist[playlist[chanlist[it]].snd_id].data!=NULL) {
00942           mix.push_back(chanlist[it]);
00943           selected++;
00944           if(selected==max_chan)
00945             return;
00946         }
00947       }
00948     } break;
00949     case Override:
00950     case Pause: { //select the youngest channels (difference between these two is in the final update)
00951       for(chanlist_t::index_t it=chanlist.begin(); it!=chanlist.end(); it=chanlist.next(it)) {
00952         if(sndlist[playlist[chanlist[it]].snd_id].data!=NULL) {
00953           mix.push_back(chanlist[it]);
00954           selected++;
00955           if(selected==max_chan)
00956             return;
00957         }
00958       }
00959     } break;
00960     case Stop: { //select the youngest, stop anything that remains
00961       unsigned int numkeep=0;
00962       chanlist_t::index_t it=chanlist.begin();
00963       for(;it!=chanlist.end(); it=chanlist.next(it), numkeep++) {
00964         if(sndlist[playlist[chanlist[it]].snd_id].data!=NULL) {
00965           mix.push_back(chanlist[it]);
00966           selected++;
00967           if(selected==max_chan) {
00968             for(unsigned int i=chanlist.size()-numkeep-1; i>0; i--)
00969               endPlay(chanlist.back());
00970             return;
00971           }
00972         }
00973       }
00974     } break;
00975     default:
00976       cerr << "SoundManager::selectChannels(): Illegal queue mode" << endl;
00977   }
00978 }
00979 
00980 void
00981 SoundManager::updateChannels(const std::vector<Play_ID>& mixs,size_t used) {
00982   switch(queue_mode) {
00983     case Enqueue:
00984     case Pause:
00985     case Stop: 
00986       break;
00987     case Override: { //increase offset of everything that wasn't selected
00988       //assumes mode hasn't changed since the mix list was created... (so order is same as chanlist)
00989       chanlist_t::index_t it=chanlist.begin(); 
00990       std::vector<Play_ID>::const_iterator mixit=mixs.begin();
00991       for(;it!=chanlist.end(); it=chanlist.next(it)) {
00992         for(;mixit!=mixs.end(); mixit++) //some mixs may have been stopped during play
00993           if(*mixit==chanlist[it])
00994             break;
00995         if(mixit==mixs.end())
00996           break;
00997       }
00998       for(;it!=chanlist.end(); it=chanlist.next(it)) {
00999         const Play_ID channelId = chanlist[it];
01000         PlayState &channel = playlist[channelId];
01001         size_t skip = used;
01002         while (skip > 0) {
01003           SoundData &buffer = sndlist[channel.snd_id];
01004           // FIXME: Don't know why the buffer.data != 0 check is done 
01005           if (buffer.data != 0) {
01006             size_t remain = buffer.len - channel.offset;
01007             if (remain < skip) {
01008               channel.offset = buffer.len;
01009               skip -= buffer.len;
01010               if (endPlay(channelId)) {
01011                 break;
01012               }
01013             } else {
01014               channel.offset += skip;
01015               skip = 0;
01016             }
01017           } else {
01018             break;
01019           }
01020         }
01021       }
01022     } break;
01023     default:
01024       cerr << "SoundManager::updateChannels(): Illegal queue mode" << endl;
01025   }
01026 }
01027 
01028 bool
01029 SoundManager::endPlay(Play_ID id) {
01030   if(playlist[id].next_id==invalid_Play_ID) {
01031     stopPlay(id);
01032     return true;
01033   } else {
01034 #ifdef __APPLE__
01035     ASSERTRETVAL(playlist[id].snd_id!=invalid_Snd_ID,"playlist entry has invalid sound id",false);
01036     bool lastWasSpeech=(sndlist[playlist[id].snd_id].macSpeech);
01037 #endif
01038     //copies the next one into current so that the Play_ID consistently refers to the same "sound"
01039     Play_ID next=playlist[id].next_id;
01040     //    cout << "play " << id << " moving from " << playlist[id].snd_id << " to " << playlist[next].snd_id << endl;
01041     release(playlist[id].snd_id);
01042     playlist[id].snd_id=playlist[next].snd_id;
01043     playlist[id].cumulative+=playlist[id].offset;
01044     playlist[id].offset=0;
01045     playlist[id].next_id=playlist[next].next_id;
01046     playlist.erase(next);
01047     unsigned int ms=playlist[id].cumulative/(config->sound.sample_bits/8)/(config->sound.sample_rate/1000);
01048     const char * name=sndlist[playlist[id].snd_id].name;
01049     if(name[0]!='\0')
01050       erouter->postEvent(EventBase::audioEGID,id,EventBase::statusETID,ms,name,1);
01051     else
01052       erouter->postEvent(EventBase::audioEGID,id,EventBase::statusETID,ms);
01053 #ifdef __APPLE__
01054     if(lastWasSpeech)
01055       resumePlay(id);
01056 #endif
01057     return false;
01058   }
01059 }
01060 
01061 SoundManager::SoundData::SoundData()
01062 : rcr(NULL), data(NULL), len(0), ref(0), sn(0)
01063 #ifdef __APPLE__
01064 , macSpeech(NULL)
01065 #endif
01066 {
01067   name[0]='\0';
01068 }
01069 
01070 SoundManager::PlayState::PlayState()
01071 : snd_id(invalid_Snd_ID), offset(0), cumulative(0), next_id(invalid_Play_ID)
01072 {}
01073 
01074 #ifdef __APPLE__
01075 void SoundManager::Lock::releaseResource(Data& d) {
01076   if(get_lock_level()!=1 || ( completedSpeech.size()==0 && initiatedSpeech.size()==0) ) {
01077     MutexLock<ProcessID::NumProcesses>::releaseResource(d);
01078   } else {
01079     // last lock level and have resources to free...
01080     std::vector<MacSpeechState*> live(initiatedSpeech);
01081     std::vector<MacSpeechState*> dead(completedSpeech);
01082     // clear this before we release so other releases don't come back in here!
01083     initiatedSpeech.clear();
01084     completedSpeech.clear();
01085     // this is the key, want to release SoundManager::lock before we free OS resources or else we deadlock
01086     MutexLock<ProcessID::NumProcesses>::releaseResource(d);
01087     for(size_t i=0; i<live.size(); ++i) {
01088       if(noErr != SpeakText(live[i]->chan,live[i]->text.c_str(),live[i]->text.size())) {
01089         std::cerr << "ERROR SoundManager: could not pass text to SpeakText" << std::endl;
01090         speechDoneCleanup(live[i]->id);
01091       }
01092     }
01093     for(size_t i=0; i<dead.size(); ++i)
01094       delete dead[i];
01095   }
01096 }
01097 void SoundManager::Lock::initiated(MacSpeechState* mss) {
01098   initiatedSpeech.push_back(mss);
01099 }
01100 void SoundManager::Lock::completed(MacSpeechState* mss) {
01101   completedSpeech.push_back(mss);
01102 }
01103 #endif
01104 
01105 
01106 /*! @file
01107  * @brief Implements SoundManager, which provides sound effects and caching services, as well as mixing buffers for the SoundPlay process
01108  * @author ejt (Creator)
01109  */
01110 
01111 
01112 
01113 
01114 //This is a faster mix algo using bit shifting, but it doesn't work with
01115 // non power of two number of channels, despite my efforts... eh, maybe
01116 // i'll fix it later...
01117 // NOT WORKING
01118 /*
01119  if(mixs.size()==2 || mix_mode==Fast) {
01120  unsigned int shift=0;
01121  unsigned int offset=0;
01122  unsigned int tmp=mixs.size();
01123  while(tmp>1) {
01124  tmp>>=1;
01125  shift++;
01126  }
01127  unsigned int mask;
01128  if(config->sound.sample_bits==8) {
01129  unsigned int c=(unsigned char)~0;
01130  c>>=shift;
01131  mask=(c<<24)|(c<<16)|(c<<8)|c;
01132  offset=(1<<7)|(1<<15)|(1<<23)|(1<<31);
01133  } else {
01134  unsigned int c=(unsigned short)~0;
01135  c>>=shift;
01136  mask=(c<<16)|c;
01137  offset=(1<<31)|(1<<15);
01138  }
01139  memset(dest,0,avail);
01140  for(unsigned int c=0; c<mixs.size(); c++) {
01141  if(ends[c]-srcs[c]>avail) {
01142  for(unsigned int * beg=(unsigned int*)dest;beg<(unsigned int*)end;beg++) {
01143  const unsigned int src=*(unsigned int*)srcs[c];
01144  if(beg==(unsigned int*)dest) {
01145  cout << src <<' '<< (void*)src << endl;
01146  unsigned int x=((src^offset)>>shift)&mask;
01147  cout << x <<' '<< (void*)x << endl;
01148  cout << "****" << endl;
01149  }
01150  *beg+=((src^offset)>>shift)&mask;
01151  if(beg==(unsigned int*)dest)
01152  cout << *beg <<' '<< (void*)*beg << endl << "########" << endl;
01153  srcs[c]+=sizeof(int);
01154  }
01155  playlist[mixs[c]].offset+=avail;
01156  } else {
01157  unsigned int * beg=(unsigned int*)dest;
01158  for(;srcs[c]<ends[c];srcs[c]+=sizeof(int)) {
01159  const unsigned int src=*(unsigned int*)srcs[c];
01160  *beg+=((src^offset)>>shift)&mask;
01161  beg++;
01162  }
01163  for(;beg<(unsigned int*)end; beg++)
01164  *beg+=offset>>shift;
01165  playlist[mixs[c]].offset=sndlist[playlist[mixs[c]].snd_id].len;
01166  stopPlay(mixs[c]);
01167  }
01168  }
01169  unsigned int leftover=(offset>>shift)*((1<<shift)-mixs.size());
01170  for(unsigned int * beg=(unsigned int*)dest;beg<(unsigned int*)end;beg++)
01171  *beg=*(beg+leftover)^offset;
01172  updateChannels(avail);
01173  return mixs.size();
01174  } else*/

Tekkotsu v5.1CVS
Generated Fri Mar 16 05:26:52 2012 by Doxygen 1.6.3