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 }