From f6f7c36909fa161efe53c40e9b4c34856e751536 Mon Sep 17 00:00:00 2001 From: Calvin Morrison Date: Tue, 12 May 2026 21:32:53 -0400 Subject: Initial tmix skeleton — model layer + basic UI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PulseModel: stable PulseDevice objects keyed by PA index, updated in-place via postEvent reconciliation. No bulk rebuilds. Three signals: deviceAdded, deviceRemoved (device changed handled per-device via volumeChanged/muteChanged/nameChanged). MixerWindow: four-tab layout (Output/Input/Playback/Recording), adds and removes individual DeviceWidgets in response to model signals. Builds and links cleanly against TQt3/TDE + libpulse. Co-Authored-By: Claude Sonnet 4.6 --- src/model/pulsedevice.cpp | 101 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 src/model/pulsedevice.cpp (limited to 'src/model/pulsedevice.cpp') diff --git a/src/model/pulsedevice.cpp b/src/model/pulsedevice.cpp new file mode 100644 index 0000000..e0f84ab --- /dev/null +++ b/src/model/pulsedevice.cpp @@ -0,0 +1,101 @@ +#include "pulsedevice.h" +#include "../ui/devicewidget.h" +#include + +PulseDevice::PulseDevice( AudioDevice::Category cat, uint32_t paIndex, TQObject *parent ) + : AudioDevice(parent), + m_category(cat), m_paIndex(paIndex), + m_volume(0), m_muted(false), + m_context(0), m_mainloop(0) +{ +} + +void PulseDevice::setPAContext( pa_context *ctx, pa_threaded_mainloop *mainloop ) +{ + m_context = ctx; + m_mainloop = mainloop; +} + +void PulseDevice::update( const TQString &name, int volume, bool muted ) +{ + bool nameChange = ( name != m_name ); + bool volumeChange = ( volume != m_volume ); + bool muteChange = ( muted != m_muted ); + + m_name = name; + m_volume = volume; + m_muted = muted; + + if ( nameChange ) emit nameChanged( m_name ); + if ( volumeChange ) emit volumeChanged( m_volume ); + if ( muteChange ) emit muteChanged( m_muted ); +} + +// ---- write back to PA ------------------------------------------------------- + +static pa_volume_t percentToPA( int pct ) +{ + if ( pct <= 0 ) return 0; + if ( pct >= 100 ) return PA_VOLUME_NORM; + return (pa_volume_t)( (double)pct / 100.0 * PA_VOLUME_NORM + 0.5 ); +} + +void PulseDevice::setVolume( int v ) +{ + if ( !m_context ) return; + v = v < 0 ? 0 : v > 100 ? 100 : v; + + pa_cvolume cv; + pa_cvolume_set( &cv, 2, percentToPA(v) ); + + pa_threaded_mainloop_lock( m_mainloop ); + pa_operation *op = 0; + switch ( m_category ) { + case Output: + op = pa_context_set_sink_volume_by_index( m_context, m_paIndex, &cv, 0, 0 ); + break; + case Input: + op = pa_context_set_source_volume_by_index( m_context, m_paIndex, &cv, 0, 0 ); + break; + case Playback: + op = pa_context_set_sink_input_volume( m_context, m_paIndex, &cv, 0, 0 ); + break; + case Recording: + op = pa_context_set_source_output_volume( m_context, m_paIndex, &cv, 0, 0 ); + break; + } + if ( op ) pa_operation_unref( op ); + pa_threaded_mainloop_unlock( m_mainloop ); +} + +void PulseDevice::setMuted( bool m ) +{ + if ( !m_context ) return; + + pa_threaded_mainloop_lock( m_mainloop ); + pa_operation *op = 0; + int mute = m ? 1 : 0; + switch ( m_category ) { + case Output: + op = pa_context_set_sink_mute_by_index( m_context, m_paIndex, mute, 0, 0 ); + break; + case Input: + op = pa_context_set_source_mute_by_index( m_context, m_paIndex, mute, 0, 0 ); + break; + case Playback: + op = pa_context_set_sink_input_mute( m_context, m_paIndex, mute, 0, 0 ); + break; + case Recording: + op = pa_context_set_source_output_mute( m_context, m_paIndex, mute, 0, 0 ); + break; + } + if ( op ) pa_operation_unref( op ); + pa_threaded_mainloop_unlock( m_mainloop ); +} + +TQWidget *PulseDevice::createWidget( TQWidget *parent ) +{ + return new DeviceWidget( this, parent ); +} + +#include "pulsedevice.moc" -- cgit v1.2.3