diff options
Diffstat (limited to 'src/model/pulsemodel.cpp')
| -rw-r--r-- | src/model/pulsemodel.cpp | 451 |
1 files changed, 434 insertions, 17 deletions
diff --git a/src/model/pulsemodel.cpp b/src/model/pulsemodel.cpp index de745bc..a677edc 100644 --- a/src/model/pulsemodel.cpp +++ b/src/model/pulsemodel.cpp @@ -3,8 +3,17 @@ #include <tqapplication.h> -// Custom event posted from the PA thread to the main thread. -static const int PA_EVENT = TQEvent::User + 1; +// Custom events posted from the PA thread to the main thread. +static const int PA_EVENT = TQEvent::User + 1; +static const int PA_SERVER_EVENT = TQEvent::User + 3; +static const int PA_CARD_EVENT = TQEvent::User + 4; + +struct PAServerEvent : public TQCustomEvent { + TQString defaultSinkName; + TQString defaultSourceName; + PAServerEvent( const TQString &sink, const TQString &source ) + : TQCustomEvent(PA_SERVER_EVENT), defaultSinkName(sink), defaultSourceName(source) {} +}; struct PAEvent : public TQCustomEvent { enum Kind { DeviceAdded, DeviceRemoved, DeviceUpdated }; @@ -14,19 +23,123 @@ struct PAEvent : public TQCustomEvent { TQString name; int volume; bool muted; + uint8_t channels; + int pan; + TQString monitorName; + TQString iconName; + TQString paName; // raw PA name (sinks/sources only, for default tracking) + uint32_t parentIndex; // for Playback: parent sink PA index PAEvent( Kind k, AudioDevice::Category c, uint32_t idx, - const TQString &n = TQString(), int vol = 0, bool m = false ) + const TQString &n = TQString(), int vol = 0, bool m = false, + uint8_t ch = 2, int p = 0, const TQString &mon = TQString(), + const TQString &icon = TQString(), uint32_t parent = PA_INVALID_INDEX, + const TQString &pname = TQString() ) : TQCustomEvent(PA_EVENT), kind(k), cat(c), paIndex(idx), - name(n), volume(vol), muted(m) {} + name(n), volume(vol), muted(m), channels(ch), pan(p), + monitorName(mon), iconName(icon), paName(pname), parentIndex(parent) {} +}; + +struct PACardEvent : public TQCustomEvent { + struct Profile { TQString name; TQString description; bool available; }; + struct Port { + TQString name; TQString description; + int available; int direction; uint32_t type; TQString availGroup; + }; + bool removed; + uint32_t index; + TQString name; + TQString description; + TQString activeProfile; + TQString vendor; + TQString product; + TQString formFactor; + TQString busType; + TQValueList<Profile> profiles; + TQValueList<Port> ports; + + explicit PACardEvent( uint32_t idx ) + : TQCustomEvent(PA_CARD_EVENT), removed(true), index(idx) {} + PACardEvent( uint32_t idx, const TQString &n, const TQString &d, const TQString &ap ) + : TQCustomEvent(PA_CARD_EVENT), removed(false), index(idx), + name(n), description(d), activeProfile(ap) {} }; // ---- helpers ---------------------------------------------------------------- +static bool isGenericMediaName( const TQString &s ) +{ + TQString l = s.lower().stripWhiteSpace(); + return l == "audio stream" || l == "alsa playback" || l == "playback" + || l == "audio output" || l == "output" || l.isEmpty(); +} + +static TQString paCleanAppName( const char *raw ) +{ + if ( !raw || !*raw ) return TQString(); + TQString s = TQString::fromUtf8( raw ); + + // Strip "ALSA plug-in [Foo]" wrapper → "Foo" + int lb = s.find('['), rb = s.findRev(']'); + if ( lb != -1 && rb > lb ) + s = s.mid( lb + 1, rb - lb - 1 ).stripWhiteSpace(); + + // Strip trailing "app" (amarokapp → amarok), then capitalise first letter + if ( s.endsWith("app") && s.length() > 3 ) + s = s.left( s.length() - 3 ); + if ( !s.isEmpty() ) + s[0] = s[0].upper(); + + return s; +} + +static TQString paStreamName( pa_proplist *pl, const char *fallback ) +{ + TQString appName = paCleanAppName( pa_proplist_gets(pl, PA_PROP_APPLICATION_NAME) ); + const char *mn = pa_proplist_gets(pl, PA_PROP_MEDIA_NAME); + TQString mediaName = mn ? TQString::fromUtf8(mn).stripWhiteSpace() : TQString(); + + if ( !appName.isEmpty() && !isGenericMediaName(mediaName) && !mediaName.isEmpty() ) + return appName + " - " + mediaName; + if ( !appName.isEmpty() ) + return appName; + if ( !isGenericMediaName(mediaName) && !mediaName.isEmpty() ) + return mediaName; + return fallback ? TQString::fromUtf8(fallback) : TQString(); +} + +static TQString paAppIcon( pa_proplist *pl ) +{ + const char *s; + if ( (s = pa_proplist_gets(pl, PA_PROP_APPLICATION_ICON_NAME)) && *s ) + return TQString::fromUtf8(s); + if ( (s = pa_proplist_gets(pl, "application.process.binary")) && *s ) { + TQString bin = TQString::fromUtf8(s).lower(); + // Some TDE apps register binary as "fooapp" but icon as "foo" + if ( bin.endsWith("app") ) + return bin.left( bin.length() - 3 ); + return bin; + } + if ( (s = pa_proplist_gets(pl, PA_PROP_APPLICATION_NAME)) && *s ) + return TQString::fromUtf8(s).lower(); + return TQString(); +} + static int paVolumeToPercent( const pa_cvolume &cv ) { - pa_volume_t avg = pa_cvolume_avg( &cv ); - return (int)( (double)avg / PA_VOLUME_NORM * 100.0 + 0.5 ); + pa_volume_t mx = pa_cvolume_max( &cv ); + return (int)( (double)mx / PA_VOLUME_NORM * 100.0 + 0.5 ); +} + +// Derive pan -50..+50 from the first two channels of a cvolume. +static int cvolumeToPan( const pa_cvolume &cv ) +{ + if ( cv.channels < 2 ) return 0; + double l = cv.values[0]; + double r = cv.values[1]; + double mx = ( l > r ) ? l : r; + if ( mx < 1.0 ) return 0; + return (int)( ( r - l ) / mx * 50.0 ); } // ---- ctor / dtor ------------------------------------------------------------ @@ -70,6 +183,12 @@ bool PulseModel::open() void PulseModel::close() { + // Detach monitor streams on all devices before tearing down the mainloop. + TQPtrList<PulseDevice> *allLists[4] = { &m_sinks, &m_sources, &m_sinkInputs, &m_sourceOutputs }; + for ( int i = 0; i < 4; i++ ) + for ( TQPtrListIterator<PulseDevice> it(*allLists[i]); *it; ++it ) + (*it)->detach(); + if ( m_context ) { pa_threaded_mainloop_lock( m_mainloop ); pa_context_disconnect( m_context ); @@ -119,8 +238,11 @@ void PulseModel::contextStateCb( pa_context *c, void *userdata ) PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE | PA_SUBSCRIPTION_MASK_SINK_INPUT | - PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT ), + PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT | + PA_SUBSCRIPTION_MASK_SERVER | + PA_SUBSCRIPTION_MASK_CARD ), 0, 0 ); + pa_operation_unref( pa_context_get_server_info( c, serverInfoCb, self ) ); self->enumerateAll(); break; case PA_CONTEXT_FAILED: @@ -138,16 +260,32 @@ void PulseModel::enumerateAll() pa_operation_unref( pa_context_get_source_info_list( m_context, sourceInfoCb, this ) ); pa_operation_unref( pa_context_get_sink_input_info_list( m_context, sinkInputInfoCb, this ) ); pa_operation_unref( pa_context_get_source_output_info_list( m_context, sourceOutputInfoCb, this ) ); + pa_operation_unref( pa_context_get_card_info_list( m_context, cardInfoCb, this ) ); +} + +void PulseModel::serverInfoCb( pa_context *, const pa_server_info *info, void *userdata ) +{ + if ( !info ) return; + PulseModel *self = static_cast<PulseModel *>(userdata); + TQApplication::postEvent( self, new PAServerEvent( + TQString::fromUtf8( info->default_sink_name ? info->default_sink_name : "" ), + TQString::fromUtf8( info->default_source_name ? info->default_source_name : "" ) ) ); } void PulseModel::sinkInfoCb( pa_context *, const pa_sink_info *info, int eol, void *userdata ) { if ( eol || !info ) return; PulseModel *self = static_cast<PulseModel *>(userdata); - TQApplication::postEvent( self, new PAEvent( + PAEvent *ev = new PAEvent( PAEvent::DeviceAdded, AudioDevice::Output, info->index, TQString::fromUtf8( info->description ), - paVolumeToPercent( info->volume ), info->mute != 0 ) ); + paVolumeToPercent( info->volume ), info->mute != 0, + info->volume.channels, cvolumeToPan( info->volume ), + TQString::fromUtf8( info->monitor_source_name ), + TQString::fromUtf8( pa_proplist_gets( info->proplist, PA_PROP_DEVICE_ICON_NAME ) ? : "" ), + PA_INVALID_INDEX, + TQString::fromUtf8( info->name ) ); + TQApplication::postEvent( self, ev ); } void PulseModel::sourceInfoCb( pa_context *, const pa_source_info *info, int eol, void *userdata ) @@ -159,7 +297,12 @@ void PulseModel::sourceInfoCb( pa_context *, const pa_source_info *info, int eol TQApplication::postEvent( self, new PAEvent( PAEvent::DeviceAdded, AudioDevice::Input, info->index, TQString::fromUtf8( info->description ), - paVolumeToPercent( info->volume ), info->mute != 0 ) ); + paVolumeToPercent( info->volume ), info->mute != 0, + info->volume.channels, cvolumeToPan( info->volume ), + TQString::fromUtf8( info->name ), + TQString::fromUtf8( pa_proplist_gets( info->proplist, PA_PROP_DEVICE_ICON_NAME ) ? : "" ), + PA_INVALID_INDEX, + TQString::fromUtf8( info->name ) ) ); } void PulseModel::sinkInputInfoCb( pa_context *, const pa_sink_input_info *info, int eol, void *userdata ) @@ -168,18 +311,27 @@ void PulseModel::sinkInputInfoCb( pa_context *, const pa_sink_input_info *info, PulseModel *self = static_cast<PulseModel *>(userdata); TQApplication::postEvent( self, new PAEvent( PAEvent::DeviceAdded, AudioDevice::Playback, info->index, - TQString::fromUtf8( info->name ), - paVolumeToPercent( info->volume ), info->mute != 0 ) ); + paStreamName( info->proplist, info->name ), + paVolumeToPercent( info->volume ), info->mute != 0, + info->volume.channels, cvolumeToPan( info->volume ), + TQString(), // monitor name filled in by customEvent from parent sink + paAppIcon( info->proplist ), + info->sink ) ); // parent sink index for peak monitoring } void PulseModel::sourceOutputInfoCb( pa_context *, const pa_source_output_info *info, int eol, void *userdata ) { if ( eol || !info ) return; + // Filter out our own peak-monitor streams. + if ( info->name && strcmp(info->name, "tmix-peak") == 0 ) return; PulseModel *self = static_cast<PulseModel *>(userdata); TQApplication::postEvent( self, new PAEvent( PAEvent::DeviceAdded, AudioDevice::Recording, info->index, - TQString::fromUtf8( info->name ), - paVolumeToPercent( info->volume ), info->mute != 0 ) ); + paStreamName( info->proplist, info->name ), + paVolumeToPercent( info->volume ), info->mute != 0, + info->volume.channels, cvolumeToPan( info->volume ), + TQString(), paAppIcon( info->proplist ), + info->source ) ); // parentIndex = source being recorded from } void PulseModel::subscribeCb( pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata ) @@ -189,6 +341,21 @@ void PulseModel::subscribeCb( pa_context *c, pa_subscription_event_type_t t, uin pa_subscription_event_type_t facility = (pa_subscription_event_type_t)( t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK ); pa_subscription_event_type_t evtype = (pa_subscription_event_type_t)( t & PA_SUBSCRIPTION_EVENT_TYPE_MASK ); + if ( facility == PA_SUBSCRIPTION_EVENT_SERVER ) { + pa_operation_unref( pa_context_get_server_info( c, serverInfoCb, self ) ); + return; + } + + if ( facility == PA_SUBSCRIPTION_EVENT_CARD ) { + if ( evtype == PA_SUBSCRIPTION_EVENT_REMOVE ) { + TQApplication::postEvent( self, new PACardEvent( idx ) ); + } else { + pa_operation *op = pa_context_get_card_info_by_index( c, idx, cardInfoCb, self ); + if ( op ) pa_operation_unref( op ); + } + return; + } + AudioDevice::Category cat; switch ( facility ) { case PA_SUBSCRIPTION_EVENT_SINK: cat = AudioDevice::Output; break; @@ -223,10 +390,134 @@ void PulseModel::subscribeCb( pa_context *c, pa_subscription_event_type_t t, uin if ( op ) pa_operation_unref( op ); } +void PulseModel::cardInfoCb( pa_context *, const pa_card_info *info, int eol, void *userdata ) +{ + if ( eol || !info ) return; + PulseModel *self = static_cast<PulseModel *>(userdata); + + const char *desc = pa_proplist_gets( info->proplist, PA_PROP_DEVICE_DESCRIPTION ); + const char *vendor = pa_proplist_gets( info->proplist, PA_PROP_DEVICE_VENDOR_NAME ); + const char *product = pa_proplist_gets( info->proplist, PA_PROP_DEVICE_PRODUCT_NAME ); + const char *ff = pa_proplist_gets( info->proplist, PA_PROP_DEVICE_FORM_FACTOR ); + const char *bus = pa_proplist_gets( info->proplist, "device.bus" ); + + PACardEvent *ev = new PACardEvent( + info->index, + TQString::fromUtf8( info->name ), + TQString::fromUtf8( desc ? desc : info->name ), + info->active_profile2 ? TQString::fromUtf8( info->active_profile2->name ) : TQString() ); + + ev->vendor = TQString::fromUtf8( vendor ? vendor : "" ); + ev->product = TQString::fromUtf8( product ? product : "" ); + ev->formFactor = TQString::fromUtf8( ff ? ff : "" ); + ev->busType = TQString::fromUtf8( bus ? bus : "" ); + + for ( uint32_t i = 0; info->profiles2 && info->profiles2[i]; ++i ) { + PACardEvent::Profile p; + p.name = TQString::fromUtf8( info->profiles2[i]->name ); + p.description = TQString::fromUtf8( info->profiles2[i]->description ); + p.available = info->profiles2[i]->available != 0; + ev->profiles.append( p ); + } + + for ( uint32_t i = 0; i < info->n_ports; ++i ) { + PACardEvent::Port port; + port.name = TQString::fromUtf8( info->ports[i]->name ); + port.description= TQString::fromUtf8( info->ports[i]->description ); + port.available = info->ports[i]->available; + port.direction = info->ports[i]->direction; + port.type = info->ports[i]->type; + if ( info->ports[i]->availability_group ) + port.availGroup = TQString::fromUtf8( info->ports[i]->availability_group ); + ev->ports.append( port ); + } + + TQApplication::postEvent( self, ev ); +} + // ---- main thread event handler ---------------------------------------------- void PulseModel::customEvent( TQCustomEvent *e ) { + if ( e->type() == PA_CARD_EVENT ) { + PACardEvent *ev = static_cast<PACardEvent *>(e); + if ( ev->removed ) { + for ( TQValueList<PulseCardInfo>::Iterator it = m_cards.begin(); + it != m_cards.end(); ++it ) { + if ( it->index == ev->index ) { + m_cards.remove( it ); + emit cardRemoved( ev->index ); + return; + } + } + return; + } + PulseCardInfo *existing = findCard( ev->index ); + if ( existing ) { + existing->name = ev->name; + existing->description = ev->description; + existing->activeProfile = ev->activeProfile; + existing->vendor = ev->vendor; + existing->product = ev->product; + existing->formFactor = ev->formFactor; + existing->busType = ev->busType; + existing->profiles.clear(); + existing->ports.clear(); + for ( TQValueList<PACardEvent::Profile>::Iterator it = ev->profiles.begin(); + it != ev->profiles.end(); ++it ) { + PulseCardProfile p; p.name = it->name; p.description = it->description; p.available = it->available; + existing->profiles.append( p ); + } + for ( TQValueList<PACardEvent::Port>::Iterator it = ev->ports.begin(); + it != ev->ports.end(); ++it ) { + PulseCardPort port; + port.name = it->name; port.description = it->description; + port.available = it->available; port.direction = it->direction; + port.type = it->type; port.availabilityGroup = it->availGroup; + existing->ports.append( port ); + } + emit cardUpdated( ev->index ); + } else { + PulseCardInfo info; + info.index = ev->index; + info.name = ev->name; + info.description = ev->description; + info.activeProfile = ev->activeProfile; + info.vendor = ev->vendor; + info.product = ev->product; + info.formFactor = ev->formFactor; + info.busType = ev->busType; + for ( TQValueList<PACardEvent::Profile>::Iterator it = ev->profiles.begin(); + it != ev->profiles.end(); ++it ) { + PulseCardProfile p; p.name = it->name; p.description = it->description; p.available = it->available; + info.profiles.append( p ); + } + for ( TQValueList<PACardEvent::Port>::Iterator it = ev->ports.begin(); + it != ev->ports.end(); ++it ) { + PulseCardPort port; + port.name = it->name; port.description = it->description; + port.available = it->available; port.direction = it->direction; + port.type = it->type; port.availabilityGroup = it->availGroup; + info.ports.append( port ); + } + m_cards.append( info ); + emit cardAdded( ev->index ); + } + return; + } + + if ( e->type() == PA_SERVER_EVENT ) { + PAServerEvent *ev = static_cast<PAServerEvent *>(e); + if ( ev->defaultSinkName != m_defaultSinkName ) { + m_defaultSinkName = ev->defaultSinkName; + emit defaultOutputChanged( defaultOutput() ); + } + if ( ev->defaultSourceName != m_defaultSourceName ) { + m_defaultSourceName = ev->defaultSourceName; + emit defaultInputChanged( defaultInput() ); + } + return; + } if ( e->type() != PA_EVENT ) return; PAEvent *ev = static_cast<PAEvent *>(e); @@ -241,25 +532,89 @@ void PulseModel::customEvent( TQCustomEvent *e ) if ( ev->kind == PAEvent::DeviceRemoved ) { PulseDevice *dev = findDevice( *list, ev->paIndex ); if ( dev ) { + // Remove from name maps if present. + TQMap<TQString,PulseDevice*>::Iterator it; + for ( it = m_sinksByName.begin(); it != m_sinksByName.end(); ++it ) { + if ( it.data() == dev ) { m_sinksByName.remove(it); break; } + } + for ( it = m_sourcesByName.begin(); it != m_sourcesByName.end(); ++it ) { + if ( it.data() == dev ) { m_sourcesByName.remove(it); break; } + } + // If a recording stream ended, decrement its parent source's count. + if ( ev->cat == AudioDevice::Recording ) { + TQMap<uint32_t,uint32_t>::Iterator si = m_sourceOutputToSource.find( ev->paIndex ); + if ( si != m_sourceOutputToSource.end() ) { + PulseDevice *src = findDevice( m_sources, si.data() ); + if ( src ) src->adjustRecordingCount( -1 ); + m_sourceOutputToSource.remove( si ); + } + } emit deviceRemoved( dev ); - list->remove( dev ); // autoDelete=true, so dev is deleted here + list->remove( dev ); } return; } // DeviceAdded or DeviceUpdated (both come through the same info callbacks). + // For Playback streams, resolve the parent sink's monitor source name so the + // level meter can attach a peak-detect stream to just this sink input. + TQString monitorName = ev->monitorName; + if ( ev->cat == AudioDevice::Playback && monitorName.isEmpty() + && ev->parentIndex != PA_INVALID_INDEX ) { + PulseDevice *sink = findDevice( m_sinks, ev->parentIndex ); + if ( sink ) monitorName = sink->monitorName(); + } + PulseDevice *dev = findDevice( *list, ev->paIndex ); if ( dev ) { - dev->update( ev->name, ev->volume, ev->muted ); + dev->update( ev->name, ev->volume, ev->muted, ev->channels, ev->pan, monitorName, ev->iconName ); + if ( ev->cat == AudioDevice::Playback && ev->parentIndex != PA_INVALID_INDEX ) + dev->setSinkIndex( ev->parentIndex ); } else { dev = new PulseDevice( ev->cat, ev->paIndex, this ); + dev->setModel( this ); dev->setPAContext( m_context, m_mainloop ); - dev->update( ev->name, ev->volume, ev->muted ); + dev->update( ev->name, ev->volume, ev->muted, ev->channels, ev->pan, monitorName, ev->iconName ); + if ( ev->cat == AudioDevice::Playback && ev->parentIndex != PA_INVALID_INDEX ) + dev->setSinkIndex( ev->parentIndex ); + if ( !ev->paName.isEmpty() ) { + dev->setPaName( ev->paName ); + } list->append( dev ); + if ( !ev->paName.isEmpty() ) { + if ( ev->cat == AudioDevice::Output ) + m_sinksByName.insert( ev->paName, dev ); + else if ( ev->cat == AudioDevice::Input ) + m_sourcesByName.insert( ev->paName, dev ); + } emit deviceAdded( dev ); + if ( ev->cat == AudioDevice::Output && ev->paName == m_defaultSinkName ) + emit defaultOutputChanged( dev ); + if ( ev->cat == AudioDevice::Input && ev->paName == m_defaultSourceName ) + emit defaultInputChanged( dev ); + // Track which source a new recording stream is attached to. + if ( ev->cat == AudioDevice::Recording && ev->parentIndex != PA_INVALID_INDEX ) { + m_sourceOutputToSource.insert( ev->paIndex, ev->parentIndex ); + PulseDevice *src = findDevice( m_sources, ev->parentIndex ); + if ( src ) src->adjustRecordingCount( +1 ); + } } } +AudioDevice *PulseModel::defaultOutput() const +{ + if ( m_sinksByName.contains(m_defaultSinkName) ) + return m_sinksByName[m_defaultSinkName]; + return 0; +} + +AudioDevice *PulseModel::defaultInput() const +{ + if ( m_sourcesByName.contains(m_defaultSourceName) ) + return m_sourcesByName[m_defaultSourceName]; + return 0; +} + PulseDevice *PulseModel::findDevice( TQPtrList<PulseDevice> &list, uint32_t paIndex ) { for ( TQPtrListIterator<PulseDevice> it(list); *it; ++it ) @@ -268,4 +623,66 @@ PulseDevice *PulseModel::findDevice( TQPtrList<PulseDevice> &list, uint32_t paIn return 0; } +// ---- operations called from UI (main thread) --------------------------------- + +void PulseModel::setDefaultOutput( AudioDevice *dev ) +{ + PulseDevice *pd = dynamic_cast<PulseDevice*>(dev); + if ( !pd || pd->paName().isEmpty() || !m_context || !m_mainloop ) return; + pa_threaded_mainloop_lock( m_mainloop ); + pa_operation *op = pa_context_set_default_sink( m_context, pd->paName().utf8().data(), 0, 0 ); + if ( op ) pa_operation_unref( op ); + pa_threaded_mainloop_unlock( m_mainloop ); +} + +void PulseModel::setDefaultInput( AudioDevice *dev ) +{ + PulseDevice *pd = dynamic_cast<PulseDevice*>(dev); + if ( !pd || pd->paName().isEmpty() || !m_context || !m_mainloop ) return; + pa_threaded_mainloop_lock( m_mainloop ); + pa_operation *op = pa_context_set_default_source( m_context, pd->paName().utf8().data(), 0, 0 ); + if ( op ) pa_operation_unref( op ); + pa_threaded_mainloop_unlock( m_mainloop ); +} + +void PulseModel::moveSinkInputToSink( AudioDevice *playbackDev, AudioDevice *outputDev ) +{ + PulseDevice *si = dynamic_cast<PulseDevice*>(playbackDev); + PulseDevice *sink = dynamic_cast<PulseDevice*>(outputDev); + if ( !si || !sink || !m_context || !m_mainloop ) return; + pa_threaded_mainloop_lock( m_mainloop ); + pa_operation *op = pa_context_move_sink_input_by_index( + m_context, si->paIndex(), sink->paIndex(), 0, 0 ); + if ( op ) pa_operation_unref( op ); + pa_threaded_mainloop_unlock( m_mainloop ); +} + +const PulseCardInfo *PulseModel::card( uint32_t index ) const +{ + for ( TQValueList<PulseCardInfo>::ConstIterator it = m_cards.begin(); + it != m_cards.end(); ++it ) + if ( (*it).index == index ) + return &(*it); + return 0; +} + +PulseCardInfo *PulseModel::findCard( uint32_t index ) +{ + for ( TQValueList<PulseCardInfo>::Iterator it = m_cards.begin(); + it != m_cards.end(); ++it ) + if ( it->index == index ) + return &(*it); + return 0; +} + +void PulseModel::setCardProfile( uint32_t cardIndex, const TQString &profileName ) +{ + if ( !m_context || !m_mainloop || profileName.isEmpty() ) return; + pa_threaded_mainloop_lock( m_mainloop ); + pa_operation *op = pa_context_set_card_profile_by_index( + m_context, cardIndex, profileName.utf8().data(), 0, 0 ); + if ( op ) pa_operation_unref( op ); + pa_threaded_mainloop_unlock( m_mainloop ); +} + #include "pulsemodel.moc" |
