├── .gitignore ├── LICENSE ├── doc ├── README.html.in ├── html2txt ├── meson.build ├── screenshot.png └── style.css ├── meson.build ├── meson_options.txt ├── po ├── .gitignore ├── LINGUAS ├── POTFILES.in ├── af.po ├── ar.po ├── as.po ├── ast.po ├── be.po ├── bn_IN.po ├── ca.po ├── ca@valencia.po ├── cs.po ├── da.po ├── de.po ├── el.po ├── es.po ├── eu.po ├── fi.po ├── fr.po ├── gl.po ├── gu.po ├── he.po ├── hi.po ├── hr.po ├── hu.po ├── id.po ├── it.po ├── ja.po ├── ka.po ├── kk.po ├── kn.po ├── ko.po ├── lt.po ├── meson.build ├── ml.po ├── mr.po ├── nb_NO.po ├── nl.po ├── nn.po ├── or.po ├── pa.po ├── pavucontrol.pot ├── pl.po ├── pt.po ├── pt_BR.po ├── ro.po ├── ru.po ├── si.po ├── sk.po ├── sl.po ├── sr.po ├── sr@latin.po ├── sv.po ├── ta.po ├── te.po ├── th.po ├── tr.po ├── uk.po ├── zh_CN.po └── zh_TW.po └── src ├── .gitignore ├── cardwidget.cc ├── cardwidget.h ├── cardwidget.ui ├── channelwidget.cc ├── channelwidget.h ├── channelwidget.ui ├── devicewidget.cc ├── devicewidget.h ├── devicewidget.ui ├── i18n.h ├── mainwindow.cc ├── mainwindow.h ├── mainwindow.ui ├── meson.build ├── minimalstreamwidget.cc ├── minimalstreamwidget.h ├── org.pulseaudio.pavucontrol-symbolic.svg ├── org.pulseaudio.pavucontrol.Devel.svg ├── org.pulseaudio.pavucontrol.Source.svg ├── org.pulseaudio.pavucontrol.desktop.in ├── org.pulseaudio.pavucontrol.metainfo.xml.in ├── org.pulseaudio.pavucontrol.svg ├── pavuapplication.cc ├── pavuapplication.h ├── pavucontrol.cc ├── pavucontrol.h ├── renamedialog.ui ├── resources.gresource.xml ├── rolewidget.cc ├── rolewidget.h ├── sinkinputwidget.cc ├── sinkinputwidget.h ├── sinkwidget.cc ├── sinkwidget.h ├── sourceoutputwidget.cc ├── sourceoutputwidget.h ├── sourcewidget.cc ├── sourcewidget.h ├── streamwidget.cc ├── streamwidget.h └── streamwidget.ui /.gitignore: -------------------------------------------------------------------------------- 1 | ABOUT-NLS 2 | ChangeLog 3 | *~ 4 | config.guess 5 | config.rpath 6 | config.sub 7 | aclocal.m4 8 | *.cache 9 | compile 10 | config.h 11 | config.h.in 12 | config.log 13 | config.status 14 | configure 15 | build 16 | depcomp 17 | README 18 | README.html 19 | install-sh 20 | missing 21 | *.o 22 | stamp* 23 | Makefile 24 | Makefile.in 25 | .deps 26 | *.tar.gz 27 | *.swp 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /doc/README.html.in: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | PulseAudio Volume Control @PACKAGE_VERSION@ 8 | 9 | 10 | 11 | 12 |

PulseAudio Volume Control @PACKAGE_VERSION@

13 | 14 | 27 | 28 |

License

29 | 30 |

This program is free software; you can redistribute it and/or 31 | modify it under the terms of the GNU Public License as published by 32 | the Free Software Foundation, either version 2 of the License, or (at 33 | your option) any later version.

34 | 35 |

This program is distributed in the hope that it will be useful, but 36 | WITHOUT ANY WARRANTY; without even the implied warranty of 37 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 38 | General Public License for more details.

39 | 40 |

News

41 | 42 |
Fri Aug 01 2024:
43 |

44 | Version 6.1 released. 45 | Changes:

46 |
    47 |
  • Fix translations not being correctly picked up.
  • 48 |
  • Fix bug where cards don't go away when unplugged.
  • 49 |
  • Fix app naming in desktop file (Volume Control).
  • 50 |
  • Fix Bluetooth codec selection alignment.
  • 51 |
  • Update Swedish translation.
  • 52 |
53 | 54 |
Tue May 21 2024:
55 |

56 | Version 6.0 released. 57 | Changes:

58 |
    59 |
  • Migrate from Gtk 3 to 4.
  • 60 |
  • Embed UI resources in executable.
  • 61 |
  • Rename "Set as fallback" to "Default" for better legibility.
  • 62 |
  • Support 144 Hz monitors with level bars.
  • 63 |
  • App icons will fallback to generic rather than missing image, 64 | and this will be more common with Gtk 4.
  • 65 |
  • Lots of translation updates.
  • 66 |
  • Drop autotools build in favour of meson.
  • 67 |
  • Make libcanberra dependency optional.
  • 68 |
69 | 70 |
Sun Aug 15 2021:
71 |

72 | Version 5.0 released. 73 | Changes:

74 |
    75 |
  • Support for switching Bluetooth codecs (new in PulseAudio 15.0).
  • 76 |
  • Support for locking card profiles (new in PulseAudio 15.0). Locking 77 | a profile prevents PulseAudio from automatically switching away from that 78 | profile on plug/unplug events.
  • 79 |
  • New translations: Asturian, Basque, Belarusian, Galician, Hebrew, Kazakh, 80 | Norwegian Bokmål, Sinhala, Slovenian
  • 81 |
  • Updated translations: Catalan, Chinese (Simplified), Chinese (Traditional), 82 | Croatian, Danish, Dutch, Finnish, French, German, Hungarian, Italian, Japanese, 83 | Korean, Lithuanian, Norwegian Nynorsk, Polish, Portugese, Portugese (Brazil), 84 | Slovak, Spanish, Swedish, Turkish, Ukrainian.
  • 85 |
  • Some bug fixes.
  • 86 |
  • New dependency: json-glib.
  • 87 |
  • Minimum gtkmm version bumped to 3.22.
  • 88 |
89 | 90 |
Tue Mar 5 2019:
91 |

92 | Version 4.0 released. 93 | Changes:

94 |
    95 |
  • There can now be only one pavucontrol window open at a time. Trying to 96 | start pavucontrol for a second time brings the first window to foreground.
  • 97 |
  • Added a "Show volume meters" checkbox to the Configuration tab. Disabling 98 | the volume meters reduces CPU use.
  • 99 |
  • Improve the use of space (remove useless margins and paddings).
  • 100 |
  • Use a more appropriate icon for the channel lock button.
  • 101 |
  • Better channel label layout, prevents volume sliders from getting 102 | unaligned.
  • 103 |
  • Maximum latency offset increased from 2 to 5 seconds to accommodate AirPlay 104 | devices that often have higher latency than 2 seconds (this is not that useful 105 | on newer PulseAudio versions, though, because the latency is reported much more 106 | accurately than before).
  • 107 |
  • New --version command line option.
  • 108 |
  • New translations: Chinese (Taiwan), Croatian, Korean, Norwegian Nynorsk, 109 | Lithuanian, Valencian.
  • 110 |
  • Updated translations: Finnish, French, German, Italian, Japanese, Polish, 111 | Swedish.
  • 112 |
  • Dropped support for Gtk+ 2.
  • 113 |
  • Bumped the minimum supported libpulse version to 5.0.
  • 114 |
  • Improved compatibility with newer Glade versions.
  • 115 |
116 | 117 |
Tue Mar 24 118 | 2015:

Version 119 | 3.0 released; improved error handling; remember device selection visibility; window sizing improvements.

120 | 121 |
Fri Mar 8 122 | 2013:

Version 123 | 2.0 released.

124 | 125 |
Tue Sep 27 126 | 2011:

Version 127 | 1.0 released.

128 | 129 |
Thu Sep 15 130 | 2011:

Version 131 | 0.99.2 (v1 RC2) released; digital receiver UI.

132 | 133 |
Tue Aug 2 134 | 2011:

Version 135 | 0.99.1 (v1 RC1) released; gtk3 support; source output volume support; key command handling; bugfixes.

136 | 137 |
Wed Oct 14 138 | 2009:

Version 139 | 0.9.10 released; minor fixes; translation updates.

140 | 141 |
Thu Sep 10 142 | 2009:

Version 143 | 0.9.9 released; allow configuring device ports; various updates.

144 | 145 |
Mon Apr 13 146 | 2009:

Version 147 | 0.9.8 released; allow configuring card profiles; various updates.

148 | 149 |
Tue Sep 9 150 | 2008:

Version 151 | 0.9.7 released; show volume meter for each stream and device.

152 | 153 |
Fri Mar 28 154 | 2008:

Version 155 | 0.9.6 released; draw radio buttons instead of check boxes when 156 | moving streams; fix crasher when connecting to a PA server that has no 157 | default sink/source configured.

158 | 159 |
Tue Oct 30 2007:

Version 0.9.5 161 | released; rework UI; add context menu option to kill a playback stream; add context menu option to select the default device; use prettier channel names; filter non-application streams by default; support muting streams

162 | 163 |
Sat Aug 26 2006:

Version 0.9.4 165 | released; add the ability to move a stream to another device while it 166 | is playing; limit the list of sinks/sources shown by their type; other cleanups

167 | 168 |
Mon Jul 24 2006:

Version 0.9.3 released; show client names only if they are set

170 | 171 |
Sat Jul 8 2006:

Version 0.9.2 released; update for PulseAudio 0.9.2

173 | 174 |
Fri Jun 2 2006:

Version 0.9.1 released; add a .desktop file; update icons everywhere

176 | 177 |
Sat May 27 2006:

Version 0.9.0 released; update for Polypaudio 0.9.0; show client name of playback streams; make volume slider more responsive

179 | 180 |
Fri Apr 21 2006:

Version 0.8 released; initial release

182 | 183 |

Overview

184 | 185 |

PulseAudio Volume Control (pavucontrol) is a simple GTK 186 | based volume control tool ("mixer") for the PulseAudio 188 | sound server. In contrast to classic mixer tools this one allows 189 | you to control both the volume of hardware devices and of each 190 | playback stream separately.

191 | 192 |

Everybody loves screenshots.

193 | 194 |

Current Status

195 | 196 |

Works perfectly.

197 | 198 |

Documentation

199 | 200 |

There is not much to say. Just run pavucontrol and see for yourself.

201 | 202 |

Requirements

203 | 204 |

Currently, pavucontrol is tested on Linux only. It should work on 205 | most Linux distributions.

206 | 207 |

pavucontrol requires gtkmm and glademmm installed.

208 | 209 |

Obviously pavucontrol requires an installation of PulseAudio.

210 | 211 |

Installation

212 | 213 |

As this package is made with the GNU autotools you should run 214 | ./configure inside the distribution directory for configuring 215 | the source tree. After that you should run make for 216 | compilation and make install (as root) for installation of 217 | pavucontrol.

218 | 219 |

Download

220 | 221 |

The newest release is always available from @PACKAGE_URL@

222 | 223 |

The current release is @PACKAGE_VERSION@

224 | 225 |

Get pavucontrol's development sources from the git repository (GitLab):

226 | 227 |
git clone https://gitlab.freedesktop.org/pulseaudio/pavucontrol.git
228 | 229 |

Bug Reports

230 | 231 |

Here's the list of open bug reports: https://gitlab.freedesktop.org/pulseaudio/pavucontrol/issues

232 | 233 |

And here you can create a new bug report: https://gitlab.freedesktop.org/pulseaudio/pavucontrol/issues/new

234 | 235 |

Contributing Code

236 | 237 |

If you have patches for pavucontrol, the primary method for submitting them is by creating a merge request in GitLab.

238 | 239 |

Contributing Translations

240 | 241 |

Translations can be contributed in Fedora's Weblate translation service. Pavucontrol isn't affiliated with Fedora otherwise, but Fedora kindly offered to host the translation service for us.

