libGizmod/AlsaSoundCard.cpp

Go to the documentation of this file.
00001 
00012 /*
00013   
00014   Copyright (c) 2007, Tim Burrell
00015   Licensed under the Apache License, Version 2.0 (the "License");
00016   you may not use this file except in compliance with the License.
00017   You may obtain a copy of the License at 
00018 
00019         http://www.apache.org/licenses/LICENSE-2.0
00020 
00021   Unless required by applicable law or agreed to in writing, software
00022   distributed under the License is distributed on an "AS IS" BASIS,
00023   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00024   See the License for the specific language governing permissions and 
00025   limitations under the License. 
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 // Typedef's, structs
00046 
00051 #define CARD_NAME_UNKNOWN       "Unknown"
00052 
00057 #define ALSA_FAST_CALLS_BUG     5000000
00058 
00063 #define POLL_TIMEOUT            1000
00064 
00066 // Callbacks
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                 // check for duplicates
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                 // set the callback
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 // Construction
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 // Class Body
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         // set hardware identifier
00203         mCardHWID = str(format("hw:%1%") % mCardID);
00204                 
00205         // get the card name
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         // long name
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         // check for duplicate sound cards
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         // initialize
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         // attach a mixer and watch for events on it
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         // fire the event
00248         static_cast<Alsa *>(mpiAlsa)->onAlsaEventSoundCardAttach(AlsaEvent(ALSAEVENT_SOUNDCARD_ATTACH), *this);
00249         
00250         // set the callback
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         // initialize the event handler thread
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                         // FIX for a nasty alsa bug!
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         // wait for the thread to exit
00288         mWatching = false;
00289         while (mThreading) {
00290                 cdbg5 << "Waiting on AlsaSoundCard Thread to Finish..." << endl;
00291                 UtilTime::sleep(0.1f);
00292         }
00293         
00294         // shut down alsa connection to the sound card
00295         if (mMixerHandle)
00296                 snd_mixer_close(mMixerHandle); mMixerHandle = NULL;
00297         
00298         if (mCTLHandle)
00299                 snd_ctl_close(mCTLHandle); mCTLHandle = NULL;   
00300         
00301         // clear the mixers
00302         mMixers.clear();        
00303         
00304         // fire the event
00305         static_cast<Alsa *>(mpiAlsa)->onAlsaEventSoundCardDetach(AlsaEvent(ALSAEVENT_SOUNDCARD_DETACH), *this); 
00306         
00307         // signal we've shutdown okay
00308         mShutdown = true;
00309 }
00310 
00316 void AlsaSoundCard::threadProc() {
00317         // wait for events to occur
00318         int err;
00319         mWatching = true;
00320         while (mWatching) {
00321                 // check for manual mixer events
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                 // wait for the next event
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 }

Generated on Wed Nov 7 10:04:16 2007 for gizmod by  doxygen 1.5.3