00001 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 
00025 
00026 
00027 
00028 
00029 #include "Alsa.hpp"
00030 #include "AlsaSoundCard.hpp"
00031 #include "../libH/Debug.hpp"
00032 #include "../libH/Exception.hpp"
00033 #include "../libH/stringconverter.hpp"
00034 #include "../libH/UtilTime.hpp"
00035 #include <boost/format.hpp>
00036 #include <boost/thread/thread.hpp>
00037 
00038 using namespace std;
00039 using namespace boost;
00040 using namespace H;
00041 using namespace Gizmod;
00042 
00044 
00046 
00051 #define CARD_NAME_UNKNOWN       "Unknown"
00052 
00057 #define ALSA_FAST_CALLS_BUG     5000000
00058 
00063 #define POLL_TIMEOUT            1000
00064 
00066 
00068 
00078 int AlsaSoundCard::MixerCallback(snd_mixer_t * Mixer, unsigned int EventMask, snd_mixer_elem_t * MixerElement) {
00079         AlsaSoundCard * pAlsaSoundCard = static_cast<AlsaSoundCard*>(snd_mixer_get_callback_private(Mixer));
00080         if (pAlsaSoundCard)
00081                 return pAlsaSoundCard->mixerCallback(Mixer, EventMask, MixerElement);
00082         return 0;
00083 }
00084 
00094 int AlsaSoundCard::mixerCallback(snd_mixer_t * Mixer, unsigned int EventMask, snd_mixer_elem_t * MixerElement) {
00095         if (EventMask && SND_CTL_EVENT_MASK_ADD) {
00096                 unsigned int MixerIndex = snd_mixer_selem_get_index(MixerElement);
00097                 const char * MixerName = snd_mixer_selem_get_name(MixerElement);
00098                 string MixerNameUnique = MixerIndex ? str(format("%1% %2%") % MixerName % MixerIndex) : MixerName;
00099                 
00100                 if (mMixers.count(MixerNameUnique)) {
00101                         cdbg << "Mixer [" << MixerNameUnique << "] Duplicated on Card [" << mCardName << "]" << endl;
00102                         return 0;
00103                 }
00104                 shared_ptr<AlsaMixer> pMixer(new AlsaMixer(this, MixerElement, MixerName, MixerNameUnique, MixerIndex));
00105                 mMixers.insert(make_pair(MixerNameUnique, pMixer));
00106                                                 
00107                 
00108                 snd_mixer_elem_set_callback_private(MixerElement, pMixer.get());
00109                 snd_mixer_elem_set_callback(MixerElement, AlsaMixer::MixerElemCallback);
00110         }
00111         return 0;
00112 }
00113 
00115 
00117 
00121 AlsaSoundCard::AlsaSoundCard(AlsaInterface * piAlsa, int CardID) : AlsaSoundCardInterface(piAlsa), mThreadProc(this) {
00122         mCardID = CardID;
00123         mCTLHandle = NULL;
00124         mHWInfo = NULL;
00125         mMixerHandle = NULL;
00126         mWatching = false;
00127         mShutdown = false;
00128         mThreading = false;
00129         init();
00130 }
00131 
00135 AlsaSoundCard::~AlsaSoundCard() {
00136         shutdown();
00137 }
00138 
00140 
00142 
00147 std::string AlsaSoundCard::getCardHardwareID() const {
00148         return mCardHWID;
00149 }
00150 
00155 int AlsaSoundCard::getCardID() const {
00156         return mCardID;
00157 }
00158 
00163 std::string AlsaSoundCard::getCardName() const {
00164         return mCardName;
00165 }
00166 
00171 std::string AlsaSoundCard::getCardNameLong() const {
00172         return mCardNameLong;
00173 }
00174 
00179 AlsaMixer const * AlsaSoundCard::getMixer(std::string Name) {
00180         if (!mMixers.count(Name))
00181                 return NULL;
00182         return mMixers[Name].get();
00183 }
00184 
00189 size_t AlsaSoundCard::getNumMixers() {
00190         return mMixers.size();
00191 }
00192 
00196 void AlsaSoundCard::init() {
00197         if (mWatching) {
00198                 cdbg << "AlsaSoundCard already watching!!" << endl;
00199                 return;
00200         }
00201                          
00202         
00203         mCardHWID = str(format("hw:%1%") % mCardID);
00204                 
00205         
00206         char * Name;
00207         if (snd_card_get_name(mCardID, &Name) == -1) {
00208                 mCardName = CARD_NAME_UNKNOWN;
00209                 cdbg << "Failed to retreive name of Sound Card [" << mCardID << "]" << endl;
00210         } else
00211                 mCardName = Name;               
00212 
00213         
00214         if (snd_card_get_longname(mCardID, &Name) == -1) {
00215                 mCardNameLong = CARD_NAME_UNKNOWN;
00216                 cdbg << "Failed to retreive long name of Sound Card [" << mCardID << "]" << endl;
00217         } else
00218                 mCardNameLong = Name;
00219                         
00220         
00221         Alsa * pAlsa = static_cast<Alsa *>(mpiAlsa);
00222         for (size_t lp = 0; lp < pAlsa->getNumSoundCards(); lp ++)
00223                 if ( (pAlsa->getSoundCard(lp)->getCardHardwareID() == mCardHWID) &&
00224                      (pAlsa->getSoundCard(lp)->getCardNameLong() == mCardNameLong) )
00225                         throw H::Exception("Duplicate Alsa Sound Card Detected!", __FILE__, __FUNCTION__, __LINE__);
00226 
00227         
00228         cdbg1 << "Initializing Connection to Sound Card [" << mCardName << "]" << endl; 
00229         int err;
00230         if ((err = snd_ctl_open(&mCTLHandle, mCardHWID.c_str(), 0)) < 0) 
00231                 throw H::Exception("Could not open Control Interface Handle on Card [" + mCardName + "] -- " + snd_strerror(err), __FILE__, __FUNCTION__, __LINE__);
00232 
00233         snd_ctl_card_info_alloca(&mHWInfo);
00234         if ((err = snd_ctl_card_info(mCTLHandle, mHWInfo)) < 0)
00235                 throw H::Exception("Could not Query Card Information on Card [" + mCardName + "] -- " + snd_strerror(err), __FILE__, __FUNCTION__, __LINE__);
00236         
00237         
00238         if ((err = snd_mixer_open(&mMixerHandle, 0)) < 0) 
00239                 throw H::Exception("Could not Open Mixer on Card [" + mCardName + "] -- " + snd_strerror(err), __FILE__, __FUNCTION__, __LINE__);       
00240                 
00241         if ((err = snd_mixer_attach(mMixerHandle, mCardHWID.c_str())) < 0) 
00242                 throw H::Exception("Could not Attach to Mixer on Card [" + mCardName + "] -- " + snd_strerror(err), __FILE__, __FUNCTION__, __LINE__);
00243                 
00244         if ((err = snd_mixer_selem_register(mMixerHandle, NULL, NULL)) < 0)
00245                 throw H::Exception("Could not Register Mixer on Card [" + mCardName + "] -- " + snd_strerror(err), __FILE__, __FUNCTION__, __LINE__);
00246         
00247         
00248         static_cast<Alsa *>(mpiAlsa)->onAlsaEventSoundCardAttach(AlsaEvent(ALSAEVENT_SOUNDCARD_ATTACH), *this);
00249         
00250         
00251         snd_mixer_set_callback_private(mMixerHandle, (void*) this);
00252         snd_mixer_set_callback(mMixerHandle, MixerCallback);
00253         
00254         if ((err = snd_mixer_load(mMixerHandle)) < 0)
00255                 throw H::Exception("Could not Load Mixer on Card [" + mCardName + "] -- " + snd_strerror(err), __FILE__, __FUNCTION__, __LINE__);
00256         
00257         
00258         boost::thread thrd(mThreadProc);
00259 }
00260 
00265 void AlsaSoundCard::setAllPlaybackSwitches(bool Enabled) {
00266         map< string, shared_ptr<AlsaMixer> >::iterator iter;
00267         for (iter = mMixers.begin(); iter != mMixers.end(); iter ++) {
00268                 shared_ptr<AlsaMixer> pMixer = iter->second;
00269                 if (pMixer->HasPlaybackSwitch) {
00270                         pMixer->setSwitchPlayback(Enabled);
00271                         
00272                         if (ALSA_FAST_CALLS_BUG)
00273                                 UtilTime::nanoSleep(ALSA_FAST_CALLS_BUG);
00274                 }
00275         }
00276 }
00277 
00281 void AlsaSoundCard::shutdown() {
00282         if (mShutdown)
00283                 return;
00284         
00285         cdbg1 << "Closing connection to Sound Card [" << mCardHWID << " - " << mCardName << "]" << endl;
00286                         
00287         
00288         mWatching = false;
00289         while (mThreading) {
00290                 cdbg5 << "Waiting on AlsaSoundCard Thread to Finish..." << endl;
00291                 UtilTime::sleep(0.1f);
00292         }
00293         
00294         
00295         if (mMixerHandle)
00296                 snd_mixer_close(mMixerHandle); mMixerHandle = NULL;
00297         
00298         if (mCTLHandle)
00299                 snd_ctl_close(mCTLHandle); mCTLHandle = NULL;   
00300         
00301         
00302         mMixers.clear();        
00303         
00304         
00305         static_cast<Alsa *>(mpiAlsa)->onAlsaEventSoundCardDetach(AlsaEvent(ALSAEVENT_SOUNDCARD_DETACH), *this); 
00306         
00307         
00308         mShutdown = true;
00309 }
00310 
00316 void AlsaSoundCard::threadProc() {
00317         
00318         int err;
00319         mWatching = true;
00320         while (mWatching) {
00321                 
00322                 if (mUpdateList.size()) {
00323                         for (list<AlsaMixerInterface *>::iterator iter = mUpdateList.begin(); iter != mUpdateList.end(); iter ++) 
00324                                 (*iter)->signalMixerEvent();
00325                         mUpdateList.clear();
00326                 }
00327                 
00328                 
00329                 cdbg5 << "Processing Alsa Events..." << endl;
00330                 if ((err = snd_mixer_wait(mMixerHandle, POLL_TIMEOUT)) < 0) {
00331                         cdbg5 << "AlsaSoundCard :: Mixer Wait Error -- " << snd_strerror(err) << endl;
00332                 } else {
00333                         snd_mixer_handle_events(mMixerHandle);
00334                 }
00335         }
00336 }