From 2a529357b7516922c600e5e95815da9f32dda3c9 Mon Sep 17 00:00:00 2001 From: Dave Reisner Date: Mon, 31 Dec 2012 17:05:35 -0500 Subject: initial commit --- Makefile | 22 +++ ponymix.cc | 520 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ pulse.cc | 561 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ pulse.h | 216 ++++++++++++++++++++++++ 4 files changed, 1319 insertions(+) create mode 100644 Makefile create mode 100644 ponymix.cc create mode 100644 pulse.cc create mode 100644 pulse.h diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e6cb6ca --- /dev/null +++ b/Makefile @@ -0,0 +1,22 @@ +CXX = g++ -std=c++11 + +base_CFLAGS = -Wall -Wextra -pedantic -O2 -g +base_LIBS = -lm + +libpulse_CFLAGS = $(shell pkg-config --cflags libpulse) +libpulse_LIBS = $(shell pkg-config --libs libpulse) + +CXXFLAGS := $(base_CFLAGS) $(libpulse_CFLAGS) $(CXXFLAGS) +LDLIBS := $(base_LIBS) $(libpulse_LIBS) + +all: ponymix + +ponymix: ponymix.cc pulse.o +pulse.o: pulse.cc pulse.h + +install: ponymix + install -Dm755 ponymix $(DESTDIR)/usr/bin/ponymix + install -Dm644 ponymix.1 $(DESTDIR)/usr/share/man/man1/ponymix.1 + +clean: + $(RM) ponymix pulse.o diff --git a/ponymix.cc b/ponymix.cc new file mode 100644 index 0000000..1bd256f --- /dev/null +++ b/ponymix.cc @@ -0,0 +1,520 @@ +#include "pulse.h" + +#include +#include + +#include +#include +#include + +#define _unused_ __attribute__((unused)) + +enum Action { + ACTION_DEFAULTS, + ACTION_LIST, + ACTION_LISTCARDS, + ACTION_LISTPROFILES, + ACTION_GETVOL, + ACTION_SETVOL, + ACTION_GETBAL, + ACTION_SETBAL, + ACTION_ADJBAL, + ACTION_INCREASE, + ACTION_DECREASE, + ACTION_MUTE, + ACTION_UNMUTE, + ACTION_TOGGLE, + ACTION_ISMUTED, + ACTION_SETDEFAULT, + ACTION_GETPROFILE, + ACTION_SETPROFILE, + ACTION_MOVE, + ACTION_KILL, + ACTION_INVALID, +}; + +static enum DeviceType opt_devtype; +static enum Action opt_action; +static const char* opt_device; +static const char* opt_card; + +static const int kMinVolume = 0; +static const int kMaxVolume = 150; +static const int kMinBalance = -100; +static const int kMaxBalance = 100; + +static const char* type_to_string(enum DeviceType t) { + switch (t) { + case DEVTYPE_SINK: + return "sink"; + case DEVTYPE_SOURCE: + return "source"; + case DEVTYPE_SINK_INPUT: + return "sink input"; + case DEVTYPE_SOURCE_OUTPUT: + return "source output"; + } + + /* impossibiru! */ + return NULL; +} + +static enum Action string_to_action(const char* str) { + static std::map actionmap = { + { "defaults", ACTION_DEFAULTS }, + { "list", ACTION_LIST }, + { "list-cards", ACTION_LISTCARDS }, + { "list-profiles", ACTION_LISTPROFILES }, + { "get-volume", ACTION_GETVOL }, + { "set-volume", ACTION_SETVOL }, + { "get-balance", ACTION_GETBAL }, + { "set-balance", ACTION_SETBAL }, + { "adj-balance", ACTION_ADJBAL }, + { "increase", ACTION_INCREASE }, + { "decrease", ACTION_DECREASE }, + { "mute", ACTION_MUTE }, + { "unmute", ACTION_UNMUTE }, + { "toggle", ACTION_TOGGLE }, + { "is-muted", ACTION_ISMUTED }, + { "set-default", ACTION_SETDEFAULT }, + { "get-profile", ACTION_GETPROFILE }, + { "set-profile", ACTION_SETPROFILE }, + { "move", ACTION_MOVE }, + { "kill", ACTION_KILL } + }; + + try { + return actionmap.at(str); + } catch(std::out_of_range) { + errx(1, "error: Invalid option specified: %s", str); + } +} + +static enum DeviceType string_to_devtype_or_die(const char* str) { + static std::map typemap = { + { "sink", DEVTYPE_SINK }, + { "source", DEVTYPE_SOURCE }, + { "sink-input", DEVTYPE_SINK_INPUT }, + { "source-output", DEVTYPE_SOURCE_OUTPUT }, + }; + try { + return typemap.at(str); + } catch(std::out_of_range) { + errx(1, "error: Invalid device type specified: %s", str); + } +} + +static Device* string_to_device(Pulse& ponymix, string arg, enum DeviceType type) { + switch (type) { + case DEVTYPE_SINK: + return ponymix.GetSink(arg); + case DEVTYPE_SOURCE: + return ponymix.GetSource(arg); + case DEVTYPE_SOURCE_OUTPUT: + return ponymix.GetSourceOutput(arg); + case DEVTYPE_SINK_INPUT: + return ponymix.GetSinkInput(arg); + default: + return nullptr; + } +} + +static Device* string_to_device_or_die(Pulse& ponymix, + string arg, + enum DeviceType type) { + Device* device = string_to_device(ponymix, arg, type); + if (device == nullptr) errx(1, "no match found for device: %s", arg.c_str()); + return device; +} + +static void Print(const Device& device) { + printf("%s %d: %s\n" + " %s\n" + " Avg. Volume: %d%%\n", + type_to_string(device.Type()), + device.Index(), + device.Name().c_str(), + device.Desc().c_str(), + device.Volume()); +} + +static void Print(const Card& card) { + printf("%s\n", card.Name().c_str()); +} + +static void Print(const Profile& profile, bool active) { + printf("%s: %s%s\n", + profile.name.c_str(), profile.desc.c_str(), active ? " [active]" : ""); +} + +static int ShowDefaults(Pulse& ponymix, + int argc _unused_, + char* argv[] _unused_) { + const auto& info = ponymix.GetDefaults(); + Print(*ponymix.GetSink(info.sink)); + Print(*ponymix.GetSource(info.source)); + return 0; +} + +static int List(Pulse& ponymix, int argc _unused_, char* argv[] _unused_) { + if (argc != 0) errx(1, "error: list requires 0 arguments"); + + const auto& sinks = ponymix.GetSinks(); + for (const auto& s : sinks) Print(s); + + const auto& sources = ponymix.GetSources(); + for (const auto& s : sources) Print(s); + + const auto& sinkinputs = ponymix.GetSinkInputs(); + for (const auto& s : sinkinputs) Print(s); + + const auto& sourceoutputs = ponymix.GetSourceOutputs(); + for (const auto& s : sourceoutputs) Print(s); + + return 0; +} + +static int ListCards(Pulse& ponymix, int argc _unused_, char* argv[] _unused_) { + if (argc != 0) errx(1, "error: list-cards requires 0 arguments"); + + const auto& cards = ponymix.GetCards(); + for (const auto& c : cards) Print(c); + + return 0; +} + +static int ListProfiles(Pulse& ponymix, int argc _unused_, char* argv[] _unused_) { + if (argc != 0) errx(1, "error: list-profiles requires 0 arguments"); + + // TODO: figure out how to get a list of cards? + auto card = ponymix.GetCard(opt_card); + if (card == nullptr) errx(1, "error: no match found for card: %s", opt_card); + + const auto& profiles = card->Profiles(); + for (const auto& p : profiles) Print(p, p.name == card->ActiveProfile().name); + + return 0; +} + +static int GetVolume(Pulse& ponymix, int argc, char* argv[] _unused_) { + if (argc != 0) errx(1, "error: get-volume requires 0 arguments"); + + auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); + printf("%d\n", device->Volume()); + return 0; +} + +static int SetVolume(Pulse& ponymix, int argc, char* argv[]) { + if (argc != 1) errx(1, "error: set-volume requires exactly 1 argument"); + + auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); + + long volume; + try { + volume = std::stol(argv[0]); + } catch (std::invalid_argument _) { + errx(1, "error: failed to convert string to integer: %s", argv[0]); + } + + if (!ponymix.SetVolume(*device, volume)) return 1; + + printf("%d\n", device->Volume()); + + return 0; +} + +static int GetBalance(Pulse& ponymix, int argc, char* argv[] _unused_) { + if (argc != 0) errx(1, "error: get-balance requires 0 arguments"); + + auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); + printf("%d\n", device->Balance()); + return 0; +} + +static int SetBalance(Pulse& ponymix, int argc, char* argv[]) { + if (argc != 1) errx(1, "error: set-balance requires exactly 1 argument"); + + auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); + + long balance; + try { + balance = std::stol(argv[0]); + } catch (std::invalid_argument _) { + errx(1, "error: failed to convert string to integer: %s", argv[0]); + } + + if (!ponymix.SetBalance(*device, balance)) return 1; + + printf("%d\n", device->Balance()); + + return 0; +} + +static int AdjBalance(Pulse& ponymix, int argc, char* argv[]) { + if (argc != 1) errx(1, "error: adj-balance requires exactly 1 argument"); + + auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); + + long balance; + try { + balance = std::stol(argv[0]); + } catch (std::invalid_argument _) { + errx(1, "error: failed to convert string to integer: %s", argv[0]); + } + + if (!ponymix.SetBalance(*device, device->Balance() + balance)) return 1; + + printf("%d\n", device->Balance()); + + return 0; +} + +static int IncreaseVolume(Pulse& ponymix, int argc, char* argv[]) { + if (argc != 1) errx(1, "error: increase requires exactly 1 argument"); + + auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); + + long delta; + try { + delta = std::stol(argv[0]); + } catch (std::invalid_argument _) { + errx(1, "error: failed to convert string to integer: %s", argv[0]); + } + + if (!ponymix.SetVolume(*device, device->Volume() + delta)) return 1; + + printf("%d\n", device->Volume()); + + return 0; +} + +static int DecreaseVolume(Pulse& ponymix, int argc, char* argv[]) { + if (argc != 1) errx(1, "error: decrease requires exactly 1 argument"); + + auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); + + long delta; + try { + delta = std::stol(argv[0]); + } catch (std::invalid_argument _) { + errx(1, "error: failed to convert string to integer: %s", argv[0]); + } + + if (!ponymix.SetVolume(*device, device->Volume() - delta)) return 1; + + printf("%d\n", device->Volume()); + + return 0; +} + +static int Mute(Pulse& ponymix, int argc, char* argv[] _unused_) { + if (argc != 0) errx(1, "error: mute requires 0 arguments"); + + auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); + + if (!ponymix.SetMute(*device, true)) return 1; + + printf("%d\n", device->Volume()); + + return 0; +} + +static int Unmute(Pulse& ponymix, int argc, char* argv[] _unused_) { + if (argc != 0) errx(1, "error: unmute requires 0 arguments"); + + auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); + + if (!ponymix.SetMute(*device, false)) return 1; + + printf("%d\n", device->Volume()); + + return 0; +} + +static int ToggleMute(Pulse& ponymix, int argc, char* argv[] _unused_) { + if (argc != 0) errx(1, "error: toggle requires 0 arguments"); + + auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); + + if (!ponymix.SetMute(*device, !ponymix.IsMuted(*device))) return 1; + + printf("%d\n", device->Volume()); + + return 0; +} + +static int IsMuted(Pulse& ponymix, int argc, char* argv[] _unused_) { + if (argc != 0) errx(1, "error: is-muted requires 0 arguments"); + + auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); + return !ponymix.IsMuted(*device); +} + +static int SetDefault(Pulse& ponymix, int argc, char* argv[] _unused_) { + if (argc != 0) errx(1, "error: set-default requires 0 arguments"); + + auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); + + return !ponymix.SetDefault(*device); +} + +static int GetProfile(Pulse& ponymix, int argc, char* argv[] _unused_) { + if (argc != 0) errx(1, "error: get-profile requires 0 arguments"); + + auto card = ponymix.GetCard(opt_card); + if (card == nullptr) errx(1, "error: no match found for card: %s", opt_card); + + printf("%s\n", card->ActiveProfile().name.c_str()); + + return true; +} + +static int SetProfile(Pulse& ponymix, int argc, char* argv[] _unused_) { + if (argc != 1) errx(1, "error: set-profile requires 1 argument"); + + auto card = ponymix.GetCard(opt_card); + if (card == nullptr) errx(1, "error: no match found for card: %s", opt_card); + + return !ponymix.SetProfile(*card, argv[0]); +} + +static int Move(Pulse& ponymix, int argc, char* argv[]) { + if (argc != 1) errx(1, "error: move requires 1 argument"); + + // Does this even work? + auto source = string_to_device_or_die(ponymix, opt_device, opt_devtype); + auto target = string_to_device_or_die(ponymix, argv[0], opt_devtype); + + return !ponymix.Move(*source, *target); +} + +static int Kill(Pulse& ponymix, int argc, char* argv[] _unused_) { + if (argc != 0) errx(1, "error: set-default requires 0 arguments"); + + auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); + + return !ponymix.Kill(*device); +} + +static int (*fn[])(Pulse& ponymix, int argc, char* argv[]) = { + [ACTION_DEFAULTS] = ShowDefaults, + [ACTION_LIST] = List, + [ACTION_LISTCARDS] = ListCards, + [ACTION_LISTPROFILES] = ListProfiles, + [ACTION_GETVOL] = GetVolume, + [ACTION_SETVOL] = SetVolume, + [ACTION_GETBAL] = GetBalance, + [ACTION_SETBAL] = SetBalance, + [ACTION_ADJBAL] = AdjBalance, + [ACTION_INCREASE] = IncreaseVolume, + [ACTION_DECREASE] = DecreaseVolume, + [ACTION_MUTE] = Mute, + [ACTION_UNMUTE] = Unmute, + [ACTION_TOGGLE] = ToggleMute, + [ACTION_ISMUTED] = IsMuted, + [ACTION_SETDEFAULT] = SetDefault, + [ACTION_GETPROFILE] = GetProfile, + [ACTION_SETPROFILE] = SetProfile, + [ACTION_MOVE] = Move, + [ACTION_KILL] = Kill, +}; + +void usage() { + printf("usage: %s [options] ...\n", program_invocation_short_name); + fputs("\nOptions:\n" + " -h, --help display this help and exit\n\n" + + " -c, --card CARD target card (index or name)\n" + " -d, --device DEVICE target device (index or name)\n" + " -t, --devtype TYPE device type\n", stdout); + + fputs("\nCommon Commands:\n" + " list list available devices\n" + " list-cards list available cards\n" + " get-volume get volume for device\n" + " set-volume VALUE set volume for device\n" + " get-balance get balance for device\n" + " set-balance VALUE set balance for device\n" + " adj-balance VALUE increase or decrease balance for device\n" + " increase VALUE increase volume\n", stdout); + fputs(" decrease VALUE decrease volume\n" + " mute mute device\n" + " unmute unmute device\n" + " toggle toggle mute\n" + " is-muted check if muted\n", stdout); + + fputs("\nCard Commands:\n" + " list-profiles list available profiles for a card\n" + " get-profile get active profile for card\n" + " set-profile PROFILE set profile for a card\n" + + "\nDevice Commands:\n" + " defaults list default devices (default command)\n" + " set-default DEVICE set default device by ID\n" + + "\nApplication Commands:\n" + " move DEVICE move target device to DEVICE\n" + " kill DEVICE kill target DEVICE\n", stdout); + + exit(EXIT_SUCCESS); +} + +bool parse_options(int argc, char** argv) { + static const struct option opts[] = { + { "card", required_argument, 0, 'c' }, + { "device", required_argument, 0, 'd' }, + { "help", no_argument, 0, 'h' }, + { "type", required_argument, 0, 't' }, + { 0, 0, 0, 0 }, + }; + + for (;;) { + int opt = getopt_long(argc, argv, "c:d:ht:", opts, NULL); + if (opt == -1) + break; + + switch (opt) { + case 'c': + opt_card = optarg; + break; + case 'd': + opt_device = optarg; + break; + case 'h': + usage(); + break; + case 't': + opt_devtype = string_to_devtype_or_die(optarg); + break; + default: + return false; + } + } + + return true; +} + +int main(int argc, char* argv[]) { + Pulse ponymix("ponymix"); + ponymix.Populate(); + + // defaults + opt_action = ACTION_DEFAULTS; + opt_devtype = DEVTYPE_SINK; + opt_device = ponymix.GetDefaults().sink.c_str(); + opt_card = ponymix.GetCards()[0].Name().c_str(); + + if (!parse_options(argc, argv)) return 1; + argc -= optind; + argv += optind; + + if (argc > 0) { + opt_action = string_to_action(argv[0]); + argc--; + argv++; + } + + return fn[opt_action](ponymix, argc, argv); +} + +// vim: set et ts=2 sw=2: diff --git a/pulse.cc b/pulse.cc new file mode 100644 index 0000000..c09b61b --- /dev/null +++ b/pulse.cc @@ -0,0 +1,561 @@ +// Self +#include "pulse.h" + +// C +#include +#include +#include +#include + +// C++ +#include +#include + +// External +#include + +namespace { +static void connect_state_cb(pa_context* context, void* raw) { + enum pa_context_state *state = (enum pa_context_state*)raw; + *state = pa_context_get_state(context); +} + +static void success_cb(pa_context* context, int success, void* raw) { + int *r = (int*)raw; + *r = success; + if (!success) { + fprintf(stderr, + "operation failed: %s\n", + pa_strerror(pa_context_errno(context))); + } +} + +static void card_info_cb(pa_context* context, + const pa_card_info* info, + int eol, + void* raw) { + if (eol < 0) { + fprintf(stderr, "%s error in %s: \n", __func__, + pa_strerror(pa_context_errno(context))); + return; + } + + if (!eol) { + vector* cards = (vector*)raw; + cards->push_back(info); + } +} + +template +static void device_info_cb(pa_context* context, + const T* info, + int eol, + void* raw) { + if (eol < 0) { + fprintf(stderr, "%s error in %s: \n", __func__, + pa_strerror(pa_context_errno(context))); + return; + } + + if (!eol) { + vector* devices_ = (vector*)raw; + devices_->push_back(info); + } +} + +static void server_info_cb(pa_context* context __attribute__((unused)), + const pa_server_info* i, + void* raw) { + ServerInfo* defaults = (ServerInfo*)raw; + defaults->sink = i->default_sink_name; + defaults->source = i->default_source_name; +} + +static pa_cvolume* value_to_cvol(long value, pa_cvolume *cvol) { + return pa_cvolume_set(cvol, cvol->channels, + fmax((double)(value + .5) * PA_VOLUME_NORM / 100, 0)); +} + +static int volume_as_percent(const pa_cvolume* cvol) { + return pa_cvolume_avg(cvol) * 100.0 / PA_VOLUME_NORM; +} + +static int xstrtol(const char *str, long *out) { + char *end = NULL; + + if (str == NULL || *str == '\0') return -1; + errno = 0; + + *out = strtol(str, &end, 10); + if (errno || str == end || (end && *end)) return -1; + + return 0; +} + + +} // anonymous namespace + +Pulse::Pulse(string client_name) : + client_name_(client_name), + volume_range_(0, 150), + balance_range_(-100, 100) { + enum pa_context_state state = PA_CONTEXT_CONNECTING; + + pa_proplist* proplist = pa_proplist_new(); + pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, client_name.c_str()); + pa_proplist_sets(proplist, PA_PROP_APPLICATION_ID, "com.falconindy.ponymix"); + pa_proplist_sets(proplist, PA_PROP_APPLICATION_VERSION, "0.1"); + pa_proplist_sets(proplist, PA_PROP_APPLICATION_ICON_NAME, "audio-card"); + + mainloop_ = pa_mainloop_new(); + context_ = pa_context_new_with_proplist(pa_mainloop_get_api(mainloop_), + nullptr, proplist); + + pa_proplist_free(proplist); + + pa_context_set_state_callback(context_, connect_state_cb, &state); + pa_context_connect(context_, nullptr, PA_CONTEXT_NOFAIL, nullptr); + while (state != PA_CONTEXT_READY && state != PA_CONTEXT_FAILED) { + pa_mainloop_iterate(mainloop_, 1, nullptr); + } + + if (state != PA_CONTEXT_READY) { + fprintf(stderr, "failed to connect to pulse daemon: %s\n", + pa_strerror(pa_context_errno(context_))); + exit(EXIT_FAILURE); + } +} + +// +// Pulse Client +// +Pulse::~Pulse() { + pa_context_unref(context_); + pa_mainloop_free(mainloop_); +} + +void Pulse::Populate() { + populate_server_info(); + populate_sinks(); + populate_sources(); + populate_cards(); +} + +Card* Pulse::GetCard(const uint32_t& index) { + for (Card& card : cards_) { + if (card.index_ == index) return &card; + } + return nullptr; +} + +Card* Pulse::GetCard(const string& name) { + for (Card& card : cards_) { + if (card.name_ == name) return &card; + } + return nullptr; +} + +Device* Pulse::get_device(vector& devices, const uint32_t& index) { + for (Device& device : devices) { + if (device.index_ == index) return &device; + } + return nullptr; +} + +Device* Pulse::get_device(vector& devices, const string& name) { + long val; + if (xstrtol(name.c_str(), &val) == 0) return get_device(devices, val); + + for (Device& device : devices) { + if (device.name_ == name) return &device; + } + return nullptr; +} + +Device* Pulse::GetSink(const uint32_t& index) { + return get_device(sinks_, index); +} + +Device* Pulse::GetSink(const string& name) { + return get_device(sinks_, name); +} + +Device* Pulse::GetSource(const uint32_t& index) { + return get_device(sources_, index); +} + +Device* Pulse::GetSource(const string& name) { + return get_device(sources_, name); +} + +Device* Pulse::GetSinkInput(const uint32_t& index) { + return get_device(sink_inputs_, index); +} + +Device* Pulse::GetSinkInput(const string& name) { + return get_device(sink_inputs_, name); +} + +Device* Pulse::GetSourceOutput(const uint32_t& index) { + return get_device(source_outputs_, index); +} + +Device* Pulse::GetSourceOutput(const string& name) { + return get_device(source_outputs_, name); +} + +void Pulse::mainloop_iterate(pa_operation* op) { + int r; + while (pa_operation_get_state(op) == PA_OPERATION_RUNNING) { + pa_mainloop_iterate(mainloop_, 1, &r); + } +} + +void Pulse::populate_cards() { + cards_.clear(); + pa_operation* op = pa_context_get_card_info_list(context_, + card_info_cb, + (void*)&cards_); + mainloop_iterate(op); + pa_operation_unref(op); +} + +void Pulse::populate_server_info() { + pa_operation* op = pa_context_get_server_info(context_, + server_info_cb, + &defaults_); + mainloop_iterate(op); + pa_operation_unref(op); +} + +void Pulse::populate_sinks() { + sinks_.clear(); + pa_operation* op = pa_context_get_sink_info_list(context_, + device_info_cb, + (void*)&sinks_); + mainloop_iterate(op); + pa_operation_unref(op); + + sink_inputs_.clear(); + op = pa_context_get_sink_input_info_list(context_, + device_info_cb, + (void*)&sink_inputs_); + mainloop_iterate(op); + pa_operation_unref(op); +} + +void Pulse::populate_sources() { + sources_.clear(); + pa_operation* op = pa_context_get_source_info_list(context_, + device_info_cb, + (void*)&sources_); + mainloop_iterate(op); + pa_operation_unref(op); + + source_outputs_.clear(); + op = pa_context_get_source_output_info_list(context_, + device_info_cb, + (void*)&source_outputs_); + mainloop_iterate(op); + pa_operation_unref(op); +} + +bool Pulse::SetMute(Device& device, bool mute) { + int success; + + if (device.ops_.Mute == nullptr) { + warnx("device does not support muting."); + return false; + } + + pa_operation* op = device.ops_.Mute(context_, + device.index_, + mute, + success_cb, + &success); + mainloop_iterate(op); + pa_operation_unref(op); + + if (success) device.mute_ = mute; + + return success; +} + +bool Pulse::SetVolume(Device& device, long volume) { + int success; + + if (device.ops_.SetVolume == nullptr) { + warnx("device does not support setting balance."); + return false; + } + + const pa_cvolume *cvol = value_to_cvol(volume, &device.volume_); + pa_operation* op = device.ops_.SetVolume(context_, + device.index_, + cvol, + success_cb, + &success); + mainloop_iterate(op); + pa_operation_unref(op); + + if (success) device.update_volume(*cvol); + + return success; +} + +bool Pulse::IncreaseVolume(Device& device, long increment) { + return SetVolume(device, + volume_range_.clamp(device.volume_percent_ + increment)); +} + +bool Pulse::DecreaseVolume(Device& device, long increment) { + return SetVolume(device, + volume_range_.clamp(device.volume_percent_ - increment)); +} + +bool Pulse::SetBalance(Device& device, long balance) { + int success; + + if (device.ops_.SetVolume == nullptr) { + warnx("device does not support setting balance."); + return false; + } + + pa_cvolume *cvol = pa_cvolume_set_balance(&device.volume_, + &device.channels_, + balance / 100.0); + pa_operation* op = device.ops_.SetVolume(context_, + device.index_, + cvol, + success_cb, + &success); + mainloop_iterate(op); + pa_operation_unref(op); + + if (success) device.update_volume(*cvol); + + return success; +} + +bool Pulse::IncreaseBalance(Device& device, long increment) { + return SetBalance(device, + balance_range_.clamp(device.balance_ + increment)); +} + +bool Pulse::DecreaseBalance(Device& device, long increment) { + return SetBalance(device, + balance_range_.clamp(device.balance_ - increment)); +} + +int Pulse::GetVolume(const Device& device) const { + return device.Volume(); +} + +int Pulse::GetBalance(const Device& device) const { + return device.Balance(); +} + +bool Pulse::SetProfile(Card& card, const string& profile) { + int success; + pa_operation* op = + pa_context_set_card_profile_by_index(context_, + card.index_, + profile.c_str(), + success_cb, + &success); + mainloop_iterate(op); + pa_operation_unref(op); + + if (success) { + // Update the profile + for (const Profile p : card.profiles_) { + if (p.name == profile) { + card.active_profile_ = p; + break; + } + } + } + + return success; +} + +bool Pulse::Move(Device& source, Device& dest) { + int success; + + if (source.ops_.Move == nullptr) { + warnx("source device does not support moving."); + return false; + } + + pa_operation* op = source.ops_.Move(context_, + source.index_, + dest.index_, + success_cb, + &success); + mainloop_iterate(op); + pa_operation_unref(op); + + return success; +} + +bool Pulse::Kill(Device& device) { + int success; + + if (device.ops_.Kill == nullptr) { + warnx("source device does not support being killed."); + return false; + } + + pa_operation* op = device.ops_.Kill(context_, + device.index_, + success_cb, + &success); + mainloop_iterate(op); + pa_operation_unref(op); + + if (success) remove_device(device); + + return success; +} + +bool Pulse::SetDefault(Device& device) { + int success; + + if (device.ops_.SetDefault == nullptr) { + warnx("device does not support defaults"); + return false; + } + + pa_operation* op = device.ops_.SetDefault(context_, + device.name_.c_str(), + success_cb, + &success); + mainloop_iterate(op); + pa_operation_unref(op); + + if (success) { + switch (device.type_) { + case DEVTYPE_SINK: + defaults_.sink = device.name_; + break; + case DEVTYPE_SOURCE: + defaults_.source = device.name_; + break; + default: + errx(1, "impossible to set a default for device type %d", + device.type_); + } + } + + return success; +} + +void Pulse::remove_device(Device& device) { + vector* devlist; + + switch (device.type_) { + case DEVTYPE_SINK: + devlist = &sinks_; + case DEVTYPE_SINK_INPUT: + devlist = &sink_inputs_; + break; + case DEVTYPE_SOURCE: + devlist = &sources_; + break; + case DEVTYPE_SOURCE_OUTPUT: + devlist = &source_outputs_; + break; + } + devlist->erase( + std::remove_if( + devlist->begin(), devlist->end(), + [=](Device& d) { return d.index_ == device.index_; }), + devlist->end()); +} + +// +// Cards +// +Card::Card(const pa_card_info* info) : + index_(info->index), + name_(info->name), + owner_module_(info->owner_module), + driver_(info->driver), + active_profile_(*info->active_profile) { + for (int i = 0; info->profiles[i].name != nullptr; i++) { + profiles_.push_back(info->profiles[i]); + } +} + +// +// Devices +// +Device::Device(const pa_sink_info* info) : + type_(DEVTYPE_SINK), + index_(info->index), + name_(info->name), + desc_(info->description), + mute_(info->mute) { + update_volume(info->volume); + memcpy(&channels_, &info->channel_map, sizeof(pa_channel_map)); + balance_ = pa_cvolume_get_balance(&volume_, &channels_) * 100.0; + + ops_.SetVolume = pa_context_set_sink_volume_by_index; + ops_.Mute = pa_context_set_sink_mute_by_index; + ops_.SetDefault = pa_context_set_default_sink; +} + +Device::Device(const pa_source_info* info) : + type_(DEVTYPE_SOURCE), + index_(info->index), + name_(info->name), + desc_(info->description), + mute_(info->mute) { + update_volume(info->volume); + memcpy(&channels_, &info->channel_map, sizeof(pa_channel_map)); + balance_ = pa_cvolume_get_balance(&volume_, &channels_) * 100.0; + + ops_.SetVolume = pa_context_set_source_volume_by_index; + ops_.Mute = pa_context_set_source_mute_by_index; + ops_.SetDefault = pa_context_set_default_source; +} + +Device::Device(const pa_sink_input_info* info) : + type_(DEVTYPE_SINK_INPUT), + index_(info->index), + name_(info->name), + desc_(pa_proplist_gets(info->proplist, PA_PROP_APPLICATION_NAME)), + mute_(info->mute) { + update_volume(info->volume); + memcpy(&channels_, &info->channel_map, sizeof(pa_channel_map)); + balance_ = pa_cvolume_get_balance(&volume_, &channels_) * 100.0; + + ops_.SetVolume = pa_context_set_sink_input_volume; + ops_.Mute = pa_context_set_sink_input_mute; + ops_.Kill = pa_context_kill_sink_input; + ops_.Move = pa_context_move_sink_input_by_index; +} + +Device::Device(const pa_source_output_info* info) : + type_(DEVTYPE_SOURCE_OUTPUT), + index_(info->index), + name_(info->name), + desc_(pa_proplist_gets(info->proplist, PA_PROP_APPLICATION_NAME)), + mute_(info->mute) { + update_volume(info->volume); + volume_percent_ = volume_as_percent(&volume_); + balance_ = pa_cvolume_get_balance(&volume_, &channels_) * 100.0; + + ops_.SetVolume = pa_context_set_source_output_volume; + ops_.Mute = pa_context_set_source_output_mute; + ops_.Kill = pa_context_kill_source_output; + ops_.Move = pa_context_move_source_output_by_index; +} + +void Device::update_volume(const pa_cvolume& newvol) { + memcpy(&volume_, &newvol, sizeof(pa_cvolume)); + volume_percent_ = volume_as_percent(&volume_); + balance_ = pa_cvolume_get_balance(&volume_, &channels_) * 100.0; +} + +// vim: set et ts=2 sw=2: diff --git a/pulse.h b/pulse.h new file mode 100644 index 0000000..1e91352 --- /dev/null +++ b/pulse.h @@ -0,0 +1,216 @@ +#pragma once + +// C +#include + +// C++ +#include +#include +#include + +// external +#include + +using std::string; +using std::vector; +using std::unique_ptr; + +enum DeviceType { + DEVTYPE_SINK, + DEVTYPE_SOURCE, + DEVTYPE_SINK_INPUT, + DEVTYPE_SOURCE_OUTPUT, +}; + +struct Profile { + Profile(const pa_card_profile_info& info) : + name(info.name), + desc(info.description) {} + + string name; + string desc; +}; + +struct Operations { + pa_operation* (*Mute)(pa_context*, uint32_t, int, pa_context_success_cb_t, void*); + pa_operation* (*SetVolume)(pa_context*, uint32_t, const pa_cvolume*, pa_context_success_cb_t, void*); + pa_operation* (*SetDefault)(pa_context*, const char*, pa_context_success_cb_t, void*); + pa_operation* (*Kill)(pa_context*, uint32_t, pa_context_success_cb_t, void*); + pa_operation* (*Move)(pa_context *, uint32_t, uint32_t, pa_context_success_cb_t, void *); +}; + +class Device { + public: + Device(const pa_source_info* info); + Device(const pa_sink_info* info); + Device(const pa_sink_input_info* info); + Device(const pa_source_output_info* info); + + uint32_t Index() const { return index_; } + const string& Name() const { return name_; } + const string& Desc() const { return desc_; } + int Volume() const { return volume_percent_; } + int Balance() const { return balance_; } + bool Muted() const { return mute_; } + enum DeviceType Type() const { return type_; } + + private: + friend class Pulse; + + void update_volume(const pa_cvolume& newvol); + + enum DeviceType type_; + uint32_t index_; + string name_; + string desc_; + pa_cvolume volume_; + int volume_percent_; + pa_channel_map channels_; + int mute_; + int balance_; + Operations ops_; +}; + +class Card { + public: + Card(const pa_card_info* info); + + const string& Name() const { return name_; } + uint32_t Index() const { return index_; } + const string& Driver() const { return driver_; } + + const vector& Profiles() const { return profiles_; } + const Profile& ActiveProfile() const { return active_profile_; } + + private: + friend class Pulse; + + uint32_t index_; + string name_; + uint32_t owner_module_; + string driver_; + vector profiles_; + Profile active_profile_; +}; + +struct ServerInfo { + string sink; + string source; +}; + +template +struct Range { + Range(T min, T max) : min(min), max(max) {} + + T clamp(T value) { + return value < min ? min : (value > max ? max : value); + } + + T min; + T max; +}; + +class Pulse { + public: + Pulse(string client_name); + ~Pulse(); + + // Populates all known devices and cards. Any currently known + // devices and cards are cleared before the new data is stored. + void Populate(); + + // Get a sink by index or name, or all sinks. + Device* GetSink(const uint32_t& index); + Device* GetSink(const string& name); + const vector& GetSinks() const { return sinks_; } + + // Get a source by index or name, or all sources. + Device* GetSource(const uint32_t& index); + Device* GetSource(const string& name); + const vector& GetSources() const { return sources_; } + + // Get a sink input by index or name, or all sink inputs. + Device* GetSinkInput(const uint32_t& name); + Device* GetSinkInput(const string& name); + const vector& GetSinkInputs() const { return sink_inputs_; } + + // Get a source output by index or name, or all source outputs. + Device* GetSourceOutput(const uint32_t& name); + Device* GetSourceOutput(const string& name); + const vector& GetSourceOutputs() const { return source_outputs_; } + + // Get a card by index or name, or all cards. + Card* GetCard(const uint32_t& index); + Card* GetCard(const string& name); + const vector& GetCards() const { return cards_; } + + // Get or set the volume of a device. + int GetVolume(const Device& device) const; + bool SetVolume(Device& device, long value); + + // Convenience wrappers for adjusting volume + bool IncreaseVolume(Device& device, long increment); + bool DecreaseVolume(Device& device, long decrement); + + // Get or set the volume of a device. Not all devices support this. + int GetBalance(const Device& device) const; + bool SetBalance(Device& device, long value); + + // Convenience wrappers for adjusting balance + bool IncreaseBalance(Device& device, long increment); + bool DecreaseBalance(Device& device, long decrement); + + // Get and set mute for a device. + bool IsMuted(const Device& device) const { return device.mute_; }; + bool SetMute(Device& device, bool mute); + + // Set the profile for a card by name. + bool SetProfile(Card& card, const string& profile); + + // Move a given source output or sink input to the destination. + bool Move(Device& source, Device& dest); + + // Kill a source output or sink input. + bool Kill(Device& device); + + // Get or set the default sink and source. + const ServerInfo& GetDefaults() const { return defaults_; } + bool SetDefault(Device& device); + + // Set minimum and maximum allowed volume + void SetVolumeRange(int min, int max) { + volume_range_ = { min, max }; + } + + // Set minimum and maximum allowed balance + void SetBalanceRange(int min, int max) { + balance_range_ = { min, max }; + } + + private: + void mainloop_iterate(pa_operation* op); + + void populate_server_info(); + void populate_cards(); + void populate_sinks(); + void populate_sources(); + + Device* get_device(vector& devices, const uint32_t& index); + Device* get_device(vector& devices, const string& name); + + void remove_device(Device& device); + + string client_name_; + pa_context* context_; + pa_mainloop* mainloop_; + vector sinks_; + vector sources_; + vector sink_inputs_; + vector source_outputs_; + vector cards_; + ServerInfo defaults_; + Range volume_range_; + Range balance_range_; +}; + +// vim: set et ts=2 sw=2: -- cgit v1.2.3 From 544047c06ffc7be6fa7f22f895eaab691038ee22 Mon Sep 17 00:00:00 2001 From: Dave Reisner Date: Tue, 1 Jan 2013 21:07:29 -0500 Subject: fix null constructions --- pulse.cc | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/pulse.cc b/pulse.cc index c09b61b..1ac1867 100644 --- a/pulse.cc +++ b/pulse.cc @@ -493,7 +493,7 @@ Card::Card(const pa_card_info* info) : Device::Device(const pa_sink_info* info) : type_(DEVTYPE_SINK), index_(info->index), - name_(info->name), + name_(!info->name.empty() ? info->name : ""), desc_(info->description), mute_(info->mute) { update_volume(info->volume); @@ -508,7 +508,7 @@ Device::Device(const pa_sink_info* info) : Device::Device(const pa_source_info* info) : type_(DEVTYPE_SOURCE), index_(info->index), - name_(info->name), + name_(!info->name.empty() ? info->name : ""), desc_(info->description), mute_(info->mute) { update_volume(info->volume); @@ -523,13 +523,16 @@ Device::Device(const pa_source_info* info) : Device::Device(const pa_sink_input_info* info) : type_(DEVTYPE_SINK_INPUT), index_(info->index), - name_(info->name), - desc_(pa_proplist_gets(info->proplist, PA_PROP_APPLICATION_NAME)), + name_(!info->name.empty() ? info->name : ""), mute_(info->mute) { update_volume(info->volume); memcpy(&channels_, &info->channel_map, sizeof(pa_channel_map)); balance_ = pa_cvolume_get_balance(&volume_, &channels_) * 100.0; + const char *desc = pa_proplist_gets(info->proplist, + PA_PROP_APPLICATION_NAME); + if (desc) desc_ = desc; + ops_.SetVolume = pa_context_set_sink_input_volume; ops_.Mute = pa_context_set_sink_input_mute; ops_.Kill = pa_context_kill_sink_input; @@ -539,13 +542,16 @@ Device::Device(const pa_sink_input_info* info) : Device::Device(const pa_source_output_info* info) : type_(DEVTYPE_SOURCE_OUTPUT), index_(info->index), - name_(info->name), - desc_(pa_proplist_gets(info->proplist, PA_PROP_APPLICATION_NAME)), + name_(!info->name.empty() ? info->name : ""), mute_(info->mute) { update_volume(info->volume); volume_percent_ = volume_as_percent(&volume_); balance_ = pa_cvolume_get_balance(&volume_, &channels_) * 100.0; + const char *desc = pa_proplist_gets(info->proplist, + PA_PROP_APPLICATION_NAME); + if (desc) desc_ = desc; + ops_.SetVolume = pa_context_set_source_output_volume; ops_.Mute = pa_context_set_source_output_mute; ops_.Kill = pa_context_kill_source_output; -- cgit v1.2.3 From 07347623642f6f841580fac139dba2b3b6d97b35 Mon Sep 17 00:00:00 2001 From: Dave Reisner Date: Tue, 1 Jan 2013 21:12:37 -0500 Subject: oops --- pulse.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pulse.cc b/pulse.cc index 1ac1867..77a1568 100644 --- a/pulse.cc +++ b/pulse.cc @@ -493,7 +493,7 @@ Card::Card(const pa_card_info* info) : Device::Device(const pa_sink_info* info) : type_(DEVTYPE_SINK), index_(info->index), - name_(!info->name.empty() ? info->name : ""), + name_(info->name ? info->name : ""), desc_(info->description), mute_(info->mute) { update_volume(info->volume); @@ -508,7 +508,7 @@ Device::Device(const pa_sink_info* info) : Device::Device(const pa_source_info* info) : type_(DEVTYPE_SOURCE), index_(info->index), - name_(!info->name.empty() ? info->name : ""), + name_(info->name ? info->name : ""), desc_(info->description), mute_(info->mute) { update_volume(info->volume); @@ -523,7 +523,7 @@ Device::Device(const pa_source_info* info) : Device::Device(const pa_sink_input_info* info) : type_(DEVTYPE_SINK_INPUT), index_(info->index), - name_(!info->name.empty() ? info->name : ""), + name_(info->name ? info->name : ""), mute_(info->mute) { update_volume(info->volume); memcpy(&channels_, &info->channel_map, sizeof(pa_channel_map)); @@ -542,7 +542,7 @@ Device::Device(const pa_sink_input_info* info) : Device::Device(const pa_source_output_info* info) : type_(DEVTYPE_SOURCE_OUTPUT), index_(info->index), - name_(!info->name.empty() ? info->name : ""), + name_(info->name ? info->name : ""), mute_(info->mute) { update_volume(info->volume); volume_percent_ = volume_as_percent(&volume_); -- cgit v1.2.3 From 598c7174b5b917a2a47ea1cca724f77c0f673485 Mon Sep 17 00:00:00 2001 From: Dave Reisner Date: Tue, 1 Jan 2013 21:24:13 -0500 Subject: action, not option --- ponymix.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ponymix.cc b/ponymix.cc index 1bd256f..69d13f8 100644 --- a/ponymix.cc +++ b/ponymix.cc @@ -86,7 +86,7 @@ static enum Action string_to_action(const char* str) { try { return actionmap.at(str); } catch(std::out_of_range) { - errx(1, "error: Invalid option specified: %s", str); + errx(1, "error: Invalid action specified: %s", str); } } @@ -130,12 +130,13 @@ static Device* string_to_device_or_die(Pulse& ponymix, static void Print(const Device& device) { printf("%s %d: %s\n" " %s\n" - " Avg. Volume: %d%%\n", + " Avg. Volume: %d%%%s\n", type_to_string(device.Type()), device.Index(), device.Name().c_str(), device.Desc().c_str(), - device.Volume()); + device.Volume(), + device.Muted() ? " [muted]" : ""); } static void Print(const Card& card) { -- cgit v1.2.3 From 3b7cca5b99b6daa5c138a86373f7ca335147df90 Mon Sep 17 00:00:00 2001 From: Dave Reisner Date: Tue, 1 Jan 2013 21:37:22 -0500 Subject: maybe fix app mode? A sources-output can only be moved to a new source, and a sink-input can only be moved to a new sink. So, derive the target type based on this. Also, reroute sink -> sink-input and source -> source-output to save some keystrokes. --- ponymix.cc | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/ponymix.cc b/ponymix.cc index 69d13f8..08c2ad5 100644 --- a/ponymix.cc +++ b/ponymix.cc @@ -382,9 +382,24 @@ static int SetProfile(Pulse& ponymix, int argc, char* argv[] _unused_) { static int Move(Pulse& ponymix, int argc, char* argv[]) { if (argc != 1) errx(1, "error: move requires 1 argument"); + // this assignment is a lie. stfu g++ + enum DeviceType target_type = opt_devtype; + switch (opt_devtype) { + case DEVTYPE_SOURCE: + opt_devtype = DEVTYPE_SOURCE_OUTPUT; + case DEVTYPE_SOURCE_OUTPUT: + target_type = DEVTYPE_SOURCE; + break; + case DEVTYPE_SINK: + opt_devtype = DEVTYPE_SINK_INPUT; + case DEVTYPE_SINK_INPUT: + target_type = DEVTYPE_SINK; + break; + } + // Does this even work? auto source = string_to_device_or_die(ponymix, opt_device, opt_devtype); - auto target = string_to_device_or_die(ponymix, argv[0], opt_devtype); + auto target = string_to_device_or_die(ponymix, argv[0], target_type); return !ponymix.Move(*source, *target); } -- cgit v1.2.3 From 4268c55004a2f942bd48e114422fc88cec0c6bc2 Mon Sep 17 00:00:00 2001 From: Dave Reisner Date: Tue, 1 Jan 2013 22:05:03 -0500 Subject: alias sane types to --device flags --- ponymix.cc | 44 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/ponymix.cc b/ponymix.cc index 08c2ad5..d5b807f 100644 --- a/ponymix.cc +++ b/ponymix.cc @@ -383,23 +383,23 @@ static int Move(Pulse& ponymix, int argc, char* argv[]) { if (argc != 1) errx(1, "error: move requires 1 argument"); // this assignment is a lie. stfu g++ - enum DeviceType target_type = opt_devtype; + enum DeviceType target_devtype = opt_devtype; switch (opt_devtype) { case DEVTYPE_SOURCE: opt_devtype = DEVTYPE_SOURCE_OUTPUT; case DEVTYPE_SOURCE_OUTPUT: - target_type = DEVTYPE_SOURCE; + target_devtype = DEVTYPE_SOURCE; break; case DEVTYPE_SINK: opt_devtype = DEVTYPE_SINK_INPUT; case DEVTYPE_SINK_INPUT: - target_type = DEVTYPE_SINK; + target_devtype = DEVTYPE_SINK; break; } // Does this even work? auto source = string_to_device_or_die(ponymix, opt_device, opt_devtype); - auto target = string_to_device_or_die(ponymix, argv[0], target_type); + auto target = string_to_device_or_die(ponymix, argv[0], target_devtype); return !ponymix.Move(*source, *target); } @@ -442,7 +442,13 @@ void usage() { " -c, --card CARD target card (index or name)\n" " -d, --device DEVICE target device (index or name)\n" - " -t, --devtype TYPE device type\n", stdout); + " -t, --devtype TYPE device type\n" + " --source alias to -t source\n" + " --input alais to -t source\n" + " --sink alias to -t sink\n" + " --output alias to -t sink\n" + " --sink-input alias to -t sink-input\n" + " --source-output alias to -t source-output\n", stdout); fputs("\nCommon Commands:\n" " list list available devices\n" @@ -477,10 +483,16 @@ void usage() { bool parse_options(int argc, char** argv) { static const struct option opts[] = { - { "card", required_argument, 0, 'c' }, - { "device", required_argument, 0, 'd' }, - { "help", no_argument, 0, 'h' }, - { "type", required_argument, 0, 't' }, + { "card", required_argument, 0, 'c' }, + { "device", required_argument, 0, 'd' }, + { "help", no_argument, 0, 'h' }, + { "type", required_argument, 0, 't' }, + { "sink", no_argument, 0, 0x100 }, + { "output", no_argument, 0, 0x101 }, + { "source", no_argument, 0, 0x102 }, + { "input", no_argument, 0, 0x103 }, + { "sink-input", no_argument, 0, 0x104 }, + { "source-output", no_argument, 0, 0x105 }, { 0, 0, 0, 0 }, }; @@ -502,6 +514,20 @@ bool parse_options(int argc, char** argv) { case 't': opt_devtype = string_to_devtype_or_die(optarg); break; + case 0x100: + case 0x101: + opt_devtype = DEVTYPE_SINK; + break; + case 0x102: + case 0x103: + opt_devtype = DEVTYPE_SOURCE; + break; + case 0x104: + opt_devtype = DEVTYPE_SINK_INPUT; + break; + case 0x105: + opt_devtype = DEVTYPE_SOURCE_OUTPUT; + break; default: return false; } -- cgit v1.2.3 From a6d5dd764d5b30ebf90af523986452d34834d7bd Mon Sep 17 00:00:00 2001 From: Dave Reisner Date: Tue, 1 Jan 2013 22:48:02 -0500 Subject: eschew _unused_ attribute --- ponymix.cc | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/ponymix.cc b/ponymix.cc index d5b807f..1f124df 100644 --- a/ponymix.cc +++ b/ponymix.cc @@ -7,8 +7,6 @@ #include #include -#define _unused_ __attribute__((unused)) - enum Action { ACTION_DEFAULTS, ACTION_LIST, @@ -148,16 +146,14 @@ static void Print(const Profile& profile, bool active) { profile.name.c_str(), profile.desc.c_str(), active ? " [active]" : ""); } -static int ShowDefaults(Pulse& ponymix, - int argc _unused_, - char* argv[] _unused_) { +static int ShowDefaults(Pulse& ponymix, int, char*[]) { const auto& info = ponymix.GetDefaults(); Print(*ponymix.GetSink(info.sink)); Print(*ponymix.GetSource(info.source)); return 0; } -static int List(Pulse& ponymix, int argc _unused_, char* argv[] _unused_) { +static int List(Pulse& ponymix, int argc, char*[]) { if (argc != 0) errx(1, "error: list requires 0 arguments"); const auto& sinks = ponymix.GetSinks(); @@ -175,7 +171,7 @@ static int List(Pulse& ponymix, int argc _unused_, char* argv[] _unused_) { return 0; } -static int ListCards(Pulse& ponymix, int argc _unused_, char* argv[] _unused_) { +static int ListCards(Pulse& ponymix, int argc, char*[]) { if (argc != 0) errx(1, "error: list-cards requires 0 arguments"); const auto& cards = ponymix.GetCards(); @@ -184,7 +180,7 @@ static int ListCards(Pulse& ponymix, int argc _unused_, char* argv[] _unused_) { return 0; } -static int ListProfiles(Pulse& ponymix, int argc _unused_, char* argv[] _unused_) { +static int ListProfiles(Pulse& ponymix, int argc, char*[]) { if (argc != 0) errx(1, "error: list-profiles requires 0 arguments"); // TODO: figure out how to get a list of cards? @@ -197,7 +193,7 @@ static int ListProfiles(Pulse& ponymix, int argc _unused_, char* argv[] _unused_ return 0; } -static int GetVolume(Pulse& ponymix, int argc, char* argv[] _unused_) { +static int GetVolume(Pulse& ponymix, int argc, char*[]) { if (argc != 0) errx(1, "error: get-volume requires 0 arguments"); auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); @@ -224,7 +220,7 @@ static int SetVolume(Pulse& ponymix, int argc, char* argv[]) { return 0; } -static int GetBalance(Pulse& ponymix, int argc, char* argv[] _unused_) { +static int GetBalance(Pulse& ponymix, int argc, char*[]) { if (argc != 0) errx(1, "error: get-balance requires 0 arguments"); auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); @@ -308,7 +304,7 @@ static int DecreaseVolume(Pulse& ponymix, int argc, char* argv[]) { return 0; } -static int Mute(Pulse& ponymix, int argc, char* argv[] _unused_) { +static int Mute(Pulse& ponymix, int argc, char*[]) { if (argc != 0) errx(1, "error: mute requires 0 arguments"); auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); @@ -320,7 +316,7 @@ static int Mute(Pulse& ponymix, int argc, char* argv[] _unused_) { return 0; } -static int Unmute(Pulse& ponymix, int argc, char* argv[] _unused_) { +static int Unmute(Pulse& ponymix, int argc, char*[]) { if (argc != 0) errx(1, "error: unmute requires 0 arguments"); auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); @@ -332,7 +328,7 @@ static int Unmute(Pulse& ponymix, int argc, char* argv[] _unused_) { return 0; } -static int ToggleMute(Pulse& ponymix, int argc, char* argv[] _unused_) { +static int ToggleMute(Pulse& ponymix, int argc, char*[]) { if (argc != 0) errx(1, "error: toggle requires 0 arguments"); auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); @@ -344,14 +340,14 @@ static int ToggleMute(Pulse& ponymix, int argc, char* argv[] _unused_) { return 0; } -static int IsMuted(Pulse& ponymix, int argc, char* argv[] _unused_) { +static int IsMuted(Pulse& ponymix, int argc, char*[]) { if (argc != 0) errx(1, "error: is-muted requires 0 arguments"); auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); return !ponymix.IsMuted(*device); } -static int SetDefault(Pulse& ponymix, int argc, char* argv[] _unused_) { +static int SetDefault(Pulse& ponymix, int argc, char*[]) { if (argc != 0) errx(1, "error: set-default requires 0 arguments"); auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); @@ -359,7 +355,7 @@ static int SetDefault(Pulse& ponymix, int argc, char* argv[] _unused_) { return !ponymix.SetDefault(*device); } -static int GetProfile(Pulse& ponymix, int argc, char* argv[] _unused_) { +static int GetProfile(Pulse& ponymix, int argc, char*[]) { if (argc != 0) errx(1, "error: get-profile requires 0 arguments"); auto card = ponymix.GetCard(opt_card); @@ -370,7 +366,7 @@ static int GetProfile(Pulse& ponymix, int argc, char* argv[] _unused_) { return true; } -static int SetProfile(Pulse& ponymix, int argc, char* argv[] _unused_) { +static int SetProfile(Pulse& ponymix, int argc, char* argv[]) { if (argc != 1) errx(1, "error: set-profile requires 1 argument"); auto card = ponymix.GetCard(opt_card); @@ -404,7 +400,7 @@ static int Move(Pulse& ponymix, int argc, char* argv[]) { return !ponymix.Move(*source, *target); } -static int Kill(Pulse& ponymix, int argc, char* argv[] _unused_) { +static int Kill(Pulse& ponymix, int argc, char*[]) { if (argc != 0) errx(1, "error: set-default requires 0 arguments"); auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); -- cgit v1.2.3 From fa689e0995b6c31f2cf529916399c4d9db299495 Mon Sep 17 00:00:00 2001 From: Dave Reisner Date: Tue, 1 Jan 2013 22:49:04 -0500 Subject: makefile: CFLAGS -> CXXFLAGS --- Makefile | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index e6cb6ca..b41b05c 100644 --- a/Makefile +++ b/Makefile @@ -1,13 +1,19 @@ CXX = g++ -std=c++11 -base_CFLAGS = -Wall -Wextra -pedantic -O2 -g +base_CXXFLAGS = -Wall -Wextra -pedantic -O2 -g base_LIBS = -lm -libpulse_CFLAGS = $(shell pkg-config --cflags libpulse) +libpulse_CXXFLAGS = $(shell pkg-config --cflags libpulse) libpulse_LIBS = $(shell pkg-config --libs libpulse) -CXXFLAGS := $(base_CFLAGS) $(libpulse_CFLAGS) $(CXXFLAGS) -LDLIBS := $(base_LIBS) $(libpulse_LIBS) +CXXFLAGS := \ + $(base_CXXFLAGS) \ + $(libpulse_CXXFLAGS) \ + $(CXXFLAGS) + +LDLIBS := \ + $(base_LIBS) \ + $(libpulse_LIBS) all: ponymix -- cgit v1.2.3 From aa6847b94511728250333125c3dd9bec7b65759a Mon Sep 17 00:00:00 2001 From: Dave Reisner Date: Wed, 2 Jan 2013 09:24:59 -0500 Subject: rename class Pulse to PulseClient --- ponymix.cc | 48 ++++++++++++++++++++++---------------------- pulse.cc | 68 +++++++++++++++++++++++++++++++------------------------------- pulse.h | 10 ++++----- 3 files changed, 63 insertions(+), 63 deletions(-) diff --git a/ponymix.cc b/ponymix.cc index 1f124df..a6ce950 100644 --- a/ponymix.cc +++ b/ponymix.cc @@ -102,7 +102,7 @@ static enum DeviceType string_to_devtype_or_die(const char* str) { } } -static Device* string_to_device(Pulse& ponymix, string arg, enum DeviceType type) { +static Device* string_to_device(PulseClient& ponymix, string arg, enum DeviceType type) { switch (type) { case DEVTYPE_SINK: return ponymix.GetSink(arg); @@ -117,7 +117,7 @@ static Device* string_to_device(Pulse& ponymix, string arg, enum DeviceType type } } -static Device* string_to_device_or_die(Pulse& ponymix, +static Device* string_to_device_or_die(PulseClient& ponymix, string arg, enum DeviceType type) { Device* device = string_to_device(ponymix, arg, type); @@ -146,14 +146,14 @@ static void Print(const Profile& profile, bool active) { profile.name.c_str(), profile.desc.c_str(), active ? " [active]" : ""); } -static int ShowDefaults(Pulse& ponymix, int, char*[]) { +static int ShowDefaults(PulseClient& ponymix, int, char*[]) { const auto& info = ponymix.GetDefaults(); Print(*ponymix.GetSink(info.sink)); Print(*ponymix.GetSource(info.source)); return 0; } -static int List(Pulse& ponymix, int argc, char*[]) { +static int List(PulseClient& ponymix, int argc, char*[]) { if (argc != 0) errx(1, "error: list requires 0 arguments"); const auto& sinks = ponymix.GetSinks(); @@ -171,7 +171,7 @@ static int List(Pulse& ponymix, int argc, char*[]) { return 0; } -static int ListCards(Pulse& ponymix, int argc, char*[]) { +static int ListCards(PulseClient& ponymix, int argc, char*[]) { if (argc != 0) errx(1, "error: list-cards requires 0 arguments"); const auto& cards = ponymix.GetCards(); @@ -180,7 +180,7 @@ static int ListCards(Pulse& ponymix, int argc, char*[]) { return 0; } -static int ListProfiles(Pulse& ponymix, int argc, char*[]) { +static int ListProfiles(PulseClient& ponymix, int argc, char*[]) { if (argc != 0) errx(1, "error: list-profiles requires 0 arguments"); // TODO: figure out how to get a list of cards? @@ -193,7 +193,7 @@ static int ListProfiles(Pulse& ponymix, int argc, char*[]) { return 0; } -static int GetVolume(Pulse& ponymix, int argc, char*[]) { +static int GetVolume(PulseClient& ponymix, int argc, char*[]) { if (argc != 0) errx(1, "error: get-volume requires 0 arguments"); auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); @@ -201,7 +201,7 @@ static int GetVolume(Pulse& ponymix, int argc, char*[]) { return 0; } -static int SetVolume(Pulse& ponymix, int argc, char* argv[]) { +static int SetVolume(PulseClient& ponymix, int argc, char* argv[]) { if (argc != 1) errx(1, "error: set-volume requires exactly 1 argument"); auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); @@ -220,7 +220,7 @@ static int SetVolume(Pulse& ponymix, int argc, char* argv[]) { return 0; } -static int GetBalance(Pulse& ponymix, int argc, char*[]) { +static int GetBalance(PulseClient& ponymix, int argc, char*[]) { if (argc != 0) errx(1, "error: get-balance requires 0 arguments"); auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); @@ -228,7 +228,7 @@ static int GetBalance(Pulse& ponymix, int argc, char*[]) { return 0; } -static int SetBalance(Pulse& ponymix, int argc, char* argv[]) { +static int SetBalance(PulseClient& ponymix, int argc, char* argv[]) { if (argc != 1) errx(1, "error: set-balance requires exactly 1 argument"); auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); @@ -247,7 +247,7 @@ static int SetBalance(Pulse& ponymix, int argc, char* argv[]) { return 0; } -static int AdjBalance(Pulse& ponymix, int argc, char* argv[]) { +static int AdjBalance(PulseClient& ponymix, int argc, char* argv[]) { if (argc != 1) errx(1, "error: adj-balance requires exactly 1 argument"); auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); @@ -266,7 +266,7 @@ static int AdjBalance(Pulse& ponymix, int argc, char* argv[]) { return 0; } -static int IncreaseVolume(Pulse& ponymix, int argc, char* argv[]) { +static int IncreaseVolume(PulseClient& ponymix, int argc, char* argv[]) { if (argc != 1) errx(1, "error: increase requires exactly 1 argument"); auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); @@ -285,7 +285,7 @@ static int IncreaseVolume(Pulse& ponymix, int argc, char* argv[]) { return 0; } -static int DecreaseVolume(Pulse& ponymix, int argc, char* argv[]) { +static int DecreaseVolume(PulseClient& ponymix, int argc, char* argv[]) { if (argc != 1) errx(1, "error: decrease requires exactly 1 argument"); auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); @@ -304,7 +304,7 @@ static int DecreaseVolume(Pulse& ponymix, int argc, char* argv[]) { return 0; } -static int Mute(Pulse& ponymix, int argc, char*[]) { +static int Mute(PulseClient& ponymix, int argc, char*[]) { if (argc != 0) errx(1, "error: mute requires 0 arguments"); auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); @@ -316,7 +316,7 @@ static int Mute(Pulse& ponymix, int argc, char*[]) { return 0; } -static int Unmute(Pulse& ponymix, int argc, char*[]) { +static int Unmute(PulseClient& ponymix, int argc, char*[]) { if (argc != 0) errx(1, "error: unmute requires 0 arguments"); auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); @@ -328,7 +328,7 @@ static int Unmute(Pulse& ponymix, int argc, char*[]) { return 0; } -static int ToggleMute(Pulse& ponymix, int argc, char*[]) { +static int ToggleMute(PulseClient& ponymix, int argc, char*[]) { if (argc != 0) errx(1, "error: toggle requires 0 arguments"); auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); @@ -340,14 +340,14 @@ static int ToggleMute(Pulse& ponymix, int argc, char*[]) { return 0; } -static int IsMuted(Pulse& ponymix, int argc, char*[]) { +static int IsMuted(PulseClient& ponymix, int argc, char*[]) { if (argc != 0) errx(1, "error: is-muted requires 0 arguments"); auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); return !ponymix.IsMuted(*device); } -static int SetDefault(Pulse& ponymix, int argc, char*[]) { +static int SetDefault(PulseClient& ponymix, int argc, char*[]) { if (argc != 0) errx(1, "error: set-default requires 0 arguments"); auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); @@ -355,7 +355,7 @@ static int SetDefault(Pulse& ponymix, int argc, char*[]) { return !ponymix.SetDefault(*device); } -static int GetProfile(Pulse& ponymix, int argc, char*[]) { +static int GetProfile(PulseClient& ponymix, int argc, char*[]) { if (argc != 0) errx(1, "error: get-profile requires 0 arguments"); auto card = ponymix.GetCard(opt_card); @@ -366,7 +366,7 @@ static int GetProfile(Pulse& ponymix, int argc, char*[]) { return true; } -static int SetProfile(Pulse& ponymix, int argc, char* argv[]) { +static int SetProfile(PulseClient& ponymix, int argc, char* argv[]) { if (argc != 1) errx(1, "error: set-profile requires 1 argument"); auto card = ponymix.GetCard(opt_card); @@ -375,7 +375,7 @@ static int SetProfile(Pulse& ponymix, int argc, char* argv[]) { return !ponymix.SetProfile(*card, argv[0]); } -static int Move(Pulse& ponymix, int argc, char* argv[]) { +static int Move(PulseClient& ponymix, int argc, char* argv[]) { if (argc != 1) errx(1, "error: move requires 1 argument"); // this assignment is a lie. stfu g++ @@ -400,7 +400,7 @@ static int Move(Pulse& ponymix, int argc, char* argv[]) { return !ponymix.Move(*source, *target); } -static int Kill(Pulse& ponymix, int argc, char*[]) { +static int Kill(PulseClient& ponymix, int argc, char*[]) { if (argc != 0) errx(1, "error: set-default requires 0 arguments"); auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); @@ -408,7 +408,7 @@ static int Kill(Pulse& ponymix, int argc, char*[]) { return !ponymix.Kill(*device); } -static int (*fn[])(Pulse& ponymix, int argc, char* argv[]) = { +static int (*fn[])(PulseClient& ponymix, int argc, char* argv[]) = { [ACTION_DEFAULTS] = ShowDefaults, [ACTION_LIST] = List, [ACTION_LISTCARDS] = ListCards, @@ -533,7 +533,7 @@ bool parse_options(int argc, char** argv) { } int main(int argc, char* argv[]) { - Pulse ponymix("ponymix"); + PulseClient ponymix("ponymix"); ponymix.Populate(); // defaults diff --git a/pulse.cc b/pulse.cc index 77a1568..cbbe405 100644 --- a/pulse.cc +++ b/pulse.cc @@ -95,7 +95,7 @@ static int xstrtol(const char *str, long *out) { } // anonymous namespace -Pulse::Pulse(string client_name) : +PulseClient::PulseClient(string client_name) : client_name_(client_name), volume_range_(0, 150), balance_range_(-100, 100) { @@ -129,40 +129,40 @@ Pulse::Pulse(string client_name) : // // Pulse Client // -Pulse::~Pulse() { +PulseClient::~PulseClient() { pa_context_unref(context_); pa_mainloop_free(mainloop_); } -void Pulse::Populate() { +void PulseClient::Populate() { populate_server_info(); populate_sinks(); populate_sources(); populate_cards(); } -Card* Pulse::GetCard(const uint32_t& index) { +Card* PulseClient::GetCard(const uint32_t& index) { for (Card& card : cards_) { if (card.index_ == index) return &card; } return nullptr; } -Card* Pulse::GetCard(const string& name) { +Card* PulseClient::GetCard(const string& name) { for (Card& card : cards_) { if (card.name_ == name) return &card; } return nullptr; } -Device* Pulse::get_device(vector& devices, const uint32_t& index) { +Device* PulseClient::get_device(vector& devices, const uint32_t& index) { for (Device& device : devices) { if (device.index_ == index) return &device; } return nullptr; } -Device* Pulse::get_device(vector& devices, const string& name) { +Device* PulseClient::get_device(vector& devices, const string& name) { long val; if (xstrtol(name.c_str(), &val) == 0) return get_device(devices, val); @@ -172,46 +172,46 @@ Device* Pulse::get_device(vector& devices, const string& name) { return nullptr; } -Device* Pulse::GetSink(const uint32_t& index) { +Device* PulseClient::GetSink(const uint32_t& index) { return get_device(sinks_, index); } -Device* Pulse::GetSink(const string& name) { +Device* PulseClient::GetSink(const string& name) { return get_device(sinks_, name); } -Device* Pulse::GetSource(const uint32_t& index) { +Device* PulseClient::GetSource(const uint32_t& index) { return get_device(sources_, index); } -Device* Pulse::GetSource(const string& name) { +Device* PulseClient::GetSource(const string& name) { return get_device(sources_, name); } -Device* Pulse::GetSinkInput(const uint32_t& index) { +Device* PulseClient::GetSinkInput(const uint32_t& index) { return get_device(sink_inputs_, index); } -Device* Pulse::GetSinkInput(const string& name) { +Device* PulseClient::GetSinkInput(const string& name) { return get_device(sink_inputs_, name); } -Device* Pulse::GetSourceOutput(const uint32_t& index) { +Device* PulseClient::GetSourceOutput(const uint32_t& index) { return get_device(source_outputs_, index); } -Device* Pulse::GetSourceOutput(const string& name) { +Device* PulseClient::GetSourceOutput(const string& name) { return get_device(source_outputs_, name); } -void Pulse::mainloop_iterate(pa_operation* op) { +void PulseClient::mainloop_iterate(pa_operation* op) { int r; while (pa_operation_get_state(op) == PA_OPERATION_RUNNING) { pa_mainloop_iterate(mainloop_, 1, &r); } } -void Pulse::populate_cards() { +void PulseClient::populate_cards() { cards_.clear(); pa_operation* op = pa_context_get_card_info_list(context_, card_info_cb, @@ -220,7 +220,7 @@ void Pulse::populate_cards() { pa_operation_unref(op); } -void Pulse::populate_server_info() { +void PulseClient::populate_server_info() { pa_operation* op = pa_context_get_server_info(context_, server_info_cb, &defaults_); @@ -228,7 +228,7 @@ void Pulse::populate_server_info() { pa_operation_unref(op); } -void Pulse::populate_sinks() { +void PulseClient::populate_sinks() { sinks_.clear(); pa_operation* op = pa_context_get_sink_info_list(context_, device_info_cb, @@ -244,7 +244,7 @@ void Pulse::populate_sinks() { pa_operation_unref(op); } -void Pulse::populate_sources() { +void PulseClient::populate_sources() { sources_.clear(); pa_operation* op = pa_context_get_source_info_list(context_, device_info_cb, @@ -260,7 +260,7 @@ void Pulse::populate_sources() { pa_operation_unref(op); } -bool Pulse::SetMute(Device& device, bool mute) { +bool PulseClient::SetMute(Device& device, bool mute) { int success; if (device.ops_.Mute == nullptr) { @@ -281,7 +281,7 @@ bool Pulse::SetMute(Device& device, bool mute) { return success; } -bool Pulse::SetVolume(Device& device, long volume) { +bool PulseClient::SetVolume(Device& device, long volume) { int success; if (device.ops_.SetVolume == nullptr) { @@ -303,17 +303,17 @@ bool Pulse::SetVolume(Device& device, long volume) { return success; } -bool Pulse::IncreaseVolume(Device& device, long increment) { +bool PulseClient::IncreaseVolume(Device& device, long increment) { return SetVolume(device, volume_range_.clamp(device.volume_percent_ + increment)); } -bool Pulse::DecreaseVolume(Device& device, long increment) { +bool PulseClient::DecreaseVolume(Device& device, long increment) { return SetVolume(device, volume_range_.clamp(device.volume_percent_ - increment)); } -bool Pulse::SetBalance(Device& device, long balance) { +bool PulseClient::SetBalance(Device& device, long balance) { int success; if (device.ops_.SetVolume == nullptr) { @@ -337,25 +337,25 @@ bool Pulse::SetBalance(Device& device, long balance) { return success; } -bool Pulse::IncreaseBalance(Device& device, long increment) { +bool PulseClient::IncreaseBalance(Device& device, long increment) { return SetBalance(device, balance_range_.clamp(device.balance_ + increment)); } -bool Pulse::DecreaseBalance(Device& device, long increment) { +bool PulseClient::DecreaseBalance(Device& device, long increment) { return SetBalance(device, balance_range_.clamp(device.balance_ - increment)); } -int Pulse::GetVolume(const Device& device) const { +int PulseClient::GetVolume(const Device& device) const { return device.Volume(); } -int Pulse::GetBalance(const Device& device) const { +int PulseClient::GetBalance(const Device& device) const { return device.Balance(); } -bool Pulse::SetProfile(Card& card, const string& profile) { +bool PulseClient::SetProfile(Card& card, const string& profile) { int success; pa_operation* op = pa_context_set_card_profile_by_index(context_, @@ -379,7 +379,7 @@ bool Pulse::SetProfile(Card& card, const string& profile) { return success; } -bool Pulse::Move(Device& source, Device& dest) { +bool PulseClient::Move(Device& source, Device& dest) { int success; if (source.ops_.Move == nullptr) { @@ -398,7 +398,7 @@ bool Pulse::Move(Device& source, Device& dest) { return success; } -bool Pulse::Kill(Device& device) { +bool PulseClient::Kill(Device& device) { int success; if (device.ops_.Kill == nullptr) { @@ -418,7 +418,7 @@ bool Pulse::Kill(Device& device) { return success; } -bool Pulse::SetDefault(Device& device) { +bool PulseClient::SetDefault(Device& device) { int success; if (device.ops_.SetDefault == nullptr) { @@ -450,7 +450,7 @@ bool Pulse::SetDefault(Device& device) { return success; } -void Pulse::remove_device(Device& device) { +void PulseClient::remove_device(Device& device) { vector* devlist; switch (device.type_) { diff --git a/pulse.h b/pulse.h index 1e91352..9a61108 100644 --- a/pulse.h +++ b/pulse.h @@ -55,7 +55,7 @@ class Device { enum DeviceType Type() const { return type_; } private: - friend class Pulse; + friend class PulseClient; void update_volume(const pa_cvolume& newvol); @@ -83,7 +83,7 @@ class Card { const Profile& ActiveProfile() const { return active_profile_; } private: - friend class Pulse; + friend class PulseClient; uint32_t index_; string name_; @@ -110,10 +110,10 @@ struct Range { T max; }; -class Pulse { +class PulseClient { public: - Pulse(string client_name); - ~Pulse(); + PulseClient(string client_name); + ~PulseClient(); // Populates all known devices and cards. Any currently known // devices and cards are cleared before the new data is stored. -- cgit v1.2.3 From e35ce0069094c135ed9065f2bda945fdf4b6d83e Mon Sep 17 00:00:00 2001 From: Dave Reisner Date: Wed, 2 Jan 2013 11:33:59 -0500 Subject: ponymix: validate arg count before invoking function A few changes make this fun and easy: - Merge the function array into the string to action lookup and return a Command instead of simply an enum. A Command is the function and the min/max arg count. - Implement an InRange method for the Range class. - Add a Dispatch function to convert the string to Command and validate the arguments. This leaves us in a position where the argc parameter to each method is never used, but maybe some day a command will be added that takes a range of args rather than a fixed number. --- ponymix.cc | 209 ++++++++++++++++++++++++------------------------------------- pulse.cc | 8 +-- pulse.h | 10 ++- 3 files changed, 96 insertions(+), 131 deletions(-) diff --git a/ponymix.cc b/ponymix.cc index a6ce950..824daa6 100644 --- a/ponymix.cc +++ b/ponymix.cc @@ -31,16 +31,16 @@ enum Action { ACTION_INVALID, }; +struct Command { + int (*fn)(PulseClient&, int, char*[]); + Range args; +}; + static enum DeviceType opt_devtype; -static enum Action opt_action; +static const char* opt_action; static const char* opt_device; static const char* opt_card; -static const int kMinVolume = 0; -static const int kMaxVolume = 150; -static const int kMinBalance = -100; -static const int kMaxBalance = 100; - static const char* type_to_string(enum DeviceType t) { switch (t) { case DEVTYPE_SINK: @@ -54,38 +54,7 @@ static const char* type_to_string(enum DeviceType t) { } /* impossibiru! */ - return NULL; -} - -static enum Action string_to_action(const char* str) { - static std::map actionmap = { - { "defaults", ACTION_DEFAULTS }, - { "list", ACTION_LIST }, - { "list-cards", ACTION_LISTCARDS }, - { "list-profiles", ACTION_LISTPROFILES }, - { "get-volume", ACTION_GETVOL }, - { "set-volume", ACTION_SETVOL }, - { "get-balance", ACTION_GETBAL }, - { "set-balance", ACTION_SETBAL }, - { "adj-balance", ACTION_ADJBAL }, - { "increase", ACTION_INCREASE }, - { "decrease", ACTION_DECREASE }, - { "mute", ACTION_MUTE }, - { "unmute", ACTION_UNMUTE }, - { "toggle", ACTION_TOGGLE }, - { "is-muted", ACTION_ISMUTED }, - { "set-default", ACTION_SETDEFAULT }, - { "get-profile", ACTION_GETPROFILE }, - { "set-profile", ACTION_SETPROFILE }, - { "move", ACTION_MOVE }, - { "kill", ACTION_KILL } - }; - - try { - return actionmap.at(str); - } catch(std::out_of_range) { - errx(1, "error: Invalid action specified: %s", str); - } + throw std::out_of_range("device type out of range"); } static enum DeviceType string_to_devtype_or_die(const char* str) { @@ -153,9 +122,7 @@ static int ShowDefaults(PulseClient& ponymix, int, char*[]) { return 0; } -static int List(PulseClient& ponymix, int argc, char*[]) { - if (argc != 0) errx(1, "error: list requires 0 arguments"); - +static int List(PulseClient& ponymix, int, char*[]) { const auto& sinks = ponymix.GetSinks(); for (const auto& s : sinks) Print(s); @@ -171,19 +138,15 @@ static int List(PulseClient& ponymix, int argc, char*[]) { return 0; } -static int ListCards(PulseClient& ponymix, int argc, char*[]) { - if (argc != 0) errx(1, "error: list-cards requires 0 arguments"); - +static int ListCards(PulseClient& ponymix, int, char*[]) { const auto& cards = ponymix.GetCards(); for (const auto& c : cards) Print(c); return 0; } -static int ListProfiles(PulseClient& ponymix, int argc, char*[]) { - if (argc != 0) errx(1, "error: list-profiles requires 0 arguments"); - - // TODO: figure out how to get a list of cards? +static int ListProfiles(PulseClient& ponymix, int, char*[]) { + // TODO: Is there any sense of a "default card" ? auto card = ponymix.GetCard(opt_card); if (card == nullptr) errx(1, "error: no match found for card: %s", opt_card); @@ -193,17 +156,13 @@ static int ListProfiles(PulseClient& ponymix, int argc, char*[]) { return 0; } -static int GetVolume(PulseClient& ponymix, int argc, char*[]) { - if (argc != 0) errx(1, "error: get-volume requires 0 arguments"); - +static int GetVolume(PulseClient& ponymix, int, char*[]) { auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); printf("%d\n", device->Volume()); return 0; } -static int SetVolume(PulseClient& ponymix, int argc, char* argv[]) { - if (argc != 1) errx(1, "error: set-volume requires exactly 1 argument"); - +static int SetVolume(PulseClient& ponymix, int, char* argv[]) { auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); long volume; @@ -220,17 +179,13 @@ static int SetVolume(PulseClient& ponymix, int argc, char* argv[]) { return 0; } -static int GetBalance(PulseClient& ponymix, int argc, char*[]) { - if (argc != 0) errx(1, "error: get-balance requires 0 arguments"); - +static int GetBalance(PulseClient& ponymix, int, char*[]) { auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); printf("%d\n", device->Balance()); return 0; } -static int SetBalance(PulseClient& ponymix, int argc, char* argv[]) { - if (argc != 1) errx(1, "error: set-balance requires exactly 1 argument"); - +static int SetBalance(PulseClient& ponymix, int, char* argv[]) { auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); long balance; @@ -247,9 +202,7 @@ static int SetBalance(PulseClient& ponymix, int argc, char* argv[]) { return 0; } -static int AdjBalance(PulseClient& ponymix, int argc, char* argv[]) { - if (argc != 1) errx(1, "error: adj-balance requires exactly 1 argument"); - +static int AdjBalance(PulseClient& ponymix, int, char* argv[]) { auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); long balance; @@ -266,9 +219,7 @@ static int AdjBalance(PulseClient& ponymix, int argc, char* argv[]) { return 0; } -static int IncreaseVolume(PulseClient& ponymix, int argc, char* argv[]) { - if (argc != 1) errx(1, "error: increase requires exactly 1 argument"); - +static int IncreaseVolume(PulseClient& ponymix, int, char* argv[]) { auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); long delta; @@ -285,9 +236,7 @@ static int IncreaseVolume(PulseClient& ponymix, int argc, char* argv[]) { return 0; } -static int DecreaseVolume(PulseClient& ponymix, int argc, char* argv[]) { - if (argc != 1) errx(1, "error: decrease requires exactly 1 argument"); - +static int DecreaseVolume(PulseClient& ponymix, int, char* argv[]) { auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); long delta; @@ -304,9 +253,7 @@ static int DecreaseVolume(PulseClient& ponymix, int argc, char* argv[]) { return 0; } -static int Mute(PulseClient& ponymix, int argc, char*[]) { - if (argc != 0) errx(1, "error: mute requires 0 arguments"); - +static int Mute(PulseClient& ponymix, int, char*[]) { auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); if (!ponymix.SetMute(*device, true)) return 1; @@ -316,9 +263,7 @@ static int Mute(PulseClient& ponymix, int argc, char*[]) { return 0; } -static int Unmute(PulseClient& ponymix, int argc, char*[]) { - if (argc != 0) errx(1, "error: unmute requires 0 arguments"); - +static int Unmute(PulseClient& ponymix, int, char*[]) { auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); if (!ponymix.SetMute(*device, false)) return 1; @@ -328,9 +273,7 @@ static int Unmute(PulseClient& ponymix, int argc, char*[]) { return 0; } -static int ToggleMute(PulseClient& ponymix, int argc, char*[]) { - if (argc != 0) errx(1, "error: toggle requires 0 arguments"); - +static int ToggleMute(PulseClient& ponymix, int, char*[]) { auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); if (!ponymix.SetMute(*device, !ponymix.IsMuted(*device))) return 1; @@ -340,24 +283,18 @@ static int ToggleMute(PulseClient& ponymix, int argc, char*[]) { return 0; } -static int IsMuted(PulseClient& ponymix, int argc, char*[]) { - if (argc != 0) errx(1, "error: is-muted requires 0 arguments"); - +static int IsMuted(PulseClient& ponymix, int, char*[]) { auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); return !ponymix.IsMuted(*device); } -static int SetDefault(PulseClient& ponymix, int argc, char*[]) { - if (argc != 0) errx(1, "error: set-default requires 0 arguments"); - +static int SetDefault(PulseClient& ponymix, int, char*[]) { auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); return !ponymix.SetDefault(*device); } -static int GetProfile(PulseClient& ponymix, int argc, char*[]) { - if (argc != 0) errx(1, "error: get-profile requires 0 arguments"); - +static int GetProfile(PulseClient& ponymix, int, char*[]) { auto card = ponymix.GetCard(opt_card); if (card == nullptr) errx(1, "error: no match found for card: %s", opt_card); @@ -366,18 +303,14 @@ static int GetProfile(PulseClient& ponymix, int argc, char*[]) { return true; } -static int SetProfile(PulseClient& ponymix, int argc, char* argv[]) { - if (argc != 1) errx(1, "error: set-profile requires 1 argument"); - +static int SetProfile(PulseClient& ponymix, int, char* argv[]) { auto card = ponymix.GetCard(opt_card); if (card == nullptr) errx(1, "error: no match found for card: %s", opt_card); return !ponymix.SetProfile(*card, argv[0]); } -static int Move(PulseClient& ponymix, int argc, char* argv[]) { - if (argc != 1) errx(1, "error: move requires 1 argument"); - +static int Move(PulseClient& ponymix, int, char* argv[]) { // this assignment is a lie. stfu g++ enum DeviceType target_devtype = opt_devtype; switch (opt_devtype) { @@ -400,36 +333,66 @@ static int Move(PulseClient& ponymix, int argc, char* argv[]) { return !ponymix.Move(*source, *target); } -static int Kill(PulseClient& ponymix, int argc, char*[]) { - if (argc != 0) errx(1, "error: set-default requires 0 arguments"); - +static int Kill(PulseClient& ponymix, int, char*[]) { auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); return !ponymix.Kill(*device); } -static int (*fn[])(PulseClient& ponymix, int argc, char* argv[]) = { - [ACTION_DEFAULTS] = ShowDefaults, - [ACTION_LIST] = List, - [ACTION_LISTCARDS] = ListCards, - [ACTION_LISTPROFILES] = ListProfiles, - [ACTION_GETVOL] = GetVolume, - [ACTION_SETVOL] = SetVolume, - [ACTION_GETBAL] = GetBalance, - [ACTION_SETBAL] = SetBalance, - [ACTION_ADJBAL] = AdjBalance, - [ACTION_INCREASE] = IncreaseVolume, - [ACTION_DECREASE] = DecreaseVolume, - [ACTION_MUTE] = Mute, - [ACTION_UNMUTE] = Unmute, - [ACTION_TOGGLE] = ToggleMute, - [ACTION_ISMUTED] = IsMuted, - [ACTION_SETDEFAULT] = SetDefault, - [ACTION_GETPROFILE] = GetProfile, - [ACTION_SETPROFILE] = SetProfile, - [ACTION_MOVE] = Move, - [ACTION_KILL] = Kill, -}; +static const Command& string_to_command(const char* str) { + static std::map actionmap = { + // command name function arg min arg max + { "defaults", { ShowDefaults, { 0, 0 } } }, + { "list", { List, { 0, 0 } } }, + { "list-cards", { ListCards, { 0, 0 } } }, + { "list-profiles", { ListProfiles, { 0, 0 } } }, + { "get-volume", { GetVolume, { 0, 0 } } }, + { "set-volume", { SetVolume, { 1, 1 } } }, + { "get-balance", { GetBalance, { 0, 0 } } }, + { "set-balance", { SetBalance, { 1, 1 } } }, + { "adj-balance", { AdjBalance, { 1, 1 } } }, + { "increase", { IncreaseVolume, { 1, 1 } } }, + { "decrease", { DecreaseVolume, { 1, 1 } } }, + { "mute", { Mute, { 0, 0 } } }, + { "unmute", { Unmute, { 0, 0 } } }, + { "toggle", { ToggleMute, { 0, 0 } } }, + { "is-muted", { IsMuted, { 0, 0 } } }, + { "set-default", { SetDefault, { 0, 0 } } }, + { "get-profile", { GetProfile, { 0, 0 } } }, + { "set-profile", { SetProfile, { 1, 1 } } }, + { "move", { Move, { 1, 1 } } }, + { "kill", { Kill, { 0, 0 } } } + }; + + try { + return actionmap.at(str); + } catch(std::out_of_range) { + errx(1, "error: Invalid action specified: %s", str); + } +} + +void error_wrong_args(const Command& cmd, const char* cmdname) { + if (cmd.args.min == cmd.args.max) { + errx(1, "error: %s takes exactly %d argument%c", + cmdname, cmd.args.min, cmd.args.min == 1 ? '\0' : 's'); + } else { + errx(1, "error: %s takes %d to %d arguments\n", + cmdname, cmd.args.min, cmd.args.max); + } +} + +static int CommandDispatch(PulseClient& ponymix, int argc, char *argv[]) { + if (argc > 0) { + opt_action = argv[0]; + argv++; + argc--; + } + + const Command& cmd = string_to_command(opt_action); + if (cmd.args.InRange(argc) != 0) error_wrong_args(cmd, opt_action); + + return cmd.fn(ponymix, argc, argv); +} void usage() { printf("usage: %s [options] ...\n", program_invocation_short_name); @@ -537,7 +500,7 @@ int main(int argc, char* argv[]) { ponymix.Populate(); // defaults - opt_action = ACTION_DEFAULTS; + opt_action = "defaults"; opt_devtype = DEVTYPE_SINK; opt_device = ponymix.GetDefaults().sink.c_str(); opt_card = ponymix.GetCards()[0].Name().c_str(); @@ -546,13 +509,7 @@ int main(int argc, char* argv[]) { argc -= optind; argv += optind; - if (argc > 0) { - opt_action = string_to_action(argv[0]); - argc--; - argv++; - } - - return fn[opt_action](ponymix, argc, argv); + return CommandDispatch(ponymix, argc, argv); } // vim: set et ts=2 sw=2: diff --git a/pulse.cc b/pulse.cc index cbbe405..e42db12 100644 --- a/pulse.cc +++ b/pulse.cc @@ -305,12 +305,12 @@ bool PulseClient::SetVolume(Device& device, long volume) { bool PulseClient::IncreaseVolume(Device& device, long increment) { return SetVolume(device, - volume_range_.clamp(device.volume_percent_ + increment)); + volume_range_.Clamp(device.volume_percent_ + increment)); } bool PulseClient::DecreaseVolume(Device& device, long increment) { return SetVolume(device, - volume_range_.clamp(device.volume_percent_ - increment)); + volume_range_.Clamp(device.volume_percent_ - increment)); } bool PulseClient::SetBalance(Device& device, long balance) { @@ -339,12 +339,12 @@ bool PulseClient::SetBalance(Device& device, long balance) { bool PulseClient::IncreaseBalance(Device& device, long increment) { return SetBalance(device, - balance_range_.clamp(device.balance_ + increment)); + balance_range_.Clamp(device.balance_ + increment)); } bool PulseClient::DecreaseBalance(Device& device, long increment) { return SetBalance(device, - balance_range_.clamp(device.balance_ - increment)); + balance_range_.Clamp(device.balance_ - increment)); } int PulseClient::GetVolume(const Device& device) const { diff --git a/pulse.h b/pulse.h index 9a61108..9bb0733 100644 --- a/pulse.h +++ b/pulse.h @@ -102,10 +102,18 @@ template struct Range { Range(T min, T max) : min(min), max(max) {} - T clamp(T value) { + // Clamps a value to the stored range + T Clamp(T value) { return value < min ? min : (value > max ? max : value); } + // Determine if the passed value is within the range. Returns 0 + // on success, else -1 if lower than the minimum, and 1 if higher + // than the maximum. + int InRange(T value) const { + return value < min ? -1 : (value > max ? 1 : 0); + } + T min; T max; }; -- cgit v1.2.3 From 4292241040661f4b9b042b24d2f0773de0eb1366 Mon Sep 17 00:00:00 2001 From: Dave Reisner Date: Wed, 2 Jan 2013 15:27:14 -0500 Subject: Derive the card from the targetted device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unless explicitly specified, target a card based on the selected device. Note that not all devices will be tied to a card. falconindy » put differently, if i have multiple cards, what determines which card is used by pulse for a given app? tanuk » In theory, the logic can be anything (it depends on what policy-implementing modules are loaded). By default, routing is mostly handled by module-stream-restore, which chooses the sink based on the user's previous routing choices. tanuk » If the user hasn't done any routing choices, the fallback logic is to select the current "default sink". tanuk » I don't recommend trying to guess the routing policy. falconindy » i guess my understanding of pulse internals is lacking falconindy » but that's rather enlightening falconindy » is there any way to figure out the connection between a sink and a card? tanuk » Yes... (One moment, I'll look up things.) falconindy » ah. uint32_t card falconindy » appears to be in pa_sink_info falconindy » so that ties the sink to the index of a card? tanuk » Yep. falconindy » awesome, that's good enough for what i need to do tanuk » Not all sinks are part of a card, though, but those that are will have the card index set. falconindy » also good to know --- ponymix.cc | 16 +++++++++++++--- pulse.cc | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- pulse.h | 21 ++++++++++++++++++++- 3 files changed, 91 insertions(+), 8 deletions(-) diff --git a/ponymix.cc b/ponymix.cc index 824daa6..874728f 100644 --- a/ponymix.cc +++ b/ponymix.cc @@ -146,7 +146,8 @@ static int ListCards(PulseClient& ponymix, int, char*[]) { } static int ListProfiles(PulseClient& ponymix, int, char*[]) { - // TODO: Is there any sense of a "default card" ? + if (opt_card == nullptr) errx(1, "error: no card selected"); + auto card = ponymix.GetCard(opt_card); if (card == nullptr) errx(1, "error: no match found for card: %s", opt_card); @@ -500,15 +501,24 @@ int main(int argc, char* argv[]) { ponymix.Populate(); // defaults + ServerInfo defaults = ponymix.GetDefaults(); opt_action = "defaults"; opt_devtype = DEVTYPE_SINK; - opt_device = ponymix.GetDefaults().sink.c_str(); - opt_card = ponymix.GetCards()[0].Name().c_str(); + opt_device = defaults.sink.c_str(); if (!parse_options(argc, argv)) return 1; argc -= optind; argv += optind; + // cards are tricky... find the one that belongs to the chosen sink. + if (opt_card == nullptr) { + const Device* device = ponymix.GetDevice(opt_device, opt_devtype); + if (device) { + const Card* card = ponymix.GetCard(*device); + if (card) opt_card = card->Name().c_str(); + } + } + return CommandDispatch(ponymix, argc, argv); } diff --git a/pulse.cc b/pulse.cc index e42db12..b765d64 100644 --- a/pulse.cc +++ b/pulse.cc @@ -10,6 +10,7 @@ // C++ #include #include +#include // External #include @@ -155,6 +156,13 @@ Card* PulseClient::GetCard(const string& name) { return nullptr; } +Card* PulseClient::GetCard(const Device& device) { + for (Card& card : cards_) { + if (device.card_idx_ == card.index_) return &card; + } + return nullptr; +} + Device* PulseClient::get_device(vector& devices, const uint32_t& index) { for (Device& device : devices) { if (device.index_ == index) return &device; @@ -172,6 +180,48 @@ Device* PulseClient::get_device(vector& devices, const string& name) { return nullptr; } +Device* PulseClient::GetDevice(const uint32_t& index, enum DeviceType type) { + switch (type) { + case DEVTYPE_SINK: + return GetSink(index); + case DEVTYPE_SOURCE: + return GetSource(index); + case DEVTYPE_SINK_INPUT: + return GetSinkInput(index); + case DEVTYPE_SOURCE_OUTPUT: + return GetSourceOutput(index); + } + throw std::runtime_error("Impossible DeviceType encountered in GetDevice"); +} + +Device* PulseClient::GetDevice(const string& name, enum DeviceType type) { + switch (type) { + case DEVTYPE_SINK: + return GetSink(name); + case DEVTYPE_SOURCE: + return GetSource(name); + case DEVTYPE_SINK_INPUT: + return GetSinkInput(name); + case DEVTYPE_SOURCE_OUTPUT: + return GetSourceOutput(name); + } + throw std::runtime_error("Impossible DeviceType encountered in GetDevice"); +} + +const vector& PulseClient::GetDevices(enum DeviceType type) const { + switch (type) { + case DEVTYPE_SINK: + return GetSinks(); + case DEVTYPE_SOURCE: + return GetSources(); + case DEVTYPE_SINK_INPUT: + return GetSinkInputs(); + case DEVTYPE_SOURCE_OUTPUT: + return GetSourceOutputs(); + } + throw std::runtime_error("Impossible DeviceType encountered in GetDevices"); +} + Device* PulseClient::GetSink(const uint32_t& index) { return get_device(sinks_, index); } @@ -495,7 +545,8 @@ Device::Device(const pa_sink_info* info) : index_(info->index), name_(info->name ? info->name : ""), desc_(info->description), - mute_(info->mute) { + mute_(info->mute), + card_idx_(info->card) { update_volume(info->volume); memcpy(&channels_, &info->channel_map, sizeof(pa_channel_map)); balance_ = pa_cvolume_get_balance(&volume_, &channels_) * 100.0; @@ -510,7 +561,8 @@ Device::Device(const pa_source_info* info) : index_(info->index), name_(info->name ? info->name : ""), desc_(info->description), - mute_(info->mute) { + mute_(info->mute), + card_idx_(info->card) { update_volume(info->volume); memcpy(&channels_, &info->channel_map, sizeof(pa_channel_map)); balance_ = pa_cvolume_get_balance(&volume_, &channels_) * 100.0; @@ -524,7 +576,8 @@ Device::Device(const pa_sink_input_info* info) : type_(DEVTYPE_SINK_INPUT), index_(info->index), name_(info->name ? info->name : ""), - mute_(info->mute) { + mute_(info->mute), + card_idx_(-1) { update_volume(info->volume); memcpy(&channels_, &info->channel_map, sizeof(pa_channel_map)); balance_ = pa_cvolume_get_balance(&volume_, &channels_) * 100.0; @@ -543,7 +596,8 @@ Device::Device(const pa_source_output_info* info) : type_(DEVTYPE_SOURCE_OUTPUT), index_(info->index), name_(info->name ? info->name : ""), - mute_(info->mute) { + mute_(info->mute), + card_idx_(-1) { update_volume(info->volume); volume_percent_ = volume_as_percent(&volume_); balance_ = pa_cvolume_get_balance(&volume_, &channels_) * 100.0; diff --git a/pulse.h b/pulse.h index 9bb0733..2c010b2 100644 --- a/pulse.h +++ b/pulse.h @@ -68,6 +68,7 @@ class Device { pa_channel_map channels_; int mute_; int balance_; + uint32_t card_idx_; Operations ops_; }; @@ -96,6 +97,17 @@ class Card { struct ServerInfo { string sink; string source; + + const string GetDefault(enum DeviceType type) { + switch (type) { + case DEVTYPE_SINK: + return sink; + case DEVTYPE_SOURCE: + return source; + default: + return ""; + } + } }; template @@ -127,6 +139,11 @@ class PulseClient { // devices and cards are cleared before the new data is stored. void Populate(); + // Get a device by index or name and type, or all devices by type. + Device* GetDevice(const uint32_t& index, enum DeviceType type); + Device* GetDevice(const string& name, enum DeviceType type); + const vector& GetDevices(enum DeviceType type) const; + // Get a sink by index or name, or all sinks. Device* GetSink(const uint32_t& index); Device* GetSink(const string& name); @@ -147,9 +164,11 @@ class PulseClient { Device* GetSourceOutput(const string& name); const vector& GetSourceOutputs() const { return source_outputs_; } - // Get a card by index or name, or all cards. + // Get a card by index or name, all cards, or get the card which + // a sink is attached to. Card* GetCard(const uint32_t& index); Card* GetCard(const string& name); + Card* GetCard(const Device& device); const vector& GetCards() const { return cards_; } // Get or set the volume of a device. -- cgit v1.2.3 From f44dd5995a8da0e9f9e1d6372d9fc879b944b768 Mon Sep 17 00:00:00 2001 From: Dave Reisner Date: Wed, 2 Jan 2013 15:33:28 -0500 Subject: always defined behaviors for devices --- pulse.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pulse.cc b/pulse.cc index b765d64..7fc1bd1 100644 --- a/pulse.cc +++ b/pulse.cc @@ -553,6 +553,8 @@ Device::Device(const pa_sink_info* info) : ops_.SetVolume = pa_context_set_sink_volume_by_index; ops_.Mute = pa_context_set_sink_mute_by_index; + ops_.Kill = nullptr; + ops_.Move = nullptr; ops_.SetDefault = pa_context_set_default_sink; } @@ -569,6 +571,8 @@ Device::Device(const pa_source_info* info) : ops_.SetVolume = pa_context_set_source_volume_by_index; ops_.Mute = pa_context_set_source_mute_by_index; + ops_.Kill = nullptr; + ops_.Move = nullptr; ops_.SetDefault = pa_context_set_default_source; } @@ -590,6 +594,7 @@ Device::Device(const pa_sink_input_info* info) : ops_.Mute = pa_context_set_sink_input_mute; ops_.Kill = pa_context_kill_sink_input; ops_.Move = pa_context_move_sink_input_by_index; + ops_.SetDefault = nullptr; } Device::Device(const pa_source_output_info* info) : @@ -610,6 +615,7 @@ Device::Device(const pa_source_output_info* info) : ops_.Mute = pa_context_set_source_output_mute; ops_.Kill = pa_context_kill_source_output; ops_.Move = pa_context_move_source_output_by_index; + ops_.SetDefault = nullptr; } void Device::update_volume(const pa_cvolume& newvol) { -- cgit v1.2.3 From 560812acfb0441ad29b89febffdf5d016c9deff3 Mon Sep 17 00:00:00 2001 From: Dave Reisner Date: Wed, 2 Jan 2013 18:47:12 -0500 Subject: remove string_to_device; PulseClient can do this now --- ponymix.cc | 27 ++++++--------------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/ponymix.cc b/ponymix.cc index 874728f..74ffc75 100644 --- a/ponymix.cc +++ b/ponymix.cc @@ -71,25 +71,10 @@ static enum DeviceType string_to_devtype_or_die(const char* str) { } } -static Device* string_to_device(PulseClient& ponymix, string arg, enum DeviceType type) { - switch (type) { - case DEVTYPE_SINK: - return ponymix.GetSink(arg); - case DEVTYPE_SOURCE: - return ponymix.GetSource(arg); - case DEVTYPE_SOURCE_OUTPUT: - return ponymix.GetSourceOutput(arg); - case DEVTYPE_SINK_INPUT: - return ponymix.GetSinkInput(arg); - default: - return nullptr; - } -} - static Device* string_to_device_or_die(PulseClient& ponymix, string arg, enum DeviceType type) { - Device* device = string_to_device(ponymix, arg, type); + Device* device = ponymix.GetDevice(arg, type); if (device == nullptr) errx(1, "no match found for device: %s", arg.c_str()); return device; } @@ -169,7 +154,7 @@ static int SetVolume(PulseClient& ponymix, int, char* argv[]) { long volume; try { volume = std::stol(argv[0]); - } catch (std::invalid_argument _) { + } catch (std::invalid_argument) { errx(1, "error: failed to convert string to integer: %s", argv[0]); } @@ -192,7 +177,7 @@ static int SetBalance(PulseClient& ponymix, int, char* argv[]) { long balance; try { balance = std::stol(argv[0]); - } catch (std::invalid_argument _) { + } catch (std::invalid_argument) { errx(1, "error: failed to convert string to integer: %s", argv[0]); } @@ -209,7 +194,7 @@ static int AdjBalance(PulseClient& ponymix, int, char* argv[]) { long balance; try { balance = std::stol(argv[0]); - } catch (std::invalid_argument _) { + } catch (std::invalid_argument) { errx(1, "error: failed to convert string to integer: %s", argv[0]); } @@ -226,7 +211,7 @@ static int IncreaseVolume(PulseClient& ponymix, int, char* argv[]) { long delta; try { delta = std::stol(argv[0]); - } catch (std::invalid_argument _) { + } catch (std::invalid_argument) { errx(1, "error: failed to convert string to integer: %s", argv[0]); } @@ -243,7 +228,7 @@ static int DecreaseVolume(PulseClient& ponymix, int, char* argv[]) { long delta; try { delta = std::stol(argv[0]); - } catch (std::invalid_argument _) { + } catch (std::invalid_argument) { errx(1, "error: failed to convert string to integer: %s", argv[0]); } -- cgit v1.2.3 From acbd81fc241a400325a4e47a3f09d2f4f4d9f0d2 Mon Sep 17 00:00:00 2001 From: Dave Reisner Date: Wed, 2 Jan 2013 19:11:54 -0500 Subject: whitespace police --- pulse.cc | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/pulse.cc b/pulse.cc index 7fc1bd1..edd0449 100644 --- a/pulse.cc +++ b/pulse.cc @@ -182,42 +182,42 @@ Device* PulseClient::get_device(vector& devices, const string& name) { Device* PulseClient::GetDevice(const uint32_t& index, enum DeviceType type) { switch (type) { - case DEVTYPE_SINK: - return GetSink(index); - case DEVTYPE_SOURCE: - return GetSource(index); - case DEVTYPE_SINK_INPUT: - return GetSinkInput(index); - case DEVTYPE_SOURCE_OUTPUT: - return GetSourceOutput(index); + case DEVTYPE_SINK: + return GetSink(index); + case DEVTYPE_SOURCE: + return GetSource(index); + case DEVTYPE_SINK_INPUT: + return GetSinkInput(index); + case DEVTYPE_SOURCE_OUTPUT: + return GetSourceOutput(index); } throw std::runtime_error("Impossible DeviceType encountered in GetDevice"); } Device* PulseClient::GetDevice(const string& name, enum DeviceType type) { switch (type) { - case DEVTYPE_SINK: - return GetSink(name); - case DEVTYPE_SOURCE: - return GetSource(name); - case DEVTYPE_SINK_INPUT: - return GetSinkInput(name); - case DEVTYPE_SOURCE_OUTPUT: - return GetSourceOutput(name); + case DEVTYPE_SINK: + return GetSink(name); + case DEVTYPE_SOURCE: + return GetSource(name); + case DEVTYPE_SINK_INPUT: + return GetSinkInput(name); + case DEVTYPE_SOURCE_OUTPUT: + return GetSourceOutput(name); } throw std::runtime_error("Impossible DeviceType encountered in GetDevice"); } const vector& PulseClient::GetDevices(enum DeviceType type) const { switch (type) { - case DEVTYPE_SINK: - return GetSinks(); - case DEVTYPE_SOURCE: - return GetSources(); - case DEVTYPE_SINK_INPUT: - return GetSinkInputs(); - case DEVTYPE_SOURCE_OUTPUT: - return GetSourceOutputs(); + case DEVTYPE_SINK: + return GetSinks(); + case DEVTYPE_SOURCE: + return GetSources(); + case DEVTYPE_SINK_INPUT: + return GetSinkInputs(); + case DEVTYPE_SOURCE_OUTPUT: + return GetSourceOutputs(); } throw std::runtime_error("Impossible DeviceType encountered in GetDevices"); } -- cgit v1.2.3 From 67f158298175936767387191cb5d548c5a980055 Mon Sep 17 00:00:00 2001 From: Dave Reisner Date: Wed, 2 Jan 2013 20:07:15 -0500 Subject: fix abort on set-profile after removing profile ponymix set-profile off ponymix set-profile output:stereo-da+input:stereo-analog --- ponymix.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ponymix.cc b/ponymix.cc index 74ffc75..ab287da 100644 --- a/ponymix.cc +++ b/ponymix.cc @@ -281,6 +281,8 @@ static int SetDefault(PulseClient& ponymix, int, char*[]) { } static int GetProfile(PulseClient& ponymix, int, char*[]) { + if (opt_card == nullptr) errx(1, "error: no card selected"); + auto card = ponymix.GetCard(opt_card); if (card == nullptr) errx(1, "error: no match found for card: %s", opt_card); @@ -290,6 +292,8 @@ static int GetProfile(PulseClient& ponymix, int, char*[]) { } static int SetProfile(PulseClient& ponymix, int, char* argv[]) { + if (opt_card == nullptr) errx(1, "error: no card selected"); + auto card = ponymix.GetCard(opt_card); if (card == nullptr) errx(1, "error: no match found for card: %s", opt_card); -- cgit v1.2.3 From 46a2bf82c2e4c8c9ecd885ea099e948f119fabb4 Mon Sep 17 00:00:00 2001 From: Dave Reisner Date: Wed, 2 Jan 2013 20:17:27 -0500 Subject: A dash of color... --- ponymix.cc | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 85 insertions(+), 6 deletions(-) diff --git a/ponymix.cc b/ponymix.cc index ab287da..1aaeb1f 100644 --- a/ponymix.cc +++ b/ponymix.cc @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -36,10 +37,49 @@ struct Command { Range args; }; +struct Color { + Color() { + if (isatty(fileno(stdout))) { + name = "\033[1m"; + reset = "\033[0m"; + over9000 = "\033[7m;31m"; + veryhigh = "\033[31m"; + high = "\033[35m"; + mid = "\033[33m"; + low = "\033[32m"; + verylow = "\033[34m"; + mute = "\033[1;31m"; + } else { + name = ""; + reset = ""; + over9000 = ""; + veryhigh = ""; + high = ""; + mid = ""; + low = ""; + verylow = ""; + mute = ""; + } + } + + const char* name; + const char* reset; + + // Volume levels + const char* over9000; + const char* veryhigh; + const char* high; + const char* mid; + const char* low; + const char* verylow; + const char* mute; +}; + static enum DeviceType opt_devtype; static const char* opt_action; static const char* opt_device; static const char* opt_card; +static Color color; static const char* type_to_string(enum DeviceType t) { switch (t) { @@ -80,24 +120,63 @@ static Device* string_to_device_or_die(PulseClient& ponymix, } static void Print(const Device& device) { - printf("%s %d: %s\n" + const char *mute = device.Muted() ? " [Muted]" : ""; + const char *volume_color; + + if (device.Volume() < 20) { + volume_color = color.verylow; + } else if (device.Volume() < 40) { + volume_color = color.low; + } else if (device.Volume() < 60) { + volume_color = color.mid; + } else if (device.Volume() < 80) { + volume_color = color.high; + } else if (device.Volume() <= 100) { + volume_color = color.veryhigh; + } else { + volume_color = color.over9000; + } + + printf("%s%s %d:%s %s\n" " %s\n" - " Avg. Volume: %d%%%s\n", + " Avg. Volume: %s%d%%%s%s%s%s\n", + color.name, type_to_string(device.Type()), device.Index(), + color.reset, device.Name().c_str(), device.Desc().c_str(), + volume_color, device.Volume(), - device.Muted() ? " [muted]" : ""); + color.reset, + color.mute, + mute, + color.reset); } static void Print(const Card& card) { - printf("%s\n", card.Name().c_str()); + printf("%scard %d:%s %s\n" + " Driver: %s\n" + " Active Profile: %s\n", + color.name, + card.Index(), + color.reset, + card.Name().c_str(), + card.Driver().c_str(), + card.ActiveProfile().name.c_str()); } static void Print(const Profile& profile, bool active) { - printf("%s: %s%s\n", - profile.name.c_str(), profile.desc.c_str(), active ? " [active]" : ""); + const char* active_str = active ? " [active]" : ""; + printf("%s%s%s%s%s%s\n" + " %s\n", + color.name, + profile.name.c_str(), + color.reset, + color.low, + active_str, + color.reset, + profile.desc.c_str()); } static int ShowDefaults(PulseClient& ponymix, int, char*[]) { -- cgit v1.2.3 From 46fb647db1d914b856c799ea5ba15ba20c1dc5e4 Mon Sep 17 00:00:00 2001 From: Dave Reisner Date: Wed, 2 Jan 2013 20:42:48 -0500 Subject: implement -short variations of list verbs --- ponymix.cc | 190 ++++++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 130 insertions(+), 60 deletions(-) diff --git a/ponymix.cc b/ponymix.cc index 1aaeb1f..71de714 100644 --- a/ponymix.cc +++ b/ponymix.cc @@ -11,8 +11,11 @@ enum Action { ACTION_DEFAULTS, ACTION_LIST, + ACTION_LIST_SHORT, ACTION_LISTCARDS, + ACTION_LISTCARDS_SHORT, ACTION_LISTPROFILES, + ACTION_LISTPROFILES_SHORT, ACTION_GETVOL, ACTION_SETVOL, ACTION_GETBAL, @@ -76,6 +79,7 @@ struct Color { }; static enum DeviceType opt_devtype; +static bool opt_listrestrict; static const char* opt_action; static const char* opt_device; static const char* opt_card; @@ -88,9 +92,9 @@ static const char* type_to_string(enum DeviceType t) { case DEVTYPE_SOURCE: return "source"; case DEVTYPE_SINK_INPUT: - return "sink input"; + return "sink-input"; case DEVTYPE_SOURCE_OUTPUT: - return "source output"; + return "source-output"; } /* impossibiru! */ @@ -119,7 +123,15 @@ static Device* string_to_device_or_die(PulseClient& ponymix, return device; } -static void Print(const Device& device) { +static void Print(const Device& device, bool shirt) { + if (shirt) { + printf("%s\t%d\t%s\n", + type_to_string(device.Type()), + device.Index(), + device.Name().c_str()); + return; + } + const char *mute = device.Muted() ? " [Muted]" : ""; const char *volume_color; @@ -154,7 +166,12 @@ static void Print(const Device& device) { color.reset); } -static void Print(const Card& card) { +static void Print(const Card& card, bool shirt) { + if (shirt) { + printf("%s\n", card.Name().c_str()); + return; + } + printf("%scard %d:%s %s\n" " Driver: %s\n" " Active Profile: %s\n", @@ -166,7 +183,12 @@ static void Print(const Card& card) { card.ActiveProfile().name.c_str()); } -static void Print(const Profile& profile, bool active) { +static void Print(const Profile& profile, bool active, bool shirt) { + if (shirt) { + printf("%s\n", profile.name.c_str()); + return; + } + const char* active_str = active ? " [active]" : ""; printf("%s%s%s%s%s%s\n" " %s\n", @@ -181,46 +203,78 @@ static void Print(const Profile& profile, bool active) { static int ShowDefaults(PulseClient& ponymix, int, char*[]) { const auto& info = ponymix.GetDefaults(); - Print(*ponymix.GetSink(info.sink)); - Print(*ponymix.GetSource(info.source)); + Print(*ponymix.GetSink(info.sink), false); + Print(*ponymix.GetSource(info.source), false); return 0; } -static int List(PulseClient& ponymix, int, char*[]) { +static int list_devices(PulseClient& ponymix, bool shirt) { + if (opt_listrestrict) { + const auto& devices = ponymix.GetDevices(opt_devtype); + for (const auto& d : devices) Print(d, shirt); + return 0; + } + const auto& sinks = ponymix.GetSinks(); - for (const auto& s : sinks) Print(s); + for (const auto& s : sinks) Print(s, shirt); const auto& sources = ponymix.GetSources(); - for (const auto& s : sources) Print(s); + for (const auto& s : sources) Print(s, shirt); const auto& sinkinputs = ponymix.GetSinkInputs(); - for (const auto& s : sinkinputs) Print(s); + for (const auto& s : sinkinputs) Print(s, shirt); const auto& sourceoutputs = ponymix.GetSourceOutputs(); - for (const auto& s : sourceoutputs) Print(s); + for (const auto& s : sourceoutputs) Print(s, shirt); return 0; } -static int ListCards(PulseClient& ponymix, int, char*[]) { +static int List(PulseClient& ponymix, int, char*[]) { + return list_devices(ponymix, false); +} + +static int ListShort(PulseClient& ponymix, int, char*[]) { + return list_devices(ponymix, true); +} + +static int list_cards(PulseClient& ponymix, bool shirt) { const auto& cards = ponymix.GetCards(); - for (const auto& c : cards) Print(c); + for (const auto& c : cards) Print(c, shirt); return 0; } -static int ListProfiles(PulseClient& ponymix, int, char*[]) { +static int ListCards(PulseClient& ponymix, int, char*[]) { + return list_cards(ponymix, false); +} + +static int ListCardsShort(PulseClient& ponymix, int, char*[]) { + return list_cards(ponymix, true); +} + +static int list_profiles(PulseClient& ponymix, bool shirt) { if (opt_card == nullptr) errx(1, "error: no card selected"); auto card = ponymix.GetCard(opt_card); if (card == nullptr) errx(1, "error: no match found for card: %s", opt_card); const auto& profiles = card->Profiles(); - for (const auto& p : profiles) Print(p, p.name == card->ActiveProfile().name); + for (const auto& p : profiles) Print(p, + p.name == card->ActiveProfile().name, + shirt); return 0; } +static int ListProfiles(PulseClient& ponymix, int, char*[]) { + return list_profiles(ponymix, false); +} + +static int ListProfilesShort(PulseClient& ponymix, int, char*[]) { + return list_profiles(ponymix, true); +} + static int GetVolume(PulseClient& ponymix, int, char*[]) { auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); printf("%d\n", device->Volume()); @@ -411,26 +465,29 @@ static int Kill(PulseClient& ponymix, int, char*[]) { static const Command& string_to_command(const char* str) { static std::map actionmap = { // command name function arg min arg max - { "defaults", { ShowDefaults, { 0, 0 } } }, - { "list", { List, { 0, 0 } } }, - { "list-cards", { ListCards, { 0, 0 } } }, - { "list-profiles", { ListProfiles, { 0, 0 } } }, - { "get-volume", { GetVolume, { 0, 0 } } }, - { "set-volume", { SetVolume, { 1, 1 } } }, - { "get-balance", { GetBalance, { 0, 0 } } }, - { "set-balance", { SetBalance, { 1, 1 } } }, - { "adj-balance", { AdjBalance, { 1, 1 } } }, - { "increase", { IncreaseVolume, { 1, 1 } } }, - { "decrease", { DecreaseVolume, { 1, 1 } } }, - { "mute", { Mute, { 0, 0 } } }, - { "unmute", { Unmute, { 0, 0 } } }, - { "toggle", { ToggleMute, { 0, 0 } } }, - { "is-muted", { IsMuted, { 0, 0 } } }, - { "set-default", { SetDefault, { 0, 0 } } }, - { "get-profile", { GetProfile, { 0, 0 } } }, - { "set-profile", { SetProfile, { 1, 1 } } }, - { "move", { Move, { 1, 1 } } }, - { "kill", { Kill, { 0, 0 } } } + { "defaults", { ShowDefaults, { 0, 0 } } }, + { "list", { List, { 0, 0 } } }, + { "list-short", { ListShort, { 0, 0 } } }, + { "list-cards", { ListCards, { 0, 0 } } }, + { "list-cards-short", { ListCardsShort, { 0, 0 } } }, + { "list-profiles", { ListProfiles, { 0, 0 } } }, + { "list-profiles-short", { ListProfilesShort, { 0, 0 } } }, + { "get-volume", { GetVolume, { 0, 0 } } }, + { "set-volume", { SetVolume, { 1, 1 } } }, + { "get-balance", { GetBalance, { 0, 0 } } }, + { "set-balance", { SetBalance, { 1, 1 } } }, + { "adj-balance", { AdjBalance, { 1, 1 } } }, + { "increase", { IncreaseVolume, { 1, 1 } } }, + { "decrease", { DecreaseVolume, { 1, 1 } } }, + { "mute", { Mute, { 0, 0 } } }, + { "unmute", { Unmute, { 0, 0 } } }, + { "toggle", { ToggleMute, { 0, 0 } } }, + { "is-muted", { IsMuted, { 0, 0 } } }, + { "set-default", { SetDefault, { 0, 0 } } }, + { "get-profile", { GetProfile, { 0, 0 } } }, + { "set-profile", { SetProfile, { 1, 1 } } }, + { "move", { Move, { 1, 1 } } }, + { "kill", { Kill, { 0, 0 } } } }; try { @@ -440,30 +497,7 @@ static const Command& string_to_command(const char* str) { } } -void error_wrong_args(const Command& cmd, const char* cmdname) { - if (cmd.args.min == cmd.args.max) { - errx(1, "error: %s takes exactly %d argument%c", - cmdname, cmd.args.min, cmd.args.min == 1 ? '\0' : 's'); - } else { - errx(1, "error: %s takes %d to %d arguments\n", - cmdname, cmd.args.min, cmd.args.max); - } -} - -static int CommandDispatch(PulseClient& ponymix, int argc, char *argv[]) { - if (argc > 0) { - opt_action = argv[0]; - argv++; - argc--; - } - - const Command& cmd = string_to_command(opt_action); - if (cmd.args.InRange(argc) != 0) error_wrong_args(cmd, opt_action); - - return cmd.fn(ponymix, argc, argv); -} - -void usage() { +static void usage() { printf("usage: %s [options] ...\n", program_invocation_short_name); fputs("\nOptions:\n" " -h, --help display this help and exit\n\n" @@ -479,8 +513,11 @@ void usage() { " --source-output alias to -t source-output\n", stdout); fputs("\nCommon Commands:\n" + " help display this message\n" " list list available devices\n" + " list-short list available devices (short form)\n" " list-cards list available cards\n" + " list-cards-short list available cards (short form)\n" " get-volume get volume for device\n" " set-volume VALUE set volume for device\n" " get-balance get balance for device\n" @@ -495,6 +532,7 @@ void usage() { fputs("\nCard Commands:\n" " list-profiles list available profiles for a card\n" + " list-profiles-short list available profiles for a card (short form)\n" " get-profile get active profile for card\n" " set-profile PROFILE set profile for a card\n" @@ -509,6 +547,34 @@ void usage() { exit(EXIT_SUCCESS); } +void error_wrong_args(const Command& cmd, const char* cmdname) { + if (cmd.args.min == cmd.args.max) { + errx(1, "error: %s takes exactly %d argument%c", + cmdname, cmd.args.min, cmd.args.min == 1 ? '\0' : 's'); + } else { + errx(1, "error: %s takes %d to %d arguments\n", + cmdname, cmd.args.min, cmd.args.max); + } +} + +static int CommandDispatch(PulseClient& ponymix, int argc, char *argv[]) { + if (argc > 0) { + opt_action = argv[0]; + argv++; + argc--; + } + + if (strcmp(opt_action, "help") == 0) { + usage(); + return 0; + } + + const Command& cmd = string_to_command(opt_action); + if (cmd.args.InRange(argc) != 0) error_wrong_args(cmd, opt_action); + + return cmd.fn(ponymix, argc, argv); +} + bool parse_options(int argc, char** argv) { static const struct option opts[] = { { "card", required_argument, 0, 'c' }, @@ -545,16 +611,20 @@ bool parse_options(int argc, char** argv) { case 0x100: case 0x101: opt_devtype = DEVTYPE_SINK; + opt_listrestrict = true; break; case 0x102: case 0x103: opt_devtype = DEVTYPE_SOURCE; + opt_listrestrict = true; break; case 0x104: opt_devtype = DEVTYPE_SINK_INPUT; + opt_listrestrict = true; break; case 0x105: opt_devtype = DEVTYPE_SOURCE_OUTPUT; + opt_listrestrict = true; break; default: return false; -- cgit v1.2.3 From b5cbd96d9cb25b5c4922c52df7ab93b9a4d0707f Mon Sep 17 00:00:00 2001 From: Dave Reisner Date: Wed, 2 Jan 2013 21:15:59 -0500 Subject: remove defunct enum --- ponymix.cc | 29 +---------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/ponymix.cc b/ponymix.cc index 71de714..b1524f5 100644 --- a/ponymix.cc +++ b/ponymix.cc @@ -8,33 +8,6 @@ #include #include -enum Action { - ACTION_DEFAULTS, - ACTION_LIST, - ACTION_LIST_SHORT, - ACTION_LISTCARDS, - ACTION_LISTCARDS_SHORT, - ACTION_LISTPROFILES, - ACTION_LISTPROFILES_SHORT, - ACTION_GETVOL, - ACTION_SETVOL, - ACTION_GETBAL, - ACTION_SETBAL, - ACTION_ADJBAL, - ACTION_INCREASE, - ACTION_DECREASE, - ACTION_MUTE, - ACTION_UNMUTE, - ACTION_TOGGLE, - ACTION_ISMUTED, - ACTION_SETDEFAULT, - ACTION_GETPROFILE, - ACTION_SETPROFILE, - ACTION_MOVE, - ACTION_KILL, - ACTION_INVALID, -}; - struct Command { int (*fn)(PulseClient&, int, char*[]); Range args; @@ -45,7 +18,7 @@ struct Color { if (isatty(fileno(stdout))) { name = "\033[1m"; reset = "\033[0m"; - over9000 = "\033[7m;31m"; + over9000 = "\033[7;31m"; veryhigh = "\033[31m"; high = "\033[35m"; mid = "\033[33m"; -- cgit v1.2.3 From 99d86934399e9691a0b8dae5ce273e3909476429 Mon Sep 17 00:00:00 2001 From: Dave Reisner Date: Wed, 2 Jan 2013 22:48:29 -0500 Subject: reorg usage output --- ponymix.cc | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/ponymix.cc b/ponymix.cc index b1524f5..2f76905 100644 --- a/ponymix.cc +++ b/ponymix.cc @@ -485,8 +485,10 @@ static void usage() { " --sink-input alias to -t sink-input\n" " --source-output alias to -t source-output\n", stdout); - fputs("\nCommon Commands:\n" + fputs("\nDevice Commands:\n" " help display this message\n" + " defaults list default devices (default command)\n" + " set-default set default device by ID\n" " list list available devices\n" " list-short list available devices (short form)\n" " list-cards list available cards\n" @@ -501,21 +503,15 @@ static void usage() { " mute mute device\n" " unmute unmute device\n" " toggle toggle mute\n" - " is-muted check if muted\n", stdout); + " is-muted check if muted\n" + " move DEVICE move target device to DEVICE\n" + " kill DEVICE kill target DEVICE\n", stdout); fputs("\nCard Commands:\n" " list-profiles list available profiles for a card\n" " list-profiles-short list available profiles for a card (short form)\n" " get-profile get active profile for card\n" - " set-profile PROFILE set profile for a card\n" - - "\nDevice Commands:\n" - " defaults list default devices (default command)\n" - " set-default DEVICE set default device by ID\n" - - "\nApplication Commands:\n" - " move DEVICE move target device to DEVICE\n" - " kill DEVICE kill target DEVICE\n", stdout); + " set-profile PROFILE set profile for a card\n", stdout); exit(EXIT_SUCCESS); } -- cgit v1.2.3 From 89e5f853b56023ea951085ee5b74594723644427 Mon Sep 17 00:00:00 2001 From: Dave Reisner Date: Wed, 2 Jan 2013 22:48:35 -0500 Subject: add manpage Yay, the makefile is no longer a lie! --- ponymix.1 | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 ponymix.1 diff --git a/ponymix.1 b/ponymix.1 new file mode 100644 index 0000000..470f294 --- /dev/null +++ b/ponymix.1 @@ -0,0 +1,99 @@ +.TH ponymix "1" "2013-01-02" "ponymix" "User Commands" +.SH NAME +ponymix \- cli volume control for PulseAudio +.SH SYNOPSIS +\fBponymix\fP [options] \fIoperation\fP [args] +.SH DESCRIPTION +\fBponymix\fP is a command line volume control for PulseAudio, letting you +perform many operations on both device and application sinks and source. +.SH OPTIONS +.PP +.IP "\fB\-c\fR, \fB\-\-card\fR \fICARD\fR" +Specify a card. By default, the card associated with the specified device +is used. Cards can be specified by name or numeric index. +.IP "\fB\-d\fR, \fB\-\-device\fR \fIDEVICE\fR" +Specify a device other than the default. Devices can be specified by name +or numeric index. +.IP "\fB\-t\fR, \fB\-\-devtype\fR \fITYPE\fR" +Specify a device type to examine, usually in conjunction with the \fB--device\fR +flag. \fITYPE\fR must be one of \fIsink\fR, \fIsource\fR, \fIsink-input\fR, or +\fIsource-output\fR. +.IP "\fB--sink\fR, \fB--output\fR" +Aliases to \fB--devtype\fR \fIsink\fR. +.IP "\fB--source\fR, \fB--input\fR" +Aliases to \fB--devtype\fR \fIsource\fR. +.IP "\fB--source-output\fR" +Alias to \fB--devtype\fR \fIsource-output\fR. +.IP "\fB--sink-input\fR" +Alias to \fB--devtype\fR \fIsink-input\fR. +.SH OPERATIONS +.SS Generic Commands +.IP "\fBhelp\fR" +Display the usage and exit. +.SS Device Commands +These commands are specific to devices. Not all devices will support +all the listed commands. +.PP +.IP "\fBlist\fR" +List all available devices. This can be filtered using the \fB--devtype\fR flag. +.IP "\fBlist-short\fR" +List all available devices in a parseable format. This can be filtered using the +\fB--devtype\fR flag. +.IP "\fBdefaults\fR" +Display the default sink and source. This is the default command if none +is specified. +.IP "\fBset-default\fR \fIDEVICE\fR" +Set a sink or source by name or numeric index as the default. +.IP "\fBget-volume\fR" +Get the volume of a device. +.IP "\fBset-volume\fR \fIVALUE\fR" +Set the volume of a device. \fIVALUE\fR is an integer between 0 and 150. +.IP "\fBget-balance\fR" +Get the balance of a device. +.IP "\fBset-balance\fR \fIVALUE\fR" +Set the balance of a device. \fIVALUE\fR is an integer from -100 (all left) to 100 +(all right). +.IP "\fBadj-balance\fR \fIVALUE\fR" +Adjust balance by the integer increment \fIVALUE\fR. The resulting balance has +the same bounds as those set by \fBset-balance\fR. The end-of-options indicator +(\fI--\fR) is required when passing a negative increment. +.IP "\fBincrease\fR \fIVALUE\fR" +Increase the volume percentage of a device or application by integer +VALUE. Increasing the volume in this way is capped at 100. +.IP "\fBdecrease\fR \fIVALUE\fR" +Decrease the volume percentage of a device by the integer \fIVALUE\fR. +Decreasing the volume in this way is capped at 0. +.IP "\fBmute\fR, \fBunmute\fR, \fBtoggle\fR" +Mute, unmute, or toggle the mute status of a device. +.IP "\fBis-muted\fR" +Check if a device is muted. ponymix will exit zero if muted, and non-zero +otherwise. +.IP "\fBmove\fR \fIDEVICE\fR" +Move a device's stream to the given device, specified using the \fI--devtype\fR +and \fI--device\fR flags. Note that a source output can only be moved to +another source, and a sink input can only be moved to another sink. +.IP "\fBkill\fR +Kill a device's stream, specified using the \fI--device\fR and \fI--devtype\fR +flags. This only applies to sink-inputs and source-outputs. +.SS Card Commands +These commands are specific to cards. +.PP +.IP "\fBlist-cards\fR" +List all available cards. +.IP "\fBlist-cards-short\fR" +List names for all available cards. +.IP "\fBlist-profiles\fR" +List profiles for a card. +.IP "\fBlist-profiles-short\fR" +List only names for the available profiles for a card. +.IP "\fBset-profile\fR" \fIPROFILE\fR +Set the specified profile for a card. + +.SH AUTHORS +.nf +Dave Reisner +Simon Gomizelj +.fi +.SH SEE ALSO +.BR pulseaudio (1) + -- cgit v1.2.3 From 244fa2d8b1ced32fd9f06ea6e44785d2fdd32b3c Mon Sep 17 00:00:00 2001 From: Dave Reisner Date: Thu, 3 Jan 2013 09:17:25 -0500 Subject: Cleanup manpage, reintroduce application commands section 99d86934399e9 implies that I lied. move and kill have special behavior since they only ever really operate on sink-inputs and source-outputs. Reflect this in Kill on the frontend, since it previously required the exact device type. --- ponymix.1 | 13 +++++++++---- ponymix.cc | 14 +++++++++++++- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/ponymix.1 b/ponymix.1 index 470f294..b222440 100644 --- a/ponymix.1 +++ b/ponymix.1 @@ -68,12 +68,17 @@ Mute, unmute, or toggle the mute status of a device. .IP "\fBis-muted\fR" Check if a device is muted. ponymix will exit zero if muted, and non-zero otherwise. +.SS Application Commands +These commands are specific to devices which refer to streams of applications. +For these commands, \fIsink\fR and \fIsource\fR are synonymous with \fIsink-input\fR +and \fIsource-output\fR, respectively. .IP "\fBmove\fR \fIDEVICE\fR" -Move a device's stream to the given device, specified using the \fI--devtype\fR -and \fI--device\fR flags. Note that a source output can only be moved to -another source, and a sink input can only be moved to another sink. +Move a device's stream to the given device, specified using the \fB--devtype\fR +and \fB--device\fR flags. Note that a source output can only be moved to +another source, and a sink input can only be moved to another sink. The type of +the target device will be inferred using this logic. .IP "\fBkill\fR -Kill a device's stream, specified using the \fI--device\fR and \fI--devtype\fR +Kill a device's stream, specified using the \fB--device\fR and \fB--devtype\fR flags. This only applies to sink-inputs and source-outputs. .SS Card Commands These commands are specific to cards. diff --git a/ponymix.cc b/ponymix.cc index 2f76905..31765fa 100644 --- a/ponymix.cc +++ b/ponymix.cc @@ -430,6 +430,17 @@ static int Move(PulseClient& ponymix, int, char* argv[]) { } static int Kill(PulseClient& ponymix, int, char*[]) { + switch (opt_devtype) { + case DEVTYPE_SOURCE: + opt_devtype = DEVTYPE_SOURCE_OUTPUT; + break; + case DEVTYPE_SINK: + opt_devtype = DEVTYPE_SINK_INPUT; + break; + default: + break; + } + auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); return !ponymix.Kill(*device); @@ -503,7 +514,8 @@ static void usage() { " mute mute device\n" " unmute unmute device\n" " toggle toggle mute\n" - " is-muted check if muted\n" + " is-muted check if muted\n", stdout); + fputs("\nApplication Commands:\n" " move DEVICE move target device to DEVICE\n" " kill DEVICE kill target DEVICE\n", stdout); -- cgit v1.2.3 From 6d04ac9ad89293ecc08013d6df97b880ec1e3a69 Mon Sep 17 00:00:00 2001 From: Dave Reisner Date: Thu, 3 Jan 2013 10:58:32 -0500 Subject: allow card lookup by index --- pulse.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pulse.cc b/pulse.cc index edd0449..c423c72 100644 --- a/pulse.cc +++ b/pulse.cc @@ -150,6 +150,9 @@ Card* PulseClient::GetCard(const uint32_t& index) { } Card* PulseClient::GetCard(const string& name) { + long val; + if (xstrtol(name.c_str(), &val) == 0) return GetCard(val); + for (Card& card : cards_) { if (card.name_ == name) return &card; } -- cgit v1.2.3 From 130d9bbe6fa59404f71536fd9e7e97f2cdf05b14 Mon Sep 17 00:00:00 2001 From: Dave Reisner Date: Thu, 3 Jan 2013 11:12:03 -0500 Subject: resolve the card choice as late as possible Avoid some needless churn of back and forth between a card name and Card*. We rarely actually need the card unless we're performing an operation on it, so delay it as long as possible. Add a convenience function to resolve the active card or die. --- ponymix.cc | 45 +++++++++++++++++++++------------------------ 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/ponymix.cc b/ponymix.cc index 31765fa..459305f 100644 --- a/ponymix.cc +++ b/ponymix.cc @@ -226,11 +226,24 @@ static int ListCardsShort(PulseClient& ponymix, int, char*[]) { return list_cards(ponymix, true); } -static int list_profiles(PulseClient& ponymix, bool shirt) { - if (opt_card == nullptr) errx(1, "error: no card selected"); +static Card* resolve_active_card_or_die(PulseClient& ponymix) { + Card* card; + if (opt_card == nullptr) { + auto device = string_to_device_or_die(ponymix, opt_device, opt_devtype); + card = ponymix.GetCard(*device); + if (card == nullptr) errx(1, "error: no card found or selected."); + } else { + card = ponymix.GetCard(opt_card); + if (card == nullptr) { + errx(1, "error: no match found for card: %s", opt_card); + } + } + + return card; +} - auto card = ponymix.GetCard(opt_card); - if (card == nullptr) errx(1, "error: no match found for card: %s", opt_card); +static int list_profiles(PulseClient& ponymix, bool shirt) { + auto card = resolve_active_card_or_die(ponymix); const auto& profiles = card->Profiles(); for (const auto& p : profiles) Print(p, @@ -387,22 +400,14 @@ static int SetDefault(PulseClient& ponymix, int, char*[]) { } static int GetProfile(PulseClient& ponymix, int, char*[]) { - if (opt_card == nullptr) errx(1, "error: no card selected"); - - auto card = ponymix.GetCard(opt_card); - if (card == nullptr) errx(1, "error: no match found for card: %s", opt_card); - + auto card = resolve_active_card_or_die(ponymix); printf("%s\n", card->ActiveProfile().name.c_str()); return true; } static int SetProfile(PulseClient& ponymix, int, char* argv[]) { - if (opt_card == nullptr) errx(1, "error: no card selected"); - - auto card = ponymix.GetCard(opt_card); - if (card == nullptr) errx(1, "error: no match found for card: %s", opt_card); - + auto card = resolve_active_card_or_die(ponymix); return !ponymix.SetProfile(*card, argv[0]); } @@ -619,7 +624,8 @@ int main(int argc, char* argv[]) { PulseClient ponymix("ponymix"); ponymix.Populate(); - // defaults + // defaults. intentionally, we don't set a card -- only get + // that on demand if a function needs it. ServerInfo defaults = ponymix.GetDefaults(); opt_action = "defaults"; opt_devtype = DEVTYPE_SINK; @@ -629,15 +635,6 @@ int main(int argc, char* argv[]) { argc -= optind; argv += optind; - // cards are tricky... find the one that belongs to the chosen sink. - if (opt_card == nullptr) { - const Device* device = ponymix.GetDevice(opt_device, opt_devtype); - if (device) { - const Card* card = ponymix.GetCard(*device); - if (card) opt_card = card->Name().c_str(); - } - } - return CommandDispatch(ponymix, argc, argv); } -- cgit v1.2.3 From f374fb2792f5a9fb0aefe4dd1387b2bdace418b3 Mon Sep 17 00:00:00 2001 From: Dave Reisner Date: Thu, 3 Jan 2013 14:36:46 -0500 Subject: properly clamp values in Set{Balance,Volume} --- pulse.cc | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/pulse.cc b/pulse.cc index c423c72..0488717 100644 --- a/pulse.cc +++ b/pulse.cc @@ -342,6 +342,7 @@ bool PulseClient::SetVolume(Device& device, long volume) { return false; } + volume = volume_range_.Clamp(volume); const pa_cvolume *cvol = value_to_cvol(volume, &device.volume_); pa_operation* op = device.ops_.SetVolume(context_, device.index_, @@ -357,13 +358,11 @@ bool PulseClient::SetVolume(Device& device, long volume) { } bool PulseClient::IncreaseVolume(Device& device, long increment) { - return SetVolume(device, - volume_range_.Clamp(device.volume_percent_ + increment)); + return SetVolume(device, device.volume_percent_ + increment); } bool PulseClient::DecreaseVolume(Device& device, long increment) { - return SetVolume(device, - volume_range_.Clamp(device.volume_percent_ - increment)); + return SetVolume(device, device.volume_percent_ - increment); } bool PulseClient::SetBalance(Device& device, long balance) { @@ -374,6 +373,7 @@ bool PulseClient::SetBalance(Device& device, long balance) { return false; } + balance = balance_range_.Clamp(balance); pa_cvolume *cvol = pa_cvolume_set_balance(&device.volume_, &device.channels_, balance / 100.0); @@ -391,13 +391,11 @@ bool PulseClient::SetBalance(Device& device, long balance) { } bool PulseClient::IncreaseBalance(Device& device, long increment) { - return SetBalance(device, - balance_range_.Clamp(device.balance_ + increment)); + return SetBalance(device, device.balance_ + increment); } bool PulseClient::DecreaseBalance(Device& device, long increment) { - return SetBalance(device, - balance_range_.Clamp(device.balance_ - increment)); + return SetBalance(device, device.balance_ - increment); } int PulseClient::GetVolume(const Device& device) const { -- cgit v1.2.3 From 270910b82218a840b99b72859b308451c73c5c1b Mon Sep 17 00:00:00 2001 From: Dave Reisner Date: Thu, 3 Jan 2013 14:45:38 -0500 Subject: show description in list-short --- ponymix.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ponymix.cc b/ponymix.cc index 459305f..fc20315 100644 --- a/ponymix.cc +++ b/ponymix.cc @@ -98,10 +98,11 @@ static Device* string_to_device_or_die(PulseClient& ponymix, static void Print(const Device& device, bool shirt) { if (shirt) { - printf("%s\t%d\t%s\n", + printf("%s\t%d\t%s\t%s\n", type_to_string(device.Type()), device.Index(), - device.Name().c_str()); + device.Name().c_str(), + device.Desc().c_str()); return; } -- cgit v1.2.3