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

Tekkotsu v5.1CVS
Generated Mon May 9 04:58:51 2016 by Doxygen 1.6.3