Main Page | Class Hierarchy | Alphabetical List | Class List | File List | Class Members | Related Pages | Examples

ArSoundsQueue.cpp

00001 /*
00002 MobileRobots Advanced Robotics Interface for Applications (ARIA)
00003 Copyright (C) 2004, 2005 ActivMedia Robotics LLC
00004 Copyright (C) 2006, 2007 MobileRobots Inc.
00005 
00006      This program is free software; you can redistribute it and/or modify
00007      it under the terms of the GNU General Public License as published by
00008      the Free Software Foundation; either version 2 of the License, or
00009      (at your option) any later version.
00010 
00011      This program is distributed in the hope that it will be useful,
00012      but WITHOUT ANY WARRANTY; without even the implied warranty of
00013      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014      GNU General Public License for more details.
00015 
00016      You should have received a copy of the GNU General Public License
00017      along with this program; if not, write to the Free Software
00018      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019 
00020 If you wish to redistribute ARIA under different terms, contact 
00021 MobileRobots for information about a commercial version of ARIA at 
00022 robots@mobilerobots.com or 
00023 MobileRobots Inc, 19 Columbia Drive, Amherst, NH 03031; 800-639-9481
00024 */
00025 
00026 #include "ArExport.h"
00027 #include "ArLog.h"
00028 #include "ariaUtil.h"
00029 #include "ArSoundsQueue.h"
00030 #include "ArSoundPlayer.h"
00031 #include <assert.h>
00032 
00033 
00034 // For debugging:
00035 //#define ARSOUNDSQUEUE_DEBUG 1
00036 
00037 
00038 #ifdef ARSOUNDSQUEUE_DEBUG
00039 #ifndef __PRETTY_FUNCTION__
00040 #define __PRETTY_FUNCTION__ __FUNCTION__
00041 #endif
00042 #define debuglog(msg) ArLog::log(ArLog::Verbose, "%s: %s", __PRETTY_FUNCTION__, (msg));
00043 #else
00044 #define debuglog(msg) {}
00045 #endif
00046 
00047 
00048 using namespace std;
00049 
00050 
00051 AREXPORT ArSoundsQueue::Item::Item() :
00052   data(""), type(OTHER), params(""), priority(0)
00053 {
00054 }
00055 
00056 AREXPORT ArSoundsQueue::Item::Item(std::string _data, ItemType _type, std::string _params, int _priority, std::list<PlayItemFunctor*> _callbacks) : 
00057       data(_data), type(_type), params(_params), priority(_priority), playCallbacks(_callbacks)
00058 {
00059 }
00060 
00061 AREXPORT ArSoundsQueue::Item::Item(std::string _data, ItemType _type, std::string _params, int _priority) : 
00062   data(_data), type(_type), params(_params), priority(_priority)
00063 {
00064 }
00065 
00066 AREXPORT ArSoundsQueue::Item::Item(const ArSoundsQueue::Item& toCopy) 
00067 {
00068   data = toCopy.data;
00069   type = toCopy.type;
00070   params = toCopy.params;
00071   priority = toCopy.priority;
00072   playCallbacks = toCopy.playCallbacks;
00073   interruptCallbacks = toCopy.interruptCallbacks;
00074   doneCallbacks = toCopy.doneCallbacks;
00075   playbackConditionCallbacks = toCopy.playbackConditionCallbacks;
00076 }
00077 
00078 void ArSoundsQueue::Item::play()
00079 {
00080   for(std::list<PlayItemFunctor*>::const_iterator i = playCallbacks.begin(); i != playCallbacks.end(); i++) 
00081   {
00082     if(*i) {
00083       (*i)->invokeR(data.c_str(), params.c_str());
00084     }
00085   }
00086 }
00087 
00088 void ArSoundsQueue::Item::interrupt()
00089 {
00090   for(std::list<ArFunctor*>::const_iterator i = interruptCallbacks.begin(); i != interruptCallbacks.end(); i++) 
00091     if(*i) (*i)->invoke();
00092 }
00093 
00094 
00095 void ArSoundsQueue::Item::done()
00096 {
00097   for(std::list<ArFunctor*>::const_iterator i = doneCallbacks.begin(); i != doneCallbacks.end(); i++) 
00098     if(*i) {
00099       (*i)->invoke();
00100     }
00101 }
00102 
00108 class ItemComparator {
00109 protected:
00110   ArSoundsQueue::Item myItem;
00111 public:
00112   ItemComparator(string data = "", ArSoundsQueue::ItemType type = ArSoundsQueue::OTHER, string params = "", int priority = 0) : 
00113     myItem(data, type, params, priority)
00114   {}
00115   virtual ~ItemComparator() {}
00116   virtual bool operator()(const ArSoundsQueue::Item& other)
00117   {
00118     return (other == myItem && other.priority == myItem.priority);
00119   }
00120 };
00121 
00125 class ItemComparator_OnlyData : public virtual ItemComparator {
00126 public:
00127   ItemComparator_OnlyData(string data) : 
00128     ItemComparator(data)
00129   {}
00130   virtual bool operator()(const ArSoundsQueue::Item& other)
00131   {
00132     return(other.data == myItem.data);
00133   }
00134 };
00135 
00139 class ItemComparator_TypeAndData : public virtual ItemComparator {
00140 public:
00141   ItemComparator_TypeAndData(string data, ArSoundsQueue::ItemType type) : 
00142     ItemComparator(data, type)
00143   {}
00144   virtual bool operator()(const ArSoundsQueue::Item& other)
00145   {
00146     return(other.type == myItem.type && other.data == myItem.data);
00147   }
00148 };
00149 
00153 class ItemComparator_PriorityLessThan : public virtual ItemComparator {
00154   int myPriority;
00155 public:
00156   ItemComparator_PriorityLessThan(int priority) : myPriority(priority)
00157   {}
00158   virtual bool operator()(const ArSoundsQueue::Item& other)
00159   {
00160     return(other.priority < myPriority);
00161   }
00162 };
00163 
00167 class ItemComparator_WithTypePriorityLessThan : public virtual ItemComparator {
00168   ArSoundsQueue::ItemType myType;
00169   int myPriority;
00170 public:
00171   ItemComparator_WithTypePriorityLessThan(ArSoundsQueue::ItemType type, int priority) : myType(type), myPriority(priority)
00172   {}
00173   virtual bool operator()(const ArSoundsQueue::Item& other)
00174   {
00175     return(other.type == myType && other.priority < myPriority);
00176   }
00177 };
00178 
00183 class ItemComparator_WithType : public virtual ItemComparator {
00184   ArSoundsQueue::ItemType myType;
00185 public:
00186   ItemComparator_WithType(ArSoundsQueue::ItemType type) : myType(type)
00187   {}
00188   virtual bool operator()(const ArSoundsQueue::Item& other)
00189   {
00190     return(other.type == myType);
00191   }
00192 };
00193 
00197 AREXPORT ArSoundsQueue::ArSoundsQueue()  :
00198   myInitialized(false),
00199   myPlayingSomething(false),
00200   myDefaultSpeakCB(0), myDefaultInterruptSpeechCB(0),
00201   myDefaultPlayFileCB(0), myDefaultInterruptFileCB(0),
00202   myPauseRequestCount(0),
00203   myDefaultPlayConditionCB(0)
00204 {
00205   setThreadName("ArSoundsQueue");
00206 }
00207 
00208 AREXPORT ArSoundsQueue::ArSoundsQueue(ArRetFunctor<bool> *speakInitCB, 
00209             PlayItemFunctor *speakCB, 
00210         InterruptItemFunctor *interruptSpeechCB,
00211         ArRetFunctor<bool> *playInitCB, 
00212         PlayItemFunctor *playCB,
00213         InterruptItemFunctor *interruptFileCB) :
00214   myInitialized(false), myPlayingSomething(false),
00215   myDefaultSpeakCB(speakCB), myDefaultInterruptSpeechCB(interruptSpeechCB),
00216   myDefaultPlayFileCB(playCB), myDefaultInterruptFileCB(interruptFileCB),
00217   myPauseRequestCount(0)
00218 { 
00219   setThreadName("ArSoundsQueue");
00220   if(speakInitCB)
00221     myInitCallbacks.push_back(speakInitCB);
00222   if(playInitCB)
00223     myInitCallbacks.push_back(playInitCB);
00224   if(playCB == 0)
00225     myDefaultPlayFileCB = ArSoundPlayer::getPlayWavFileCallback();
00226   if(interruptFileCB == 0)
00227     myDefaultInterruptFileCB = ArSoundPlayer::getStopPlayingCallback();
00228 }
00229 
00230 AREXPORT ArSoundsQueue::ArSoundsQueue(ArSpeechSynth* speechSynth, 
00231             ArRetFunctor<bool> *playInitCB,
00232             PlayItemFunctor *playFileCB,
00233         InterruptItemFunctor *interruptFileCB) 
00234   : myInitialized(false), myPlayingSomething(false),
00235   myDefaultSpeakCB(0), myDefaultInterruptSpeechCB(0),
00236   myDefaultPlayFileCB(playFileCB), myDefaultInterruptFileCB(interruptFileCB),
00237   myPauseRequestCount(0)
00238 {
00239   setThreadName("ArSoundsQueue");
00240   if(playInitCB)
00241     myInitCallbacks.push_back(playInitCB);
00242   if(speechSynth)
00243   {
00244     myInitCallbacks.push_back(speechSynth->getInitCallback());
00245     myDefaultSpeakCB = speechSynth->getSpeakCallback();
00246     myDefaultInterruptSpeechCB = speechSynth->getInterruptCallback();
00247   }
00248 }
00249 
00250 AREXPORT ArSoundsQueue::~ArSoundsQueue()
00251 {
00252 
00253 }
00254 
00255 
00256 void ArSoundsQueue::invokeCallbacks(const std::list<ArFunctor*>& lst)
00257 {
00258   for(std::list<ArFunctor*>::const_iterator i = lst.begin(); i != lst.end(); i++)
00259   {
00260     if(*i) (*i)->invoke();
00261     else ArLog::log(ArLog::Verbose, "ArSoundsQueue: warning: skipped NULL callback (simple functor).");
00262   }
00263 }
00264 
00265 void ArSoundsQueue::invokeCallbacks(const std::list<ArRetFunctor<bool>*>& lst)
00266 {
00267   for(std::list<ArRetFunctor<bool>*>::const_iterator i = lst.begin(); i != lst.end(); i++)
00268   {
00269     if(*i) (*i)->invokeR();
00270     else ArLog::log(ArLog::Verbose, "ArSoundsQueue: warning: skipped NULL callback (bool ret. funct.).");
00271   }
00272 }
00273 
00274 
00275 // This is the public method, but all we have to do is call the private push
00276 // method.
00277 AREXPORT void ArSoundsQueue::addItem(ItemType type, const char* data, std::list<PlayItemFunctor*> callbacks, int priority, const char* params)
00278 {
00279   assert(data);
00280   pushQueueItem(Item(data, type, params?params:"", priority, callbacks));
00281 }
00282 
00283 // This is the public method, but all we have to do is call the private push
00284 // method.
00285 AREXPORT void ArSoundsQueue::addItem(ArSoundsQueue::Item item)
00286 {
00287   pushQueueItem(item);
00288 }
00289 
00290 // Class-protected version.
00291 void ArSoundsQueue::pushQueueItem(ArSoundsQueue::Item item)
00292 {
00293   lock();
00294   pushQueueItem_NoLock(item);
00295   unlock();
00296 }
00297 
00298 // Class-protected version that does not lock (so caller can do it manually as
00299 // needed)
00300 void ArSoundsQueue::pushQueueItem_NoLock(ArSoundsQueue::Item item)
00301 {
00302   ArLog::log(ArLog::Verbose, "ArSoundsQueue: pushing \"%s\" with type=%d, priority=%d, params=\"%s\".", item.data.c_str(), item.type, item.priority, item.params.c_str());
00303   myQueue.push_back(item);
00304 }
00305 
00306 ArSoundsQueue::Item ArSoundsQueue::popQueueItem()
00307 {
00308   lock();
00309   ArSoundsQueue::Item item = *(myQueue.begin());
00310   myQueue.pop_front();
00311   unlock();
00312   return item;
00313 }
00314 
00315 ArSoundsQueue::Item ArSoundsQueue::popQueueItem_NoLock()
00316 {
00317   ArSoundsQueue::Item item = *(myQueue.begin());
00318   myQueue.pop_front();
00319   return item;
00320 }
00321 
00322 AREXPORT ArSoundsQueue::Item ArSoundsQueue::createDefaultSpeechItem(const char* speech)
00323 {
00324   Item item;
00325   item.type = SPEECH;
00326   if(myDefaultSpeakCB)
00327     item.playCallbacks.push_back(myDefaultSpeakCB);
00328   if(myDefaultInterruptSpeechCB)
00329     item.interruptCallbacks.push_back(myDefaultInterruptSpeechCB);
00330   if(speech)
00331     item.data = speech; // copy char* contents into std::string
00332   if(myDefaultPlayConditionCB)
00333     item.playbackConditionCallbacks.push_back(myDefaultPlayConditionCB);
00334   return item;
00335 }
00336 
00337 
00338 #if !(defined(WIN32) && defined(_MANAGED)) // MS Managed C++ does not allow varargs
00339 
00340 AREXPORT void ArSoundsQueue::speak(const char *str, ...)
00341 {
00342   if(myQueue.size() == 0)
00343     invokeCallbacks(myQueueNonemptyCallbacks);
00344 
00345   char *buf;
00346   size_t buflen = (strlen(str) + 1000 * 2);
00347   buf = new char[buflen];
00348 
00349   Item item = createDefaultSpeechItem();
00350 
00351   lock(); // need to lock out here to protect the un-threadsafe va_list functions
00352   va_list ptr;
00353   va_start(ptr, str);
00354   vsnprintf(buf, buflen, str, ptr);
00355   item.data = buf; // std::string constructor will duplicate char* contents
00356   pushQueueItem_NoLock(item); 
00357   va_end(ptr);
00358   delete[] buf;
00359   unlock();
00360 }
00361 
00362 AREXPORT void ArSoundsQueue::speakWithVoice(const char* voice, const char* str, ...)
00363 {
00364   if(myQueue.size() == 0)
00365     invokeCallbacks(myQueueNonemptyCallbacks);
00366 
00367   char *buf;
00368   size_t buflen = (strlen(str) + 1000 * 2);
00369   buf = new char[buflen];
00370 
00371   Item item = createDefaultSpeechItem();
00372   string params = "name=";
00373   params += voice;
00374   item.params = params;
00375 
00376   lock(); // need to lock out here to protect the un-threadsafe va_list functions
00377   va_list ptr;
00378   va_start(ptr, str);
00379   vsnprintf(buf, buflen, str, ptr);
00380   item.data = buf;// std::string constructor will duplicate char* contents
00381   pushQueueItem_NoLock(item);
00382   va_end(ptr);
00383   delete[] buf;
00384   unlock();
00385 }
00386 
00387 AREXPORT void ArSoundsQueue::speakWithPriority(int priority, const char* str, ...)
00388 {
00389   if(myQueue.size() == 0)
00390     invokeCallbacks(myQueueNonemptyCallbacks);
00391 
00392   char *buf;
00393   size_t buflen = (strlen(str) + 1000 * 2);
00394   buf = new char[buflen];
00395 
00396   Item item = createDefaultSpeechItem();
00397   item.priority = priority;
00398 
00399   lock(); // need to lock out here to protect the un-threadsafe va_list functions
00400   va_list ptr;
00401   va_start(ptr, str);
00402   vsnprintf(buf, buflen, str, ptr);
00403   item.data = buf;// std::string constructor will duplicate char* contents
00404   pushQueueItem_NoLock(item);
00405   va_end(ptr);
00406   delete[] buf;
00407   unlock();
00408 }
00409 
00410 
00411 AREXPORT void ArSoundsQueue::play(const char *str, ...)
00412 {
00413   if(myQueue.size() == 0)
00414     invokeCallbacks(myQueueNonemptyCallbacks);
00415 
00416   char buf[2048];
00417   Item item = createDefaultFileItem();
00418 
00419   lock();  // va_list is not threadsafe
00420   va_list ptr;
00421   va_start(ptr, str);
00422   vsnprintf(buf, 2048, str, ptr);
00423   item.data = buf;  // std::string constructor will duplicate char* contents
00424   va_end(ptr);
00425   unlock();
00426 
00427   pushQueueItem(item);
00428 }
00429 
00430 #endif // MS Managed C++
00431 
00432 AREXPORT ArSoundsQueue::Item ArSoundsQueue::createDefaultFileItem(const char* filename)
00433 {
00434   Item item;
00435   item.type = SOUND_FILE;
00436   if(myDefaultPlayFileCB)
00437     item.playCallbacks.push_back(myDefaultPlayFileCB);
00438   else
00439     ArLog::log(ArLog::Normal, "ArSoundsQueue: Internal Warning: no default PlayFile callback.");
00440   if(myDefaultInterruptFileCB)
00441     item.interruptCallbacks.push_back(myDefaultInterruptFileCB);
00442   if(filename)
00443     item.data = filename; // copy into std::string
00444   if(myDefaultPlayConditionCB)
00445     item.playbackConditionCallbacks.push_back(myDefaultPlayConditionCB);
00446   return item;
00447 }
00448 
00449 
00450 
00451 AREXPORT void *ArSoundsQueue::runThread(void *arg)
00452 {
00453   threadStarted();
00454   invokeCallbacks(myInitCallbacks);
00455   debuglog("the init callbacks were called.");
00456   myInitialized = true;
00457 
00458   while (getRunning())
00459   {
00460     lock();
00461     if(myPauseRequestCount > 0) 
00462     {
00463       unlock();
00464       myPausedCondition.wait();
00465       lock();
00466     }
00467     if (myQueue.size() > 0)
00468     {
00469       myLastItem = popQueueItem_NoLock();
00470 
00471 #ifdef DEBUG
00472       ArLog::log(ArLog::Normal, "* DEBUG * ArSoundsQueue: Popped an item from the queue. There are %d condition callbacks for this item.", myLastItem.playbackConditionCallbacks.size());
00473 #endif
00474 
00475       // Call some callbacks to tell them that play is about to begin
00476       invokeCallbacks(myStartPlaybackCBList);
00477 
00478       // Abort if any conditions return false
00479       bool doPlayback = true;
00480       for(std::list<PlaybackConditionFunctor*>::const_iterator i = myLastItem.playbackConditionCallbacks.begin(); 
00481           i != myLastItem.playbackConditionCallbacks.end(); i++)
00482       {
00483         if( (*i) && (*i)->invokeR() == false) {
00484           ArLog::log(ArLog::Normal, "ArSoundsQueue: A condition callback prevents this item from playing. Skipping this item.");
00485           doPlayback = false;
00486           break;
00487         }
00488 #ifdef DEBUG
00489         else {
00490           ArLog::log(ArLog::Normal, "* DEBUG * ArSoundsQueue: Condition callback returned true.");
00491         }
00492 #endif
00493       }
00494 
00495       if(doPlayback)
00496       {
00497 
00498         myPlayingSomething = true;
00499         unlock();
00500 
00501       // Play the item.
00502 #ifdef DEBUG
00503         ArLog::log(ArLog::Normal, "* DEBUG* Acting on item. type=%d", myLastItem.type);
00504 #endif
00505         myLastItem.play();
00506 
00507         // Sleep a bit for some "recover" time (especially for sound playback
00508         // processing)
00509         ArUtil::sleep(200);
00510 
00511         lock();
00512         myPlayingSomething = false;
00513       }
00514 
00515 
00516       // Call some more callbacks to tell them that play ended.
00517       debuglog("Finished acting on item. Invoking endPlayback callbacks...");
00518       unlock();
00519       myLastItem.done();
00520       invokeCallbacks(myEndPlaybackCBList);
00521       lock();
00522 
00523       if(myQueue.size() == 0)
00524       {
00525         debuglog("invoking queue-empty callbacks!");
00526         unlock();
00527         invokeCallbacks(myQueueEmptyCallbacks);
00528       }
00529       else
00530       {
00531         unlock();
00532       }
00533 
00534     }
00535     else
00536     {
00537       unlock();
00538       ArUtil::sleep(20);
00539     }
00540   }
00541   return NULL;
00542 }
00543 
00544 
00545 AREXPORT void ArSoundsQueue::pause()
00546 {
00547   lock();
00548   myPauseRequestCount++;
00549   unlock();
00550 }
00551 
00552 AREXPORT void ArSoundsQueue::resume()
00553 {
00554   ArLog::log(ArLog::Verbose, "ArSoundsQueue::resume: requested.");
00555   lock();
00556   if(--myPauseRequestCount <= 0) 
00557   {
00558     myPauseRequestCount = 0;
00559     ArLog::log(ArLog::Verbose, "ArSoundsQueue::resume: unpausing.");
00560     unlock();
00561     myPausedCondition.signal();
00562   } else {
00563     unlock();
00564   }
00565 }
00566 
00567 AREXPORT void ArSoundsQueue::stop()
00568 {
00569   stopRunning();
00570 }
00571 
00572 AREXPORT bool ArSoundsQueue::isPaused()
00573 {
00574   return (myPauseRequestCount > 0);
00575 }
00576 
00577 AREXPORT void ArSoundsQueue::interrupt()
00578 {
00579   lock();
00580   // Don't try to interrupt the last item removed from the queue if
00581   // it's not currently being played.
00582   //printf("interrupt: myPlayingSomething=%d\n", myPlayingSomething);
00583   if(myPlayingSomething)
00584     myLastItem.interrupt();
00585   myPlayingSomething = false;
00586   unlock();
00587 }
00588 
00589 AREXPORT void ArSoundsQueue::clearQueue() 
00590 {
00591   lock();
00592   myQueue.clear();
00593   unlock();
00594   invokeCallbacks(myQueueEmptyCallbacks);
00595 }
00596 
00597 AREXPORT set<int> ArSoundsQueue::findPendingItems(const char* item)
00598 {
00599   lock();
00600   set<int> found;
00601   int pos = 0;
00602   for(list<Item>::const_iterator i = myQueue.begin(); i != myQueue.end(); i++)
00603   {
00604     if((*i).data == item)
00605       found.insert(pos);
00606     pos++;
00607   }
00608   unlock();
00609   return found;
00610 }
00611 
00612 AREXPORT void ArSoundsQueue::removePendingItems(const char* item, ItemType type) 
00613 {
00614   lock();
00615   myQueue.remove_if<ItemComparator_TypeAndData>(ItemComparator_TypeAndData(item, type));
00616   unlock();
00617 }
00618 
00619 
00620 AREXPORT void ArSoundsQueue::removePendingItems(const char* data)
00621 {
00622   lock();
00623   myQueue.remove_if<ItemComparator_OnlyData>(ItemComparator_OnlyData(data));
00624   unlock();
00625 }
00626 
00627 AREXPORT void ArSoundsQueue::removePendingItems(int priority) 
00628 {
00629   lock();
00630   myQueue.remove_if<ItemComparator_PriorityLessThan>(ItemComparator_PriorityLessThan(priority));
00631   unlock();
00632 }
00633 
00634 AREXPORT void ArSoundsQueue::removePendingItems(int priority, ItemType type)
00635 {
00636   lock();
00637   myQueue.remove_if<ItemComparator_WithTypePriorityLessThan>(ItemComparator_WithTypePriorityLessThan(type, priority));
00638   unlock();
00639 }
00640 
00641 AREXPORT void ArSoundsQueue::removePendingItems(ItemType type)
00642 {
00643   lock();
00644   myQueue.remove_if<ItemComparator_WithType>(ItemComparator_WithType(type));
00645   unlock();
00646 }
00647 
00648 AREXPORT string ArSoundsQueue::nextItem(ItemType type)
00649 {
00650   lock();
00651   for(list<Item>::const_iterator i = myQueue.begin(); i != myQueue.end(); i++)
00652   {
00653     if(type == (*i).type) {
00654       string found = (*i).data;
00655       unlock();
00656       return found;
00657     }
00658   }
00659   unlock();
00660   return "";
00661 }
00662 
00663 AREXPORT string ArSoundsQueue::nextItem(int priority)
00664 {
00665   lock();
00666   for(list<Item>::const_iterator i = myQueue.begin(); i != myQueue.end(); i++)
00667   {
00668     if((*i).priority >= priority) {
00669       string found = (*i).data;
00670       unlock();
00671       return found;
00672     }
00673   }
00674   unlock();
00675   return "";
00676 }
00677 
00678 AREXPORT string ArSoundsQueue::nextItem(ItemType type, int priority)
00679 {
00680   lock();
00681   for(list<Item>::const_iterator i = myQueue.begin(); i != myQueue.end(); i++)
00682   {
00683     if(type == (*i).type && (*i).priority >= priority) {
00684       string found = (*i).data;
00685       unlock();
00686       return found;
00687     }
00688   }
00689   unlock();
00690   return "";
00691 }
00692 
00693 

Generated on Tue Feb 20 10:51:41 2007 for Aria by  doxygen 1.4.0