242 | 243 | 244 | 245 | -------------------------------------------------------------------------------- /doc/html2txt: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | lynx --dump $1 | sed 's,file://localhost/.*/doc/README.html,README,' 3 | -------------------------------------------------------------------------------- /doc/meson.build: -------------------------------------------------------------------------------- 1 | doc_cdata = configuration_data() 2 | 3 | doc_cdata.set('PACKAGE_VERSION', meson.project_version()) 4 | doc_cdata.set('PACKAGE_URL', 'http://freedesktop.org/software/pulseaudio/pavucontrol/') 5 | 6 | readme_html = configure_file( 7 | input : 'README.html.in', 8 | output : 'README.html', 9 | configuration : doc_cdata, 10 | install : true, 11 | install_dir : docdir, 12 | ) 13 | 14 | if tidy.found() 15 | test('XHTML documentation uses correct mark-up', tidy, args: ['-e', readme_html]) 16 | endif 17 | 18 | if with_lynx 19 | custom_target( 20 | 'README', 21 | input: readme_html, 22 | output: 'README', 23 | command: [find_program('./html2txt'), '@INPUT@'], 24 | capture: true, 25 | build_by_default: true, 26 | install : true, 27 | install_dir : docdir, 28 | ) 29 | endif 30 | 31 | install_data( 32 | sources : 'style.css', 33 | install_dir : docdir, 34 | ) 35 | -------------------------------------------------------------------------------- /doc/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pulseaudio/pavucontrol/cd7bd578aed022a3dc9c2c7c111519f940435c83/doc/screenshot.png -------------------------------------------------------------------------------- /doc/style.css: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | 6 | pavucontrol is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | pavucontrol is distributed in the hope that it will be useful, but 12 | WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with pavucontrol. If not, see . 18 | ***/ 19 | 20 | body { color: black; background-color: white; } 21 | a:link, a:visited { color: #900000; } 22 | div.news-date { font-size: 80%; font-style: italic; } 23 | pre { background-color: #f0f0f0; padding: 0.4cm; } 24 | .grey { color: #8f8f8f; font-size: 80%; } 25 | table { margin-left: 1cm; border:1px solid lightgrey; padding: 0.2cm; } 26 | td { padding-left:10px; padding-right:10px; } 27 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project('pavucontrol', 'cpp', 2 | version : '6.1', 3 | meson_version : '>= 0.50.0', 4 | default_options : [ 'c_std=gnu11', 'cpp_std=c++17' ] 5 | ) 6 | 7 | with_lynx = get_option('lynx') 8 | 9 | cpp = meson.get_compiler('cpp') 10 | 11 | gtkmm_dep = dependency('gtkmm-4.0', version : '>= 4.0', required : true) 12 | sigcpp_dep = dependency('sigc++-3.0', required : true) 13 | canberragtk_dep = dependency('libcanberra', version : '>= 0.16', required : false) 14 | 15 | libpulse_dep = dependency('libpulse', version : '>= 5.0', required : true) 16 | libpulsemlglib_dep = dependency('libpulse-mainloop-glib', version : '>= 0.9.16', required : true) 17 | 18 | have_pulse_messaging_api = cpp.has_function('pa_context_send_message_to_object', 19 | dependencies: [libpulse_dep], 20 | ) 21 | 22 | json_glib_dep = dependency('json-glib-1.0', required : have_pulse_messaging_api) 23 | 24 | lynx = find_program('lynx', required: with_lynx) 25 | tidy = find_program('tidy', required: false) 26 | 27 | configinc = include_directories('.') 28 | 29 | prefix = get_option('prefix') 30 | datadir = join_paths(prefix, get_option('datadir')) 31 | docdir = join_paths(datadir, 'doc', 'pavucontrol') 32 | localedir = join_paths(prefix, get_option('localedir')) 33 | 34 | cdata = configuration_data() 35 | cdata.set('ENABLE_NLS', 1) 36 | cdata.set_quoted('PACKAGE', 'pavucontrol') 37 | cdata.set_quoted('PACKAGE_NAME', 'pavucontrol') 38 | cdata.set_quoted('PACKAGE_STRING', 'pavucontrol') 39 | cdata.set_quoted('PACKAGE_VERSION', meson.project_version()) 40 | cdata.set_quoted('GETTEXT_PACKAGE', 'pavucontrol') 41 | cdata.set_quoted('LOCALEDIR', localedir) 42 | 43 | cdata.set('HAVE_LIBCANBERRA', canberragtk_dep.found()) 44 | 45 | # Now generate config.h from everything above 46 | configure_file(output : 'config.h', configuration : cdata) 47 | 48 | subdir('doc') 49 | subdir('po') 50 | subdir('src') 51 | -------------------------------------------------------------------------------- /meson_options.txt: -------------------------------------------------------------------------------- 1 | option('lynx', 2 | type : 'boolean', value : true, 3 | description : 'Enable building of the README text file for installation') 4 | -------------------------------------------------------------------------------- /po/.gitignore: -------------------------------------------------------------------------------- 1 | .intltool-merge-cache 2 | Makefile.in.in 3 | Makevars.template 4 | POTFILES 5 | Rules-quot 6 | boldquot.sed 7 | en@boldquot.header 8 | en@quot.header 9 | insert-header.sin 10 | quot.sed 11 | remove-potcdate.sin 12 | *.gmo 13 | *.mo 14 | -------------------------------------------------------------------------------- /po/LINGUAS: -------------------------------------------------------------------------------- 1 | as 2 | bn_IN 3 | ca 4 | ca@valencia 5 | cs 6 | da 7 | de 8 | el 9 | es 10 | fi 11 | fr 12 | gu 13 | hi 14 | hr 15 | hu 16 | it 17 | ja 18 | kn 19 | ko 20 | lt 21 | ml 22 | mr 23 | nl 24 | nn 25 | or 26 | pa 27 | pl 28 | pt 29 | pt_BR 30 | ru 31 | sk 32 | sr 33 | sr@latin 34 | sv 35 | ta 36 | te 37 | th 38 | tr 39 | uk 40 | zh_CN 41 | zh_TW 42 | ast 43 | gl 44 | nb_NO 45 | sl 46 | kk 47 | eu 48 | be 49 | he 50 | si 51 | ro 52 | id 53 | ka 54 | af 55 | ar 56 | -------------------------------------------------------------------------------- /po/POTFILES.in: -------------------------------------------------------------------------------- 1 | src/org.pulseaudio.pavucontrol.metainfo.xml.in 2 | src/org.pulseaudio.pavucontrol.desktop.in 3 | src/mainwindow.ui 4 | src/cardwidget.ui 5 | src/channelwidget.ui 6 | src/renamedialog.ui 7 | src/streamwidget.ui 8 | src/devicewidget.ui 9 | src/pavucontrol.cc 10 | src/cardwidget.cc 11 | src/channelwidget.cc 12 | src/devicewidget.cc 13 | src/mainwindow.cc 14 | src/rolewidget.cc 15 | src/sinkinputwidget.cc 16 | src/sinkwidget.cc 17 | src/sourceoutputwidget.cc 18 | src/sourcewidget.cc 19 | src/streamwidget.cc 20 | src/pavuapplication.cc 21 | -------------------------------------------------------------------------------- /po/ast.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the PACKAGE package. 4 | # Enol P. , 2020. 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: PACKAGE VERSION\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "POT-Creation-Date: 2024-05-24 17:04-0400\n" 10 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 11 | "Last-Translator: Automatically generated\n" 12 | "Language-Team: none\n" 13 | "Language: ast\n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | 18 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:7 19 | #: src/org.pulseaudio.pavucontrol.desktop.in:5 src/mainwindow.ui:5 20 | msgid "Volume Control" 21 | msgstr "" 22 | 23 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:8 24 | msgid "Adjust device and app volumes" 25 | msgstr "" 26 | 27 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:9 28 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:11 29 | msgid "The PulseAudio Developers" 30 | msgstr "" 31 | 32 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:14 33 | msgid "" 34 | "PulseAudio Volume Control (pavucontrol) is a volume control tool (“mixer”) " 35 | "for the PulseAudio sound server. In contrast to classic mixer tools, this " 36 | "one allows you to control both the volume of hardware devices and of each " 37 | "playback stream separately." 38 | msgstr "" 39 | 40 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:21 41 | msgid "The “Playback” tab" 42 | msgstr "" 43 | 44 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:25 45 | msgid "The “Recording” tab" 46 | msgstr "" 47 | 48 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:29 49 | msgid "The “Output” tab" 50 | msgstr "" 51 | 52 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:33 53 | msgid "The “Input” tab" 54 | msgstr "" 55 | 56 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:37 57 | msgid "The “Configuration” tab" 58 | msgstr "" 59 | 60 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:45 61 | msgid "Migrate from Gtk 3 to 4." 62 | msgstr "" 63 | 64 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:46 65 | msgid "Embed UI resources in executable." 66 | msgstr "" 67 | 68 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:47 69 | msgid "Rename \"Set as fallback\" to \"Default\" for better legibility." 70 | msgstr "" 71 | 72 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:48 73 | msgid "Support 144 Hz monitors with level bars." 74 | msgstr "" 75 | 76 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:49 77 | msgid "" 78 | "App icons will fallback to generic rather than missing image, and this will " 79 | "be more common with Gtk 4." 80 | msgstr "" 81 | 82 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:51 83 | msgid "Lots of translation updates." 84 | msgstr "" 85 | 86 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:52 87 | msgid "Drop autotools build in favour of meson." 88 | msgstr "" 89 | 90 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:53 91 | msgid "Make libcanberra dependency optional." 92 | msgstr "" 93 | 94 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:60 95 | msgid "Support for switching Bluetooth codecs (new in PulseAudio 15.0)." 96 | msgstr "" 97 | 98 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:61 99 | msgid "" 100 | "Support for locking card profiles (new in PulseAudio 15.0). Locking a " 101 | "profile prevents PulseAudio from automatically switching away from that " 102 | "profile on plug/unplug events." 103 | msgstr "" 104 | 105 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:62 106 | msgid "" 107 | "New translations: Asturian, Basque, Belarusian, Galician, Hebrew, Kazakh, " 108 | "Norwegian Bokmål, Sinhala, Slovenian" 109 | msgstr "" 110 | 111 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:63 112 | msgid "" 113 | "Updated translations: Catalan, Chinese (Simplified), Chinese (Traditional), " 114 | "Croatian, Danish, Dutch, Finnish, French, German, Hungarian, Italian, " 115 | "Japanese, Korean, Lithuanian, Norwegian Nynorsk, Polish, Portugese, " 116 | "Portugese (Brazil), Slovak, Spanish, Swedish, Turkish, Ukrainian." 117 | msgstr "" 118 | 119 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:70 120 | msgid "" 121 | "There can now be only one pavucontrol window open at a time. Trying to start " 122 | "pavucontrol for a second time brings the first window to foreground." 123 | msgstr "" 124 | 125 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:71 126 | msgid "" 127 | "Added a \"Show volume meters\" checkbox to the Configuration tab. Disabling " 128 | "the volume meters reduces CPU use." 129 | msgstr "" 130 | 131 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:72 132 | msgid "Improve the use of space (remove useless margins and paddings)." 133 | msgstr "" 134 | 135 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:73 136 | msgid "Use a more appropriate icon for the channel lock button." 137 | msgstr "" 138 | 139 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:74 140 | msgid "" 141 | "Better channel label layout, prevents volume sliders from getting unaligned." 142 | msgstr "" 143 | 144 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:75 145 | msgid "" 146 | "Maximum latency offset increased from 2 to 5 seconds to accommodate AirPlay " 147 | "devices that often have higher latency than 2 seconds (this is not that " 148 | "useful on newer PulseAudio versions, though, because the latency is reported " 149 | "much more accurately than before)." 150 | msgstr "" 151 | 152 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:76 153 | msgid "New --version command line option." 154 | msgstr "" 155 | 156 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:77 157 | msgid "" 158 | "New translations: Chinese (Taiwan), Croatian, Korean, Norwegian Nynorsk, " 159 | "Lithuanian, Valencian." 160 | msgstr "" 161 | 162 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:78 163 | msgid "" 164 | "Updated translations: Finnish, French, German, Italian, Japanese, Polish, " 165 | "Swedish." 166 | msgstr "" 167 | 168 | #: src/org.pulseaudio.pavucontrol.desktop.in:4 src/pavucontrol.cc:877 169 | msgid "PulseAudio Volume Control" 170 | msgstr "" 171 | 172 | #: src/org.pulseaudio.pavucontrol.desktop.in:6 173 | msgid "Adjust the volume level" 174 | msgstr "" 175 | 176 | #: src/org.pulseaudio.pavucontrol.desktop.in:12 177 | msgid "" 178 | "pavucontrol;Microphone;Volume;Fade;Balance;Headset;Speakers;Headphones;Audio;" 179 | "Mixer;Output;Input;Devices;Playback;Recording;System Sounds;Sound Card;" 180 | "Settings;Preferences;" 181 | msgstr "" 182 | 183 | #: src/mainwindow.ui:40 184 | msgid "No application is currently playing audio." 185 | msgstr "" 186 | 187 | #: src/mainwindow.ui:66 src/mainwindow.ui:157 188 | msgid "_Show:" 189 | msgstr "" 190 | 191 | #: src/mainwindow.ui:82 src/mainwindow.ui:173 192 | msgid "All Streams" 193 | msgstr "" 194 | 195 | #: src/mainwindow.ui:85 src/mainwindow.ui:176 196 | msgid "Applications" 197 | msgstr "" 198 | 199 | #: src/mainwindow.ui:88 src/mainwindow.ui:179 200 | msgid "Virtual Streams" 201 | msgstr "" 202 | 203 | #: src/mainwindow.ui:107 204 | msgid "_Playback" 205 | msgstr "" 206 | 207 | #: src/mainwindow.ui:131 208 | msgid "No application is currently recording audio." 209 | msgstr "" 210 | 211 | #: src/mainwindow.ui:198 212 | msgid "_Recording" 213 | msgstr "" 214 | 215 | #: src/mainwindow.ui:222 216 | msgid "No output devices available" 217 | msgstr "" 218 | 219 | #: src/mainwindow.ui:248 220 | msgid "S_how:" 221 | msgstr "" 222 | 223 | #: src/mainwindow.ui:265 224 | msgid "All Output Devices" 225 | msgstr "" 226 | 227 | #: src/mainwindow.ui:268 228 | msgid "Hardware Output Devices" 229 | msgstr "" 230 | 231 | #: src/mainwindow.ui:271 232 | msgid "Virtual Output Devices" 233 | msgstr "" 234 | 235 | #: src/mainwindow.ui:290 236 | msgid "_Output Devices" 237 | msgstr "" 238 | 239 | #: src/mainwindow.ui:314 240 | msgid "No input devices available" 241 | msgstr "" 242 | 243 | #: src/mainwindow.ui:340 244 | msgid "Sho_w:" 245 | msgstr "" 246 | 247 | #: src/mainwindow.ui:357 248 | msgid "All Input Devices" 249 | msgstr "" 250 | 251 | #: src/mainwindow.ui:360 252 | msgid "All Except Monitors" 253 | msgstr "" 254 | 255 | #: src/mainwindow.ui:363 256 | msgid "Hardware Input Devices" 257 | msgstr "" 258 | 259 | #: src/mainwindow.ui:366 260 | msgid "Virtual Input Devices" 261 | msgstr "" 262 | 263 | #: src/mainwindow.ui:369 264 | msgid "Monitors" 265 | msgstr "" 266 | 267 | #: src/mainwindow.ui:388 268 | msgid "_Input Devices" 269 | msgstr "" 270 | 271 | #: src/mainwindow.ui:416 272 | msgid "No cards available for configuration" 273 | msgstr "" 274 | 275 | #: src/mainwindow.ui:431 276 | msgid "Show volume meters" 277 | msgstr "" 278 | 279 | #: src/mainwindow.ui:448 280 | msgid "_Configuration" 281 | msgstr "" 282 | 283 | #: src/cardwidget.ui:30 284 | msgid "Card Name" 285 | msgstr "" 286 | 287 | #: src/cardwidget.ui:45 288 | msgid "Lock card to this profile" 289 | msgstr "" 290 | 291 | #: src/cardwidget.ui:66 292 | msgid "Profile:" 293 | msgstr "" 294 | 295 | #: src/cardwidget.ui:82 296 | msgid "Codec:" 297 | msgstr "" 298 | 299 | #: src/channelwidget.ui:9 300 | msgid "left-front" 301 | msgstr "" 302 | 303 | #: src/channelwidget.ui:33 304 | msgid "50%" 305 | msgstr "" 306 | 307 | #: src/renamedialog.ui:16 308 | msgid "Rename device to:" 309 | msgstr "" 310 | 311 | #: src/renamedialog.ui:37 312 | msgid "_Cancel" 313 | msgstr "" 314 | 315 | #: src/renamedialog.ui:45 316 | msgid "_Ok" 317 | msgstr "" 318 | 319 | #: src/streamwidget.ui:34 320 | msgid "Stream Title" 321 | msgstr "" 322 | 323 | #: src/streamwidget.ui:44 324 | msgid "direction" 325 | msgstr "" 326 | 327 | #: src/streamwidget.ui:64 src/devicewidget.ui:48 328 | msgid "Mute audio" 329 | msgstr "" 330 | 331 | #: src/streamwidget.ui:78 src/devicewidget.ui:62 332 | msgid "Lock channels together" 333 | msgstr "" 334 | 335 | #: src/devicewidget.ui:33 336 | msgid "Device Title" 337 | msgstr "" 338 | 339 | #: src/devicewidget.ui:77 340 | msgid "Set as default" 341 | msgstr "" 342 | 343 | #: src/devicewidget.ui:95 344 | msgid "Port:" 345 | msgstr "" 346 | 347 | #: src/devicewidget.ui:136 348 | msgid "PCM" 349 | msgstr "" 350 | 351 | #: src/devicewidget.ui:148 352 | msgid "AC-3" 353 | msgstr "" 354 | 355 | #: src/devicewidget.ui:158 356 | msgid "DTS" 357 | msgstr "" 358 | 359 | #: src/devicewidget.ui:168 360 | msgid "E-AC-3" 361 | msgstr "" 362 | 363 | #: src/devicewidget.ui:178 364 | msgid "MPEG" 365 | msgstr "" 366 | 367 | #: src/devicewidget.ui:188 368 | msgid "AAC" 369 | msgstr "" 370 | 371 | #: src/devicewidget.ui:198 372 | msgid "TrueHD" 373 | msgstr "" 374 | 375 | #: src/devicewidget.ui:208 376 | msgid "DTS-HD" 377 | msgstr "" 378 | 379 | #: src/devicewidget.ui:224 380 | msgid "Latency offset:" 381 | msgstr "" 382 | 383 | #: src/devicewidget.ui:237 384 | msgid "ms" 385 | msgstr "" 386 | 387 | #: src/devicewidget.ui:247 388 | msgid "Advanced" 389 | msgstr "" 390 | 391 | #: src/pavucontrol.cc:107 392 | #, c-format 393 | msgid "could not read JSON from list-codecs message response: %s" 394 | msgstr "" 395 | 396 | #: src/pavucontrol.cc:116 397 | msgid "list-codecs message response is not a JSON array" 398 | msgstr "" 399 | 400 | #: src/pavucontrol.cc:164 401 | msgid "list-codecs message response could not be parsed correctly" 402 | msgstr "" 403 | 404 | #: src/pavucontrol.cc:184 405 | #, c-format 406 | msgid "could not read JSON from get-codec message response: %s" 407 | msgstr "" 408 | 409 | #: src/pavucontrol.cc:193 410 | msgid "get-codec message response is not a JSON value" 411 | msgstr "" 412 | 413 | #: src/pavucontrol.cc:201 414 | msgid "could not get codec name from get-codec message response" 415 | msgstr "" 416 | 417 | #: src/pavucontrol.cc:223 418 | #, c-format 419 | msgid "could not read JSON from get-profile-sticky message response: %s" 420 | msgstr "" 421 | 422 | #: src/pavucontrol.cc:232 423 | msgid "get-profile-sticky message response is not a JSON value" 424 | msgstr "" 425 | 426 | #: src/pavucontrol.cc:252 src/cardwidget.cc:153 src/cardwidget.cc:181 427 | #, c-format 428 | msgid "pa_context_send_message_to_object() failed: %s" 429 | msgstr "" 430 | 431 | #: src/pavucontrol.cc:270 432 | #, c-format 433 | msgid "could not read JSON from list-handlers message response: %s" 434 | msgstr "" 435 | 436 | #: src/pavucontrol.cc:279 437 | msgid "list-handlers message response is not a JSON array" 438 | msgstr "" 439 | 440 | #: src/pavucontrol.cc:327 441 | msgid "list-handlers message response could not be parsed correctly" 442 | msgstr "" 443 | 444 | #: src/pavucontrol.cc:361 445 | msgid "Card callback failure" 446 | msgstr "" 447 | 448 | #: src/pavucontrol.cc:389 449 | msgid "Sink callback failure" 450 | msgstr "" 451 | 452 | #: src/pavucontrol.cc:413 453 | msgid "Source callback failure" 454 | msgstr "" 455 | 456 | #: src/pavucontrol.cc:432 457 | msgid "Sink input callback failure" 458 | msgstr "" 459 | 460 | #: src/pavucontrol.cc:451 461 | msgid "Source output callback failure" 462 | msgstr "" 463 | 464 | #: src/pavucontrol.cc:481 465 | msgid "Client callback failure" 466 | msgstr "" 467 | 468 | #: src/pavucontrol.cc:497 469 | msgid "Server info callback failure" 470 | msgstr "" 471 | 472 | #: src/pavucontrol.cc:515 src/pavucontrol.cc:812 473 | #, c-format 474 | msgid "Failed to initialize stream_restore extension: %s" 475 | msgstr "" 476 | 477 | #: src/pavucontrol.cc:533 478 | msgid "pa_ext_stream_restore_read() failed" 479 | msgstr "" 480 | 481 | #: src/pavucontrol.cc:551 src/pavucontrol.cc:826 482 | #, c-format 483 | msgid "Failed to initialize device restore extension: %s" 484 | msgstr "" 485 | 486 | #: src/pavucontrol.cc:572 487 | msgid "pa_ext_device_restore_read_sink_formats() failed" 488 | msgstr "" 489 | 490 | #: src/pavucontrol.cc:590 src/pavucontrol.cc:839 491 | #, c-format 492 | msgid "Failed to initialize device manager extension: %s" 493 | msgstr "" 494 | 495 | #: src/pavucontrol.cc:609 496 | msgid "pa_ext_device_manager_read() failed" 497 | msgstr "" 498 | 499 | #: src/pavucontrol.cc:626 500 | msgid "pa_context_get_sink_info_by_index() failed" 501 | msgstr "" 502 | 503 | #: src/pavucontrol.cc:639 504 | msgid "pa_context_get_source_info_by_index() failed" 505 | msgstr "" 506 | 507 | #: src/pavucontrol.cc:652 src/pavucontrol.cc:665 508 | msgid "pa_context_get_sink_input_info() failed" 509 | msgstr "" 510 | 511 | #: src/pavucontrol.cc:678 512 | msgid "pa_context_get_client_info() failed" 513 | msgstr "" 514 | 515 | #: src/pavucontrol.cc:688 src/pavucontrol.cc:753 516 | msgid "pa_context_get_server_info() failed" 517 | msgstr "" 518 | 519 | #: src/pavucontrol.cc:701 520 | msgid "pa_context_get_card_info_by_index() failed" 521 | msgstr "" 522 | 523 | #: src/pavucontrol.cc:744 524 | msgid "pa_context_subscribe() failed" 525 | msgstr "" 526 | 527 | #: src/pavucontrol.cc:760 528 | msgid "pa_context_client_info_list() failed" 529 | msgstr "" 530 | 531 | #: src/pavucontrol.cc:767 532 | msgid "pa_context_get_card_info_list() failed" 533 | msgstr "" 534 | 535 | #: src/pavucontrol.cc:774 536 | msgid "pa_context_get_sink_info_list() failed" 537 | msgstr "" 538 | 539 | #: src/pavucontrol.cc:781 540 | msgid "pa_context_get_source_info_list() failed" 541 | msgstr "" 542 | 543 | #: src/pavucontrol.cc:788 544 | msgid "pa_context_get_sink_input_info_list() failed" 545 | msgstr "" 546 | 547 | #: src/pavucontrol.cc:795 548 | msgid "pa_context_get_source_output_info_list() failed" 549 | msgstr "" 550 | 551 | #: src/pavucontrol.cc:854 src/pavucontrol.cc:905 552 | msgid "Connection failed, attempting reconnect" 553 | msgstr "" 554 | 555 | #: src/pavucontrol.cc:892 556 | msgid "" 557 | "Connection to PulseAudio failed. Automatic retry in 5s\n" 558 | "\n" 559 | "In this case this is likely because PULSE_SERVER in the Environment/X11 Root " 560 | "Window Properties\n" 561 | "or default-server in client.conf is misconfigured.\n" 562 | "This situation can also arise when PulseAudio crashed and left stale details " 563 | "in the X11 Root Window.\n" 564 | "If this is the case, then PulseAudio should autospawn again, or if this is " 565 | "not configured you should\n" 566 | "run start-pulseaudio-x11 manually." 567 | msgstr "" 568 | 569 | #: src/cardwidget.cc:126 570 | msgid "pa_context_set_card_profile_by_index() failed" 571 | msgstr "" 572 | 573 | #: src/channelwidget.cc:99 574 | #, c-format 575 | msgid "%0.0f%% (%0.2f dB)" 576 | msgstr "" 577 | 578 | #: src/channelwidget.cc:101 579 | #, c-format 580 | msgid "%0.0f%% (-∞ dB)" 581 | msgstr "" 582 | 583 | #: src/channelwidget.cc:104 584 | #, c-format 585 | msgid "%0.0f%%" 586 | msgstr "" 587 | 588 | #: src/channelwidget.cc:139 589 | msgid "Silence" 590 | msgstr "" 591 | 592 | #: src/channelwidget.cc:139 593 | msgid "Min" 594 | msgstr "" 595 | 596 | #: src/channelwidget.cc:141 597 | msgid "100% (0 dB)" 598 | msgstr "" 599 | 600 | #: src/channelwidget.cc:145 601 | msgid "Base" 602 | msgstr "" 603 | 604 | #: src/devicewidget.cc:75 605 | msgid "Rename Device..." 606 | msgstr "" 607 | 608 | #: src/devicewidget.cc:179 609 | msgid "pa_context_set_port_latency_offset() failed" 610 | msgstr "" 611 | 612 | #: src/devicewidget.cc:257 613 | msgid "Sorry, but device renaming is not supported." 614 | msgstr "" 615 | 616 | #: src/devicewidget.cc:259 617 | msgid "" 618 | "You need to load module-device-manager in the PulseAudio server in order to " 619 | "rename devices" 620 | msgstr "" 621 | 622 | #: src/devicewidget.cc:294 623 | msgid "pa_ext_device_manager_write() failed" 624 | msgstr "" 625 | 626 | #: src/mainwindow.cc:171 627 | #, c-format 628 | msgid "Error reading config file %s: %s" 629 | msgstr "" 630 | 631 | #: src/mainwindow.cc:245 632 | msgid "Error saving preferences" 633 | msgstr "" 634 | 635 | #: src/mainwindow.cc:253 636 | #, c-format 637 | msgid "Error writing config file %s" 638 | msgstr "" 639 | 640 | #: src/mainwindow.cc:314 641 | msgid " (plugged in)" 642 | msgstr "" 643 | 644 | #: src/mainwindow.cc:318 src/mainwindow.cc:426 645 | msgid " (unavailable)" 646 | msgstr "" 647 | 648 | #: src/mainwindow.cc:320 src/mainwindow.cc:423 649 | msgid " (unplugged)" 650 | msgstr "" 651 | 652 | #: src/mainwindow.cc:625 653 | msgid "Failed to read data from stream" 654 | msgstr "" 655 | 656 | #: src/mainwindow.cc:669 657 | msgid "Peak detect" 658 | msgstr "" 659 | 660 | #: src/mainwindow.cc:670 661 | msgid "Failed to create monitoring stream" 662 | msgstr "" 663 | 664 | #: src/mainwindow.cc:685 665 | msgid "Failed to connect monitoring stream" 666 | msgstr "" 667 | 668 | #: src/mainwindow.cc:819 669 | msgid "" 670 | "Ignoring sink-input due to it being designated as an event and thus handled " 671 | "by the Event widget" 672 | msgstr "" 673 | 674 | #: src/mainwindow.cc:994 675 | msgid "System Sounds" 676 | msgstr "" 677 | 678 | #: src/mainwindow.cc:1340 679 | msgid "Establishing connection to PulseAudio. Please wait..." 680 | msgstr "" 681 | 682 | #: src/rolewidget.cc:68 683 | msgid "pa_ext_stream_restore_write() failed" 684 | msgstr "" 685 | 686 | #: src/sinkinputwidget.cc:35 687 | msgid "on" 688 | msgstr "" 689 | 690 | #: src/sinkinputwidget.cc:38 691 | msgid "Terminate Playback" 692 | msgstr "" 693 | 694 | #: src/sinkinputwidget.cc:78 695 | msgid "Unknown output" 696 | msgstr "" 697 | 698 | #: src/sinkinputwidget.cc:87 699 | msgid "pa_context_set_sink_input_volume() failed" 700 | msgstr "" 701 | 702 | #: src/sinkinputwidget.cc:102 703 | msgid "pa_context_set_sink_input_mute() failed" 704 | msgstr "" 705 | 706 | #: src/sinkinputwidget.cc:112 707 | msgid "pa_context_kill_sink_input() failed" 708 | msgstr "" 709 | 710 | #: src/sinkwidget.cc:119 711 | msgid "pa_context_set_sink_volume_by_index() failed" 712 | msgstr "" 713 | 714 | #: src/sinkwidget.cc:137 715 | msgid "Volume Control Feedback Sound" 716 | msgstr "" 717 | 718 | #: src/sinkwidget.cc:155 719 | msgid "pa_context_set_sink_mute_by_index() failed" 720 | msgstr "" 721 | 722 | #: src/sinkwidget.cc:169 723 | msgid "pa_context_set_default_sink() failed" 724 | msgstr "" 725 | 726 | #: src/sinkwidget.cc:189 727 | msgid "pa_context_set_sink_port_by_index() failed" 728 | msgstr "" 729 | 730 | #: src/sinkwidget.cc:231 731 | msgid "pa_ext_device_restore_save_sink_formats() failed" 732 | msgstr "" 733 | 734 | #: src/sourceoutputwidget.cc:35 735 | msgid "from" 736 | msgstr "" 737 | 738 | #: src/sourceoutputwidget.cc:38 739 | msgid "Terminate Recording" 740 | msgstr "" 741 | 742 | #: src/sourceoutputwidget.cc:83 743 | msgid "Unknown input" 744 | msgstr "" 745 | 746 | #: src/sourceoutputwidget.cc:93 747 | msgid "pa_context_set_source_output_volume() failed" 748 | msgstr "" 749 | 750 | #: src/sourceoutputwidget.cc:108 751 | msgid "pa_context_set_source_output_mute() failed" 752 | msgstr "" 753 | 754 | #: src/sourceoutputwidget.cc:119 755 | msgid "pa_context_kill_source_output() failed" 756 | msgstr "" 757 | 758 | #: src/sourcewidget.cc:46 759 | msgid "pa_context_set_source_volume_by_index() failed" 760 | msgstr "" 761 | 762 | #: src/sourcewidget.cc:61 763 | msgid "pa_context_set_source_mute_by_index() failed" 764 | msgstr "" 765 | 766 | #: src/sourcewidget.cc:75 767 | msgid "pa_context_set_default_source() failed" 768 | msgstr "" 769 | 770 | #: src/sourcewidget.cc:97 771 | msgid "pa_context_set_source_port_by_index() failed" 772 | msgstr "" 773 | 774 | #: src/pavuapplication.cc:158 775 | msgid "Select a specific tab on load." 776 | msgstr "" 777 | 778 | #: src/pavuapplication.cc:159 779 | msgid "number" 780 | msgstr "" 781 | 782 | #: src/pavuapplication.cc:164 783 | msgid "Retry forever if pa quits (every 5 seconds)." 784 | msgstr "" 785 | 786 | #: src/pavuapplication.cc:169 787 | msgid "Maximize the window." 788 | msgstr "" 789 | 790 | #: src/pavuapplication.cc:174 791 | msgid "Show version." 792 | msgstr "" 793 | -------------------------------------------------------------------------------- /po/meson.build: -------------------------------------------------------------------------------- 1 | i18n = import('i18n') 2 | i18n.gettext(meson.project_name(), preset: 'glib') 3 | 4 | po_dir = meson.current_source_dir() 5 | -------------------------------------------------------------------------------- /po/pavucontrol.pot: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the pavucontrol package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: pavucontrol\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2024-05-24 17:04-0400\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: LANGUAGE \n" 15 | "Language: \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | 20 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:7 21 | #: src/org.pulseaudio.pavucontrol.desktop.in:5 src/mainwindow.ui:5 22 | msgid "Volume Control" 23 | msgstr "" 24 | 25 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:8 26 | msgid "Adjust device and app volumes" 27 | msgstr "" 28 | 29 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:9 30 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:11 31 | msgid "The PulseAudio Developers" 32 | msgstr "" 33 | 34 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:14 35 | msgid "" 36 | "PulseAudio Volume Control (pavucontrol) is a volume control tool (“mixer”) " 37 | "for the PulseAudio sound server. In contrast to classic mixer tools, this " 38 | "one allows you to control both the volume of hardware devices and of each " 39 | "playback stream separately." 40 | msgstr "" 41 | 42 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:21 43 | msgid "The “Playback” tab" 44 | msgstr "" 45 | 46 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:25 47 | msgid "The “Recording” tab" 48 | msgstr "" 49 | 50 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:29 51 | msgid "The “Output” tab" 52 | msgstr "" 53 | 54 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:33 55 | msgid "The “Input” tab" 56 | msgstr "" 57 | 58 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:37 59 | msgid "The “Configuration” tab" 60 | msgstr "" 61 | 62 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:45 63 | msgid "Migrate from Gtk 3 to 4." 64 | msgstr "" 65 | 66 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:46 67 | msgid "Embed UI resources in executable." 68 | msgstr "" 69 | 70 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:47 71 | msgid "Rename \"Set as fallback\" to \"Default\" for better legibility." 72 | msgstr "" 73 | 74 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:48 75 | msgid "Support 144 Hz monitors with level bars." 76 | msgstr "" 77 | 78 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:49 79 | msgid "" 80 | "App icons will fallback to generic rather than missing image, and this will " 81 | "be more common with Gtk 4." 82 | msgstr "" 83 | 84 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:51 85 | msgid "Lots of translation updates." 86 | msgstr "" 87 | 88 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:52 89 | msgid "Drop autotools build in favour of meson." 90 | msgstr "" 91 | 92 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:53 93 | msgid "Make libcanberra dependency optional." 94 | msgstr "" 95 | 96 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:60 97 | msgid "Support for switching Bluetooth codecs (new in PulseAudio 15.0)." 98 | msgstr "" 99 | 100 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:61 101 | msgid "" 102 | "Support for locking card profiles (new in PulseAudio 15.0). Locking a " 103 | "profile prevents PulseAudio from automatically switching away from that " 104 | "profile on plug/unplug events." 105 | msgstr "" 106 | 107 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:62 108 | msgid "" 109 | "New translations: Asturian, Basque, Belarusian, Galician, Hebrew, Kazakh, " 110 | "Norwegian Bokmål, Sinhala, Slovenian" 111 | msgstr "" 112 | 113 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:63 114 | msgid "" 115 | "Updated translations: Catalan, Chinese (Simplified), Chinese (Traditional), " 116 | "Croatian, Danish, Dutch, Finnish, French, German, Hungarian, Italian, " 117 | "Japanese, Korean, Lithuanian, Norwegian Nynorsk, Polish, Portugese, " 118 | "Portugese (Brazil), Slovak, Spanish, Swedish, Turkish, Ukrainian." 119 | msgstr "" 120 | 121 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:70 122 | msgid "" 123 | "There can now be only one pavucontrol window open at a time. Trying to start " 124 | "pavucontrol for a second time brings the first window to foreground." 125 | msgstr "" 126 | 127 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:71 128 | msgid "" 129 | "Added a \"Show volume meters\" checkbox to the Configuration tab. Disabling " 130 | "the volume meters reduces CPU use." 131 | msgstr "" 132 | 133 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:72 134 | msgid "Improve the use of space (remove useless margins and paddings)." 135 | msgstr "" 136 | 137 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:73 138 | msgid "Use a more appropriate icon for the channel lock button." 139 | msgstr "" 140 | 141 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:74 142 | msgid "" 143 | "Better channel label layout, prevents volume sliders from getting unaligned." 144 | msgstr "" 145 | 146 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:75 147 | msgid "" 148 | "Maximum latency offset increased from 2 to 5 seconds to accommodate AirPlay " 149 | "devices that often have higher latency than 2 seconds (this is not that " 150 | "useful on newer PulseAudio versions, though, because the latency is reported " 151 | "much more accurately than before)." 152 | msgstr "" 153 | 154 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:76 155 | msgid "New --version command line option." 156 | msgstr "" 157 | 158 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:77 159 | msgid "" 160 | "New translations: Chinese (Taiwan), Croatian, Korean, Norwegian Nynorsk, " 161 | "Lithuanian, Valencian." 162 | msgstr "" 163 | 164 | #: src/org.pulseaudio.pavucontrol.metainfo.xml.in:78 165 | msgid "" 166 | "Updated translations: Finnish, French, German, Italian, Japanese, Polish, " 167 | "Swedish." 168 | msgstr "" 169 | 170 | #: src/org.pulseaudio.pavucontrol.desktop.in:4 src/pavucontrol.cc:877 171 | msgid "PulseAudio Volume Control" 172 | msgstr "" 173 | 174 | #: src/org.pulseaudio.pavucontrol.desktop.in:6 175 | msgid "Adjust the volume level" 176 | msgstr "" 177 | 178 | #: src/org.pulseaudio.pavucontrol.desktop.in:12 179 | msgid "" 180 | "pavucontrol;Microphone;Volume;Fade;Balance;Headset;Speakers;Headphones;Audio;" 181 | "Mixer;Output;Input;Devices;Playback;Recording;System Sounds;Sound Card;" 182 | "Settings;Preferences;" 183 | msgstr "" 184 | 185 | #: src/mainwindow.ui:40 186 | msgid "No application is currently playing audio." 187 | msgstr "" 188 | 189 | #: src/mainwindow.ui:66 src/mainwindow.ui:157 190 | msgid "_Show:" 191 | msgstr "" 192 | 193 | #: src/mainwindow.ui:82 src/mainwindow.ui:173 194 | msgid "All Streams" 195 | msgstr "" 196 | 197 | #: src/mainwindow.ui:85 src/mainwindow.ui:176 198 | msgid "Applications" 199 | msgstr "" 200 | 201 | #: src/mainwindow.ui:88 src/mainwindow.ui:179 202 | msgid "Virtual Streams" 203 | msgstr "" 204 | 205 | #: src/mainwindow.ui:107 206 | msgid "_Playback" 207 | msgstr "" 208 | 209 | #: src/mainwindow.ui:131 210 | msgid "No application is currently recording audio." 211 | msgstr "" 212 | 213 | #: src/mainwindow.ui:198 214 | msgid "_Recording" 215 | msgstr "" 216 | 217 | #: src/mainwindow.ui:222 218 | msgid "No output devices available" 219 | msgstr "" 220 | 221 | #: src/mainwindow.ui:248 222 | msgid "S_how:" 223 | msgstr "" 224 | 225 | #: src/mainwindow.ui:265 226 | msgid "All Output Devices" 227 | msgstr "" 228 | 229 | #: src/mainwindow.ui:268 230 | msgid "Hardware Output Devices" 231 | msgstr "" 232 | 233 | #: src/mainwindow.ui:271 234 | msgid "Virtual Output Devices" 235 | msgstr "" 236 | 237 | #: src/mainwindow.ui:290 238 | msgid "_Output Devices" 239 | msgstr "" 240 | 241 | #: src/mainwindow.ui:314 242 | msgid "No input devices available" 243 | msgstr "" 244 | 245 | #: src/mainwindow.ui:340 246 | msgid "Sho_w:" 247 | msgstr "" 248 | 249 | #: src/mainwindow.ui:357 250 | msgid "All Input Devices" 251 | msgstr "" 252 | 253 | #: src/mainwindow.ui:360 254 | msgid "All Except Monitors" 255 | msgstr "" 256 | 257 | #: src/mainwindow.ui:363 258 | msgid "Hardware Input Devices" 259 | msgstr "" 260 | 261 | #: src/mainwindow.ui:366 262 | msgid "Virtual Input Devices" 263 | msgstr "" 264 | 265 | #: src/mainwindow.ui:369 266 | msgid "Monitors" 267 | msgstr "" 268 | 269 | #: src/mainwindow.ui:388 270 | msgid "_Input Devices" 271 | msgstr "" 272 | 273 | #: src/mainwindow.ui:416 274 | msgid "No cards available for configuration" 275 | msgstr "" 276 | 277 | #: src/mainwindow.ui:431 278 | msgid "Show volume meters" 279 | msgstr "" 280 | 281 | #: src/mainwindow.ui:448 282 | msgid "_Configuration" 283 | msgstr "" 284 | 285 | #: src/cardwidget.ui:30 286 | msgid "Card Name" 287 | msgstr "" 288 | 289 | #: src/cardwidget.ui:45 290 | msgid "Lock card to this profile" 291 | msgstr "" 292 | 293 | #: src/cardwidget.ui:66 294 | msgid "Profile:" 295 | msgstr "" 296 | 297 | #: src/cardwidget.ui:82 298 | msgid "Codec:" 299 | msgstr "" 300 | 301 | #: src/channelwidget.ui:9 302 | msgid "left-front" 303 | msgstr "" 304 | 305 | #: src/channelwidget.ui:33 306 | msgid "50%" 307 | msgstr "" 308 | 309 | #: src/renamedialog.ui:16 310 | msgid "Rename device to:" 311 | msgstr "" 312 | 313 | #: src/renamedialog.ui:37 314 | msgid "_Cancel" 315 | msgstr "" 316 | 317 | #: src/renamedialog.ui:45 318 | msgid "_Ok" 319 | msgstr "" 320 | 321 | #: src/streamwidget.ui:34 322 | msgid "Stream Title" 323 | msgstr "" 324 | 325 | #: src/streamwidget.ui:44 326 | msgid "direction" 327 | msgstr "" 328 | 329 | #: src/streamwidget.ui:64 src/devicewidget.ui:48 330 | msgid "Mute audio" 331 | msgstr "" 332 | 333 | #: src/streamwidget.ui:78 src/devicewidget.ui:62 334 | msgid "Lock channels together" 335 | msgstr "" 336 | 337 | #: src/devicewidget.ui:33 338 | msgid "Device Title" 339 | msgstr "" 340 | 341 | #: src/devicewidget.ui:77 342 | msgid "Set as default" 343 | msgstr "" 344 | 345 | #: src/devicewidget.ui:95 346 | msgid "Port:" 347 | msgstr "" 348 | 349 | #: src/devicewidget.ui:136 350 | msgid "PCM" 351 | msgstr "" 352 | 353 | #: src/devicewidget.ui:148 354 | msgid "AC-3" 355 | msgstr "" 356 | 357 | #: src/devicewidget.ui:158 358 | msgid "DTS" 359 | msgstr "" 360 | 361 | #: src/devicewidget.ui:168 362 | msgid "E-AC-3" 363 | msgstr "" 364 | 365 | #: src/devicewidget.ui:178 366 | msgid "MPEG" 367 | msgstr "" 368 | 369 | #: src/devicewidget.ui:188 370 | msgid "AAC" 371 | msgstr "" 372 | 373 | #: src/devicewidget.ui:198 374 | msgid "TrueHD" 375 | msgstr "" 376 | 377 | #: src/devicewidget.ui:208 378 | msgid "DTS-HD" 379 | msgstr "" 380 | 381 | #: src/devicewidget.ui:224 382 | msgid "Latency offset:" 383 | msgstr "" 384 | 385 | #: src/devicewidget.ui:237 386 | msgid "ms" 387 | msgstr "" 388 | 389 | #: src/devicewidget.ui:247 390 | msgid "Advanced" 391 | msgstr "" 392 | 393 | #: src/pavucontrol.cc:107 394 | #, c-format 395 | msgid "could not read JSON from list-codecs message response: %s" 396 | msgstr "" 397 | 398 | #: src/pavucontrol.cc:116 399 | msgid "list-codecs message response is not a JSON array" 400 | msgstr "" 401 | 402 | #: src/pavucontrol.cc:164 403 | msgid "list-codecs message response could not be parsed correctly" 404 | msgstr "" 405 | 406 | #: src/pavucontrol.cc:184 407 | #, c-format 408 | msgid "could not read JSON from get-codec message response: %s" 409 | msgstr "" 410 | 411 | #: src/pavucontrol.cc:193 412 | msgid "get-codec message response is not a JSON value" 413 | msgstr "" 414 | 415 | #: src/pavucontrol.cc:201 416 | msgid "could not get codec name from get-codec message response" 417 | msgstr "" 418 | 419 | #: src/pavucontrol.cc:223 420 | #, c-format 421 | msgid "could not read JSON from get-profile-sticky message response: %s" 422 | msgstr "" 423 | 424 | #: src/pavucontrol.cc:232 425 | msgid "get-profile-sticky message response is not a JSON value" 426 | msgstr "" 427 | 428 | #: src/pavucontrol.cc:252 src/cardwidget.cc:153 src/cardwidget.cc:181 429 | #, c-format 430 | msgid "pa_context_send_message_to_object() failed: %s" 431 | msgstr "" 432 | 433 | #: src/pavucontrol.cc:270 434 | #, c-format 435 | msgid "could not read JSON from list-handlers message response: %s" 436 | msgstr "" 437 | 438 | #: src/pavucontrol.cc:279 439 | msgid "list-handlers message response is not a JSON array" 440 | msgstr "" 441 | 442 | #: src/pavucontrol.cc:327 443 | msgid "list-handlers message response could not be parsed correctly" 444 | msgstr "" 445 | 446 | #: src/pavucontrol.cc:361 447 | msgid "Card callback failure" 448 | msgstr "" 449 | 450 | #: src/pavucontrol.cc:389 451 | msgid "Sink callback failure" 452 | msgstr "" 453 | 454 | #: src/pavucontrol.cc:413 455 | msgid "Source callback failure" 456 | msgstr "" 457 | 458 | #: src/pavucontrol.cc:432 459 | msgid "Sink input callback failure" 460 | msgstr "" 461 | 462 | #: src/pavucontrol.cc:451 463 | msgid "Source output callback failure" 464 | msgstr "" 465 | 466 | #: src/pavucontrol.cc:481 467 | msgid "Client callback failure" 468 | msgstr "" 469 | 470 | #: src/pavucontrol.cc:497 471 | msgid "Server info callback failure" 472 | msgstr "" 473 | 474 | #: src/pavucontrol.cc:515 src/pavucontrol.cc:812 475 | #, c-format 476 | msgid "Failed to initialize stream_restore extension: %s" 477 | msgstr "" 478 | 479 | #: src/pavucontrol.cc:533 480 | msgid "pa_ext_stream_restore_read() failed" 481 | msgstr "" 482 | 483 | #: src/pavucontrol.cc:551 src/pavucontrol.cc:826 484 | #, c-format 485 | msgid "Failed to initialize device restore extension: %s" 486 | msgstr "" 487 | 488 | #: src/pavucontrol.cc:572 489 | msgid "pa_ext_device_restore_read_sink_formats() failed" 490 | msgstr "" 491 | 492 | #: src/pavucontrol.cc:590 src/pavucontrol.cc:839 493 | #, c-format 494 | msgid "Failed to initialize device manager extension: %s" 495 | msgstr "" 496 | 497 | #: src/pavucontrol.cc:609 498 | msgid "pa_ext_device_manager_read() failed" 499 | msgstr "" 500 | 501 | #: src/pavucontrol.cc:626 502 | msgid "pa_context_get_sink_info_by_index() failed" 503 | msgstr "" 504 | 505 | #: src/pavucontrol.cc:639 506 | msgid "pa_context_get_source_info_by_index() failed" 507 | msgstr "" 508 | 509 | #: src/pavucontrol.cc:652 src/pavucontrol.cc:665 510 | msgid "pa_context_get_sink_input_info() failed" 511 | msgstr "" 512 | 513 | #: src/pavucontrol.cc:678 514 | msgid "pa_context_get_client_info() failed" 515 | msgstr "" 516 | 517 | #: src/pavucontrol.cc:688 src/pavucontrol.cc:753 518 | msgid "pa_context_get_server_info() failed" 519 | msgstr "" 520 | 521 | #: src/pavucontrol.cc:701 522 | msgid "pa_context_get_card_info_by_index() failed" 523 | msgstr "" 524 | 525 | #: src/pavucontrol.cc:744 526 | msgid "pa_context_subscribe() failed" 527 | msgstr "" 528 | 529 | #: src/pavucontrol.cc:760 530 | msgid "pa_context_client_info_list() failed" 531 | msgstr "" 532 | 533 | #: src/pavucontrol.cc:767 534 | msgid "pa_context_get_card_info_list() failed" 535 | msgstr "" 536 | 537 | #: src/pavucontrol.cc:774 538 | msgid "pa_context_get_sink_info_list() failed" 539 | msgstr "" 540 | 541 | #: src/pavucontrol.cc:781 542 | msgid "pa_context_get_source_info_list() failed" 543 | msgstr "" 544 | 545 | #: src/pavucontrol.cc:788 546 | msgid "pa_context_get_sink_input_info_list() failed" 547 | msgstr "" 548 | 549 | #: src/pavucontrol.cc:795 550 | msgid "pa_context_get_source_output_info_list() failed" 551 | msgstr "" 552 | 553 | #: src/pavucontrol.cc:854 src/pavucontrol.cc:905 554 | msgid "Connection failed, attempting reconnect" 555 | msgstr "" 556 | 557 | #: src/pavucontrol.cc:892 558 | msgid "" 559 | "Connection to PulseAudio failed. Automatic retry in 5s\n" 560 | "\n" 561 | "In this case this is likely because PULSE_SERVER in the Environment/X11 Root " 562 | "Window Properties\n" 563 | "or default-server in client.conf is misconfigured.\n" 564 | "This situation can also arise when PulseAudio crashed and left stale details " 565 | "in the X11 Root Window.\n" 566 | "If this is the case, then PulseAudio should autospawn again, or if this is " 567 | "not configured you should\n" 568 | "run start-pulseaudio-x11 manually." 569 | msgstr "" 570 | 571 | #: src/cardwidget.cc:126 572 | msgid "pa_context_set_card_profile_by_index() failed" 573 | msgstr "" 574 | 575 | #: src/channelwidget.cc:99 576 | #, c-format 577 | msgid "%0.0f%% (%0.2f dB)" 578 | msgstr "" 579 | 580 | #: src/channelwidget.cc:101 581 | #, c-format 582 | msgid "%0.0f%% (-∞ dB)" 583 | msgstr "" 584 | 585 | #: src/channelwidget.cc:104 586 | #, c-format 587 | msgid "%0.0f%%" 588 | msgstr "" 589 | 590 | #: src/channelwidget.cc:139 591 | msgid "Silence" 592 | msgstr "" 593 | 594 | #: src/channelwidget.cc:139 595 | msgid "Min" 596 | msgstr "" 597 | 598 | #: src/channelwidget.cc:141 599 | msgid "100% (0 dB)" 600 | msgstr "" 601 | 602 | #: src/channelwidget.cc:145 603 | msgid "Base" 604 | msgstr "" 605 | 606 | #: src/devicewidget.cc:75 607 | msgid "Rename Device..." 608 | msgstr "" 609 | 610 | #: src/devicewidget.cc:179 611 | msgid "pa_context_set_port_latency_offset() failed" 612 | msgstr "" 613 | 614 | #: src/devicewidget.cc:257 615 | msgid "Sorry, but device renaming is not supported." 616 | msgstr "" 617 | 618 | #: src/devicewidget.cc:259 619 | msgid "" 620 | "You need to load module-device-manager in the PulseAudio server in order to " 621 | "rename devices" 622 | msgstr "" 623 | 624 | #: src/devicewidget.cc:294 625 | msgid "pa_ext_device_manager_write() failed" 626 | msgstr "" 627 | 628 | #: src/mainwindow.cc:171 629 | #, c-format 630 | msgid "Error reading config file %s: %s" 631 | msgstr "" 632 | 633 | #: src/mainwindow.cc:245 634 | msgid "Error saving preferences" 635 | msgstr "" 636 | 637 | #: src/mainwindow.cc:253 638 | #, c-format 639 | msgid "Error writing config file %s" 640 | msgstr "" 641 | 642 | #: src/mainwindow.cc:314 643 | msgid " (plugged in)" 644 | msgstr "" 645 | 646 | #: src/mainwindow.cc:318 src/mainwindow.cc:426 647 | msgid " (unavailable)" 648 | msgstr "" 649 | 650 | #: src/mainwindow.cc:320 src/mainwindow.cc:423 651 | msgid " (unplugged)" 652 | msgstr "" 653 | 654 | #: src/mainwindow.cc:625 655 | msgid "Failed to read data from stream" 656 | msgstr "" 657 | 658 | #: src/mainwindow.cc:669 659 | msgid "Peak detect" 660 | msgstr "" 661 | 662 | #: src/mainwindow.cc:670 663 | msgid "Failed to create monitoring stream" 664 | msgstr "" 665 | 666 | #: src/mainwindow.cc:685 667 | msgid "Failed to connect monitoring stream" 668 | msgstr "" 669 | 670 | #: src/mainwindow.cc:819 671 | msgid "" 672 | "Ignoring sink-input due to it being designated as an event and thus handled " 673 | "by the Event widget" 674 | msgstr "" 675 | 676 | #: src/mainwindow.cc:994 677 | msgid "System Sounds" 678 | msgstr "" 679 | 680 | #: src/mainwindow.cc:1340 681 | msgid "Establishing connection to PulseAudio. Please wait..." 682 | msgstr "" 683 | 684 | #: src/rolewidget.cc:68 685 | msgid "pa_ext_stream_restore_write() failed" 686 | msgstr "" 687 | 688 | #: src/sinkinputwidget.cc:35 689 | msgid "on" 690 | msgstr "" 691 | 692 | #: src/sinkinputwidget.cc:38 693 | msgid "Terminate Playback" 694 | msgstr "" 695 | 696 | #: src/sinkinputwidget.cc:78 697 | msgid "Unknown output" 698 | msgstr "" 699 | 700 | #: src/sinkinputwidget.cc:87 701 | msgid "pa_context_set_sink_input_volume() failed" 702 | msgstr "" 703 | 704 | #: src/sinkinputwidget.cc:102 705 | msgid "pa_context_set_sink_input_mute() failed" 706 | msgstr "" 707 | 708 | #: src/sinkinputwidget.cc:112 709 | msgid "pa_context_kill_sink_input() failed" 710 | msgstr "" 711 | 712 | #: src/sinkwidget.cc:119 713 | msgid "pa_context_set_sink_volume_by_index() failed" 714 | msgstr "" 715 | 716 | #: src/sinkwidget.cc:137 717 | msgid "Volume Control Feedback Sound" 718 | msgstr "" 719 | 720 | #: src/sinkwidget.cc:155 721 | msgid "pa_context_set_sink_mute_by_index() failed" 722 | msgstr "" 723 | 724 | #: src/sinkwidget.cc:169 725 | msgid "pa_context_set_default_sink() failed" 726 | msgstr "" 727 | 728 | #: src/sinkwidget.cc:189 729 | msgid "pa_context_set_sink_port_by_index() failed" 730 | msgstr "" 731 | 732 | #: src/sinkwidget.cc:231 733 | msgid "pa_ext_device_restore_save_sink_formats() failed" 734 | msgstr "" 735 | 736 | #: src/sourceoutputwidget.cc:35 737 | msgid "from" 738 | msgstr "" 739 | 740 | #: src/sourceoutputwidget.cc:38 741 | msgid "Terminate Recording" 742 | msgstr "" 743 | 744 | #: src/sourceoutputwidget.cc:83 745 | msgid "Unknown input" 746 | msgstr "" 747 | 748 | #: src/sourceoutputwidget.cc:93 749 | msgid "pa_context_set_source_output_volume() failed" 750 | msgstr "" 751 | 752 | #: src/sourceoutputwidget.cc:108 753 | msgid "pa_context_set_source_output_mute() failed" 754 | msgstr "" 755 | 756 | #: src/sourceoutputwidget.cc:119 757 | msgid "pa_context_kill_source_output() failed" 758 | msgstr "" 759 | 760 | #: src/sourcewidget.cc:46 761 | msgid "pa_context_set_source_volume_by_index() failed" 762 | msgstr "" 763 | 764 | #: src/sourcewidget.cc:61 765 | msgid "pa_context_set_source_mute_by_index() failed" 766 | msgstr "" 767 | 768 | #: src/sourcewidget.cc:75 769 | msgid "pa_context_set_default_source() failed" 770 | msgstr "" 771 | 772 | #: src/sourcewidget.cc:97 773 | msgid "pa_context_set_source_port_by_index() failed" 774 | msgstr "" 775 | 776 | #: src/pavuapplication.cc:158 777 | msgid "Select a specific tab on load." 778 | msgstr "" 779 | 780 | #: src/pavuapplication.cc:159 781 | msgid "number" 782 | msgstr "" 783 | 784 | #: src/pavuapplication.cc:164 785 | msgid "Retry forever if pa quits (every 5 seconds)." 786 | msgstr "" 787 | 788 | #: src/pavuapplication.cc:169 789 | msgid "Maximize the window." 790 | msgstr "" 791 | 792 | #: src/pavuapplication.cc:174 793 | msgid "Show version." 794 | msgstr "" 795 | -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | pavucontrol 2 | pavucontrol.desktop 3 | -------------------------------------------------------------------------------- /src/cardwidget.cc: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifdef HAVE_CONFIG_H 22 | #include 23 | #endif 24 | 25 | #include "cardwidget.h" 26 | 27 | #include "i18n.h" 28 | 29 | /*** CardWidget ***/ 30 | CardWidget::CardWidget(BaseObjectType* cobject, const Glib::RefPtr& x) : 31 | Gtk::Box(cobject) { 32 | 33 | nameLabel = x->get_widget("cardNameLabel"); 34 | profileList = x->get_widget("profileList"); 35 | iconImage = x->get_widget("cardIconImage"); 36 | codecBox = x->get_widget("codecBox"); 37 | codecList = x->get_widget("codecList"); 38 | profileLockToggleButton = x->get_widget("profileLockToggleButton"); 39 | 40 | profileListStore = Gtk::ListStore::create(profileModel); 41 | profileList->set_model(profileListStore); 42 | profileList->pack_start(profileModel.desc); 43 | 44 | profileList->signal_changed().connect( sigc::mem_fun(*this, &CardWidget::onProfileChange)); 45 | 46 | codecBox->hide(); 47 | 48 | codecListStore = Gtk::ListStore::create(codecModel); 49 | codecList->set_model(codecListStore); 50 | codecList->pack_start(codecModel.desc); 51 | 52 | codecList->signal_changed().connect( sigc::mem_fun(*this, &CardWidget::onCodecChange)); 53 | 54 | hasProfileLock = false; 55 | hideUnavailableProfiles = false; 56 | 57 | profileLockToggleButton->signal_clicked().connect(sigc::mem_fun(*this, &CardWidget::onProfileLockToggleButton)); 58 | profileLockToggleButton->set_sensitive(true); 59 | profileLockToggleButton->set_visible(hasProfileLock); 60 | } 61 | 62 | CardWidget* CardWidget::create() { 63 | CardWidget* w; 64 | Glib::RefPtr x = Gtk::Builder::create_from_resource("/org/pulseaudio/pavucontrol/ui/cardwidget.ui", "cardWidget"); 65 | w = Gtk::Builder::get_widget_derived(x, "cardWidget"); 66 | w->reference(); 67 | return w; 68 | } 69 | 70 | 71 | void CardWidget::prepareMenu() { 72 | int active_idx; 73 | 74 | profileListStore->clear(); 75 | active_idx = -1; 76 | 77 | /* Fill the ComboBox's Tree Model */ 78 | for (uint32_t i = 0, idx = 0; i < profiles.size(); ++i) { 79 | if (hideUnavailableProfiles && !availableProfiles[profiles[i].first]) 80 | continue; 81 | 82 | Gtk::TreeModel::Row row = *(profileListStore->append()); 83 | row[profileModel.name] = profiles[i].first; 84 | row[profileModel.desc] = profiles[i].second; 85 | 86 | if (profiles[i].first == activeProfile) 87 | active_idx = idx; 88 | 89 | /* Track the index in the list store, as we might have few entries than 90 | * all the profiles if unavailable profiles are hidden. */ 91 | idx++; 92 | } 93 | 94 | if (profiles.size()) 95 | profileList->set_active(active_idx); 96 | 97 | codecListStore->clear(); 98 | active_idx = -1; 99 | /* Fill the ComboBox's Tree Model */ 100 | for (uint32_t i = 0; i < codecs.size(); ++i) { 101 | Gtk::TreeModel::Row row = *(codecListStore->append()); 102 | row[codecModel.name] = codecs[i].first; 103 | row[codecModel.desc] = codecs[i].second; 104 | if (codecs[i].first == activeCodec) 105 | active_idx = i; 106 | } 107 | 108 | if (active_idx >= 0) 109 | codecList->set_active(active_idx); 110 | 111 | /* unhide codec box */ 112 | if (codecs.size()) 113 | codecBox->show(); 114 | else 115 | codecBox->hide(); 116 | 117 | profileLockToggleButton->set_visible(hasProfileLock); 118 | } 119 | 120 | void CardWidget::onProfileChange() { 121 | Gtk::TreeModel::iterator iter; 122 | 123 | if (updating) 124 | return; 125 | 126 | iter = profileList->get_active(); 127 | if (iter) 128 | { 129 | Gtk::TreeModel::Row row = *iter; 130 | if (row) 131 | { 132 | pa_operation* o; 133 | Glib::ustring profile = row[profileModel.name]; 134 | 135 | if (!(o = pa_context_set_card_profile_by_index(get_context(), index, profile.c_str(), NULL, NULL))) { 136 | show_error(this, _("pa_context_set_card_profile_by_index() failed")); 137 | return; 138 | } 139 | 140 | pa_operation_unref(o); 141 | } 142 | } 143 | } 144 | 145 | void CardWidget::onCodecChange() { 146 | if (updating) 147 | return; 148 | 149 | #ifdef HAVE_PULSE_MESSAGING_API 150 | Gtk::TreeModel::iterator iter = codecList->get_active(); 151 | if (iter) 152 | { 153 | Gtk::TreeModel::Row row = *iter; 154 | if (row) 155 | { 156 | pa_operation* o; 157 | Glib::ustring codec_id = row[codecModel.name]; 158 | 159 | std::string codec_message = "\"" + std::string(codec_id) + "\""; 160 | 161 | if (!(o = pa_context_send_message_to_object(get_context(), card_bluez_message_handler_path(pulse_card_name).c_str(), 162 | "switch-codec", codec_message.c_str(), NULL, NULL))) { 163 | g_debug(_("pa_context_send_message_to_object() failed: %s"), pa_strerror(pa_context_errno(get_context()))); 164 | return; 165 | } 166 | 167 | pa_operation_unref(o); 168 | } 169 | } 170 | #endif 171 | } 172 | 173 | void CardWidget::onProfileLockToggleButton() { 174 | if (updating) 175 | return; 176 | 177 | #ifdef HAVE_PULSE_MESSAGING_API 178 | Gtk::TreeModel::iterator iter = profileList->get_active(); 179 | if (iter) 180 | { 181 | Gtk::TreeModel::Row row = *iter; 182 | if (row) 183 | { 184 | pa_operation* o; 185 | Glib::ustring profile = row[profileModel.name]; 186 | 187 | bool profileIsLocked = profileLockToggleButton->get_active(); 188 | 189 | if (!(o = pa_context_send_message_to_object(get_context(), card_message_handler_path(pulse_card_name).c_str(), 190 | "set-profile-sticky", profileIsLocked ? "true" : "false", NULL, NULL))) { 191 | g_debug(_("pa_context_send_message_to_object() failed: %s"), pa_strerror(pa_context_errno(get_context()))); 192 | return; 193 | } 194 | 195 | pa_operation_unref(o); 196 | } 197 | } 198 | #endif 199 | } 200 | -------------------------------------------------------------------------------- /src/cardwidget.h: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifndef cardwidget_h 22 | #define cardwidget_h 23 | 24 | #include "pavucontrol.h" 25 | 26 | class PortInfo { 27 | public: 28 | Glib::ustring name; 29 | Glib::ustring description; 30 | uint32_t priority; 31 | int available; 32 | int direction; 33 | int64_t latency_offset; 34 | std::vector profiles; 35 | }; 36 | 37 | class CardWidget : public Gtk::Box { 38 | public: 39 | CardWidget(BaseObjectType* cobject, const Glib::RefPtr& x); 40 | static CardWidget* create(); 41 | 42 | Gtk::Label *nameLabel; 43 | Gtk::Image *iconImage; 44 | Glib::ustring name; 45 | std::string pulse_card_name; 46 | Gtk::Box *codecBox; 47 | Gtk::ToggleButton *profileLockToggleButton; 48 | uint32_t index; 49 | bool updating; 50 | 51 | // each entry in profiles is a pair of profile name and profile description 52 | std::vector> profiles; 53 | std::map availableProfiles; 54 | std::map ports; 55 | Glib::ustring activeProfile; 56 | bool hasSinks; 57 | bool hasSources; 58 | 59 | // each entry in codecs is a pair of codec name and codec description 60 | std::vector> codecs; 61 | Glib::ustring activeCodec; 62 | 63 | bool hasProfileLock; 64 | bool hideUnavailableProfiles; 65 | 66 | void prepareMenu(); 67 | 68 | protected: 69 | virtual void onProfileChange(); 70 | virtual void onCodecChange(); 71 | virtual void onProfileLockToggleButton(); 72 | 73 | /* Tree model columns */ 74 | class ModelColumns : public Gtk::TreeModel::ColumnRecord 75 | { 76 | public: 77 | 78 | ModelColumns() 79 | { add(name); add(desc); } 80 | 81 | Gtk::TreeModelColumn name; 82 | Gtk::TreeModelColumn desc; 83 | }; 84 | 85 | ModelColumns profileModel; 86 | 87 | Gtk::ComboBox *profileList; 88 | Glib::RefPtr profileListStore; 89 | 90 | ModelColumns codecModel; 91 | 92 | Gtk::ComboBox *codecList; 93 | Glib::RefPtr codecListStore; 94 | }; 95 | 96 | #endif 97 | -------------------------------------------------------------------------------- /src/cardwidget.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | vertical 6 | 7 | 8 | center 9 | vertical 10 | 6 11 | 12 12 | 12 13 | 12 14 | 12 15 | 16 | 17 | center 18 | 6 19 | 20 | 21 | gtk-missing-image 22 | 23 | 24 | 25 | 26 | 1 27 | 28 | 29 | 1 30 | Card Name 31 | 1 32 | middle 33 | 0 34 | 35 | 36 | 37 | 38 | 39 | 40 | 3 41 | 42 | 43 | center 44 | 1 45 | Lock card to this profile 46 | 0 47 | 1 48 | 49 | 50 | changes-prevent 51 | normal 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | center 63 | 12 64 | 65 | 66 | fill 67 | 6 68 | 69 | 70 | <b>Profile:</b> 71 | 1 72 | 0 73 | 74 | 75 | 76 | 77 | 1 78 | 79 | 80 | 81 | 82 | 83 | 84 | fill 85 | 6 86 | 87 | 88 | <b>Codec:</b> 89 | 1 90 | 0 91 | 92 | 93 | 94 | 95 | 1 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | center 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /src/channelwidget.cc: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifdef HAVE_CONFIG_H 22 | #include 23 | #endif 24 | 25 | #include "channelwidget.h" 26 | #include "minimalstreamwidget.h" 27 | 28 | #include "i18n.h" 29 | 30 | /*** ChannelWidget ***/ 31 | 32 | ChannelWidget::ChannelWidget(BaseObjectType* cobject, const Glib::RefPtr& x) : 33 | Gtk::Widget(cobject), 34 | can_decibel(false), 35 | volumeScaleEnabled(true), 36 | last(false) { 37 | 38 | channelLabel = x->get_widget("channelLabel"); 39 | volumeLabel = x->get_widget("volumeLabel"); 40 | volumeScale = x->get_widget("volumeScale"); 41 | 42 | volumeScale->set_range((double)PA_VOLUME_MUTED, (double)PA_VOLUME_UI_MAX); 43 | volumeScale->set_value((double)PA_VOLUME_NORM); 44 | volumeScale->set_increments(((double)PA_VOLUME_NORM)/100.0, ((double)PA_VOLUME_NORM)/20.0); 45 | setBaseVolume(PA_VOLUME_NORM); 46 | 47 | volumeScale->signal_value_changed().connect(sigc::mem_fun(*this, &ChannelWidget::onVolumeScaleValueChanged)); 48 | } 49 | 50 | ChannelWidget* ChannelWidget::createOne(MinimalStreamWidget *owner, int channelIndex, pa_channel_position channelPosition, bool can_decibel) { 51 | ChannelWidget* w; 52 | Glib::RefPtr x = Gtk::Builder::create_from_resource("/org/pulseaudio/pavucontrol/ui/channelwidget.ui", "channelWidget"); 53 | w = Gtk::Builder::get_widget_derived(x, "channelWidget"); 54 | w->reference(); 55 | 56 | w->channel = channelIndex; 57 | w->can_decibel = can_decibel; 58 | w->minimalStreamWidget = owner; 59 | 60 | char text[64]; 61 | snprintf(text, sizeof(text), "%s", pa_channel_position_to_pretty_string(channelPosition)); 62 | w->channelLabel->set_markup(text); 63 | 64 | return w; 65 | } 66 | 67 | void ChannelWidget::create(MinimalStreamWidget *owner, const pa_channel_map &m, bool can_decibel, ChannelWidget *widgets[PA_CHANNELS_MAX]) { 68 | int maxLabelWidth = 0; 69 | 70 | for (int i = 0; i < m.channels; i++) { 71 | widgets[i] = ChannelWidget::createOne(owner, i, m.map[i], can_decibel); 72 | 73 | Gtk::Requisition minimumSize; 74 | Gtk::Requisition naturalSize; 75 | widgets[i]->channelLabel->get_preferred_size(minimumSize, naturalSize); 76 | if (naturalSize.get_width() > maxLabelWidth) 77 | maxLabelWidth = naturalSize.get_width(); 78 | } 79 | 80 | widgets[m.channels - 1]->last = true; 81 | 82 | /* The channel labels have different widths by default, which makes the 83 | * volume slider widths different too. The volume sliders must have the 84 | * same width, otherwise it's very hard to see how the volumes of different 85 | * channels relate to each other, so we have to change all channel labels 86 | * to have the same width. */ 87 | for (int i = 0; i < m.channels; i++) 88 | widgets[i]->channelLabel->set_size_request(maxLabelWidth, -1); 89 | } 90 | 91 | void ChannelWidget::setVolume(pa_volume_t volume) { 92 | double v; 93 | char txt[64]; 94 | 95 | v = ((gdouble) volume * 100) / PA_VOLUME_NORM; 96 | if (can_decibel) { 97 | double dB = pa_sw_volume_to_dB(volume); 98 | if (dB > PA_DECIBEL_MININFTY) 99 | snprintf(txt, sizeof(txt), _("%0.0f%% (%0.2f dB)"), v, dB); 100 | else 101 | snprintf(txt, sizeof(txt), _("%0.0f%% (-∞ dB)"), v); 102 | } 103 | else 104 | snprintf(txt, sizeof(txt), _("%0.0f%%"), v); 105 | volumeLabel->set_markup(txt); 106 | 107 | volumeScaleEnabled = false; 108 | volumeScale->set_value(volume > PA_VOLUME_UI_MAX ? PA_VOLUME_UI_MAX : volume); 109 | currentVolume = volumeScale->get_value(); 110 | volumeScaleEnabled = true; 111 | } 112 | 113 | void ChannelWidget::onVolumeScaleValueChanged() { 114 | 115 | if (!volumeScaleEnabled) 116 | return; 117 | 118 | if (minimalStreamWidget->updating) 119 | return; 120 | 121 | pa_volume_t volume = (pa_volume_t) volumeScale->get_value(); 122 | if (volume != currentVolume) 123 | minimalStreamWidget->updateChannelVolume(channel, volume); 124 | } 125 | 126 | void ChannelWidget::set_sensitive(bool enabled) { 127 | Gtk::Widget::set_sensitive(enabled); 128 | 129 | channelLabel->set_sensitive(enabled); 130 | volumeLabel->set_sensitive(enabled); 131 | volumeScale->set_sensitive(enabled); 132 | } 133 | 134 | void ChannelWidget::setBaseVolume(pa_volume_t v) { 135 | 136 | gtk_scale_clear_marks(GTK_SCALE(volumeScale->gobj())); 137 | 138 | gtk_scale_add_mark(GTK_SCALE(volumeScale->gobj()), (double)PA_VOLUME_MUTED, (GtkPositionType) GTK_POS_BOTTOM, 139 | last ? (can_decibel ? _("Silence") : _("Min")) : NULL); 140 | gtk_scale_add_mark(GTK_SCALE(volumeScale->gobj()), (double)PA_VOLUME_NORM, (GtkPositionType) GTK_POS_BOTTOM, 141 | last ? _("100% (0 dB)") : NULL); 142 | 143 | if (v > PA_VOLUME_MUTED && v < PA_VOLUME_NORM) { 144 | gtk_scale_add_mark(GTK_SCALE(volumeScale->gobj()), (double)v, (GtkPositionType) GTK_POS_BOTTOM, 145 | last ? _("Base") : NULL); 146 | } 147 | 148 | } 149 | -------------------------------------------------------------------------------- /src/channelwidget.h: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifndef channelwidget_h 22 | #define channelwidget_h 23 | 24 | #include "pavucontrol.h" 25 | 26 | class MinimalStreamWidget; 27 | 28 | class ChannelWidget : public Gtk::Widget { 29 | public: 30 | ChannelWidget(BaseObjectType* cobject, const Glib::RefPtr& x); 31 | 32 | /* This creates multiple ChannelWidgets based on the given channel map. The 33 | * widgets are stored in the caller-provided array. */ 34 | static void create(MinimalStreamWidget *owner, const pa_channel_map &m, bool can_decibel, 35 | ChannelWidget *widgets[PA_CHANNELS_MAX]); 36 | 37 | void setVolume(pa_volume_t volume); 38 | 39 | Gtk::Label *channelLabel; 40 | Gtk::Label *volumeLabel; 41 | Gtk::Scale *volumeScale; 42 | 43 | int channel; 44 | MinimalStreamWidget *minimalStreamWidget; 45 | 46 | void onVolumeScaleValueChanged(); 47 | 48 | bool can_decibel; 49 | bool volumeScaleEnabled; 50 | bool last; 51 | 52 | virtual void set_sensitive(bool enabled); 53 | virtual void setBaseVolume(pa_volume_t); 54 | 55 | private: 56 | pa_volume_t currentVolume; 57 | static ChannelWidget *createOne(MinimalStreamWidget *owner, int channelIndex, pa_channel_position channelPosition, 58 | bool can_decibel); 59 | }; 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /src/channelwidget.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 6 | 7 | 8 | center 9 | <b>left-front</b> 10 | 1 11 | 1 12 | 0 13 | 14 | 15 | 16 | 17 | 1 18 | 1 19 | 20 | 21 | 100 22 | 5 23 | 24 | 25 | 0 26 | 27 | 28 | 29 | 30 | center 31 | 8 32 | 8 33 | <small>50%</small> 34 | 1 35 | right 36 | 12 37 | 1 38 | 0 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/devicewidget.cc: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifdef HAVE_CONFIG_H 22 | #include 23 | #endif 24 | 25 | #include 26 | 27 | #include "mainwindow.h" 28 | #include "devicewidget.h" 29 | #include "channelwidget.h" 30 | 31 | #include "i18n.h" 32 | 33 | /*** DeviceWidget ***/ 34 | DeviceWidget::DeviceWidget(BaseObjectType* cobject, const Glib::RefPtr& x) : 35 | MinimalStreamWidget(cobject), 36 | offsetButtonEnabled(false), 37 | mDigital(false) { 38 | 39 | /* MinimalStreamWidget member variables. */ 40 | channelsVBox = x->get_widget("deviceChannelsVBox"); 41 | nameLabel = x->get_widget("deviceNameLabel"); 42 | boldNameLabel = x->get_widget("deviceBoldNameLabel"); 43 | iconImage= x->get_widget("deviceIconImage"); 44 | 45 | lockToggleButton = x->get_widget("deviceLockToggleButton"); 46 | muteToggleButton = x->get_widget("deviceMuteToggleButton"); 47 | defaultToggleButton= x->get_widget("defaultToggleButton"); 48 | portSelect = x->get_widget("portSelect"); 49 | portList = x->get_widget("portList"); 50 | advancedOptions = x->get_widget("advancedOptions"); 51 | offsetSelect = x->get_widget("offsetSelect"); 52 | offsetButton = x->get_widget("offsetButton"); 53 | 54 | muteToggleButton->signal_clicked().connect(sigc::mem_fun(*this, &DeviceWidget::onMuteToggleButton)); 55 | lockToggleButton->signal_clicked().connect(sigc::mem_fun(*this, &DeviceWidget::onLockToggleButton)); 56 | defaultToggleButton->signal_clicked().connect(sigc::mem_fun(*this, &DeviceWidget::onDefaultToggleButton)); 57 | 58 | auto gesture = Gtk::GestureClick::create(); 59 | gesture->set_button(3); 60 | gesture->set_exclusive(true); 61 | gesture->signal_pressed().connect(sigc::mem_fun(*this, &DeviceWidget::onContextTriggerEvent)); 62 | this->add_controller(gesture); 63 | 64 | const std::string actionName = "rename", groupName="devicewidget"; 65 | auto action = Gio::SimpleAction::create(actionName); 66 | action->set_enabled(true); 67 | action->signal_activate().connect(sigc::mem_fun(*this, &DeviceWidget::openRenamePopup)); 68 | 69 | auto group = Gio::SimpleActionGroup::create(); 70 | group->add_action(action); 71 | 72 | insert_action_group(groupName, group); 73 | 74 | auto menuModel = Gio::Menu::create(); 75 | menuModel->append(_("Rename Device..."), groupName + "." + actionName); 76 | contextMenu.set_menu_model(menuModel); 77 | contextMenu.set_parent(*this); 78 | 79 | 80 | treeModel = Gtk::ListStore::create(portModel); 81 | portList->set_model(treeModel); 82 | portList->pack_start(portModel.desc); 83 | 84 | portList->signal_changed().connect(sigc::mem_fun(*this, &DeviceWidget::onPortChange)); 85 | offsetButton->signal_value_changed().connect(sigc::mem_fun(*this, &DeviceWidget::onOffsetChange)); 86 | 87 | for (unsigned i = 0; i < PA_CHANNELS_MAX; i++) 88 | channelWidgets[i] = NULL; 89 | 90 | offsetAdjustment = Gtk::Adjustment::create(0.0, -2000.0, 5000.0, 10.0, 50.0, 0.0); 91 | offsetButton->configure(offsetAdjustment, 0, 2); 92 | } 93 | 94 | void DeviceWidget::init(MainWindow* mainWindow, Glib::ustring deviceType) { 95 | mpMainWindow = mainWindow; 96 | mDeviceType = deviceType; 97 | 98 | MinimalStreamWidget::init(); 99 | } 100 | 101 | void DeviceWidget::setChannelMap(const pa_channel_map &m, bool can_decibel) { 102 | channelMap = m; 103 | ChannelWidget::create(this, m, can_decibel, channelWidgets); 104 | 105 | for (int i = 0; i < m.channels; i++) { 106 | ChannelWidget *cw = channelWidgets[i]; 107 | channelsVBox->prepend(*cw); 108 | cw->unreference(); 109 | } 110 | 111 | lockToggleButton->set_sensitive(m.channels > 1); 112 | hideLockedChannels(lockToggleButton->get_active()); 113 | } 114 | 115 | void DeviceWidget::setVolume(const pa_cvolume &v, bool force) { 116 | g_assert(v.channels == channelMap.channels); 117 | 118 | volume = v; 119 | 120 | if (timeoutConnection.empty() || force) { /* do not update the volume when a volume change is still in flux */ 121 | for (int i = 0; i < volume.channels; i++) 122 | channelWidgets[i]->setVolume(volume.values[i]); 123 | } 124 | } 125 | 126 | void DeviceWidget::updateChannelVolume(int channel, pa_volume_t v) { 127 | pa_cvolume n; 128 | g_assert(channel < volume.channels); 129 | 130 | n = volume; 131 | if (lockToggleButton->get_active()) 132 | pa_cvolume_set(&n, n.channels, v); 133 | else 134 | n.values[channel] = v; 135 | 136 | setVolume(n, true); 137 | 138 | if (timeoutConnection.empty()) 139 | timeoutConnection = Glib::signal_timeout().connect(sigc::mem_fun(*this, &DeviceWidget::timeoutEvent), 100); 140 | } 141 | 142 | void DeviceWidget::hideLockedChannels(bool hide) { 143 | for (int i = 0; i < channelMap.channels - 1; i++) 144 | channelWidgets[i]->set_visible(!hide); 145 | 146 | channelWidgets[channelMap.channels - 1]->channelLabel->set_visible(!hide); 147 | } 148 | 149 | void DeviceWidget::onMuteToggleButton() { 150 | 151 | lockToggleButton->set_sensitive(!muteToggleButton->get_active()); 152 | 153 | for (int i = 0; i < channelMap.channels; i++) 154 | channelWidgets[i]->set_sensitive(!muteToggleButton->get_active()); 155 | } 156 | 157 | void DeviceWidget::onLockToggleButton() { 158 | hideLockedChannels(lockToggleButton->get_active()); 159 | } 160 | 161 | void DeviceWidget::onDefaultToggleButton() { 162 | } 163 | 164 | void DeviceWidget::onOffsetChange() { 165 | pa_operation *o; 166 | int64_t offset; 167 | std::ostringstream card_stream; 168 | Glib::ustring card_name; 169 | 170 | if (!offsetButtonEnabled) 171 | return; 172 | 173 | offset = offsetButton->get_value() * 1000.0; 174 | card_stream << card_index; 175 | card_name = card_stream.str(); 176 | 177 | if (!(o = pa_context_set_port_latency_offset(get_context(), 178 | card_name.c_str(), activePort.c_str(), offset, NULL, NULL))) { 179 | show_error(this, _("pa_context_set_port_latency_offset() failed")); 180 | return; 181 | } 182 | pa_operation_unref(o); 183 | } 184 | 185 | void DeviceWidget::setDefault(bool isDefault) { 186 | defaultToggleButton->set_active(isDefault); 187 | /*defaultToggleButton->set_sensitive(!isDefault);*/ 188 | } 189 | 190 | bool DeviceWidget::timeoutEvent() { 191 | executeVolumeUpdate(); 192 | return false; 193 | } 194 | 195 | void DeviceWidget::executeVolumeUpdate() { 196 | } 197 | 198 | void DeviceWidget::setLatencyOffset(int64_t offset) { 199 | offsetButtonEnabled = false; 200 | offsetButton->set_value(offset / 1000.0); 201 | offsetButtonEnabled = true; 202 | } 203 | 204 | void DeviceWidget::setBaseVolume(pa_volume_t v) { 205 | 206 | for (int i = 0; i < channelMap.channels; i++) 207 | channelWidgets[i]->setBaseVolume(v); 208 | } 209 | 210 | void DeviceWidget::prepareMenu() { 211 | int idx = 0; 212 | int active_idx = -1; 213 | 214 | treeModel->clear(); 215 | /* Fill the ComboBox's Tree Model */ 216 | for (uint32_t i = 0; i < ports.size(); ++i) { 217 | Gtk::TreeModel::Row row = *(treeModel->append()); 218 | row[portModel.name] = ports[i].first; 219 | row[portModel.desc] = ports[i].second; 220 | if (ports[i].first == activePort) 221 | active_idx = idx; 222 | idx++; 223 | } 224 | 225 | if (active_idx >= 0) 226 | portList->set_active(active_idx); 227 | 228 | if (ports.size() > 0) { 229 | portSelect->show(); 230 | 231 | if (pa_context_get_server_protocol_version(get_context()) >= 27) 232 | offsetSelect->show(); 233 | else 234 | offsetSelect->hide(); 235 | 236 | } else { 237 | portSelect->hide(); 238 | offsetSelect->hide(); 239 | } 240 | 241 | updateAdvancedOptionsVisibility(); 242 | } 243 | 244 | 245 | void DeviceWidget::onContextTriggerEvent(gint n_press, gdouble x, gdouble y) { 246 | if (n_press == 1) { 247 | contextMenu.set_pointing_to(Gdk::Rectangle {(int) x, (int) y, 0 , 0}); 248 | contextMenu.popup(); 249 | } 250 | } 251 | 252 | void DeviceWidget::openRenamePopup(const Glib::VariantBase& parameter) { 253 | if (updating) 254 | return; 255 | 256 | if (!mpMainWindow->canRenameDevices) { 257 | auto dialog = Gtk::AlertDialog::create(_("Sorry, but device renaming is not supported.")); 258 | dialog->set_modal(true); 259 | dialog->set_detail(_("You need to load module-device-manager in the PulseAudio server in order to rename devices")); 260 | dialog->show(*mpMainWindow); 261 | return; 262 | } 263 | 264 | Glib::RefPtr x = Gtk::Builder::create_from_resource("/org/pulseaudio/pavucontrol/ui/renamedialog.ui", "renameDialog"); 265 | gchar *key = g_markup_printf_escaped("%s:%s", mDeviceType.c_str(), name.c_str()); 266 | RenameWindow* renameDialog = Gtk::Builder::get_widget_derived(x, "renameDialog", description.c_str(), key); 267 | renameDialog->set_transient_for(*mpMainWindow); 268 | 269 | renameDialog->present(); 270 | } 271 | 272 | RenameWindow::RenameWindow(BaseObjectType* cobject, const Glib::RefPtr& x, const gchar* name, const gchar* key) : 273 | Gtk::ApplicationWindow(cobject), 274 | deviceKey(key) { 275 | 276 | renameText = x->get_widget("renameText"); 277 | renameText->set_text(name); 278 | 279 | Gtk::Button* renameButton = x->get_widget("renameButton"); 280 | set_default_widget(*renameButton); 281 | 282 | auto renameAction = Gio::SimpleAction::create("rename"); 283 | renameAction->set_enabled(true); 284 | renameAction->signal_activate().connect(sigc::mem_fun(*this, &RenameWindow::doRename)); 285 | 286 | add_action(renameAction); 287 | } 288 | 289 | void RenameWindow::doRename(const Glib::VariantBase& parameter) { 290 | pa_operation* o; 291 | auto name = renameText->get_text(); 292 | 293 | if (!(o = pa_ext_device_manager_set_device_description(get_context(), deviceKey, name.c_str(), NULL, NULL))) { 294 | show_error(this, _("pa_ext_device_manager_write() failed")); 295 | return; 296 | } 297 | pa_operation_unref(o); 298 | g_free((char*)deviceKey); 299 | delete this; 300 | } 301 | 302 | void DeviceWidget::updateAdvancedOptionsVisibility() { 303 | bool visible = false; 304 | 305 | if (mDigital) { 306 | /* We need to show the format configuration options. */ 307 | visible = true; 308 | } 309 | 310 | if (ports.size() > 0) { 311 | /* We need to show the latency offset spin button. */ 312 | visible = true; 313 | } 314 | 315 | if (visible) 316 | advancedOptions->show(); 317 | else 318 | advancedOptions->hide(); 319 | } 320 | -------------------------------------------------------------------------------- /src/devicewidget.h: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifndef devicewidget_h 22 | #define devicewidget_h 23 | 24 | #include "minimalstreamwidget.h" 25 | 26 | class MainWindow; 27 | class ChannelWidget; 28 | 29 | class DeviceWidget : public MinimalStreamWidget { 30 | public: 31 | DeviceWidget(BaseObjectType* cobject, const Glib::RefPtr& x); 32 | void init(MainWindow* mainWindow, Glib::ustring); 33 | 34 | void setChannelMap(const pa_channel_map &m, bool can_decibel); 35 | void setVolume(const pa_cvolume &volume, bool force = false); 36 | virtual void updateChannelVolume(int channel, pa_volume_t v); 37 | 38 | void hideLockedChannels(bool hide = true); 39 | 40 | Glib::ustring name; 41 | Glib::ustring description; 42 | uint32_t index, card_index; 43 | 44 | Gtk::ToggleButton *lockToggleButton, *muteToggleButton, *defaultToggleButton; 45 | Gtk::SpinButton *offsetButton; 46 | 47 | bool offsetButtonEnabled; 48 | 49 | pa_channel_map channelMap; 50 | pa_cvolume volume; 51 | 52 | ChannelWidget *channelWidgets[PA_CHANNELS_MAX]; 53 | 54 | virtual void onMuteToggleButton(); 55 | virtual void onLockToggleButton(); 56 | virtual void onDefaultToggleButton(); 57 | virtual void setDefault(bool isDefault); 58 | virtual void onContextTriggerEvent(gint n_press, gdouble x, gdouble y); 59 | virtual void setLatencyOffset(int64_t offset); 60 | void onOffsetChange(); 61 | 62 | sigc::connection timeoutConnection; 63 | 64 | bool timeoutEvent(); 65 | 66 | virtual void executeVolumeUpdate(); 67 | virtual void setBaseVolume(pa_volume_t v); 68 | 69 | std::vector> ports; 70 | Glib::ustring activePort; 71 | 72 | void prepareMenu(); 73 | 74 | void openRenamePopup(const Glib::VariantBase& parameter); 75 | 76 | protected: 77 | MainWindow *mpMainWindow; 78 | 79 | /* Shows or hides the advanced options expander depending on whether it's 80 | * useful or not. This is called always after ports or mDigital have been 81 | * updated. */ 82 | void updateAdvancedOptionsVisibility(); 83 | 84 | virtual void onPortChange() = 0; 85 | 86 | Gtk::PopoverMenu contextMenu; 87 | 88 | /* Tree model columns */ 89 | class ModelColumns : public Gtk::TreeModel::ColumnRecord 90 | { 91 | public: 92 | 93 | ModelColumns() 94 | { add(name); add(desc); } 95 | 96 | Gtk::TreeModelColumn name; 97 | Gtk::TreeModelColumn desc; 98 | }; 99 | 100 | ModelColumns portModel; 101 | 102 | Gtk::Expander *advancedOptions; 103 | Gtk::Box *portSelect, *offsetSelect; 104 | Gtk::ComboBox *portList; 105 | Glib::RefPtr treeModel; 106 | Glib::RefPtr offsetAdjustment; 107 | 108 | /* Set to true for "digital" sinks (in practice this means those sinks that 109 | * support format configuration). */ 110 | bool mDigital; 111 | 112 | private: 113 | Glib::ustring mDeviceType; 114 | }; 115 | 116 | class RenameWindow : public Gtk::ApplicationWindow { 117 | public: 118 | RenameWindow(BaseObjectType* cobject, const Glib::RefPtr& x, const gchar* name, const gchar* key); 119 | Gtk::Entry* renameText; 120 | const gchar* deviceKey; 121 | private: 122 | void doRename(const Glib::VariantBase& parameter); 123 | }; 124 | 125 | #endif 126 | -------------------------------------------------------------------------------- /src/devicewidget.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | vertical 5 | 6 | 7 | center 8 | vertical 9 | 6 10 | 12 11 | 12 12 | 12 13 | 12 14 | 15 | 16 | center 17 | 6 18 | 19 | 20 | gtk-missing-image 21 | 22 | 23 | 24 | 25 | 1 26 | 27 | 28 | 1 29 | 30 | 31 | 32 | 33 | Device Title 34 | 1 35 | end 36 | 0 37 | 38 | 39 | 40 | 41 | 42 | 43 | 3 44 | 45 | 46 | center 47 | 1 48 | Mute audio 49 | 0 50 | 51 | 52 | audio-volume-muted 53 | normal 54 | 55 | 56 | 57 | 58 | 59 | 60 | center 61 | 1 62 | Lock channels together 63 | 0 64 | 1 65 | 66 | 67 | changes-prevent 68 | normal 69 | 70 | 71 | 72 | 73 | 74 | 75 | center 76 | 1 77 | Set as default 78 | 79 | 80 | emblem-default 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | center 92 | 6 93 | 94 | 95 | <b>Port:</b> 96 | 1 97 | 0 98 | 99 | 100 | 101 | 102 | 1 103 | 104 | 105 | 106 | 107 | 108 | 109 | center 110 | vertical 111 | 6 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | center 123 | 0 124 | 1 125 | 126 | 127 | vertical 128 | 129 | 130 | center 131 | 0 132 | 1 133 | 1 134 | 135 | 136 | PCM 137 | 0 138 | 1 139 | 1 140 | 141 | 0 142 | 0 143 | 144 | 145 | 146 | 147 | 148 | AC-3 149 | 1 150 | 151 | 1 152 | 0 153 | 154 | 155 | 156 | 157 | 158 | DTS 159 | 1 160 | 161 | 2 162 | 0 163 | 164 | 165 | 166 | 167 | 168 | E-AC-3 169 | 1 170 | 171 | 0 172 | 1 173 | 174 | 175 | 176 | 177 | 178 | MPEG 179 | 1 180 | 181 | 1 182 | 1 183 | 184 | 185 | 186 | 187 | 188 | AAC 189 | 1 190 | 191 | 2 192 | 1 193 | 194 | 195 | 196 | 197 | 198 | TrueHD 199 | 1 200 | 201 | 0 202 | 2 203 | 204 | 205 | 206 | 207 | 208 | DTS-HD 209 | 1 210 | 211 | 1 212 | 2 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | center 221 | 6 222 | 223 | 224 | <b>Latency offset:</b> 225 | 1 226 | 0 227 | 228 | 229 | 230 | 231 | 1 232 | 1 233 | 234 | 235 | 236 | 237 | ms 238 | 1 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | Advanced 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | center 257 | 258 | 259 | 260 | 261 | -------------------------------------------------------------------------------- /src/i18n.h: -------------------------------------------------------------------------------- 1 | #ifndef fooi18nhfoo 2 | #define fooi18nhfoo 3 | 4 | /*** 5 | This file is part of pavucontrol. 6 | 7 | Copyright 2006-2008 Lennart Poettering 8 | 9 | pavucontrol is free software; you can redistribute it and/or modify 10 | it under the terms of the GNU General Public License as published by 11 | the Free Software Foundation, either version 2 of the License, or 12 | (at your option) any later version. 13 | 14 | pavucontrol is distributed in the hope that it will be useful, but 15 | WITHOUT ANY WARRANTY; without even the implied warranty of 16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | General Public License for more details. 18 | 19 | You should have received a copy of the GNU General Public License 20 | along with pavucontrol. If not, see . 21 | ***/ 22 | 23 | #if !defined(GETTEXT_PACKAGE) 24 | #error "Something is very wrong here, config.h needs to be included first" 25 | #endif 26 | 27 | #ifdef ENABLE_NLS 28 | 29 | #include 30 | 31 | #define _(String) dgettext(GETTEXT_PACKAGE, String) 32 | #ifdef gettext_noop 33 | #define N_(String) gettext_noop(String) 34 | #else 35 | #define N_(String) (String) 36 | #endif 37 | 38 | #else /* NLS is disabled */ 39 | 40 | #define _(String) (String) 41 | #define N_(String) (String) 42 | #define textdomain(String) (String) 43 | #define gettext(String) (String) 44 | #define dgettext(Domain,String) (String) 45 | #define dcgettext(Domain,String,Type) (String) 46 | #define bindtextdomain(Domain,Directory) (Domain) 47 | #define bind_textdomain_codeset(Domain,Codeset) (Codeset) 48 | 49 | #endif /* ENABLE_NLS */ 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /src/mainwindow.h: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifndef mainwindow_h 22 | #define mainwindow_h 23 | 24 | class MainWindow; 25 | 26 | #include "pavucontrol.h" 27 | #include 28 | #if HAVE_EXT_DEVICE_RESTORE_API 29 | # include 30 | #endif 31 | 32 | #ifdef HAVE_LIBCANBERRA 33 | #include 34 | #endif 35 | 36 | #include 37 | 38 | class CardWidget; 39 | class SinkWidget; 40 | class SourceWidget; 41 | class SinkInputWidget; 42 | class SourceOutputWidget; 43 | class RoleWidget; 44 | 45 | class MainWindow : public Gtk::Window { 46 | public: 47 | MainWindow(BaseObjectType* cobject, const Glib::RefPtr& x); 48 | static MainWindow* create(bool maximize); 49 | virtual ~MainWindow(); 50 | 51 | void updateCard(const pa_card_info &info); 52 | bool updateSink(const pa_sink_info &info); 53 | void updateSource(const pa_source_info &info); 54 | void updateSinkInput(const pa_sink_input_info &info); 55 | void updateSourceOutput(const pa_source_output_info &info); 56 | void updateClient(const pa_client_info &info); 57 | void updateServer(const pa_server_info &info); 58 | void updateVolumeMeter(uint32_t source_index, uint32_t sink_input_index, double v); 59 | void updateRole(const pa_ext_stream_restore_info &info); 60 | #if HAVE_EXT_DEVICE_RESTORE_API 61 | void updateDeviceInfo(const pa_ext_device_restore_info &info); 62 | #endif 63 | void updateCardCodecs(const std::string& card_name, const std::unordered_map& codecs); 64 | void setActiveCodec(const std::string& card_name, const std::string& codec); 65 | 66 | void setCardProfileIsSticky(const std::string& card_name, gboolean profile_is_sticky); 67 | 68 | void removeCard(uint32_t index); 69 | void removeSink(uint32_t index); 70 | void removeSource(uint32_t index); 71 | void removeSinkInput(uint32_t index); 72 | void removeSourceOutput(uint32_t index); 73 | void removeClient(uint32_t index); 74 | 75 | void selectBestTab(); 76 | void selectTab(int tab_number); 77 | 78 | void removeAllWidgets(); 79 | 80 | void setConnectingMessage(const char *string = NULL); 81 | 82 | Gtk::Notebook *notebook; 83 | Gtk::Box *streamsVBox, *recsVBox, *sinksVBox, *sourcesVBox, *cardsVBox; 84 | Gtk::Label *noStreamsLabel, *noRecsLabel, *noSinksLabel, *noSourcesLabel, *noCardsLabel, *connectingLabel; 85 | Gtk::ComboBox *sinkInputTypeComboBox, *sourceOutputTypeComboBox, *sinkTypeComboBox, *sourceTypeComboBox; 86 | Gtk::CheckButton *showVolumeMetersCheckButton, *hideUnavailableCardProfilesCheckButton; 87 | 88 | std::map cardWidgets; 89 | std::map sinkWidgets; 90 | std::map sourceWidgets; 91 | std::map sinkInputWidgets; 92 | std::map sourceOutputWidgets; 93 | std::map clientNames; 94 | 95 | SinkInputType showSinkInputType; 96 | SinkType showSinkType; 97 | SourceOutputType showSourceOutputType; 98 | SourceType showSourceType; 99 | 100 | virtual void onSinkInputTypeComboBoxChanged(); 101 | virtual void onSourceOutputTypeComboBoxChanged(); 102 | virtual void onSinkTypeComboBoxChanged(); 103 | virtual void onSourceTypeComboBoxChanged(); 104 | virtual void onShowVolumeMetersCheckButtonToggled(); 105 | virtual void onHideUnavailableCardProfilesCheckButtonToggled(); 106 | 107 | void setConnectionState(gboolean connected); 108 | void updateDeviceVisibility(); 109 | void reallyUpdateDeviceVisibility(); 110 | pa_stream* createMonitorStreamForSource(uint32_t source_idx, uint32_t stream_idx, bool suspend); 111 | void createMonitorStreamForSinkInput(SinkInputWidget* w, uint32_t sink_idx); 112 | 113 | void setIconFromProplist(Gtk::Image *icon, pa_proplist *l, const char *name); 114 | 115 | RoleWidget *eventRoleWidget; 116 | 117 | bool createEventRoleWidget(); 118 | void deleteEventRoleWidget(); 119 | 120 | Glib::ustring defaultSinkName, defaultSourceName; 121 | 122 | bool canRenameDevices; 123 | 124 | #ifdef HAVE_LIBCANBERRA 125 | ca_context *canberraContext; 126 | #endif 127 | 128 | protected: 129 | virtual void on_realize(); 130 | virtual bool on_key_press_event(guint keyval, guint keycode, Gdk::ModifierType state); 131 | 132 | private: 133 | gboolean m_connected; 134 | gchar* m_config_filename; 135 | }; 136 | 137 | 138 | #endif 139 | -------------------------------------------------------------------------------- /src/meson.build: -------------------------------------------------------------------------------- 1 | gnome = import('gnome') 2 | resources = gnome.compile_resources('resources', 3 | 'resources.gresource.xml', 4 | source_dir: '.', 5 | c_name: 'resources' 6 | ) 7 | 8 | pavucontrol_sources = [ 9 | 'cardwidget.cc', 10 | 'channelwidget.cc', 11 | 'devicewidget.cc', 12 | 'mainwindow.cc', 13 | 'minimalstreamwidget.cc', 14 | 'pavuapplication.cc', 15 | 'pavucontrol.cc', 16 | 'rolewidget.cc', 17 | 'sinkinputwidget.cc', 18 | 'sinkwidget.cc', 19 | 'sourceoutputwidget.cc', 20 | 'sourcewidget.cc', 21 | 'streamwidget.cc', 22 | resources 23 | ] 24 | 25 | pavucontrol_deps = [gtkmm_dep, sigcpp_dep, canberragtk_dep, libpulse_dep, libpulsemlglib_dep] 26 | pavucontrol_defines = ['-DHAVE_CONFIG_H'] 27 | 28 | if have_pulse_messaging_api 29 | pavucontrol_deps += [json_glib_dep] 30 | pavucontrol_defines += ['-DHAVE_PULSE_MESSAGING_API'] 31 | endif 32 | 33 | executable('pavucontrol', 34 | pavucontrol_sources, 35 | install: true, 36 | cpp_args: pavucontrol_defines, 37 | include_directories : configinc, 38 | dependencies : pavucontrol_deps, 39 | ) 40 | 41 | desktop_file = i18n.merge_file( 42 | input : 'org.pulseaudio.pavucontrol.desktop.in', 43 | output : 'org.pulseaudio.pavucontrol.desktop', 44 | po_dir : po_dir, 45 | type : 'desktop', 46 | install : true, 47 | install_dir : join_paths(datadir, 'applications'), 48 | ) 49 | 50 | appdata_file = i18n.merge_file( 51 | input : 'org.pulseaudio.pavucontrol.metainfo.xml.in', 52 | output : 'org.pulseaudio.pavucontrol.metainfo.xml', 53 | po_dir : po_dir, 54 | type : 'xml', 55 | install : true, 56 | install_dir : datadir / 'metainfo', 57 | ) 58 | 59 | # TODO: use the Devel version on beta builds? 60 | icon_filename = 'org.pulseaudio.pavucontrol.svg' 61 | install_data( 62 | icon_filename, 63 | install_dir: datadir / 'icons' / 'hicolor' / 'scalable' / 'apps' 64 | ) 65 | 66 | icon_filename = 'org.pulseaudio.pavucontrol-symbolic.svg' 67 | install_data( 68 | icon_filename, 69 | install_dir: datadir / 'icons' / 'hicolor' / 'symbolic' / 'apps' 70 | ) 71 | -------------------------------------------------------------------------------- /src/minimalstreamwidget.cc: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifdef HAVE_CONFIG_H 22 | #include 23 | #endif 24 | 25 | #include "minimalstreamwidget.h" 26 | 27 | /*** MinimalStreamWidget ***/ 28 | MinimalStreamWidget::MinimalStreamWidget(BaseObjectType* cobject) : 29 | Gtk::Box(cobject), 30 | channelsVBox(NULL), 31 | nameLabel(NULL), 32 | boldNameLabel(NULL), 33 | iconImage(NULL), 34 | peakProgressBar(), 35 | lastPeak(0), 36 | peak(NULL), 37 | updating(false), 38 | volumeMeterEnabled(false), 39 | volumeMeterVisible(true) { 40 | } 41 | 42 | MinimalStreamWidget::~MinimalStreamWidget() { 43 | if (peak) { 44 | pa_stream_disconnect(peak); 45 | pa_stream_unref(peak); 46 | peak = NULL; 47 | } 48 | } 49 | 50 | void MinimalStreamWidget::init() { 51 | /* Set up the peak meter. This is not done in the constructor, because 52 | * channelsVBox is initialized by the subclasses, so it's not yet available 53 | * in the constructor. */ 54 | 55 | peakProgressBar.set_size_request(-1, 10); 56 | channelsVBox->append(peakProgressBar); 57 | 58 | /* XXX: Why is the peak meter hidden by default? Maybe the idea is that if 59 | * setting up the monitoring stream fails for whatever reason, then we 60 | * shouldn't show the peak meter at all. */ 61 | peakProgressBar.hide(); 62 | } 63 | 64 | #define DECAY_STEP (1.0 / PEAKS_RATE) 65 | 66 | void MinimalStreamWidget::updatePeak(double v) { 67 | if (lastPeak >= DECAY_STEP) 68 | if (v < lastPeak - DECAY_STEP) 69 | v = lastPeak - DECAY_STEP; 70 | 71 | lastPeak = v; 72 | 73 | if (v >= 0) { 74 | peakProgressBar.set_sensitive(TRUE); 75 | peakProgressBar.set_fraction(v); 76 | } else { 77 | peakProgressBar.set_sensitive(FALSE); 78 | peakProgressBar.set_fraction(0); 79 | } 80 | 81 | enableVolumeMeter(); 82 | } 83 | 84 | void MinimalStreamWidget::enableVolumeMeter() { 85 | if (volumeMeterEnabled) 86 | return; 87 | 88 | volumeMeterEnabled = true; 89 | if (volumeMeterVisible) { 90 | peakProgressBar.show(); 91 | } 92 | } 93 | 94 | void MinimalStreamWidget::setVolumeMeterVisible(bool v) { 95 | volumeMeterVisible = v; 96 | if (v) { 97 | if (volumeMeterEnabled) { 98 | peakProgressBar.show(); 99 | } 100 | } else { 101 | peakProgressBar.hide(); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/minimalstreamwidget.h: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifndef minimalstreamwidget_h 22 | #define minimalstreamwidget_h 23 | 24 | #include 25 | #include 26 | 27 | #define PEAKS_RATE 144 28 | 29 | class MinimalStreamWidget : public Gtk::Box { 30 | public: 31 | MinimalStreamWidget(BaseObjectType* cobject); 32 | virtual ~MinimalStreamWidget(); 33 | 34 | /* Subclass constructors are expected to initialize these variables. 35 | * MinimalStreamWidget can't initialize these, because the glade object 36 | * id's depend on the subclass type. */ 37 | Gtk::Box *channelsVBox; 38 | Gtk::Label *nameLabel, *boldNameLabel; 39 | Gtk::Image *iconImage; 40 | 41 | Gtk::ProgressBar peakProgressBar; 42 | double lastPeak; 43 | pa_stream *peak; 44 | 45 | bool updating; 46 | 47 | virtual void onMuteToggleButton() = 0; 48 | virtual void onLockToggleButton() = 0; 49 | virtual void updateChannelVolume(int channel, pa_volume_t v) = 0; 50 | 51 | bool volumeMeterEnabled; 52 | void enableVolumeMeter(); 53 | void updatePeak(double v); 54 | void setVolumeMeterVisible(bool v); 55 | 56 | protected: 57 | /* Subclasses must call this after the constructor to finalize the initial 58 | * layout. */ 59 | void init(); 60 | 61 | private : 62 | bool volumeMeterVisible; 63 | 64 | }; 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /src/org.pulseaudio.pavucontrol-symbolic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/org.pulseaudio.pavucontrol.Devel.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /src/org.pulseaudio.pavucontrol.desktop.in: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Version=1.0 3 | Name=Volume Control 4 | GenericName=Volume Control 5 | Comment=Adjust the volume level 6 | Exec=pavucontrol 7 | Icon=org.pulseaudio.pavucontrol 8 | StartupNotify=true 9 | Type=Application 10 | Categories=AudioVideo;Audio;Mixer;GTK;Settings;X-XFCE-SettingsDialog;X-XFCE-HardwareSettings; 11 | Keywords=pavucontrol;PulseAudio;Microphone;Volume;Fade;Balance;Headset;Speakers;Headphones;Audio;Mixer;Output;Input;Devices;Playback;Recording;System Sounds;Sound Card;Settings;Preferences; 12 | -------------------------------------------------------------------------------- /src/org.pulseaudio.pavucontrol.metainfo.xml.in: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | org.pulseaudio.pavucontrol 5 | pavucontrol 6 | org.pulseaudio.pavucontrol.desktop 7 | Volume Control 8 | Adjust device and app volumes 9 | The PulseAudio Developers 10 | 11 | The PulseAudio Developers 12 | 13 | 14 |

PulseAudio Volume Control (pavucontrol) is a volume control tool (“mixer”) for the PulseAudio sound server. In contrast to classic mixer tools, this one allows you to control both the volume of hardware devices and of each playback stream separately.

15 |
16 | 17 | pavucontrol.desktop 18 | 19 | 20 | 21 | The “Playback” tab 22 | https://freedesktop.org/software/pulseaudio/pavucontrol/screenshots/pavucontrol-playback.png 23 | 24 | 25 | The “Recording” tab 26 | https://freedesktop.org/software/pulseaudio/pavucontrol/screenshots/pavucontrol-record.png 27 | 28 | 29 | The “Output” tab 30 | https://freedesktop.org/software/pulseaudio/pavucontrol/screenshots/pavucontrol-output.png 31 | 32 | 33 | The “Input” tab 34 | https://freedesktop.org/software/pulseaudio/pavucontrol/screenshots/pavucontrol-input.png 35 | 36 | 37 | The “Configuration” tab 38 | https://freedesktop.org/software/pulseaudio/pavucontrol/screenshots/pavucontrol-config.png 39 | 40 | 41 | 42 | 43 | 44 |
    45 |
  • Fix translations not being correctly picked up.
  • 46 |
  • Fix bug where cards don't go away when unplugged.
  • 47 |
  • Fix app naming in desktop file (Volume Control).
  • 48 |
  • Fix Bluetooth codec selection alignment.
  • 49 |
  • Update Swedish translation.
  • 50 |
51 |
52 |
53 | 54 | 55 |
    56 |
  • Migrate from Gtk 3 to 4.
  • 57 |
  • Embed UI resources in executable.
  • 58 |
  • Rename "Set as fallback" to "Default" for better legibility.
  • 59 |
  • Support 144 Hz monitors with level bars.
  • 60 |
  • App icons will fallback to generic rather than missing image, 61 | and this will be more common with Gtk 4.
  • 62 |
  • Lots of translation updates.
  • 63 |
  • Drop autotools build in favour of meson.
  • 64 |
  • Make libcanberra dependency optional.
  • 65 |
66 |
67 |
68 | 69 | 70 |
    71 |
  • Support for switching Bluetooth codecs (new in PulseAudio 15.0).
  • 72 |
  • Support for locking card profiles (new in PulseAudio 15.0). Locking a profile prevents PulseAudio from automatically switching away from that profile on plug/unplug events.
  • 73 |
  • New translations: Asturian, Basque, Belarusian, Galician, Hebrew, Kazakh, Norwegian Bokmål, Sinhala, Slovenian
  • 74 |
  • Updated translations: Catalan, Chinese (Simplified), Chinese (Traditional), Croatian, Danish, Dutch, Finnish, French, German, Hungarian, Italian, Japanese, Korean, Lithuanian, Norwegian Nynorsk, Polish, Portugese, Portugese (Brazil), Slovak, Spanish, Swedish, Turkish, Ukrainian.
  • 75 |
76 |
77 |
78 | 79 | 80 |
    81 |
  • There can now be only one pavucontrol window open at a time. Trying to start pavucontrol for a second time brings the first window to foreground.
  • 82 |
  • Added a "Show volume meters" checkbox to the Configuration tab. Disabling the volume meters reduces CPU use.
  • 83 |
  • Improve the use of space (remove useless margins and paddings).
  • 84 |
  • Use a more appropriate icon for the channel lock button.
  • 85 |
  • Better channel label layout, prevents volume sliders from getting unaligned.
  • 86 |
  • Maximum latency offset increased from 2 to 5 seconds to accommodate AirPlay devices that often have higher latency than 2 seconds (this is not that useful on newer PulseAudio versions, though, because the latency is reported much more accurately than before).
  • 87 |
  • New --version command line option.
  • 88 |
  • New translations: Chinese (Taiwan), Croatian, Korean, Norwegian Nynorsk, Lithuanian, Valencian.
  • 89 |
  • Updated translations: Finnish, French, German, Italian, Japanese, Polish, Swedish.
  • 90 |
91 |
92 |
93 |
94 | 95 | CC0-1.0 96 | GPL-2.0+ 97 | https://freedesktop.org/software/pulseaudio/pavucontrol/ 98 | https://gitlab.freedesktop.org/pulseaudio/pavucontrol/issues 99 | https://gitlab.freedesktop.org/pulseaudio/pavucontrol/issues/new 100 |
101 | -------------------------------------------------------------------------------- /src/org.pulseaudio.pavucontrol.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/pavuapplication.cc: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2008 Sjoerd Simons 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifdef HAVE_CONFIG_H 22 | #include 23 | #endif 24 | 25 | #include "i18n.h" 26 | 27 | #include "pavuapplication.h" 28 | #include "pavucontrol.h" 29 | #include "mainwindow.h" 30 | 31 | static PavuApplication globalInstance; 32 | 33 | PavuApplication& 34 | PavuApplication::get_instance() 35 | { 36 | return globalInstance; 37 | } 38 | 39 | PavuApplication::PavuApplication() : 40 | Gtk::Application("org.pulseaudio.pavucontrol", Gio::Application::Flags::HANDLES_COMMAND_LINE), 41 | mainWindow(NULL), 42 | retry(false), 43 | maximize(false), 44 | tab(0), 45 | version(false), 46 | m(NULL) { 47 | } 48 | 49 | /* 50 | * Create the main window and connect its "on_close_window" signal to our cleanup 51 | * function 52 | */ 53 | MainWindow* PavuApplication::create_window() 54 | { 55 | m = pa_glib_mainloop_new(g_main_context_default()); 56 | g_assert(m); 57 | 58 | MainWindow* pavucontrol_window = pavucontrol_get_window(m, maximize, retry, tab); 59 | 60 | pavucontrol_window->signal_close_request().connect(sigc::mem_fun(*this, 61 | &PavuApplication::on_close_window), true); 62 | 63 | return pavucontrol_window; 64 | } 65 | 66 | /* "on_activate" signal handler 67 | * This is fired via the Gtk::Application framework either directly from the 68 | * run() function, or manually from the on_command_line signal handler. 69 | * This is always executed in the first-running process. 70 | */ 71 | void PavuApplication::on_activate() 72 | { 73 | /* See if we are already running */ 74 | if (mainWindow == NULL) { 75 | /* We aren't. Create the main window */ 76 | mainWindow = create_window(); 77 | 78 | /* and register it in the Gtk::Application */ 79 | add_window(*mainWindow); 80 | } else if (tab != -1) { 81 | /* We are, and a specific tab has been requested. Select it. */ 82 | mainWindow->selectTab(tab); 83 | } 84 | 85 | /* Present the main window. */ 86 | mainWindow->present(); 87 | } 88 | 89 | /* "on_close_window" signal handler 90 | * This is executed in the first-running process and performs cleanup before 91 | * exiting : when the last registered window of Gtk::Application is closed, 92 | * the application's run() function returns. 93 | */ 94 | bool PavuApplication::on_close_window() 95 | { 96 | delete mainWindow; 97 | mainWindow = NULL; 98 | 99 | if (get_context()) { 100 | pa_context_unref(get_context()); 101 | } 102 | pa_glib_mainloop_free(m); 103 | m = NULL; 104 | return true; 105 | } 106 | 107 | template 108 | static bool get_arg_value(const Glib::RefPtr& options, const Glib::ustring& arg_name, T_ArgType& arg_value) 109 | { 110 | arg_value = T_ArgType(); 111 | if (options->lookup_value(arg_name, arg_value)) { 112 | return true; 113 | } 114 | 115 | return false; 116 | } 117 | 118 | /* 119 | * "on_command_line" signal handler 120 | * This is executed in the first-running process. 121 | */ 122 | int on_command_line(const Glib::RefPtr& command_line, 123 | PavuApplication *app) 124 | { 125 | const auto options = command_line->get_options_dict(); 126 | 127 | get_arg_value(options, "tab", app->tab); 128 | get_arg_value(options, "retry", app->retry); 129 | get_arg_value(options, "maximize", app->maximize); 130 | get_arg_value(options, "version", app->version); 131 | 132 | if (app->version) { 133 | printf("%s\n", PACKAGE_STRING); 134 | return 0; 135 | } 136 | 137 | app->activate(); 138 | 139 | return 0; 140 | } 141 | 142 | int main(int argc, char *argv[]) { 143 | 144 | /* Initialize the i18n stuff */ 145 | bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR); 146 | bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); 147 | textdomain(GETTEXT_PACKAGE); 148 | 149 | signal(SIGPIPE, SIG_IGN); 150 | 151 | /* Create the application */ 152 | globalInstance = PavuApplication(); 153 | 154 | /* Add command-line options */ 155 | globalInstance.add_main_option_entry( 156 | Gio::Application::OptionType::INT, 157 | "tab", 't', 158 | _("Select a specific tab on load."), 159 | _("number")); 160 | 161 | globalInstance.add_main_option_entry( 162 | Gio::Application::OptionType::BOOL, 163 | "retry", 'r', 164 | _("Retry forever if pa quits (every 5 seconds).")); 165 | 166 | globalInstance.add_main_option_entry( 167 | Gio::Application::OptionType::BOOL, 168 | "maximize", 'm', 169 | _("Maximize the window.")); 170 | 171 | globalInstance.add_main_option_entry( 172 | Gio::Application::OptionType::BOOL, 173 | "version", 'v', 174 | _("Show version.")); 175 | 176 | /* Connect to the "on_command_line" signal which is fired 177 | * when the application receives command-line arguments 178 | */ 179 | globalInstance.signal_command_line().connect(sigc::bind(sigc::ptr_fun(&on_command_line), &globalInstance), false); 180 | 181 | /* Run the application. 182 | * In the first launched instance, this will return when its window is 183 | * closed. In subsequently launches instances, this will only signal the 184 | * first instance to handle a new request, and exit immediately. 185 | * Handling a new request consists of presenting the existing window (and 186 | * optionally, select a tab). 187 | */ 188 | return globalInstance.run(argc, argv); 189 | } 190 | -------------------------------------------------------------------------------- /src/pavuapplication.h: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifndef pavuapplication_h 22 | #define pavuapplication_h 23 | 24 | #include "mainwindow.h" 25 | 26 | class PavuApplication : public Gtk::Application { 27 | public: 28 | PavuApplication(); 29 | 30 | /* Main window */ 31 | MainWindow *mainWindow; 32 | 33 | /* options */ 34 | bool retry; 35 | bool maximize; 36 | gint32 tab; 37 | bool version; 38 | 39 | static PavuApplication& get_instance(); 40 | 41 | protected: 42 | // Override default signal handlers: 43 | void on_activate() override; 44 | 45 | private: 46 | MainWindow* create_window(); 47 | bool on_close_window(); 48 | 49 | pa_glib_mainloop *m; 50 | }; 51 | 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /src/pavucontrol.h: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifndef pavucontrol_h 22 | #define pavucontrol_h 23 | 24 | #include 25 | 26 | #include 27 | 28 | #include 29 | #include 30 | 31 | #include 32 | #include 33 | 34 | /* Can be removed when PulseAudio 0.9.23 or newer is required */ 35 | #ifndef PA_VOLUME_UI_MAX 36 | # define PA_VOLUME_UI_MAX (pa_sw_volume_from_dB(+11.0)) 37 | #endif 38 | 39 | #define HAVE_SOURCE_OUTPUT_VOLUMES PA_CHECK_VERSION(0,99,0) 40 | #define HAVE_EXT_DEVICE_RESTORE_API PA_CHECK_VERSION(0,99,0) 41 | 42 | enum SinkInputType { 43 | SINK_INPUT_ALL, 44 | SINK_INPUT_CLIENT, 45 | SINK_INPUT_VIRTUAL 46 | }; 47 | 48 | enum SinkType { 49 | SINK_ALL, 50 | SINK_HARDWARE, 51 | SINK_VIRTUAL, 52 | }; 53 | 54 | enum SourceOutputType { 55 | SOURCE_OUTPUT_ALL, 56 | SOURCE_OUTPUT_CLIENT, 57 | SOURCE_OUTPUT_VIRTUAL 58 | }; 59 | 60 | enum SourceType { 61 | SOURCE_ALL, 62 | SOURCE_NO_MONITOR, 63 | SOURCE_HARDWARE, 64 | SOURCE_VIRTUAL, 65 | SOURCE_MONITOR, 66 | }; 67 | 68 | #include "mainwindow.h" 69 | 70 | pa_context* get_context(void); 71 | void show_error(Gtk::Widget *w, const char *txt); 72 | 73 | MainWindow* pavucontrol_get_window(pa_glib_mainloop *m, bool maximize, bool retry, int tab_number); 74 | 75 | #ifdef HAVE_PULSE_MESSAGING_API 76 | std::string card_message_handler_path(const std::string& name); 77 | std::string card_bluez_message_handler_path(const std::string& name); 78 | #endif 79 | 80 | #endif 81 | -------------------------------------------------------------------------------- /src/renamedialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1 5 | 6 | 7 | vertical 8 | 2 9 | 5 10 | 5 11 | 5 12 | 5 13 | 14 | 15 | 1 16 | <b>Rename device to:</b> 17 | 1 18 | 0 19 | 20 | 21 | 22 | 23 | 1 24 | 1 25 | 26 | 1 27 | 60 28 | 29 | 30 | 31 | 32 | bottom 33 | GTK_ALIGN_END 34 | 1 35 | 36 | 37 | _Cancel 38 | 1 39 | 1 40 | window.close 41 | 42 | 43 | 44 | 45 | _Ok 46 | 1 47 | 1 48 | win.rename 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /src/resources.gresource.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | mainwindow.ui 5 | cardwidget.ui 6 | channelwidget.ui 7 | renamedialog.ui 8 | streamwidget.ui 9 | devicewidget.ui 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/rolewidget.cc: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifdef HAVE_CONFIG_H 22 | #include 23 | #endif 24 | 25 | #include "rolewidget.h" 26 | 27 | #include 28 | 29 | #include "i18n.h" 30 | 31 | RoleWidget::RoleWidget(BaseObjectType* cobject, const Glib::RefPtr& x) : 32 | StreamWidget(cobject, x) { 33 | 34 | lockToggleButton->hide(); 35 | directionLabel->hide(); 36 | deviceComboBox->hide(); 37 | } 38 | 39 | RoleWidget* RoleWidget::create() { 40 | RoleWidget* w; 41 | Glib::RefPtr x = Gtk::Builder::create_from_resource("/org/pulseaudio/pavucontrol/ui/streamwidget.ui", "streamWidget"); 42 | w = Gtk::Builder::get_widget_derived(x, "streamWidget"); 43 | w->reference(); 44 | return w; 45 | } 46 | 47 | void RoleWidget::onMuteToggleButton() { 48 | StreamWidget::onMuteToggleButton(); 49 | 50 | executeVolumeUpdate(); 51 | } 52 | 53 | void RoleWidget::executeVolumeUpdate() { 54 | pa_ext_stream_restore_info info; 55 | 56 | if (updating) 57 | return; 58 | 59 | info.name = role.c_str(); 60 | info.channel_map.channels = 1; 61 | info.channel_map.map[0] = PA_CHANNEL_POSITION_MONO; 62 | info.volume = volume; 63 | info.device = device == "" ? NULL : device.c_str(); 64 | info.mute = muteToggleButton->get_active(); 65 | 66 | pa_operation* o; 67 | if (!(o = pa_ext_stream_restore_write(get_context(), PA_UPDATE_REPLACE, &info, 1, TRUE, NULL, NULL))) { 68 | show_error(this, _("pa_ext_stream_restore_write() failed")); 69 | return; 70 | } 71 | 72 | pa_operation_unref(o); 73 | } 74 | 75 | -------------------------------------------------------------------------------- /src/rolewidget.h: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifndef rolewidget_h 22 | #define rolewidget_h 23 | 24 | #include "pavucontrol.h" 25 | 26 | #include "streamwidget.h" 27 | 28 | class RoleWidget : public StreamWidget { 29 | public: 30 | RoleWidget(BaseObjectType* cobject, const Glib::RefPtr& x); 31 | static RoleWidget* create(); 32 | 33 | Glib::ustring role; 34 | Glib::ustring device; 35 | 36 | virtual void onMuteToggleButton(); 37 | virtual void executeVolumeUpdate(); 38 | }; 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /src/sinkinputwidget.cc: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifdef HAVE_CONFIG_H 22 | #include 23 | #endif 24 | 25 | #include "sinkinputwidget.h" 26 | #include "mainwindow.h" 27 | #include "sinkwidget.h" 28 | 29 | #include "i18n.h" 30 | 31 | SinkInputWidget::SinkInputWidget(BaseObjectType* cobject, const Glib::RefPtr& x) : 32 | StreamWidget(cobject, x) { 33 | 34 | gchar *txt; 35 | directionLabel->set_label(txt = g_markup_printf_escaped("%s", _("on"))); 36 | g_free(txt); 37 | 38 | addKillMenu(_("Terminate Playback")); 39 | } 40 | 41 | SinkInputWidget* SinkInputWidget::create(MainWindow* mainWindow) { 42 | SinkInputWidget* w; 43 | Glib::RefPtr x = Gtk::Builder::create_from_resource("/org/pulseaudio/pavucontrol/ui/streamwidget.ui", "streamWidget"); 44 | w = Gtk::Builder::get_widget_derived(x, "streamWidget"); 45 | w->init(mainWindow); 46 | w->reference(); 47 | 48 | return w; 49 | } 50 | 51 | SinkInputWidget::~SinkInputWidget(void) { 52 | } 53 | 54 | void SinkInputWidget::setSinkIndex(uint32_t idx) { 55 | mSinkIndex = idx; 56 | updateDeviceComboBox(); 57 | } 58 | 59 | uint32_t SinkInputWidget::sinkIndex() { 60 | return mSinkIndex; 61 | } 62 | 63 | void SinkInputWidget::updateDeviceComboBox() { 64 | Glib::ustring currentSinkName = UNKNOWN_DEVICE_NAME; 65 | 66 | deviceComboBox->remove_all(); 67 | 68 | for (auto i = mpMainWindow->sinkWidgets.begin(); i != mpMainWindow->sinkWidgets.end(); i++) { 69 | SinkWidget *sink = i->second; 70 | 71 | deviceComboBox->append(sink->name, sink->description); 72 | 73 | if (sink->index == mSinkIndex) 74 | currentSinkName = sink->name; 75 | } 76 | 77 | if (currentSinkName == UNKNOWN_DEVICE_NAME) 78 | deviceComboBox->append(UNKNOWN_DEVICE_NAME, _("Unknown output")); 79 | 80 | deviceComboBox->set_active_id(currentSinkName); 81 | } 82 | 83 | void SinkInputWidget::executeVolumeUpdate() { 84 | pa_operation* o; 85 | 86 | if (!(o = pa_context_set_sink_input_volume(get_context(), index, &volume, NULL, NULL))) { 87 | show_error(this, _("pa_context_set_sink_input_volume() failed")); 88 | return; 89 | } 90 | 91 | pa_operation_unref(o); 92 | } 93 | 94 | void SinkInputWidget::onMuteToggleButton() { 95 | StreamWidget::onMuteToggleButton(); 96 | 97 | if (updating) 98 | return; 99 | 100 | pa_operation* o; 101 | if (!(o = pa_context_set_sink_input_mute(get_context(), index, muteToggleButton->get_active(), NULL, NULL))) { 102 | show_error(this, _("pa_context_set_sink_input_mute() failed")); 103 | return; 104 | } 105 | 106 | pa_operation_unref(o); 107 | } 108 | 109 | void SinkInputWidget::onKill(const Glib::VariantBase& parameter) { 110 | pa_operation* o; 111 | if (!(o = pa_context_kill_sink_input(get_context(), index, NULL, NULL))) { 112 | show_error(this, _("pa_context_kill_sink_input() failed")); 113 | return; 114 | } 115 | 116 | pa_operation_unref(o); 117 | } 118 | 119 | void SinkInputWidget::onDeviceComboBoxChanged() { 120 | if (updating) 121 | return; 122 | 123 | Glib::ustring sinkName = deviceComboBox->get_active_id(); 124 | 125 | pa_operation *o = pa_context_move_sink_input_by_name(get_context(), index, sinkName.c_str(), NULL, NULL); 126 | if (o) 127 | pa_operation_unref(o); 128 | } 129 | -------------------------------------------------------------------------------- /src/sinkinputwidget.h: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifndef sinkinputwidget_h 22 | #define sinkinputwidget_h 23 | 24 | #include "pavucontrol.h" 25 | 26 | #include "streamwidget.h" 27 | 28 | class MainWindow; 29 | 30 | class SinkInputWidget : public StreamWidget { 31 | public: 32 | SinkInputWidget(BaseObjectType* cobject, const Glib::RefPtr& x); 33 | static SinkInputWidget* create(MainWindow* mainWindow); 34 | ~SinkInputWidget(void); 35 | 36 | SinkInputType type; 37 | 38 | uint32_t index, clientIndex; 39 | void setSinkIndex(uint32_t idx); 40 | uint32_t sinkIndex(); 41 | void updateDeviceComboBox(); 42 | virtual void executeVolumeUpdate(); 43 | virtual void onMuteToggleButton(); 44 | virtual void onKill(const Glib::VariantBase& parameter); 45 | virtual void onDeviceComboBoxChanged(); 46 | 47 | private: 48 | uint32_t mSinkIndex; 49 | }; 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /src/sinkwidget.cc: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifdef HAVE_CONFIG_H 22 | #include 23 | #endif 24 | 25 | #include "sinkwidget.h" 26 | 27 | #ifdef HAVE_LIBCANBERRA 28 | #include 29 | #endif 30 | 31 | #if HAVE_EXT_DEVICE_RESTORE_API 32 | # include 33 | # include 34 | #endif 35 | 36 | #include "i18n.h" 37 | 38 | SinkWidget::SinkWidget(BaseObjectType* cobject, const Glib::RefPtr& x) : 39 | DeviceWidget(cobject, x) { 40 | #if HAVE_EXT_DEVICE_RESTORE_API 41 | uint8_t i = 0; 42 | 43 | encodingSelect = x->get_widget("encodingSelect"); 44 | 45 | encodings[i].encoding = PA_ENCODING_PCM; 46 | encodings[i].widget = x->get_widget("encodingFormatPCM"); 47 | encodings[i].widget->signal_toggled().connect(sigc::mem_fun(*this, &SinkWidget::onEncodingsChange)); 48 | 49 | ++i; 50 | encodings[i].encoding = PA_ENCODING_AC3_IEC61937; 51 | encodings[i].widget = x->get_widget("encodingFormatAC3"); 52 | encodings[i].widget->signal_toggled().connect(sigc::mem_fun(*this, &SinkWidget::onEncodingsChange)); 53 | 54 | ++i; 55 | encodings[i].encoding = PA_ENCODING_EAC3_IEC61937; 56 | encodings[i].widget = x->get_widget("encodingFormatEAC3"); 57 | encodings[i].widget->signal_toggled().connect(sigc::mem_fun(*this, &SinkWidget::onEncodingsChange)); 58 | 59 | ++i; 60 | encodings[i].encoding = PA_ENCODING_MPEG_IEC61937; 61 | encodings[i].widget = x->get_widget("encodingFormatMPEG"); 62 | encodings[i].widget->signal_toggled().connect(sigc::mem_fun(*this, &SinkWidget::onEncodingsChange)); 63 | 64 | ++i; 65 | encodings[i].encoding = PA_ENCODING_DTS_IEC61937; 66 | encodings[i].widget = x->get_widget("encodingFormatDTS"); 67 | encodings[i].widget->signal_toggled().connect(sigc::mem_fun(*this, &SinkWidget::onEncodingsChange)); 68 | 69 | ++i; 70 | encodings[i].encoding = PA_ENCODING_INVALID; 71 | encodings[i].widget = x->get_widget("encodingFormatAAC"); 72 | encodings[i].widget->set_sensitive(false); 73 | #ifdef PA_ENCODING_MPEG2_AAC_IEC61937 74 | if (pa_context_get_server_protocol_version(get_context()) >= 28) { 75 | encodings[i].encoding = PA_ENCODING_MPEG2_AAC_IEC61937; 76 | encodings[i].widget->signal_toggled().connect(sigc::mem_fun(*this, &SinkWidget::onEncodingsChange)); 77 | encodings[i].widget->set_sensitive(true); 78 | } 79 | #endif 80 | ++i; 81 | encodings[i].encoding = PA_ENCODING_INVALID; 82 | encodings[i].widget = x->get_widget("encodingFormatTRUEHD"); 83 | encodings[i].widget->set_sensitive(false); 84 | #ifdef PA_ENCODING_TRUEHD_IEC61937 85 | if (pa_context_get_server_protocol_version(get_context()) >= 33) { 86 | encodings[i].encoding = PA_ENCODING_TRUEHD_IEC61937; 87 | encodings[i].widget->signal_toggled().connect(sigc::mem_fun(*this, &SinkWidget::onEncodingsChange)); 88 | encodings[i].widget->set_sensitive(true); 89 | } 90 | #endif 91 | ++i; 92 | encodings[i].encoding = PA_ENCODING_INVALID; 93 | encodings[i].widget = x->get_widget("encodingFormatDTSHD"); 94 | encodings[i].widget->set_sensitive(false); 95 | #ifdef PA_ENCODING_DTSHD_IEC61937 96 | if (pa_context_get_server_protocol_version(get_context()) >= 33) { 97 | encodings[i].encoding = PA_ENCODING_DTSHD_IEC61937; 98 | encodings[i].widget->signal_toggled().connect(sigc::mem_fun(*this, &SinkWidget::onEncodingsChange)); 99 | encodings[i].widget->set_sensitive(true); 100 | } 101 | #endif 102 | #endif 103 | } 104 | 105 | SinkWidget* SinkWidget::create(MainWindow* mainWindow) { 106 | SinkWidget* w; 107 | Glib::RefPtr x = Gtk::Builder::create_from_resource("/org/pulseaudio/pavucontrol/ui/devicewidget.ui", "deviceWidget"); 108 | w = Gtk::Builder::get_widget_derived(x, "deviceWidget"); 109 | w->init(mainWindow, "sink"); 110 | w->reference(); 111 | return w; 112 | } 113 | 114 | void SinkWidget::executeVolumeUpdate() { 115 | pa_operation* o; 116 | char dev[64]; 117 | 118 | if (!(o = pa_context_set_sink_volume_by_index(get_context(), index, &volume, NULL, NULL))) { 119 | show_error(this, _("pa_context_set_sink_volume_by_index() failed")); 120 | return; 121 | } 122 | 123 | pa_operation_unref(o); 124 | 125 | snprintf(dev, sizeof(dev), "%lu", (unsigned long) index); 126 | 127 | #ifdef HAVE_LIBCANBERRA 128 | int playing = 0; 129 | ca_context_playing(mpMainWindow->canberraContext, 2, &playing); 130 | if (playing) 131 | return; 132 | 133 | ca_context_change_device(mpMainWindow->canberraContext, dev); 134 | 135 | ca_context_play(mpMainWindow->canberraContext, 136 | 2, 137 | CA_PROP_EVENT_DESCRIPTION, _("Volume Control Feedback Sound"), 138 | CA_PROP_EVENT_ID, "audio-volume-change", 139 | CA_PROP_CANBERRA_CACHE_CONTROL, "permanent", 140 | CA_PROP_CANBERRA_ENABLE, "1", 141 | NULL); 142 | 143 | ca_context_change_device(mpMainWindow->canberraContext, NULL); 144 | #endif 145 | } 146 | 147 | void SinkWidget::onMuteToggleButton() { 148 | DeviceWidget::onMuteToggleButton(); 149 | 150 | if (updating) 151 | return; 152 | 153 | pa_operation* o; 154 | if (!(o = pa_context_set_sink_mute_by_index(get_context(), index, muteToggleButton->get_active(), NULL, NULL))) { 155 | show_error(this, _("pa_context_set_sink_mute_by_index() failed")); 156 | return; 157 | } 158 | 159 | pa_operation_unref(o); 160 | } 161 | 162 | void SinkWidget::onDefaultToggleButton() { 163 | pa_operation* o; 164 | 165 | if (updating) 166 | return; 167 | 168 | if (!(o = pa_context_set_default_sink(get_context(), name.c_str(), NULL, NULL))) { 169 | show_error(this, _("pa_context_set_default_sink() failed")); 170 | return; 171 | } 172 | pa_operation_unref(o); 173 | } 174 | 175 | void SinkWidget::onPortChange() { 176 | Gtk::TreeModel::iterator iter; 177 | 178 | if (updating) 179 | return; 180 | 181 | iter = portList->get_active(); 182 | if (iter) { 183 | Gtk::TreeModel::Row row = *iter; 184 | if (row) { 185 | pa_operation* o; 186 | Glib::ustring port = row[portModel.name]; 187 | 188 | if (!(o = pa_context_set_sink_port_by_index(get_context(), index, port.c_str(), NULL, NULL))) { 189 | show_error(this, _("pa_context_set_sink_port_by_index() failed")); 190 | return; 191 | } 192 | 193 | pa_operation_unref(o); 194 | } 195 | } 196 | } 197 | 198 | void SinkWidget::setDigital(bool digital) { 199 | mDigital = digital; 200 | 201 | #if HAVE_EXT_DEVICE_RESTORE_API 202 | if (digital) 203 | encodingSelect->show(); 204 | else 205 | encodingSelect->hide(); 206 | 207 | updateAdvancedOptionsVisibility(); 208 | #endif 209 | } 210 | 211 | void SinkWidget::onEncodingsChange() { 212 | #if HAVE_EXT_DEVICE_RESTORE_API 213 | pa_operation* o; 214 | uint8_t n_formats = 0; 215 | pa_format_info **formats; 216 | 217 | if (updating) 218 | return; 219 | 220 | formats = (pa_format_info**)malloc(sizeof(pa_format_info*) * PAVU_NUM_ENCODINGS); 221 | 222 | for (int i = 0; i < PAVU_NUM_ENCODINGS; ++i) { 223 | if (encodings[i].widget->get_active()) { 224 | formats[n_formats] = pa_format_info_new(); 225 | formats[n_formats]->encoding = encodings[i].encoding; 226 | ++n_formats; 227 | } 228 | } 229 | 230 | if (!(o = pa_ext_device_restore_save_formats(get_context(), PA_DEVICE_TYPE_SINK, index, n_formats, formats, NULL, NULL))) { 231 | show_error(this, _("pa_ext_device_restore_save_sink_formats() failed")); 232 | free(formats); 233 | return; 234 | } 235 | 236 | free(formats); 237 | pa_operation_unref(o); 238 | #endif 239 | } 240 | -------------------------------------------------------------------------------- /src/sinkwidget.h: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifndef sinkwidget_h 22 | #define sinkwidget_h 23 | 24 | #include "pavucontrol.h" 25 | #include "devicewidget.h" 26 | 27 | #if HAVE_EXT_DEVICE_RESTORE_API 28 | # include 29 | 30 | # define PAVU_NUM_ENCODINGS 8 31 | 32 | typedef struct { 33 | pa_encoding encoding; 34 | Gtk::CheckButton *widget; 35 | } encodingList; 36 | #endif 37 | 38 | class SinkWidget : public DeviceWidget { 39 | public: 40 | SinkWidget(BaseObjectType* cobject, const Glib::RefPtr& x); 41 | static SinkWidget* create(MainWindow* mainWindow); 42 | 43 | SinkType type; 44 | uint32_t monitor_index; 45 | bool can_decibel; 46 | 47 | #if HAVE_EXT_DEVICE_RESTORE_API 48 | encodingList encodings[PAVU_NUM_ENCODINGS]; 49 | Gtk::Grid *encodingSelect; 50 | #endif 51 | 52 | virtual void onMuteToggleButton(); 53 | virtual void executeVolumeUpdate(); 54 | virtual void onDefaultToggleButton(); 55 | void setDigital(bool); 56 | 57 | protected: 58 | virtual void onPortChange(); 59 | virtual void onEncodingsChange(); 60 | }; 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /src/sourceoutputwidget.cc: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifdef HAVE_CONFIG_H 22 | #include 23 | #endif 24 | 25 | #include "sourceoutputwidget.h" 26 | #include "mainwindow.h" 27 | #include "sourcewidget.h" 28 | 29 | #include "i18n.h" 30 | 31 | SourceOutputWidget::SourceOutputWidget(BaseObjectType* cobject, const Glib::RefPtr& x) : 32 | StreamWidget(cobject, x) { 33 | 34 | gchar *txt; 35 | directionLabel->set_label(txt = g_markup_printf_escaped("%s", _("from"))); 36 | g_free(txt); 37 | 38 | addKillMenu(_("Terminate Recording")); 39 | 40 | #if !HAVE_SOURCE_OUTPUT_VOLUMES 41 | /* Source Outputs do not have volume controls in versions of PA < 1.0 */ 42 | muteToggleButton->hide(); 43 | lockToggleButton->hide(); 44 | #endif 45 | } 46 | 47 | SourceOutputWidget* SourceOutputWidget::create(MainWindow* mainWindow) { 48 | SourceOutputWidget* w; 49 | Glib::RefPtr x = Gtk::Builder::create_from_resource("/org/pulseaudio/pavucontrol/ui/streamwidget.ui", "streamWidget"); 50 | w = Gtk::Builder::get_widget_derived(x, "streamWidget"); 51 | w->init(mainWindow); 52 | w->reference(); 53 | return w; 54 | } 55 | 56 | SourceOutputWidget::~SourceOutputWidget(void) { 57 | } 58 | 59 | void SourceOutputWidget::setSourceIndex(uint32_t idx) { 60 | mSourceIndex = idx; 61 | updateDeviceComboBox(); 62 | } 63 | 64 | uint32_t SourceOutputWidget::sourceIndex() { 65 | return mSourceIndex; 66 | } 67 | 68 | void SourceOutputWidget::updateDeviceComboBox() { 69 | Glib::ustring currentSourceName = UNKNOWN_DEVICE_NAME; 70 | 71 | deviceComboBox->remove_all(); 72 | 73 | for (auto i = mpMainWindow->sourceWidgets.begin(); i != mpMainWindow->sourceWidgets.end(); i++) { 74 | SourceWidget *source = i->second; 75 | 76 | deviceComboBox->append(source->name, source->description); 77 | 78 | if (source->index == mSourceIndex) 79 | currentSourceName = source->name; 80 | } 81 | 82 | if (currentSourceName == UNKNOWN_DEVICE_NAME) 83 | deviceComboBox->append(UNKNOWN_DEVICE_NAME, _("Unknown input")); 84 | 85 | deviceComboBox->set_active_id(currentSourceName); 86 | } 87 | 88 | #if HAVE_SOURCE_OUTPUT_VOLUMES 89 | void SourceOutputWidget::executeVolumeUpdate() { 90 | pa_operation* o; 91 | 92 | if (!(o = pa_context_set_source_output_volume(get_context(), index, &volume, NULL, NULL))) { 93 | show_error(this, _("pa_context_set_source_output_volume() failed")); 94 | return; 95 | } 96 | 97 | pa_operation_unref(o); 98 | } 99 | 100 | void SourceOutputWidget::onMuteToggleButton() { 101 | StreamWidget::onMuteToggleButton(); 102 | 103 | if (updating) 104 | return; 105 | 106 | pa_operation* o; 107 | if (!(o = pa_context_set_source_output_mute(get_context(), index, muteToggleButton->get_active(), NULL, NULL))) { 108 | show_error(this, _("pa_context_set_source_output_mute() failed")); 109 | return; 110 | } 111 | 112 | pa_operation_unref(o); 113 | } 114 | #endif 115 | 116 | void SourceOutputWidget::onKill(const Glib::VariantBase& parameter) { 117 | pa_operation* o; 118 | if (!(o = pa_context_kill_source_output(get_context(), index, NULL, NULL))) { 119 | show_error(this, _("pa_context_kill_source_output() failed")); 120 | return; 121 | } 122 | 123 | pa_operation_unref(o); 124 | } 125 | 126 | void SourceOutputWidget::onDeviceComboBoxChanged() { 127 | if (updating) 128 | return; 129 | 130 | Glib::ustring sourceName = deviceComboBox->get_active_id(); 131 | 132 | pa_operation *o = pa_context_move_source_output_by_name(get_context(), index, sourceName.c_str(), NULL, NULL); 133 | if (o) 134 | pa_operation_unref(o); 135 | } 136 | -------------------------------------------------------------------------------- /src/sourceoutputwidget.h: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifndef sourceoutputwidget_h 22 | #define sourceoutputwidget_h 23 | 24 | #include "pavucontrol.h" 25 | 26 | #include "streamwidget.h" 27 | 28 | class MainWindow; 29 | 30 | class SourceOutputWidget : public StreamWidget { 31 | public: 32 | SourceOutputWidget(BaseObjectType* cobject, const Glib::RefPtr& x); 33 | static SourceOutputWidget* create(MainWindow* mainWindow); 34 | ~SourceOutputWidget(void); 35 | 36 | SourceOutputType type; 37 | 38 | uint32_t index, clientIndex; 39 | void setSourceIndex(uint32_t idx); 40 | uint32_t sourceIndex(); 41 | void updateDeviceComboBox(); 42 | #if HAVE_SOURCE_OUTPUT_VOLUMES 43 | virtual void executeVolumeUpdate(); 44 | virtual void onMuteToggleButton(); 45 | #endif 46 | virtual void onKill(const Glib::VariantBase& parameter); 47 | virtual void onDeviceComboBoxChanged(); 48 | 49 | private: 50 | uint32_t mSourceIndex; 51 | }; 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /src/sourcewidget.cc: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifdef HAVE_CONFIG_H 22 | #include 23 | #endif 24 | 25 | #include "sourcewidget.h" 26 | 27 | #include "i18n.h" 28 | 29 | SourceWidget::SourceWidget(BaseObjectType* cobject, const Glib::RefPtr& x) : 30 | DeviceWidget(cobject, x) { 31 | } 32 | 33 | SourceWidget* SourceWidget::create(MainWindow* mainWindow) { 34 | SourceWidget* w; 35 | Glib::RefPtr x = Gtk::Builder::create_from_resource("/org/pulseaudio/pavucontrol/ui/devicewidget.ui", "deviceWidget"); 36 | w = Gtk::Builder::get_widget_derived(x, "deviceWidget"); 37 | w->init(mainWindow, "source"); 38 | w->reference(); 39 | return w; 40 | } 41 | 42 | void SourceWidget::executeVolumeUpdate() { 43 | pa_operation* o; 44 | 45 | if (!(o = pa_context_set_source_volume_by_index(get_context(), index, &volume, NULL, NULL))) { 46 | show_error(this, _("pa_context_set_source_volume_by_index() failed")); 47 | return; 48 | } 49 | 50 | pa_operation_unref(o); 51 | } 52 | 53 | void SourceWidget::onMuteToggleButton() { 54 | DeviceWidget::onMuteToggleButton(); 55 | 56 | if (updating) 57 | return; 58 | 59 | pa_operation* o; 60 | if (!(o = pa_context_set_source_mute_by_index(get_context(), index, muteToggleButton->get_active(), NULL, NULL))) { 61 | show_error(this, _("pa_context_set_source_mute_by_index() failed")); 62 | return; 63 | } 64 | 65 | pa_operation_unref(o); 66 | } 67 | 68 | void SourceWidget::onDefaultToggleButton() { 69 | pa_operation* o; 70 | 71 | if (updating) 72 | return; 73 | 74 | if (!(o = pa_context_set_default_source(get_context(), name.c_str(), NULL, NULL))) { 75 | show_error(this, _("pa_context_set_default_source() failed")); 76 | return; 77 | } 78 | pa_operation_unref(o); 79 | } 80 | 81 | void SourceWidget::onPortChange() { 82 | Gtk::TreeModel::iterator iter; 83 | 84 | if (updating) 85 | return; 86 | 87 | iter = portList->get_active(); 88 | if (iter) 89 | { 90 | Gtk::TreeModel::Row row = *iter; 91 | if (row) 92 | { 93 | pa_operation* o; 94 | Glib::ustring port = row[portModel.name]; 95 | 96 | if (!(o = pa_context_set_source_port_by_index(get_context(), index, port.c_str(), NULL, NULL))) { 97 | show_error(this, _("pa_context_set_source_port_by_index() failed")); 98 | return; 99 | } 100 | 101 | pa_operation_unref(o); 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/sourcewidget.h: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifndef sourcewidget_h 22 | #define sourcewidget_h 23 | 24 | #include "pavucontrol.h" 25 | 26 | #include "devicewidget.h" 27 | 28 | class SourceWidget : public DeviceWidget { 29 | public: 30 | SourceWidget(BaseObjectType* cobject, const Glib::RefPtr& x); 31 | static SourceWidget* create(MainWindow* mainWindow); 32 | 33 | SourceType type; 34 | bool can_decibel; 35 | 36 | virtual void onMuteToggleButton(); 37 | virtual void executeVolumeUpdate(); 38 | virtual void onDefaultToggleButton(); 39 | 40 | protected: 41 | virtual void onPortChange(); 42 | }; 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /src/streamwidget.cc: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifdef HAVE_CONFIG_H 22 | #include 23 | #endif 24 | 25 | #include "streamwidget.h" 26 | #include "mainwindow.h" 27 | #include "channelwidget.h" 28 | 29 | #include "i18n.h" 30 | 31 | /*** StreamWidget ***/ 32 | StreamWidget::StreamWidget(BaseObjectType* cobject, const Glib::RefPtr& x) : 33 | MinimalStreamWidget(cobject), 34 | mpMainWindow(NULL) { 35 | 36 | /* MinimalStreamWidget member variables. */ 37 | channelsVBox = x->get_widget("streamChannelsVBox"); 38 | nameLabel = x->get_widget("streamNameLabel"); 39 | boldNameLabel = x->get_widget("streamBoldNameLabel"); 40 | iconImage = x->get_widget("streamIconImage"); 41 | 42 | lockToggleButton = x->get_widget("streamLockToggleButton"); 43 | muteToggleButton = x->get_widget("streamMuteToggleButton"); 44 | directionLabel = x->get_widget("directionLabel"); 45 | deviceComboBox = x->get_widget("deviceComboBox"); 46 | 47 | muteToggleButton->signal_clicked().connect(sigc::mem_fun(*this, &StreamWidget::onMuteToggleButton)); 48 | lockToggleButton->signal_clicked().connect(sigc::mem_fun(*this, &StreamWidget::onLockToggleButton)); 49 | deviceComboBox->signal_changed().connect(sigc::mem_fun(*this, &StreamWidget::onDeviceComboBoxChanged)); 50 | 51 | for (unsigned i = 0; i < PA_CHANNELS_MAX; i++) 52 | channelWidgets[i] = NULL; 53 | } 54 | 55 | void StreamWidget::init(MainWindow* mainWindow) { 56 | mpMainWindow = mainWindow; 57 | 58 | MinimalStreamWidget::init(); 59 | } 60 | 61 | void StreamWidget::addKillMenu(const char* killLabel) { 62 | auto gesture = Gtk::GestureClick::create(); 63 | gesture->set_button(3); 64 | gesture->set_exclusive(true); 65 | gesture->signal_pressed().connect(sigc::mem_fun(*this, &StreamWidget::onContextTriggerEvent)); 66 | this->add_controller(gesture); 67 | 68 | const std::string actionName = "kill", groupName="streamwidget"; 69 | auto action = Gio::SimpleAction::create(actionName); 70 | action->set_enabled(true); 71 | action->signal_activate().connect(sigc::mem_fun(*this, &StreamWidget::onKill)); 72 | 73 | auto group = Gio::SimpleActionGroup::create(); 74 | group->add_action(action); 75 | 76 | insert_action_group(groupName, group); 77 | 78 | auto menuModel = Gio::Menu::create(); 79 | menuModel->append(killLabel, groupName + "." + actionName); 80 | contextMenu.set_menu_model(menuModel); 81 | contextMenu.set_parent(*this); 82 | } 83 | 84 | 85 | void StreamWidget::onContextTriggerEvent(gint n_press, gdouble x, gdouble y) { 86 | if (n_press == 1) { 87 | contextMenu.set_pointing_to(Gdk::Rectangle {(int) x, (int) y, 0 , 0}); 88 | contextMenu.popup(); 89 | } 90 | } 91 | 92 | void StreamWidget::setChannelMap(const pa_channel_map &m, bool can_decibel) { 93 | channelMap = m; 94 | 95 | ChannelWidget::create(this, m, can_decibel, channelWidgets); 96 | 97 | for (int i = 0; i < m.channels; i++) { 98 | ChannelWidget *cw = channelWidgets[i]; 99 | channelsVBox->prepend(*cw); 100 | cw->unreference(); 101 | } 102 | 103 | channelWidgets[m.channels-1]->setBaseVolume(PA_VOLUME_NORM); 104 | 105 | lockToggleButton->set_sensitive(m.channels > 1); 106 | hideLockedChannels(lockToggleButton->get_active()); 107 | } 108 | 109 | void StreamWidget::setVolume(const pa_cvolume &v, bool force) { 110 | g_assert(v.channels == channelMap.channels); 111 | 112 | volume = v; 113 | 114 | if (timeoutConnection.empty() || force) { /* do not update the volume when a volume change is still in flux */ 115 | for (int i = 0; i < volume.channels; i++) 116 | channelWidgets[i]->setVolume(volume.values[i]); 117 | } 118 | } 119 | 120 | void StreamWidget::updateChannelVolume(int channel, pa_volume_t v) { 121 | pa_cvolume n; 122 | g_assert(channel < volume.channels); 123 | 124 | n = volume; 125 | if (lockToggleButton->get_active()) { 126 | for (int i = 0; i < n.channels; i++) 127 | n.values[i] = v; 128 | } else 129 | n.values[channel] = v; 130 | 131 | setVolume(n, true); 132 | 133 | if (timeoutConnection.empty()) 134 | timeoutConnection = Glib::signal_timeout().connect(sigc::mem_fun(*this, &StreamWidget::timeoutEvent), 100); 135 | } 136 | 137 | void StreamWidget::hideLockedChannels(bool hide) { 138 | for (int i = 0; i < channelMap.channels - 1; i++) 139 | channelWidgets[i]->set_visible(!hide); 140 | 141 | channelWidgets[channelMap.channels - 1]->channelLabel->set_visible(!hide); 142 | } 143 | 144 | void StreamWidget::onMuteToggleButton() { 145 | 146 | lockToggleButton->set_sensitive(!muteToggleButton->get_active()); 147 | 148 | for (int i = 0; i < channelMap.channels; i++) 149 | channelWidgets[i]->set_sensitive(!muteToggleButton->get_active()); 150 | } 151 | 152 | void StreamWidget::onLockToggleButton() { 153 | hideLockedChannels(lockToggleButton->get_active()); 154 | } 155 | 156 | bool StreamWidget::timeoutEvent() { 157 | executeVolumeUpdate(); 158 | return false; 159 | } 160 | 161 | void StreamWidget::executeVolumeUpdate() { 162 | } 163 | 164 | void StreamWidget::onKill(const Glib::VariantBase& parameter) { 165 | } 166 | 167 | void StreamWidget::onDeviceComboBoxChanged() { 168 | } 169 | -------------------------------------------------------------------------------- /src/streamwidget.h: -------------------------------------------------------------------------------- 1 | /*** 2 | This file is part of pavucontrol. 3 | 4 | Copyright 2006-2008 Lennart Poettering 5 | Copyright 2009 Colin Guthrie 6 | 7 | pavucontrol is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | pavucontrol is distributed in the hope that it will be useful, but 13 | WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with pavucontrol. If not, see . 19 | ***/ 20 | 21 | #ifndef streamwidget_h 22 | #define streamwidget_h 23 | 24 | #include "pavucontrol.h" 25 | 26 | #include "minimalstreamwidget.h" 27 | 28 | class MainWindow; 29 | class ChannelWidget; 30 | 31 | /* Used as the ID for the unknown device item in deviceComboBox. */ 32 | #define UNKNOWN_DEVICE_NAME "#unknown#" 33 | 34 | class StreamWidget : public MinimalStreamWidget { 35 | public: 36 | StreamWidget(BaseObjectType* cobject, const Glib::RefPtr& x); 37 | void init(MainWindow* mainWindow); 38 | 39 | void setChannelMap(const pa_channel_map &m, bool can_decibel); 40 | void setVolume(const pa_cvolume &volume, bool force = false); 41 | virtual void updateChannelVolume(int channel, pa_volume_t v); 42 | 43 | void hideLockedChannels(bool hide = true); 44 | 45 | Gtk::ToggleButton *lockToggleButton, *muteToggleButton; 46 | Gtk::Label *directionLabel; 47 | Gtk::ComboBoxText *deviceComboBox; 48 | 49 | pa_channel_map channelMap; 50 | pa_cvolume volume; 51 | 52 | ChannelWidget *channelWidgets[PA_CHANNELS_MAX]; 53 | 54 | virtual void onMuteToggleButton(); 55 | virtual void onLockToggleButton(); 56 | virtual void onContextTriggerEvent(gint n_press, gdouble x, gdouble y); 57 | 58 | sigc::connection timeoutConnection; 59 | 60 | bool timeoutEvent(); 61 | 62 | virtual void executeVolumeUpdate(); 63 | virtual void onKill(const Glib::VariantBase& parameter); 64 | virtual void onDeviceComboBoxChanged(); 65 | 66 | protected: 67 | MainWindow* mpMainWindow; 68 | 69 | Gtk::PopoverMenu contextMenu; 70 | void addKillMenu(const char* killLabel); 71 | }; 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /src/streamwidget.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | vertical 5 | 6 | 7 | center 8 | vertical 9 | 6 10 | 12 11 | 12 12 | 12 13 | 12 14 | 15 | 16 | center 17 | 6 18 | 19 | 20 | gtk-missing-image 21 | 22 | 23 | 24 | 25 | 1 26 | 2 27 | 28 | 29 | 1 30 | 31 | 32 | 33 | 34 | Stream Title 35 | 1 36 | end 37 | 0 38 | 39 | 40 | 41 | 42 | 4 43 | 4 44 | direction 45 | 1 46 | 0 47 | 48 | 49 | 50 | 51 | GTK_ALIGN_END 52 | 1 53 | 54 | 55 | 56 | 57 | 58 | 59 | 3 60 | 61 | 62 | center 63 | 1 64 | Mute audio 65 | 0 66 | 67 | 68 | audio-volume-muted 69 | normal 70 | 71 | 72 | 73 | 74 | 75 | 76 | center 77 | 1 78 | Lock channels together 79 | 0 80 | 1 81 | 82 | 83 | changes-prevent 84 | normal 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | center 96 | vertical 97 | 6 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | center 111 | 112 | 113 | 114 | 115 | --------------------------------------------------------------------------------