Commit e3c2ca42 authored by Josué Ortega's avatar Josué Ortega

Import Upstream version 1.5

parents
-xc++
-Iinclude
-std=c++11
-I/usr/include/ncursesw
tools/
build/
Makefile.in
aclocal.m4
autom4te.cache/
config.h.in
configure
The MIT License
Copyright (c) 2016 Joshua Jensch
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
bin_PROGRAMS = pamix
pamix_CXXFLAGS = -I$(top_srcdir)/include -pthread $(NCURSES_CFLAGS) $(PULSEAUDIO_CFLAGS)
pamix_LDADD = $(NCURSES_LIBS) $(PULSEAUDIO_LIBS)
pamix_SOURCES = \
src/cardentry.cpp \
src/volumeutil.cpp\
src/sinkentry.cpp\
src/sourceentry.cpp\
src/pamix.cpp\
src/sourceoutputentry.cpp\
src/sinkinputentry.cpp\
src/entry.cpp\
src/configuration.cpp\
src/pamix_functions.cpp\
src/painterface.cpp
man1_MANS = man/pamix.1
pkgsysconfdir = $(sysconfdir)
dist_pkgsysconf_DATA = pamix.conf
# PAMix - the pulseaudio terminal mixer
# Table of Contents #
1. [**Installation**](#installation)
1. Gentoo
2. Arch
2. [**Building Manually**](#building-manually)
1. Dependencies
2. Configuration
3. Building
4. Installing
3. [**Configuration**](#configuration)
4. [**Default Keybindings**](#default-keybindings)
![alt tag](http://i.imgur.com/NuzrAXZ.gif)
# Installation: #
### Gentoo ###
`emerge media-sound/pamix`
### Arch ###
`yaourt -S pamix-git`
# Building Manually: #
## Dependencies: #
### Build ##
* autoconf
* autoconf-archive
* pkg-config
* make
### Runtime ##
* PulseAudio
* Ncurses
## Autoconf Configuration ##
Generate configure script by running `autoreconf -i` and then run `./configure` with your preferred options
### Options ###
`--disable-unicode` depends on ncurses instead of ncursesw and replaces unicode symbols with ascii
## Building ##
Run `make`
## Installing ##
Run `make install`
---
# Configuration #
Configure pamix and set keybindings using pamix.conf (see [**Configuration**](https://github.com/patroclos/PAmix/wiki/Configuration) for detailed instructions)
# Default Keybindings #
(arrow keys are also supported instead of hjkl)
| Action | Key |
|----------------------------|-----|
| Playback tab | F1 |
| Recording Tab | F2 |
| Output Devices | F3 |
| Input Devices | F4 |
| Set volume to percentage | 0-9 |
| Decrease Volume | h |
| Increase Volume | l |
| Select Next | j |
| Jump to next Entry | J |
| Select Previous | k |
| Jump to previous Entry | K |
| (Un)Mute | m |
| Next/Previous device/port | s/S |
| (Un)Lock Channels together | c |
| Quit | q |
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.69])
AC_INIT([pamix], [1.2], [jenschjoshua@gmail.com])
AC_CONFIG_SRCDIR([include/entry.hpp])
AC_CONFIG_AUX_DIR([tools])
AC_CONFIG_HEADERS([config.h])
AM_INIT_AUTOMAKE([-Wall -Werror foreign subdir-objects])
AC_LANG([C++])
AX_CXX_COMPILE_STDCXX_11()
# Checks for programs.
AC_PROG_CXX
# Check options
AC_MSG_CHECKING([--enable-unicode argument])
AC_ARG_ENABLE([unicode], AS_HELP_STRING([--enable-unicode], [Enable unicode support.]), [enable_unicode=$enableval], [enable_unicode="yes"])
AC_MSG_RESULT([$enable_unicode])
if test "$enable_unicode" = "yes"; then
AC_DEFINE([FEAT_UNICODE], [1], [Enable unicode support])
fi
# Checks for libraries.
AS_IF([test "$enable_unicode" = "yes"], [PKG_CHECK_MODULES([NCURSES], [ncursesw])], [PKG_CHECK_MODULES([NCURSES], [ncurses])])
AC_SEARCH_LIBS([use_default_colors], [ncurses curses],
AC_DEFINE(HAVE_USE_DEFAULT_COLORS, 1, [Define to 1 if the curses library has the function `use_default_colors`. ]))
PKG_CHECK_MODULES([PULSEAUDIO], [libpulse])
# Checks for header files.
AC_CHECK_HEADERS([locale.h])
# Checks for typedefs, structures, and compiler characteristics.
AC_CHECK_HEADER_STDBOOL
AC_C_INLINE
AC_TYPE_SIZE_T
AC_TYPE_UINT32_T
AC_TYPE_UINT8_T
# Checks for library functions.
AC_CHECK_FUNCS([memset setlocale])
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
#pragma once
#include <map>
#include <memory>
#include <pamix_functions.hpp>
#include <string>
#include <vector>
#define CONFIGURATION_AUTOSPAWN_PULSE "pulseaudio_autospawn"
#define CONFIGURATION_DEFAULT_TAB "default_tab"
struct keybind_t
{
union argument_t m_Argument;
void (*m_Function)(argument_t arg);
};
class Configuration
{
private:
std::map<std::string, std::string> m_Variables;
std::map<int, std::vector<keybind_t>> m_Bindings;
static std::vector<std::pair<int, const char *>> m_Keynames;
static int getKeycode(const std::string &name);
public:
bool has(const std::string &key);
std::string getString(const std::string &key);
int getInt(const std::string &key);
bool getBool(const std::string &key);
bool pressKey(int code);
void set(const std::string &key, const std::string &value);
void unset(const std::string &key);
void bind(const std::string &key, const std::string &cmd, const std::string &arg = "");
void unbind(const std::string &key);
void unbindall();
static bool loadFile(Configuration *config, const std::string &path);
};
#pragma once
#include <painterface.hpp>
#include <pulse/pulseaudio.h>
#include <string>
#include <vector>
#include <volumeutil.hpp>
class PAInterface;
enum entry_type
{
ENTRY_SINK,
ENTRY_SOURCE,
ENTRY_SINKINPUT,
ENTRY_SOURCEOUTPUT,
ENTRY_CARDS,
ENTRY_COUNT
};
struct Entry
{
PAInterface * interface;
std::string m_Name;
uint32_t m_Index;
double m_Peak = 0;
pa_stream * m_Monitor = nullptr;
uint32_t m_MonitorIndex;
bool m_Mute;
pa_cvolume m_PAVolume;
pa_channel_map m_PAChannelMap;
bool m_Lock = true;
bool m_Kill;
bool m_Meter = true;
Entry(PAInterface *iface);
~Entry();
// general methods
virtual void setVolume(const int channel, const pa_volume_t volume) = 0;
virtual void setMute(bool mute) = 0;
virtual void addVolume(const int channel, const double deltaPct);
virtual void cycleSwitch(bool increment) = 0;
virtual void update(const pa_sink_info *info) {}
virtual void update(const pa_source_info *info) {}
virtual void update(const pa_sink_input_info *info) {}
virtual void update(const pa_source_output_info *info) {}
virtual void update(const pa_card_info *info) {}
// device methods
virtual void suspend() {}
virtual void setPort(const char *port) {}
// stream methods
virtual void move(uint32_t idx) {}
virtual void kill() {}
};
struct DeviceEntry : public Entry
{
int m_Port;
std::vector<std::string> m_Ports;
DeviceEntry(PAInterface *iface)
: Entry(iface){};
// general methods
virtual void setVolume(const int channel, const pa_volume_t volume) = 0;
virtual void setMute(bool mute) = 0;
virtual void cycleSwitch(bool increment) = 0;
virtual std::string getPort() { return m_Port > -1 ? m_Ports[m_Port] : ""; }
virtual void setPort(const char *port) = 0;
};
struct StreamEntry : public Entry
{
uint32_t m_Device;
StreamEntry(PAInterface *iface)
: Entry(iface){};
// general methods
virtual void setVolume(const int channel, const pa_volume_t volume) = 0;
virtual void setMute(bool mute) = 0;
virtual void cycleSwitch(bool increment) = 0;
virtual void move(uint32_t idx) = 0;
virtual void kill() = 0;
};
struct SinkEntry : public DeviceEntry
{
pa_sink_state_t m_State;
SinkEntry(PAInterface *iface)
: DeviceEntry(iface) {}
void update(const pa_sink_info *info);
// general methods
virtual void setVolume(const int channel, const pa_volume_t volume);
virtual void setMute(bool mute);
virtual void cycleSwitch(bool increment);
virtual void setPort(const char *port);
};
struct SourceEntry : public DeviceEntry
{
pa_source_state_t m_State;
SourceEntry(PAInterface *iface)
: DeviceEntry(iface) {}
void update(const pa_source_info *info);
// general methods
virtual void setVolume(const int channel, const pa_volume_t volume);
virtual void setMute(bool mute);
virtual void cycleSwitch(bool increment);
virtual void setPort(const char *port);
};
struct SinkInputEntry : public StreamEntry
{
void update(const pa_sink_input_info *info);
SinkInputEntry(PAInterface *iface)
: StreamEntry(iface) {}
// general methods
virtual void setVolume(const int channel, const pa_volume_t volume);
virtual void setMute(bool mute);
virtual void cycleSwitch(bool increment);
virtual void move(uint32_t idx);
virtual void kill();
};
struct SourceOutputEntry : public StreamEntry
{
void update(const pa_source_output_info *info);
SourceOutputEntry(PAInterface *iface)
: StreamEntry(iface) {}
// general methods
virtual void setVolume(const int channel, const pa_volume_t volume);
virtual void setMute(bool mute);
virtual void cycleSwitch(bool increment);
virtual void move(uint32_t idx);
virtual void kill();
};
struct CardEntry : public Entry
{
struct CardProfile
{
std::string name;
std::string description;
CardProfile(pa_card_profile_info2 *profile)
: name(profile->name)
, description(profile->description) {};
};
int m_Profile;
std::vector<CardProfile> m_Profiles;
void update(const pa_card_info *info);
CardEntry(PAInterface *iface)
: Entry(iface) {}
virtual void cycleSwitch(bool increment);
virtual void setVolume(const int channel, const pa_volume_t volume) {}
virtual void setMute(bool mute) {}
virtual void move(uint32_t idx) {}
virtual void kill() {}
};
#pragma once
#include <assert.h>
#include <cstring>
#include <ctgmath>
#include <entry.hpp>
#include <map>
#include <mutex>
#include <pulse/pulseaudio.h>
#include <thread>
#include <vector>
struct mainloop_lockguard
{
pa_threaded_mainloop *m;
mainloop_lockguard(pa_threaded_mainloop *m);
~mainloop_lockguard();
};
class PAInterface;
struct Entry;
//define subscription masks
#define PAI_SUBSCRIPTION_MASK_PEAK 0x1U
#define PAI_SUBSCRIPTION_MASK_INFO 0x2U
#define PAI_SUBSCRIPTION_MASK_OTHER 0x4U
typedef uint32_t pai_subscription_type_t;
typedef void (*pai_subscription_cb)(PAInterface *, const pai_subscription_type_t);
typedef std::map<uint32_t, std::unique_ptr<Entry>>::iterator iter_entry_t;
class PAInterface
{
private:
bool m_Autospawn;
const char * m_ContextName;
pa_threaded_mainloop *m_Mainloop;
pa_mainloop_api * m_MainloopApi;
pa_context * m_Context;
std::map<uint32_t, std::unique_ptr<Entry>> m_Sinks;
std::map<uint32_t, std::unique_ptr<Entry>> m_Sources;
std::map<uint32_t, std::unique_ptr<Entry>> m_SinkInputs;
std::map<uint32_t, std::unique_ptr<Entry>> m_SourceOutputs;
std::map<uint32_t, std::unique_ptr<Entry>> m_Cards;
pai_subscription_cb m_Subscription_callback;
private:
static void signal_mainloop(void *interface);
static void _updateSinks(PAInterface *interface);
static void _updateSources(PAInterface *interface);
static void _updateInputs(PAInterface *interface);
static void _updateOutputs(PAInterface *interface);
static void _updateCards(PAInterface *interface);
static void _updatethread(pai_subscription_type_t paisubtype, pa_subscription_event_type_t type, PAInterface *interface);
//member methods
void updateSinks() { _updateSinks(this); }
void updateSources() { _updateSources(this); }
void updateInputs() { _updateInputs(this); }
void updateOutputs() { _updateOutputs(this); }
void updateCards() { _updateCards(this); }
void notifySubscription(const pai_subscription_type_t);
public:
PAInterface(const char *context_name);
~PAInterface();
inline pa_threaded_mainloop *getPAMainloop() { return m_Mainloop; }
inline pa_context * getPAContext() { return m_Context; }
bool connect();
bool isConnected();
void setAutospawn(bool as) { m_Autospawn = as; }
bool getAutospawn() { return m_Autospawn; }
void subscribe(pai_subscription_cb callback);
std::mutex m_ModifyMutex;
std::map<uint32_t, std::unique_ptr<Entry>> &getSinks() { return m_Sinks; }
std::map<uint32_t, std::unique_ptr<Entry>> &getSources() { return m_Sources; }
std::map<uint32_t, std::unique_ptr<Entry>> &getSinkInputs() { return m_SinkInputs; }
std::map<uint32_t, std::unique_ptr<Entry>> &getSourceOutputs() { return m_SourceOutputs; }
std::map<uint32_t, std::unique_ptr<Entry>> &getCards() { return m_Cards; }
std::vector<std::unique_ptr<std::pair<PAInterface *, Entry *>>> m_IEPairs;
void createMonitorStreamForEntry(Entry *entry, int type);
//PulseAudio API Callbacks
//userptr points to current PAInterface instance
static void cb_context_state(pa_context *context, void *interface);
static void cb_context_drain_complete(pa_context *context, void *null);
static void cb_success(pa_context *context, int success, void *interface);
static void cb_subscription_event(pa_context *context, pa_subscription_event_type_t type, uint32_t idx, void *interface);
static void cb_sink_info(pa_context *context, const pa_sink_info *info, int eol, void *interface);
static void cb_source_info(pa_context *context, const pa_source_info *info, int eol, void *interface);
static void cb_sink_input_info(pa_context *context, const pa_sink_input_info *info, int eol, void *interface);
static void cb_source_output_info(pa_context *context, const pa_source_output_info *info, int eol, void *interface);
static void cb_card_info(pa_context *context, const pa_card_info *info, int eol, void *interface);
static void cb_read(pa_stream *stream, size_t nbytes, void *iepair);
static void cb_stream_state(pa_stream *stream, void *entry);
};
#pragma once
#include <painterface.hpp>
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
struct UpdateData
{
bool redrawAll;
UpdateData() = default;
UpdateData(bool redrawAll) { this->redrawAll = redrawAll; }
};
#define DECAY_STEP 0.04
#define MAX_VOL 1.5
#ifdef FEAT_UNICODE
#define SYM_VOLBAR L'\u25ae' //▮
#define SYM_ARROW "\u25b6 " //▶
#define SYM_MUTE "🔇"
#define SYM_LOCK "🔒"
#define SYM_SPACE L' '
#define FEAT_UNICODE_STRING std::wstring
#define FEAT_UNICODE_MVADDNSTR(y, x, str, n) mvaddnwstr(y, x, str, n);
#else
#define SYM_VOLBAR '|'
#define SYM_ARROW "> "
#define SYM_MUTE "M"
#define SYM_LOCK "L"
#define SYM_SPACE ' '
#define FEAT_UNICODE_STRING std::string
#define FEAT_UNICODE_MVADDNSTR(y, x, str, n) mvaddnstr(y, x, str, n);
#endif
void quit();
void signal_update(bool all);
void selectEntries(PAInterface *interfae, entry_type type);
void set_volume(PAInterface *interface, double pct);
void add_volume(PAInterface *interface, double pct);
void cycle_switch(PAInterface *interface, bool inc);
void set_mute(PAInterface *interface, bool mute);
void toggle_mute(PAInterface *interface);
void set_lock(PAInterface *interface, bool lock);
void toggle_lock(PAInterface *interface);
void select_next(PAInterface *interface, bool precise);
void select_previous(PAInterface *interface, bool precise);
#pragma once
#include <pamix.hpp>
#include <string>
union argument_t {
double d;
int i;
bool b;
};
void pamix_set_interface(PAInterface *interface);
void pamix_quit(argument_t arg);
void pamix_select_tab(argument_t arg);
void pamix_select_next(argument_t arg);
void pamix_select_prev(argument_t arg);
void pamix_set_volume(argument_t arg);
void pamix_add_volume(argument_t arg);
void pamix_cycle_next(argument_t arg);
void pamix_cycle_prev(argument_t arg);
void pamix_toggle_lock(argument_t arg);
void pamix_set_lock(argument_t arg);
void pamix_toggle_mute(argument_t arg);
void pamix_set_mute(argument_t arg);
#pragma once
#include <ctgmath>
#include <pulse/volume.h>
pa_cvolume *volume_pct_delta(pa_cvolume *vol, int channel, double pctDelta);
./" this is the manpage of the pamix pulseaudio ncurses mixer
.TH pamix 1 "05 Sep 2016" "V1.4" "pamix man page"
.SH SYONPSIS
pamix
.SH DESCRIPTION
This is a pavucontrol inspired ncurses based pulseaudio mixer for the commandline
.SH CONFIGURATION
pamix is configured using a file called pamix.conf inside the $XDG_CONFIG_HOME or $XDG_CONFIG_DIRS directories or their default values, should they not be set.
.br
$XDG_CONFIG_HOME will be preferred over $XDG_CONFIG_DIRS.
.br
.start
.SH COMMANDS
.PP
PAmix conf files support the following commands:
.br
* set
* bind
* unbind
* unbindall
.PP
characters after a ';' will be interpreted as comments and ignored
.SH set
.PP
\fBSYNOPSIS:\fP set KEY=VALUE
.PP
set is used to set a variable. This is currently only relevant for the 'pulseaudio\_autospawn' and 'default\_tab' options.
.SH bind
.PP
\fBSYNOPSIS:\fP bind KEYNAME MIXER\-COMMAND [ARGUMENT]
.PP
bind is used to bind a keyname to a mixer\-command.
.br
Some mixer\-commands require an argument.
.br
You can bind a keyname to multiple mixer\-commands.
.SH unbind
.PP
\fBSYNOPSIS:\fP unbind KEYNAME
.PP
unbind will remove all bindings for the given keyname
.SH unbindall
.PP
\fBSYNOPSIS:\fP unbindall
.PP
unbindall will remove all key bindings
.SH PAMIX\-COMMANDS
.PP
Pamix\-Commands can be bound to keys using the bind command and are used to interact with pamix.
.br
The following pamix\-commands are currently supported:
* quit
* select\-tab
* select\-next
* select\-prev
* set\-volume
* add\-volume
* cycle\-next
* cycle\-prev
* toggle\-lock
* set\-lock
* toggle\-mute
* set\-mute
.SH quit
.PP
quit will cause PAmix to exit and takes no arguments.
.SH select\-tab
.PP
select\-tab will select one of the following tabs: Output Devices, Input Devices, Playback, Recording, Cards
.br
select\-tab takes the number of the tab to switch to starting at 0 in the order mentioned.
.SH select\-next and select\-prev
.PP
these commands are given the optional argument 'channel' they will select the next and previous channels.
if no argument is given they will select the next and previous entry in the displayed tab.
.SH set\-volume
.PP
this command takes the targetvalue in form of a double as an argument.
.br
depending on weather channels are locked, this command will set the volume of the selected entry/channel to the targetvalue given in the argument.
.br
\fIExample:\fP bind 0 set\-volume 1.0 \fI; this will set the volume to 100%\fP
.SH add\-volume
.PP
this command takes a deltavalue in form of a double as an argument.
.br
the deltavalue can be negative
\fIExample:\fP bind h add\-volume \-0.05 \fI; this will reduce the volume by 5%\fP
.SH cycle\-next and cycle\-prev
.PP
these commands will change the device or port of the currently selected entry.
.br
they dont take any arguments.
.SH toggle\-lock
.PP
this command toggles weather channels should be locked together for the currently selected entry
.br
and takes no arguments.
.SH set\-lock
.PP
this command takes either '0' or '1' as an argument and sets the channel\-lock like the toggle\-lock mixer\-command.
.SH toggle\-mute
.PP
toggles weather the currently selected entry is muted
.br
and takes no arguments.
.SH set\-mute
.PP
works like the set\-lock mixer\-command, but sets weather the currently selected entry is muted or not
.stop