From kde-commits Tue Feb 19 19:56:03 2013 From: Trever Fischer Date: Tue, 19 Feb 2013 19:56:03 +0000 To: kde-commits Subject: [kmix/kmix-improvements] src/daemon: Implement sink controls Message-Id: <20130219195603.3D26AA60DF () git ! kde ! org> X-MARC-Message: https://marc.info/?l=kde-commits&m=136130378528077 Git commit 6fb982842e18eb4af5bebcabc3d9bc7771ad00ea by Trever Fischer. Committed on 27/09/2012 at 19:30. Pushed by tdfischer into branch 'kmix-improvements'. Implement sink controls M +1 -0 src/daemon/CMakeLists.txt M +46 -0 src/daemon/backends/PulseAudio.cpp M +5 -0 src/daemon/backends/PulseAudio.h A +128 -0 src/daemon/backends/PulseSourceControl.cpp [License: LGP= L (v2+)] A +45 -0 src/daemon/backends/PulseSourceControl.h [License: LGPL = (v2+)] http://commits.kde.org/kmix/6fb982842e18eb4af5bebcabc3d9bc7771ad00ea diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt index b090c8d..4976a9b 100644 --- a/src/daemon/CMakeLists.txt +++ b/src/daemon/CMakeLists.txt @@ -21,6 +21,7 @@ if (PULSEAUDIO_FOUND) backends/PulseSourceOutputControl.cpp backends/PulseSinkInputControl.cpp backends/PulseSinkControl.cpp + backends/PulseSourceControl.cpp ) endif() = diff --git a/src/daemon/backends/PulseAudio.cpp b/src/daemon/backends/Pulse= Audio.cpp index d9c05d3..c4868c7 100644 --- a/src/daemon/backends/PulseAudio.cpp +++ b/src/daemon/backends/PulseAudio.cpp @@ -23,6 +23,7 @@ #include "PulseSourceOutputControl.h" #include "PulseSinkInputControl.h" #include "PulseSinkControl.h" +#include "PulseSourceControl.h" #include = namespace Backends { @@ -74,6 +75,28 @@ void PulseAudio::sink_input_cb(pa_context *cxt, const pa= _sink_input_info *info, } } = +void PulseAudio::source_cb(pa_context *cxt, const pa_source_info *info, in= t eol, gpointer user_data) +{ + PulseAudio *that =3D static_cast(user_data); + if (eol < 0) { + if (pa_context_errno(cxt) =3D=3D PA_ERR_NOENTITY) + return; + } + if (eol > 0) { + return; + } + PulseSourceControl *control; + if (!that->m_sources.contains(info->index)) { + control =3D new PulseSourceControl(cxt, info, that); + QObject::connect(control, SIGNAL(scheduleRefresh(int)), that, SLOT= (refreshSource(int))); + that->m_sources[info->index] =3D control; + that->registerControl(control); + } else { + control =3D that->m_sources[info->index]; + control->update(info); + } +} + PulseSinkControl *PulseAudio::sink(int idx) { if (m_sinks.contains(idx)) @@ -145,6 +168,11 @@ void PulseAudio::refreshSinkInput(int idx) pa_context_get_sink_input_info(m_context, idx, sink_input_cb, this); } = +void PulseAudio::refreshSource(int idx) +{ + pa_context_get_source_info_by_index(m_context, idx, source_cb, this); +} + void PulseAudio::refreshSourceOutput(int idx) { pa_context_get_source_output_info(m_context, idx, source_output_cb, th= is); @@ -172,6 +200,7 @@ void PulseAudio::subscribe_cb(pa_context *cxt, pa_subsc= ription_event_type t, uin if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) =3D=3D PA_SUBSCRIPTI= ON_EVENT_REMOVE) { PulseControl *control =3D that->m_sourceOutputs.take(index= ); that->deregisterControl(control); + that->m_excludedSourceOutputs.removeOne(control->pulseInde= x()); control->deleteLater(); } else { pa_operation *op; @@ -182,6 +211,19 @@ void PulseAudio::subscribe_cb(pa_context *cxt, pa_subs= cription_event_type t, uin pa_operation_unref(op); } break; + case PA_SUBSCRIPTION_EVENT_SOURCE: + if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) =3D=3D PA_SUBSCRIPTI= ON_EVENT_REMOVE) { + PulseControl *control =3D that->m_sources.take(index); + that->deregisterControl(control); + control->deleteLater(); + } else { + pa_operation *op; + if (!(op =3D pa_context_get_source_info_by_index(cxt, inde= x, source_cb, user_data))) { + qWarning() << "pa_context_get_source_info_by_index fai= led"; + return; + } + pa_operation_unref(op); + } case PA_SUBSCRIPTION_EVENT_SINK_INPUT: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) =3D=3D PA_SUBSCRIPTI= ON_EVENT_REMOVE) { PulseControl *control =3D that->m_sinkInputs.take(index); @@ -221,6 +263,10 @@ void PulseAudio::context_state_callback(pa_context *cx= t, gpointer user_data) return; } pa_operation_unref(op); + if (!(op =3D pa_context_get_source_info_list(cxt, source_cb, that)= )) { + return; + } + pa_operation_unref(op); if (!(op =3D pa_context_get_source_output_info_list(cxt, source_ou= tput_cb, that))) { return; } diff --git a/src/daemon/backends/PulseAudio.h b/src/daemon/backends/PulseAu= dio.h index 5ddad40..ec80729 100644 --- a/src/daemon/backends/PulseAudio.h +++ b/src/daemon/backends/PulseAudio.h @@ -31,6 +31,7 @@ class PulseControl; class PulseSinkControl; class PulseSourceOutputControl; class PulseSinkInputControl; +class PulseSourceControl; = class PulseAudio : public Backend { Q_OBJECT @@ -44,11 +45,13 @@ private slots: void refreshSink(int idx); void refreshSourceOutput(int idx); void refreshSinkInput(int idx); + void refreshSource(int idx); private: static void context_state_callback(pa_context *cxt, gpointer user_data= ); static void sink_cb(pa_context *cxt, const pa_sink_info *info, int eol= , gpointer user_data); static void source_output_cb(pa_context *cxt, const pa_source_output_i= nfo *info, int eol, gpointer user_data); static void sink_input_cb(pa_context *cxt, const pa_sink_input_info *i= nfo, int eol, gpointer user_data); + static void source_cb(pa_context *cxt, const pa_source_info *info, int= eol, gpointer user_data); static void subscribe_cb(pa_context *cxt, pa_subscription_event_type t= , uint32_t index, gpointer user_data); pa_glib_mainloop *m_loop; pa_mainloop_api *m_loopAPI; @@ -56,6 +59,8 @@ private: QMap m_sinks; QMap m_sourceOutputs; QMap m_sinkInputs; + QMap m_sources; + QList m_excludedSourceOutputs; }; = } //namespace Backends diff --git a/src/daemon/backends/PulseSourceControl.cpp b/src/daemon/backen= ds/PulseSourceControl.cpp new file mode 100644 index 0000000..1ab1017 --- /dev/null +++ b/src/daemon/backends/PulseSourceControl.cpp @@ -0,0 +1,128 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * Copyright (C) Trever Fischer + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA = 02110-1301, USA. + */ + +#include "PulseSourceControl.h" +#include +#include "PulseAudio.h" + +namespace Backends { + +PulseSourceControl::PulseSourceControl(pa_context *cxt, const pa_source_in= fo *info, PulseAudio *parent) + : PulseControl(Control::HardwareInput, cxt, parent) + , m_monitor(0) +{ + update(info); + qDebug() << displayName(); +} + +void PulseSourceControl::setVolume(Channel c, int v) +{ + m_volumes.values[(int)c] =3D v; + if (!pa_context_set_source_volume_by_index(m_context, m_idx, &m_volume= s, NULL, NULL)) { + qWarning() << "pa_context_set_source_volume_by_index() failed"; + } +} + +void PulseSourceControl::setMute(bool yes) +{ + pa_context_set_sink_mute_by_index(m_context, m_idx, yes, cb_refresh, t= his); +} + +void PulseSourceControl::update(const pa_source_info *info) +{ + m_idx =3D info->index; + m_displayName =3D QString::fromUtf8(info->description); + m_iconName =3D QString::fromUtf8(pa_proplist_gets(info->proplist, PA_P= ROP_DEVICE_ICON_NAME)); + updateVolumes(info->volume); + if (m_muted !=3D info->mute) { + emit muteChanged(info->mute); + } + m_muted =3D info->mute; +} + +bool PulseSourceControl::canMonitor() const +{ + return pa_context_get_server_protocol_version(m_context) >=3D 13; +} + +void PulseSourceControl::startMonitor() +{ + qDebug() << "Starting monitor?"; + if (m_monitor) + return; + qDebug() << "k."; + pa_stream *s; + pa_buffer_attr attr; + pa_sample_spec spec; + + spec.channels =3D 1; + spec.format =3D PA_SAMPLE_FLOAT32; + spec.rate =3D 2; + + memset(&attr, 0, sizeof(attr)); + attr.fragsize =3D sizeof(float); + attr.maxlength =3D (uint32_t) - 1; + + if (!(s =3D pa_stream_new(m_context, "Peak Detect", &spec, NULL))) { + qWarning() << "Couldn't create peak detect stream?!"; + return; + } + + pa_stream_set_read_callback(s, monitor_cb, this); + if (pa_stream_connect_record(s, NULL, &attr, (pa_stream_flags_t) (PA_S= TREAM_DONT_INHIBIT_AUTO_SUSPEND|PA_STREAM_DONT_MOVE|PA_STREAM_PEAK_DETECT|P= A_STREAM_ADJUST_LATENCY)) < 0) { + qWarning() << "Couldn't connect monitor stream?!"; + pa_stream_unref(s); + return; + } + + m_monitor =3D s; +} + +void PulseSourceControl::monitor_cb(pa_stream *s, size_t length, void *use= r_data) +{ + PulseSourceControl *that =3D static_cast(user_dat= a); + const void *data; + double v; + if (pa_stream_peek(s, &data, &length) < 0) { + qWarning() << "Couldn't read monitor stream?!"; + return; + } + + assert(length > 0); + assert(length % sizeof(float) =3D=3D 0); + + v =3D ((const float*) data)[length / sizeof(float) - 1]; + pa_stream_drop(s); + + v =3D qMax(0.0, qMin(v, 1.0)); + + that->levelUpdate(v*65536); +} + +void PulseSourceControl::stopMonitor() +{ + qDebug() << "Stopping monitor"; + if (m_monitor) + pa_stream_unref(m_monitor); +} + +} + +#include "PulseSourceControl.moc" diff --git a/src/daemon/backends/PulseSourceControl.h b/src/daemon/backends= /PulseSourceControl.h new file mode 100644 index 0000000..3c079bc --- /dev/null +++ b/src/daemon/backends/PulseSourceControl.h @@ -0,0 +1,45 @@ +/* + * KMix -- KDE's full featured mini mixer + * + * Copyright (C) Trever Fischer + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA = 02110-1301, USA. + */ + +#ifndef PULSESOURCECONTROL_H +#define PULSESOURCECONTROL_H + +#include "PulseControl.h" + +namespace Backends { + +class PulseSourceControl : public PulseControl { + Q_OBJECT +public: + PulseSourceControl(pa_context *cxt, const pa_source_info *info, PulseA= udio *parent =3D 0); + void setVolume(Channel c, int v); + void setMute(bool yes); + void update(const pa_source_info *info); + bool canMonitor() const; + void startMonitor(); + void stopMonitor(); +private: + static void monitor_cb(pa_stream *s, size_t length, void *data); + pa_stream *m_monitor; +}; + +} + +#endif // PULSESOURCECONTROL_H