From bf709a30cd25890b97d2db8e20e4921aac37dffb Mon Sep 17 00:00:00 2001 From: wasabifan Date: Mon, 20 Jun 2016 16:15:28 -0700 Subject: [PATCH 1/7] Add ALSA-based audio volume control and test logic --- CMakeLists.txt | 10 +- src/AlsaBackedMixerElement.vala | 98 ++++++++ src/AlsaInterface.vala | 97 ++++++++ src/controller/AudioController.vala | 108 +++++++++ src/main.vala | 2 + src/view/MixerElementSelectorWindow.vala | 128 +++++++++++ src/view/MixerElementVolumeWindow.vala | 149 ++++++++++++ test/ControlPanel.vala | 12 + test/controller/FakeAudioController.vala | 279 +++++++++++++++++++++++ test/glade/ControlPanel.glade | 273 +++++++++++++++++++++- test/main.vala | 1 + 11 files changed, 1148 insertions(+), 9 deletions(-) create mode 100644 src/AlsaBackedMixerElement.vala create mode 100644 src/AlsaInterface.vala create mode 100644 src/controller/AudioController.vala create mode 100644 src/view/MixerElementSelectorWindow.vala create mode 100644 src/view/MixerElementVolumeWindow.vala create mode 100644 test/controller/FakeAudioController.vala diff --git a/CMakeLists.txt b/CMakeLists.txt index 3770d46..795c98d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,7 @@ if (BRICKMAN_TEST) test/FakeBluez5.vala test/FakeConnman.vala test/controller/FakeAboutController.vala + test/controller/FakeAudioController.vala test/controller/FakeBatteryController.vala test/controller/FakeBluetoothController.vala test/controller/FakeDeviceBrowserController.vala @@ -52,12 +53,14 @@ else (BRICKMAN_TEST) lib/connman/Service.vala lib/connman/Technology.vala src/controller/AboutController.vala + src/controller/AudioController.vala src/controller/BatteryController.vala src/controller/BluetoothController.vala src/controller/DeviceBrowserController.vala src/controller/FileBrowserController.vala src/controller/NetworkController.vala src/controller/OpenRobertaController.vala + src/AlsaBackedMixerElement.vala src/GlobalManager.vala src/main.vala ) @@ -71,6 +74,7 @@ set (BRICKMAN_COMMON_SOURCE_FILES lib/systemd/Systemd.vala lib/systemd/logind-interfaces.vala lib/systemd/systemd-interfaces.vala + src/AlsaInterface.vala src/controller/IBrickManagerModule.vala src/dbus/Bluez5Agent.vala src/dbus/ConnmanAgent.vala @@ -86,6 +90,8 @@ set (BRICKMAN_COMMON_SOURCE_FILES src/view/DeviceBrowserWindow.vala src/view/FileBrowserWindow.vala src/view/HomeWindow.vala + src/view/MixerElementSelectorWindow.vala + src/view/MixerElementVolumeWindow.vala src/view/MotorBrowserWindow.vala src/view/MotorInfoWindow.vala src/view/MotorValueDialog.vala @@ -118,6 +124,7 @@ set (BRICKMAN_COMMON_SOURCE_FILES find_package(PkgConfig REQUIRED) pkg_check_modules(DEPS REQUIRED + alsa ev3devkit-0.4 glib-2.0 gobject-2.0 @@ -129,7 +136,7 @@ pkg_check_modules(DEPS REQUIRED ncurses ) add_definitions(${DEPS_CFLAGS}) -link_libraries(${DEPS_LIBRARIES}) +link_libraries(${DEPS_LIBRARIES} asound) link_directories(${DEPS_LIBRARY_DIRS}) if (BRICKMAN_TEST) @@ -174,6 +181,7 @@ PACKAGES curses posix linux + alsa ${BRICKMAN_TEST_PACKAGES} CUSTOM_VAPIS bindings/*.vapi diff --git a/src/AlsaBackedMixerElement.vala b/src/AlsaBackedMixerElement.vala new file mode 100644 index 0000000..b6d6b8c --- /dev/null +++ b/src/AlsaBackedMixerElement.vala @@ -0,0 +1,98 @@ +/* + * brickman -- Brick Manager for LEGO MINDSTORMS EV3/ev3dev + * + * Copyright (C) 2016 Kaelin Laundry + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +/* AlsaBackedMixerElement.vala - Implementation of ITestableMixerElement using ALSA API */ + +using Alsa; + +namespace BrickManager { + public class AlsaBackedMixerElement: ITestableMixerElement, Object { + private const SimpleChannelId primary_channel_id = SimpleChannelId.MONO; + + private unowned MixerElement alsa_element; + private SimpleElementId alsa_id; + + public AlsaBackedMixerElement(MixerElement element) { + this.alsa_element = element; + SimpleElementId.alloc(out alsa_id); + element.get_id(alsa_id); + } + + public string name { + get { + return alsa_id.get_name(); + } + } + + public uint index { + get { + return alsa_id.get_index(); + } + } + + public int volume { + get { + long volume = 0; + alsa_element.get_playback_volume(primary_channel_id, out volume); + + long min_volume, max_volume; + alsa_element.get_playback_volume_range(out min_volume, out max_volume); + + // Prevent division by zero + if(max_volume == min_volume) + return 0; + + // Do calculations as floats so avoid odd-looking increments from truncation + return (int)Math.round((volume - min_volume) * 100 / (float)(max_volume - min_volume)); + } + set { + long min_volume, max_volume; + alsa_element.get_playback_volume_range(out min_volume, out max_volume); + + int constrained_volume = int.min(100, int.max(0, value)); + float scaled_volume = constrained_volume * (max_volume - min_volume) / 100f + min_volume; + alsa_element.set_playback_volume_all((long)Math.round(scaled_volume)); + } + } + + public bool can_mute { + get { + return alsa_element.has_playback_switch(); + } + } + + public bool is_muted { + get { + if(!can_mute) + return false; + + int mute_switch = 0; + alsa_element.get_playback_switch(primary_channel_id, out mute_switch); + + return mute_switch == 0; + } + set { + if(can_mute) + alsa_element.set_playback_switch_all(value ? 0 : 1); + } + } + } +} \ No newline at end of file diff --git a/src/AlsaInterface.vala b/src/AlsaInterface.vala new file mode 100644 index 0000000..d79e31c --- /dev/null +++ b/src/AlsaInterface.vala @@ -0,0 +1,97 @@ +/* + * brickman -- Brick Manager for LEGO MINDSTORMS EV3/ev3dev + * + * Copyright (C) 2016 Kaelin Laundry + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +/* AlsaInterface.vala - Definitions for interfacing with ALSA */ + +using Alsa; + +namespace BrickManager { + public interface ITestableMixerElement : Object { + public const int MIN_VOLUME = 0; + public const int MAX_VOLUME = 100; + public const int HALF_VOLUME = (MAX_VOLUME + MIN_VOLUME) / 2; + + public abstract string name { get; } + public abstract uint index { get; } + public abstract int volume { get; set; } + public abstract bool can_mute { get; } + public abstract bool is_muted { get; set; } + } + + public class FakeMixerElement: ITestableMixerElement, Object { + private string _name; + private uint _index; + private int _volume; + private bool _can_mute; + + public string name { + get { + return _name; + } + } + + public uint index { + get { + return _index; + } + } + + public FakeMixerElement(string name, uint index, int volume, bool can_mute, bool is_muted) { + set_name(name); + set_index(index); + this.volume = volume; + + set_can_mute(can_mute); + this.is_muted = is_muted; + } + + public int volume { + get { + return _volume; + } + set { + _volume = int.min(100, int.max(0, value)); + } + } + + public bool can_mute { + get { + return _can_mute; + } + } + public bool is_muted { get; set; } + + public void set_name(string new_name) { + this._name = new_name; + notify_property("name"); + } + + public void set_index(uint new_index) { + this._index = new_index; + notify_property("index"); + } + + public void set_can_mute(bool can_mute) { + this._can_mute = can_mute; + notify_property("can_mute"); + } + } +} \ No newline at end of file diff --git a/src/controller/AudioController.vala b/src/controller/AudioController.vala new file mode 100644 index 0000000..332aaea --- /dev/null +++ b/src/controller/AudioController.vala @@ -0,0 +1,108 @@ +/* + * brickman -- Brick Manager for LEGO MINDSTORMS EV3/ev3dev + * + * Copyright 2014-2015 David Lechner + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +/* BatteryController.vala - Controller for monitoring battery */ + +using Ev3devKit.Devices; +using Ev3devKit.Ui; +using Alsa; + +namespace BrickManager { + public class AudioController : Object, IBrickManagerModule { + private const int VOLUME_STEP = 5; + + Mixer mixer; + MixerElementSelectorWindow mixer_select_window; + MixerElementVolumeWindow volume_window; + + public string display_name { get { return "Audio"; } } + + protected void initialize_mixer() { + mixer = null; + Mixer.open(out mixer); + mixer.attach(); + mixer.register(); + mixer.load(); + } + + void create_main_window () { + mixer_select_window = new MixerElementSelectorWindow (); + + mixer_select_window.mixer_elem_selected.connect ((selected_element) => { + if(volume_window == null) + create_volume_window(); + + volume_window.current_element = selected_element; + volume_window.show_element_details = true; + volume_window.show(); + }); + } + + void create_volume_window() { + volume_window = new MixerElementVolumeWindow(); + + // Wire up handlers for volume window signals + volume_window.volume_up.connect(() => + volume_window.current_element.volume += VOLUME_STEP); + + volume_window.volume_down.connect(() => + volume_window.current_element.volume -= VOLUME_STEP); + + volume_window.volume_half.connect(() => + volume_window.current_element.volume = ITestableMixerElement.HALF_VOLUME); + + volume_window.volume_min.connect(() => + volume_window.current_element.volume = ITestableMixerElement.MIN_VOLUME); + + volume_window.volume_max.connect(() => + volume_window.current_element.volume = ITestableMixerElement.MAX_VOLUME); + + volume_window.mute_toggled.connect((is_muted) => + volume_window.current_element.is_muted = is_muted); + } + + public void show_main_window () { + if (mixer_select_window == null) { + create_main_window (); + } + + // Whenever the audio item is launched from the main menu, + // repopulate the mixer list + mixer_select_window.clear_elements(); + // Re-initializing will return updated data, including volume + initialize_mixer(); + for(MixerElement element = mixer.first_elem(); element != null; element = element.next()) { + mixer_select_window.add_element(new AlsaBackedMixerElement(element)); + } + + if(mixer_select_window.has_single_element) { + if(volume_window == null) + create_volume_window(); + + volume_window.current_element = mixer_select_window.first_element; + volume_window.show_element_details = false; + volume_window.show(); + } + else + mixer_select_window.show (); + } + } +} diff --git a/src/main.vala b/src/main.vala index cb1f923..175a9d8 100644 --- a/src/main.vala +++ b/src/main.vala @@ -67,6 +67,8 @@ namespace BrickManager { var bluetooth_controller = new BluetoothController (); network_controller.add_controller (bluetooth_controller); network_controller.add_controller (network_controller.wifi_controller); + var audio_controller = new AudioController (); + home_window.add_controller (audio_controller); var battery_controller = new BatteryController (); home_window.add_controller (battery_controller); var open_roberta_controller = new OpenRobertaController (); diff --git a/src/view/MixerElementSelectorWindow.vala b/src/view/MixerElementSelectorWindow.vala new file mode 100644 index 0000000..0dc47fd --- /dev/null +++ b/src/view/MixerElementSelectorWindow.vala @@ -0,0 +1,128 @@ +/* + * brickman -- Brick Manager for LEGO MINDSTORMS EV3/ev3dev + * + * Copyright (C) 2016 Kaelin Laundry + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * MixerElementSelectorWindow.vala - Lists ALSA mixer elements + */ + +using Ev3devKit; +using Ev3devKit.Ui; + +namespace BrickManager { + + public class MixerElementSelectorWindow : BrickManagerWindow { + Ui.Menu element_menu; + + public signal void mixer_elem_selected (ITestableMixerElement selected_element); + + private class MixerElementWithSignalIds: Object { + public ITestableMixerElement element; + public ulong notify_signal_handler_id = 0; + public ulong button_press_signal_handler_id = 0; + } + + public MixerElementSelectorWindow () { + title = "Audio mixer elements"; + element_menu = new Ui.Menu (); + content_vbox.add (element_menu); + } + + protected string get_element_label_text(ITestableMixerElement element) { + string mute_string = (element.can_mute && element.is_muted) ? ", muted" : ""; + return "[%u] %s (%ld%%%s)".printf(element.index, element.name, element.volume, mute_string); + } + + protected void sort_element_menu() { + // TODO: we would get much better performance if we just inserted + // the item in the correct place instead of sorting the entire list + // each time an item is inserted. + element_menu.sort_menu_items ((a, b) => { + ITestableMixerElement element_a = (a.represented_object as MixerElementWithSignalIds).element; + ITestableMixerElement element_b = (b.represented_object as MixerElementWithSignalIds).element; + + // Group by name, and sort by index within the same name + if(element_a.name == element_b.name) + return (int)element_a.index - (int)element_b.index; + else + return element_a.name.ascii_casecmp(element_b.name); + }); + } + + public void add_element (ITestableMixerElement element) { + var menu_item = new Ui.MenuItem (get_element_label_text(element)); + + var represented_object = new MixerElementWithSignalIds() { + element = element + }; + + // Update the menu item whenever the represented element changes + represented_object.notify_signal_handler_id = element.notify.connect((sender, property) => { + menu_item.label.text = get_element_label_text(element); + sort_element_menu(); + }); + + // Emit a selection signal for this element when its menu item is selected + represented_object.button_press_signal_handler_id = menu_item.button.pressed.connect (() => + mixer_elem_selected (element)); + + menu_item.represented_object = (Object)represented_object; + element_menu.add_menu_item (menu_item); + } + + protected void remove_menu_item (Ui.MenuItem menu_item) { + if (menu_item != null) { + var represented_object = menu_item.represented_object as MixerElementWithSignalIds; + represented_object.element.disconnect(represented_object.notify_signal_handler_id); + menu_item.button.disconnect(represented_object.button_press_signal_handler_id); + + element_menu.remove_menu_item (menu_item); + } + } + + public void remove_element (ITestableMixerElement element) { + var menu_item = element_menu.find_menu_item (element, (menu_item, target_element) => { + var other_element = (menu_item.represented_object as MixerElementWithSignalIds).element; + return target_element == other_element; + }); + + remove_menu_item(menu_item); + } + + public void clear_elements () { + var iter = element_menu.menu_item_iter (); + while (iter.size > 0) + remove_menu_item(iter[0]); + } + + public bool has_single_element { + get { + return element_menu.menu_item_iter().size == 1; + } + } + + public ITestableMixerElement? first_element { + get { + if(element_menu.menu_item_iter().size <= 0) + return null; + + return (element_menu.menu_item_iter().get(0).represented_object as MixerElementWithSignalIds).element; + } + } + } +} diff --git a/src/view/MixerElementVolumeWindow.vala b/src/view/MixerElementVolumeWindow.vala new file mode 100644 index 0000000..3ded8eb --- /dev/null +++ b/src/view/MixerElementVolumeWindow.vala @@ -0,0 +1,149 @@ +/* + * brickman -- Brick Manager for LEGO MINDSTORMS EV3/ev3dev + * + * Copyright (C) 2016 Kaelin Laundry + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * MixerElementVolumeWindow.vala - Allows control of mixer element volume + */ + +using Ev3devKit; +using Ev3devKit.Ui; + +namespace BrickManager { + public class MixerElementVolumeWindow : BrickManagerWindow { + public signal void volume_up (); + public signal void volume_down (); + public signal void volume_max (); + public signal void volume_min (); + public signal void volume_half (); + public signal void mute_toggled (bool is_muted); + + ITestableMixerElement _current_element = null; + bool _show_element_details = true; + + Ui.Label element_label; + Ui.CheckboxMenuItem mute_checkbox; + + public MixerElementVolumeWindow () { + title = "Volume control"; + + element_label = new Ui.Label(); + content_vbox.add(element_label); + + var controls_menu = new Ui.Menu(); + + var volume_up_item = new Ui.MenuItem("+ Volume up"); + volume_up_item.button.pressed.connect(() => { + if(_current_element != null) + volume_up(); + }); + controls_menu.add_menu_item(volume_up_item); + + var volume_down_item = new Ui.MenuItem("- Volume down"); + volume_down_item.button.pressed.connect(() => { + if(_current_element != null) + volume_down(); + }); + controls_menu.add_menu_item(volume_down_item); + + var volume_half_item = new Ui.MenuItem("Half volume"); + volume_half_item.button.pressed.connect(() => { + if(_current_element != null) + volume_half(); + }); + controls_menu.add_menu_item(volume_half_item); + + var volume_max_item = new Ui.MenuItem("Maximum volume"); + volume_max_item.button.pressed.connect(() => { + if(_current_element != null) + volume_max(); + }); + controls_menu.add_menu_item(volume_max_item); + + var volume_min_item = new Ui.MenuItem("Minimum volume"); + volume_min_item.button.pressed.connect(() => { + if(_current_element != null) + volume_min(); + }); + controls_menu.add_menu_item(volume_min_item); + + mute_checkbox = new CheckboxMenuItem("Mute"); + mute_checkbox.checkbox.notify["checked"].connect(() => { + if(_current_element != null && _current_element.can_mute) + mute_toggled(mute_checkbox.checkbox.checked); + }); + controls_menu.add_menu_item(mute_checkbox); + + content_vbox.add (controls_menu); + + update_from_element(); + } + + public ITestableMixerElement current_element { + get { + return _current_element; + } + + set { + if(_current_element != null) { + _current_element.notify.disconnect(update_from_element); + } + + _current_element = value; + _current_element.notify.connect(update_from_element); + update_from_element(); + } + } + + public bool show_element_details { + get { + return _show_element_details; + } + set { + _show_element_details = value; + update_from_element(); + } + } + + private void update_from_element() { + if(_current_element == null) { + element_label.text = "???"; + if(mute_checkbox.checkbox.checked != false) + mute_checkbox.checkbox.checked = false; + + set_mute_button_enabled(false); + } + else { + string elem_details_string = show_element_details ? " ([%u] %s)".printf(_current_element.index, _current_element.name) : ""; + element_label.text = "%ld%%%s".printf(_current_element.volume, elem_details_string); + + if(mute_checkbox.checkbox.checked != _current_element.is_muted) + mute_checkbox.checkbox.checked = _current_element.is_muted; + + set_mute_button_enabled(_current_element.can_mute); + } + } + + private void set_mute_button_enabled(bool is_enabled) { + mute_checkbox.label.visible = is_enabled; + mute_checkbox.button.visible = is_enabled; + mute_checkbox.checkbox.visible = is_enabled; + mute_checkbox.button.can_focus = is_enabled; + } + } +} diff --git a/test/ControlPanel.vala b/test/ControlPanel.vala index b306855..3be62c8 100644 --- a/test/ControlPanel.vala +++ b/test/ControlPanel.vala @@ -33,6 +33,7 @@ namespace BrickManager { public FakeFileBrowserController file_browser_controller; public FakeDeviceBrowserController device_browser_controller; public FakeNetworkController network_controller; + public FakeAudioController audio_controller; public FakeBluetoothController bluetooth_controller; public FakeBatteryController battery_controller; public FakeAboutController about_controller; @@ -41,6 +42,7 @@ namespace BrickManager { enum Tab { DEVICE_BROWSER, NETWORK, + AUDIO, BLUETOOTH, BATTERY, OPEN_ROBERTA, @@ -129,6 +131,15 @@ namespace BrickManager { COLUMN_COUNT; } + enum AudioMixerElementsColumn { + NAME, + INDEX, + VOLUME, + CAN_MUTE, + MUTE, + USER_DATA + } + enum BluetoothDeviceColumn { PRESENT, NAME, @@ -147,6 +158,7 @@ namespace BrickManager { file_browser_controller = new FakeFileBrowserController (builder); device_browser_controller = new FakeDeviceBrowserController (builder); network_controller = new FakeNetworkController (builder); + audio_controller = new FakeAudioController(builder); bluetooth_controller = new FakeBluetoothController (builder); battery_controller = new FakeBatteryController (builder); about_controller = new FakeAboutController (builder); diff --git a/test/controller/FakeAudioController.vala b/test/controller/FakeAudioController.vala new file mode 100644 index 0000000..6b1b124 --- /dev/null +++ b/test/controller/FakeAudioController.vala @@ -0,0 +1,279 @@ +/* + * brickman -- Brick Manager for LEGO MINDSTORMS EV3/ev3dev + * + * Copyright (C) 2016 Kaelin Laundry + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +/* FakeAudioController.vala - Fake Audio controller for testing */ + +using Ev3devKit; +using Ev3devKit.Ui; + +namespace BrickManager { + public class FakeAudioController : Object, IBrickManagerModule { + private const int VOLUME_STEP = 5; + + MixerElementSelectorWindow mixer_select_window; + MixerElementVolumeWindow volume_window; + + public string display_name { get { return "Audio"; } } + + public FakeAudioController (Gtk.Builder builder) { + // Initialize windows that the controller needs + mixer_select_window = new MixerElementSelectorWindow (); + volume_window = new MixerElementVolumeWindow(); + + // Register for callback so that we can focus on the correct control panel tab + // the first time either window is invoked + var control_panel_notebook = builder.get_object ("control-panel-notebook") as Gtk.Notebook; + mixer_select_window.shown.connect (() => + control_panel_notebook.page = (int)ControlPanel.Tab.AUDIO); + volume_window.shown.connect (() => + control_panel_notebook.page = (int)ControlPanel.Tab.AUDIO); + + // Initialize items in brickman for all current elements + var mixer_elems_liststore = builder.get_object ("mixer-elements-liststore") as Gtk.ListStore; + mixer_elems_liststore.foreach ((model, path, iter) => { + update_fake_element_from_liststore(iter, mixer_elems_liststore); + return false; + }); + + // Propagate changes from liststore to views + mixer_elems_liststore.row_changed.connect ((path, iter) => { + update_fake_element_from_liststore(iter, mixer_elems_liststore); + }); + + // Link liststore and control panel GUI + (builder.get_object ("mixer-element-name-cellrenderertext") as Gtk.CellRendererText) + .edited.connect ((path, new_text) => ControlPanel.update_listview_text_item ( + mixer_elems_liststore, path, new_text, ControlPanel.AudioMixerElementsColumn.NAME)); + (builder.get_object ("mixer-element-index-cellrenderertext") as Gtk.CellRendererText) + .edited.connect ((path, new_text) => ControlPanel.update_listview_text_item ( + mixer_elems_liststore, path, new_text, ControlPanel.AudioMixerElementsColumn.INDEX)); + (builder.get_object ("mixer-element-volume-cellrenderertext") as Gtk.CellRendererText) + .edited.connect ((path, new_text) => ControlPanel.update_listview_text_item ( + mixer_elems_liststore, path, new_text, ControlPanel.AudioMixerElementsColumn.VOLUME)); + (builder.get_object ("mixer-element-can-mute-cellrenderertoggle") as Gtk.CellRendererToggle) + .toggled.connect ((toggle, path) => ControlPanel.update_listview_toggle_item ( + mixer_elems_liststore, toggle, path, ControlPanel.AudioMixerElementsColumn.CAN_MUTE)); + (builder.get_object ("mixer-element-mute-cellrenderertoggle") as Gtk.CellRendererToggle) + .toggled.connect ((toggle, path) => ControlPanel.update_listview_toggle_item ( + mixer_elems_liststore, toggle, path, ControlPanel.AudioMixerElementsColumn.MUTE)); + + // Configure the add button + (builder.get_object ("mixer-element-add-button") as Gtk.Button).clicked.connect (() => { + Gtk.TreeIter iter; + mixer_elems_liststore.append (out iter); + + // Doing this all at once ensures that the row_changed handler is only called once, + // and never called with partial data. + mixer_elems_liststore.set_valuesv(iter, new int[] { + ControlPanel.AudioMixerElementsColumn.NAME, + ControlPanel.AudioMixerElementsColumn.INDEX, + ControlPanel.AudioMixerElementsColumn.VOLUME, + ControlPanel.AudioMixerElementsColumn.CAN_MUTE, + ControlPanel.AudioMixerElementsColumn.MUTE + }, new Value[] { "New Element", 0, ITestableMixerElement.HALF_VOLUME, true, false }); + }); + + // Store references to the remove button + var mixer_element_remove_button = builder.get_object ("mixer-element-remove-button") as Gtk.Button; + var mixer_element_treeview_selection = (builder.get_object ("mixer-elements-treeview") as Gtk.TreeView).get_selection (); + + // Configure the remove button action + mixer_element_remove_button.clicked.connect (() => { + Gtk.TreeModel model; + Gtk.TreeIter iter; + + if (mixer_element_treeview_selection.get_selected (out model, out iter)) { + Value user_data; + model.get_value (iter, ControlPanel.AudioMixerElementsColumn.USER_DATA, out user_data); + + var mixer_element = (FakeMixerElement)user_data.get_pointer (); + if (mixer_element != null) + mixer_select_window.remove_element (mixer_element); + + mixer_elems_liststore.remove (iter); + } + }); + + // Desensitize the remove button if nothing is selected + mixer_element_treeview_selection.changed.connect (() => { + mixer_element_remove_button.sensitive = mixer_element_treeview_selection.count_selected_rows () > 0; + }); + + // Invoke the button logic once to initialize it + mixer_element_treeview_selection.changed (); + + // Configure the direct window link buttons + (builder.get_object ("audio-mixer-select-window-button") as Gtk.Button).clicked.connect (() => + mixer_select_window.show()); + + (builder.get_object ("audio-volume-window-button") as Gtk.Button).clicked.connect (() => { + if(mixer_select_window.first_element == null) + return; + + volume_window.current_element = mixer_select_window.first_element; + volume_window.show_element_details = !mixer_select_window.has_single_element; + volume_window.show(); + }); + + // Wire up handlers for volume window signals + volume_window.volume_up.connect(() => { + volume_window.current_element.volume += VOLUME_STEP; + update_liststore_for_element(mixer_elems_liststore, volume_window.current_element); + }); + + volume_window.volume_down.connect(() => { + volume_window.current_element.volume -= VOLUME_STEP; + update_liststore_for_element(mixer_elems_liststore, volume_window.current_element); + }); + + volume_window.volume_half.connect(() => { + volume_window.current_element.volume = ITestableMixerElement.HALF_VOLUME; + update_liststore_for_element(mixer_elems_liststore, volume_window.current_element); + }); + + volume_window.volume_min.connect(() => { + volume_window.current_element.volume = ITestableMixerElement.MIN_VOLUME; + update_liststore_for_element(mixer_elems_liststore, volume_window.current_element); + }); + + volume_window.volume_max.connect(() => { + volume_window.current_element.volume = ITestableMixerElement.MAX_VOLUME; + update_liststore_for_element(mixer_elems_liststore, volume_window.current_element); + }); + + volume_window.mute_toggled.connect((is_muted) => { + volume_window.current_element.is_muted = is_muted; + update_liststore_for_element(mixer_elems_liststore, volume_window.current_element); + }); + + // Show volume window when mixer element is selected + mixer_select_window.mixer_elem_selected.connect ((selected_element) => { + volume_window.current_element = selected_element; + volume_window.show_element_details = true; + volume_window.show(); + }); + } + + /** + * Updates the mixer element associated with the specified TreeIter object in the Control Panel + * GUI with data from the backing ListStore. Will create the mixer element if one does not + * already exist. + */ + private void update_fake_element_from_liststore(Gtk.TreeIter iter, Gtk.ListStore mixer_elems_liststore) { + Value name = get_liststore_value(mixer_elems_liststore, iter, ControlPanel.AudioMixerElementsColumn.NAME); + Value index = get_liststore_value(mixer_elems_liststore, iter, ControlPanel.AudioMixerElementsColumn.INDEX); + Value volume = get_liststore_value(mixer_elems_liststore, iter, ControlPanel.AudioMixerElementsColumn.VOLUME); + Value can_mute = get_liststore_value(mixer_elems_liststore, iter, ControlPanel.AudioMixerElementsColumn.CAN_MUTE); + Value mute = get_liststore_value(mixer_elems_liststore, iter, ControlPanel.AudioMixerElementsColumn.MUTE); + Value user_data = get_liststore_value(mixer_elems_liststore, iter, ControlPanel.AudioMixerElementsColumn.USER_DATA); + + // The mixer elements will make sure that these numbers are within proper bounds later + int parsed_index = (int)parse_double_with_default(index.get_string(), 0); + int parsed_volume = (int)parse_double_with_default(volume.get_string(), ITestableMixerElement.HALF_VOLUME); + + // This is guaranteed to be a fake mixer element; as such, it is referenced by the concrete implementation name + FakeMixerElement? mixer_element = (FakeMixerElement?)user_data.get_pointer (); + + if(mixer_element == null) { + mixer_element = new FakeMixerElement(name.get_string(), parsed_index, parsed_volume, can_mute.get_boolean(), mute.get_boolean()); + mixer_select_window.add_element(mixer_element); + + mixer_elems_liststore.set(iter, ControlPanel.AudioMixerElementsColumn.USER_DATA, mixer_element.ref ()); + } + else { + mixer_element.freeze_notify(); + mixer_element.set_name(name.get_string()); + mixer_element.set_index(parsed_index); + mixer_element.volume = parsed_volume; + mixer_element.set_can_mute(can_mute.get_boolean()); + mixer_element.is_muted = mute.get_boolean(); + mixer_element.thaw_notify(); + + // If the original value was invalid and the mixer object modified it, replace the entered text with the valid version + // This will invoke any active signal handlers again; while not optimal, running them twice shouldn't be a problem. + if(index.get_string() != mixer_element.index.to_string() || volume.get_string() != mixer_element.volume.to_string()) { + update_liststore_for_element(mixer_elems_liststore, mixer_element, iter); + } + } + } + + /** + * Updates the ListStore entry associated with the specified mixer element with data from + * the mixer element. + */ + private void update_liststore_for_element(Gtk.ListStore mixer_elems_liststore, ITestableMixerElement element, Gtk.TreeIter? iter = null) { + // Find the iter pointing to this element if one was not supplied + if(iter == null) { + mixer_elems_liststore.foreach((model, path, current_iter) => { + Value user_data = get_liststore_value(mixer_elems_liststore, current_iter, ControlPanel.AudioMixerElementsColumn.USER_DATA); + FakeMixerElement other_element = (FakeMixerElement)user_data.get_pointer(); + + if(other_element == element) { + iter = current_iter; + return true; + } + + return false; + }); + } + + mixer_elems_liststore.set_valuesv (iter, + new int[] { + ControlPanel.AudioMixerElementsColumn.NAME, + ControlPanel.AudioMixerElementsColumn.INDEX, + ControlPanel.AudioMixerElementsColumn.VOLUME, + ControlPanel.AudioMixerElementsColumn.CAN_MUTE, + ControlPanel.AudioMixerElementsColumn.MUTE + }, new Value[] { + element.name, + element.index.to_string(), + element.volume.to_string(), + element.can_mute, + element.is_muted + }); + } + + private Value get_liststore_value(Gtk.ListStore list_store, Gtk.TreeIter iter, int column) { + Value ret_value; + list_store.get_value (iter, column, out ret_value); + return ret_value; + } + + private double parse_double_with_default(string str, int default_value) { + double parsed_result; + if(double.try_parse(str, out parsed_result)) { + return parsed_result; + } + + return default_value; + } + + public void show_main_window () { + if(mixer_select_window.has_single_element) { + volume_window.current_element = mixer_select_window.first_element; + volume_window.show_element_details = false; + volume_window.show(); + } + else + mixer_select_window.show (); + } + } +} diff --git a/test/glade/ControlPanel.glade b/test/glade/ControlPanel.glade index 36ed074..939089e 100644 --- a/test/glade/ControlPanel.glade +++ b/test/glade/ControlPanel.glade @@ -106,6 +106,31 @@ + + + + + + + + + + + + + + + + + + Playback + 0 + 75 + True + False + + + @@ -2854,6 +2879,238 @@ False + + + True + False + vertical + + + True + False + Mixer elements + + + False + True + 0 + + + + + True + False + + + True + True + never + never + in + + + True + True + natural + mixer-elements-liststore + + + + + + Name + + + True + + + 0 + + + + + + + Index + + + True + + + 1 + + + + + + + Volume + + + True + + + 2 + + + + + + + Can mute + + + + 3 + + + + + + + Is muted + + + 0 + + + 4 + + + + + + + + + True + True + 0 + + + + + True + False + vertical + + + Add + True + True + True + + + False + True + 0 + + + + + Remove + True + True + True + + + False + True + 1 + + + + + False + True + 1 + + + + + True + True + 1 + + + + + True + False + 0 + none + + + True + False + 6 + 6 + 12 + 12 + + + True + False + + + Mixer select + True + True + True + + + False + True + 0 + + + + + Volume control + True + True + True + + + False + True + 1 + + + + + + + + + True + False + Audio windows + + + + + False + True + 2 + + + + + 2 + + + + + True + False + Audio + + + 2 + False + + True @@ -3547,7 +3804,7 @@ - 2 + 3 @@ -3557,7 +3814,7 @@ Bluetooth - 2 + 3 False @@ -3786,7 +4043,7 @@ - 3 + 4 @@ -3796,7 +4053,7 @@ Battery - 3 + 4 False @@ -4129,7 +4386,7 @@ - 4 + 5 @@ -4140,7 +4397,7 @@ right - 4 + 5 False @@ -4308,7 +4565,7 @@ - 5 + 6 @@ -4318,7 +4575,7 @@ About - 5 + 6 False diff --git a/test/main.vala b/test/main.vala index e25cb2c..23fe015 100644 --- a/test/main.vala +++ b/test/main.vala @@ -51,6 +51,7 @@ namespace BrickManager { control_panel.network_controller.add_controller (control_panel.network_controller.wifi_controller); control_panel.bluetooth_controller.show_network_connection_requested.connect ((name) => control_panel.network_controller.show_connection (name)); + home_window.add_controller (control_panel.audio_controller); home_window.add_controller (control_panel.battery_controller); home_window.add_controller (control_panel.open_roberta_controller); home_window.add_controller (control_panel.about_controller); From 13f2b9a8526ccf7e18a670914343b382e84e4ad8 Mon Sep 17 00:00:00 2001 From: wasabifan Date: Mon, 20 Jun 2016 17:11:03 -0700 Subject: [PATCH 2/7] Update debian/control for audio additions --- README.md | 2 +- debian/control | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 65cbe84..34df713 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ not in brickstrap shell: mkdir -p cd - cmake -DCMAKE_BUILD_TYPE=string:Debug -DBRICKMAN_TEST=bool:Yes + cmake -DCMAKE_BUILD_TYPE=string:Debug -DBRICKMAN_TEST=bool:Yes make make run diff --git a/debian/control b/debian/control index 6e4aa15..1fd2aa2 100644 --- a/debian/control +++ b/debian/control @@ -4,7 +4,8 @@ Priority: standard Maintainer: David Lechner Build-Depends: debhelper (>= 9), dh-systemd, cmake, valac (>= 0.24), libgirepository1.0-dev, libgudev-1.0-dev, - libncurses5-dev, libev3devkit-dev, netpbm + libncurses5-dev, libev3devkit-dev, netpbm, + libasound2-dev Standards-Version: 3.9.5 Homepage: https://www.ev3dev.org Vcs-Git: git://github.com/ev3dev/brickman.git From 240ae1e1d7dd306b20d70348a640ebda3aae8d6d Mon Sep 17 00:00:00 2001 From: wasabifan Date: Mon, 20 Jun 2016 20:47:05 -0700 Subject: [PATCH 3/7] Refine audio menu and structure --- CMakeLists.txt | 2 +- src/AlsaBackedMixerElement.vala | 21 ++++++---- src/AlsaInterface.vala | 25 +++++++++--- src/controller/AudioController.vala | 15 ++----- src/view/MixerElementSelectorWindow.vala | 18 ++++----- src/view/MixerElementVolumeWindow.vala | 50 +++--------------------- test/controller/FakeAudioController.vala | 31 ++++----------- test/glade/ControlPanel.glade | 1 + 8 files changed, 59 insertions(+), 104 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 795c98d..d7b46b1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -136,7 +136,7 @@ pkg_check_modules(DEPS REQUIRED ncurses ) add_definitions(${DEPS_CFLAGS}) -link_libraries(${DEPS_LIBRARIES} asound) +link_libraries(${DEPS_LIBRARIES}) link_directories(${DEPS_LIBRARY_DIRS}) if (BRICKMAN_TEST) diff --git a/src/AlsaBackedMixerElement.vala b/src/AlsaBackedMixerElement.vala index b6d6b8c..2976f30 100644 --- a/src/AlsaBackedMixerElement.vala +++ b/src/AlsaBackedMixerElement.vala @@ -19,12 +19,12 @@ * MA 02110-1301, USA. */ -/* AlsaBackedMixerElement.vala - Implementation of ITestableMixerElement using ALSA API */ +/* AlsaBackedMixerElement.vala - Implementation of IMixerElementViewModel using ALSA API */ using Alsa; namespace BrickManager { - public class AlsaBackedMixerElement: ITestableMixerElement, Object { + public class AlsaBackedMixerElement: IMixerElementViewModel, Object { private const SimpleChannelId primary_channel_id = SimpleChannelId.MONO; private unowned MixerElement alsa_element; @@ -69,7 +69,13 @@ namespace BrickManager { int constrained_volume = int.min(100, int.max(0, value)); float scaled_volume = constrained_volume * (max_volume - min_volume) / 100f + min_volume; - alsa_element.set_playback_volume_all((long)Math.round(scaled_volume)); + long rounded_volume = (long)Math.round(scaled_volume); + + alsa_element.set_playback_volume_all(rounded_volume); + + bool should_mute = rounded_volume <= min_volume; + if(is_muted != should_mute) + set_is_muted(should_mute); } } @@ -89,10 +95,11 @@ namespace BrickManager { return mute_switch == 0; } - set { - if(can_mute) - alsa_element.set_playback_switch_all(value ? 0 : 1); - } + } + + protected void set_is_muted(bool is_muted) { + if(can_mute) + alsa_element.set_playback_switch_all(is_muted ? 0 : 1); } } } \ No newline at end of file diff --git a/src/AlsaInterface.vala b/src/AlsaInterface.vala index d79e31c..63e6010 100644 --- a/src/AlsaInterface.vala +++ b/src/AlsaInterface.vala @@ -24,7 +24,7 @@ using Alsa; namespace BrickManager { - public interface ITestableMixerElement : Object { + public interface IMixerElementViewModel : Object { public const int MIN_VOLUME = 0; public const int MAX_VOLUME = 100; public const int HALF_VOLUME = (MAX_VOLUME + MIN_VOLUME) / 2; @@ -33,14 +33,15 @@ namespace BrickManager { public abstract uint index { get; } public abstract int volume { get; set; } public abstract bool can_mute { get; } - public abstract bool is_muted { get; set; } + public abstract bool is_muted { get; } } - public class FakeMixerElement: ITestableMixerElement, Object { + public class FakeMixerElement: IMixerElementViewModel, Object { private string _name; private uint _index; private int _volume; private bool _can_mute; + private bool _is_muted; public string name { get { @@ -54,13 +55,12 @@ namespace BrickManager { } } - public FakeMixerElement(string name, uint index, int volume, bool can_mute, bool is_muted) { + public FakeMixerElement(string name, uint index, int volume, bool can_mute) { set_name(name); set_index(index); this.volume = volume; set_can_mute(can_mute); - this.is_muted = is_muted; } public int volume { @@ -69,6 +69,10 @@ namespace BrickManager { } set { _volume = int.min(100, int.max(0, value)); + + bool should_mute = _volume <= 0; + if(_is_muted != should_mute) + set_is_muted(should_mute); } } @@ -77,7 +81,11 @@ namespace BrickManager { return _can_mute; } } - public bool is_muted { get; set; } + public bool is_muted { + get { + return _is_muted; + } + } public void set_name(string new_name) { this._name = new_name; @@ -93,5 +101,10 @@ namespace BrickManager { this._can_mute = can_mute; notify_property("can_mute"); } + + public void set_is_muted(bool is_muted) { + this._is_muted = is_muted; + notify_property("is_muted"); + } } } \ No newline at end of file diff --git a/src/controller/AudioController.vala b/src/controller/AudioController.vala index 332aaea..4aadbbe 100644 --- a/src/controller/AudioController.vala +++ b/src/controller/AudioController.vala @@ -27,13 +27,13 @@ using Alsa; namespace BrickManager { public class AudioController : Object, IBrickManagerModule { - private const int VOLUME_STEP = 5; + private const int VOLUME_STEP = 10; Mixer mixer; MixerElementSelectorWindow mixer_select_window; MixerElementVolumeWindow volume_window; - public string display_name { get { return "Audio"; } } + public string display_name { get { return "Sound"; } } protected void initialize_mixer() { mixer = null; @@ -66,17 +66,8 @@ namespace BrickManager { volume_window.volume_down.connect(() => volume_window.current_element.volume -= VOLUME_STEP); - volume_window.volume_half.connect(() => - volume_window.current_element.volume = ITestableMixerElement.HALF_VOLUME); - volume_window.volume_min.connect(() => - volume_window.current_element.volume = ITestableMixerElement.MIN_VOLUME); - - volume_window.volume_max.connect(() => - volume_window.current_element.volume = ITestableMixerElement.MAX_VOLUME); - - volume_window.mute_toggled.connect((is_muted) => - volume_window.current_element.is_muted = is_muted); + volume_window.current_element.volume = IMixerElementViewModel.MIN_VOLUME); } public void show_main_window () { diff --git a/src/view/MixerElementSelectorWindow.vala b/src/view/MixerElementSelectorWindow.vala index 0dc47fd..ec0dbe8 100644 --- a/src/view/MixerElementSelectorWindow.vala +++ b/src/view/MixerElementSelectorWindow.vala @@ -29,10 +29,10 @@ namespace BrickManager { public class MixerElementSelectorWindow : BrickManagerWindow { Ui.Menu element_menu; - public signal void mixer_elem_selected (ITestableMixerElement selected_element); + public signal void mixer_elem_selected (IMixerElementViewModel selected_element); private class MixerElementWithSignalIds: Object { - public ITestableMixerElement element; + public IMixerElementViewModel element; public ulong notify_signal_handler_id = 0; public ulong button_press_signal_handler_id = 0; } @@ -43,7 +43,7 @@ namespace BrickManager { content_vbox.add (element_menu); } - protected string get_element_label_text(ITestableMixerElement element) { + protected string get_element_label_text(IMixerElementViewModel element) { string mute_string = (element.can_mute && element.is_muted) ? ", muted" : ""; return "[%u] %s (%ld%%%s)".printf(element.index, element.name, element.volume, mute_string); } @@ -53,8 +53,8 @@ namespace BrickManager { // the item in the correct place instead of sorting the entire list // each time an item is inserted. element_menu.sort_menu_items ((a, b) => { - ITestableMixerElement element_a = (a.represented_object as MixerElementWithSignalIds).element; - ITestableMixerElement element_b = (b.represented_object as MixerElementWithSignalIds).element; + IMixerElementViewModel element_a = (a.represented_object as MixerElementWithSignalIds).element; + IMixerElementViewModel element_b = (b.represented_object as MixerElementWithSignalIds).element; // Group by name, and sort by index within the same name if(element_a.name == element_b.name) @@ -64,7 +64,7 @@ namespace BrickManager { }); } - public void add_element (ITestableMixerElement element) { + public void add_element (IMixerElementViewModel element) { var menu_item = new Ui.MenuItem (get_element_label_text(element)); var represented_object = new MixerElementWithSignalIds() { @@ -95,8 +95,8 @@ namespace BrickManager { } } - public void remove_element (ITestableMixerElement element) { - var menu_item = element_menu.find_menu_item (element, (menu_item, target_element) => { + public void remove_element (IMixerElementViewModel element) { + var menu_item = element_menu.find_menu_item (element, (menu_item, target_element) => { var other_element = (menu_item.represented_object as MixerElementWithSignalIds).element; return target_element == other_element; }); @@ -116,7 +116,7 @@ namespace BrickManager { } } - public ITestableMixerElement? first_element { + public IMixerElementViewModel? first_element { get { if(element_menu.menu_item_iter().size <= 0) return null; diff --git a/src/view/MixerElementVolumeWindow.vala b/src/view/MixerElementVolumeWindow.vala index 3ded8eb..eca2f9e 100644 --- a/src/view/MixerElementVolumeWindow.vala +++ b/src/view/MixerElementVolumeWindow.vala @@ -28,16 +28,12 @@ namespace BrickManager { public class MixerElementVolumeWindow : BrickManagerWindow { public signal void volume_up (); public signal void volume_down (); - public signal void volume_max (); public signal void volume_min (); - public signal void volume_half (); - public signal void mute_toggled (bool is_muted); - ITestableMixerElement _current_element = null; + IMixerElementViewModel _current_element = null; bool _show_element_details = true; Ui.Label element_label; - Ui.CheckboxMenuItem mute_checkbox; public MixerElementVolumeWindow () { title = "Volume control"; @@ -61,40 +57,19 @@ namespace BrickManager { }); controls_menu.add_menu_item(volume_down_item); - var volume_half_item = new Ui.MenuItem("Half volume"); - volume_half_item.button.pressed.connect(() => { - if(_current_element != null) - volume_half(); - }); - controls_menu.add_menu_item(volume_half_item); - - var volume_max_item = new Ui.MenuItem("Maximum volume"); - volume_max_item.button.pressed.connect(() => { - if(_current_element != null) - volume_max(); - }); - controls_menu.add_menu_item(volume_max_item); - - var volume_min_item = new Ui.MenuItem("Minimum volume"); + var volume_min_item = new Ui.MenuItem("Mute"); volume_min_item.button.pressed.connect(() => { if(_current_element != null) volume_min(); }); controls_menu.add_menu_item(volume_min_item); - mute_checkbox = new CheckboxMenuItem("Mute"); - mute_checkbox.checkbox.notify["checked"].connect(() => { - if(_current_element != null && _current_element.can_mute) - mute_toggled(mute_checkbox.checkbox.checked); - }); - controls_menu.add_menu_item(mute_checkbox); - content_vbox.add (controls_menu); update_from_element(); } - public ITestableMixerElement current_element { + public IMixerElementViewModel current_element { get { return _current_element; } @@ -123,27 +98,12 @@ namespace BrickManager { private void update_from_element() { if(_current_element == null) { element_label.text = "???"; - if(mute_checkbox.checkbox.checked != false) - mute_checkbox.checkbox.checked = false; - - set_mute_button_enabled(false); } else { string elem_details_string = show_element_details ? " ([%u] %s)".printf(_current_element.index, _current_element.name) : ""; - element_label.text = "%ld%%%s".printf(_current_element.volume, elem_details_string); - - if(mute_checkbox.checkbox.checked != _current_element.is_muted) - mute_checkbox.checkbox.checked = _current_element.is_muted; - - set_mute_button_enabled(_current_element.can_mute); + string volume_string = _current_element.is_muted ? "muted" : "%ld%%".printf(_current_element.volume); + element_label.text = volume_string + elem_details_string; } } - - private void set_mute_button_enabled(bool is_enabled) { - mute_checkbox.label.visible = is_enabled; - mute_checkbox.button.visible = is_enabled; - mute_checkbox.checkbox.visible = is_enabled; - mute_checkbox.button.can_focus = is_enabled; - } } } diff --git a/test/controller/FakeAudioController.vala b/test/controller/FakeAudioController.vala index 6b1b124..429b147 100644 --- a/test/controller/FakeAudioController.vala +++ b/test/controller/FakeAudioController.vala @@ -26,12 +26,12 @@ using Ev3devKit.Ui; namespace BrickManager { public class FakeAudioController : Object, IBrickManagerModule { - private const int VOLUME_STEP = 5; + private const int VOLUME_STEP = 10; MixerElementSelectorWindow mixer_select_window; MixerElementVolumeWindow volume_window; - public string display_name { get { return "Audio"; } } + public string display_name { get { return "Sound"; } } public FakeAudioController (Gtk.Builder builder) { // Initialize windows that the controller needs @@ -88,7 +88,7 @@ namespace BrickManager { ControlPanel.AudioMixerElementsColumn.VOLUME, ControlPanel.AudioMixerElementsColumn.CAN_MUTE, ControlPanel.AudioMixerElementsColumn.MUTE - }, new Value[] { "New Element", 0, ITestableMixerElement.HALF_VOLUME, true, false }); + }, new Value[] { "New Element", 0, IMixerElementViewModel.HALF_VOLUME, true, false }); }); // Store references to the remove button @@ -144,23 +144,8 @@ namespace BrickManager { update_liststore_for_element(mixer_elems_liststore, volume_window.current_element); }); - volume_window.volume_half.connect(() => { - volume_window.current_element.volume = ITestableMixerElement.HALF_VOLUME; - update_liststore_for_element(mixer_elems_liststore, volume_window.current_element); - }); - volume_window.volume_min.connect(() => { - volume_window.current_element.volume = ITestableMixerElement.MIN_VOLUME; - update_liststore_for_element(mixer_elems_liststore, volume_window.current_element); - }); - - volume_window.volume_max.connect(() => { - volume_window.current_element.volume = ITestableMixerElement.MAX_VOLUME; - update_liststore_for_element(mixer_elems_liststore, volume_window.current_element); - }); - - volume_window.mute_toggled.connect((is_muted) => { - volume_window.current_element.is_muted = is_muted; + volume_window.current_element.volume = IMixerElementViewModel.MIN_VOLUME; update_liststore_for_element(mixer_elems_liststore, volume_window.current_element); }); @@ -182,18 +167,17 @@ namespace BrickManager { Value index = get_liststore_value(mixer_elems_liststore, iter, ControlPanel.AudioMixerElementsColumn.INDEX); Value volume = get_liststore_value(mixer_elems_liststore, iter, ControlPanel.AudioMixerElementsColumn.VOLUME); Value can_mute = get_liststore_value(mixer_elems_liststore, iter, ControlPanel.AudioMixerElementsColumn.CAN_MUTE); - Value mute = get_liststore_value(mixer_elems_liststore, iter, ControlPanel.AudioMixerElementsColumn.MUTE); Value user_data = get_liststore_value(mixer_elems_liststore, iter, ControlPanel.AudioMixerElementsColumn.USER_DATA); // The mixer elements will make sure that these numbers are within proper bounds later int parsed_index = (int)parse_double_with_default(index.get_string(), 0); - int parsed_volume = (int)parse_double_with_default(volume.get_string(), ITestableMixerElement.HALF_VOLUME); + int parsed_volume = (int)parse_double_with_default(volume.get_string(), IMixerElementViewModel.HALF_VOLUME); // This is guaranteed to be a fake mixer element; as such, it is referenced by the concrete implementation name FakeMixerElement? mixer_element = (FakeMixerElement?)user_data.get_pointer (); if(mixer_element == null) { - mixer_element = new FakeMixerElement(name.get_string(), parsed_index, parsed_volume, can_mute.get_boolean(), mute.get_boolean()); + mixer_element = new FakeMixerElement(name.get_string(), parsed_index, parsed_volume, can_mute.get_boolean()); mixer_select_window.add_element(mixer_element); mixer_elems_liststore.set(iter, ControlPanel.AudioMixerElementsColumn.USER_DATA, mixer_element.ref ()); @@ -204,7 +188,6 @@ namespace BrickManager { mixer_element.set_index(parsed_index); mixer_element.volume = parsed_volume; mixer_element.set_can_mute(can_mute.get_boolean()); - mixer_element.is_muted = mute.get_boolean(); mixer_element.thaw_notify(); // If the original value was invalid and the mixer object modified it, replace the entered text with the valid version @@ -219,7 +202,7 @@ namespace BrickManager { * Updates the ListStore entry associated with the specified mixer element with data from * the mixer element. */ - private void update_liststore_for_element(Gtk.ListStore mixer_elems_liststore, ITestableMixerElement element, Gtk.TreeIter? iter = null) { + private void update_liststore_for_element(Gtk.ListStore mixer_elems_liststore, IMixerElementViewModel element, Gtk.TreeIter? iter = null) { // Find the iter pointing to this element if one was not supplied if(iter == null) { mixer_elems_liststore.foreach((model, path, current_iter) => { diff --git a/test/glade/ControlPanel.glade b/test/glade/ControlPanel.glade index 939089e..ed46ee8 100644 --- a/test/glade/ControlPanel.glade +++ b/test/glade/ControlPanel.glade @@ -2972,6 +2972,7 @@ 0 + False 4 From 55d0a87f618296686f15a3ec4f5a9ffded21ebae Mon Sep 17 00:00:00 2001 From: wasabifan Date: Fri, 24 Jun 2016 00:45:49 -0700 Subject: [PATCH 4/7] Clean up sound code based on review feedback --- CMakeLists.txt | 4 +- src/AlsaBackedMixerElement.vala | 24 ++++--- src/AlsaInterface.vala | 3 +- ...ioController.vala => SoundController.vala} | 57 +++++++++++------ src/main.vala | 4 +- src/view/MixerElementSelectorWindow.vala | 44 +++++-------- src/view/MixerElementVolumeWindow.vala | 12 ++-- test/ControlPanel.vala | 8 +-- ...ntroller.vala => FakeSoundController.vala} | 64 +++++++++---------- test/glade/ControlPanel.glade | 10 +-- test/main.vala | 2 +- 11 files changed, 123 insertions(+), 109 deletions(-) rename src/controller/{AudioController.vala => SoundController.vala} (62%) rename test/controller/{FakeAudioController.vala => FakeSoundController.vala} (86%) diff --git a/CMakeLists.txt b/CMakeLists.txt index d7b46b1..b4b07af 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,13 +34,13 @@ if (BRICKMAN_TEST) test/FakeBluez5.vala test/FakeConnman.vala test/controller/FakeAboutController.vala - test/controller/FakeAudioController.vala test/controller/FakeBatteryController.vala test/controller/FakeBluetoothController.vala test/controller/FakeDeviceBrowserController.vala test/controller/FakeFileBrowserController.vala test/controller/FakeNetworkController.vala test/controller/FakeOpenRobertaController.vala + test/controller/FakeSoundController.vala ) else (BRICKMAN_TEST) set (BRICKMAN_SOURCE_FILES @@ -53,13 +53,13 @@ else (BRICKMAN_TEST) lib/connman/Service.vala lib/connman/Technology.vala src/controller/AboutController.vala - src/controller/AudioController.vala src/controller/BatteryController.vala src/controller/BluetoothController.vala src/controller/DeviceBrowserController.vala src/controller/FileBrowserController.vala src/controller/NetworkController.vala src/controller/OpenRobertaController.vala + src/controller/SoundController.vala src/AlsaBackedMixerElement.vala src/GlobalManager.vala src/main.vala diff --git a/src/AlsaBackedMixerElement.vala b/src/AlsaBackedMixerElement.vala index 2976f30..e830006 100644 --- a/src/AlsaBackedMixerElement.vala +++ b/src/AlsaBackedMixerElement.vala @@ -25,10 +25,10 @@ using Alsa; namespace BrickManager { public class AlsaBackedMixerElement: IMixerElementViewModel, Object { - private const SimpleChannelId primary_channel_id = SimpleChannelId.MONO; + const SimpleChannelId primary_channel_id = SimpleChannelId.MONO; - private unowned MixerElement alsa_element; - private SimpleElementId alsa_id; + unowned MixerElement alsa_element; + SimpleElementId alsa_id; public AlsaBackedMixerElement(MixerElement element) { this.alsa_element = element; @@ -53,7 +53,7 @@ namespace BrickManager { long volume = 0; alsa_element.get_playback_volume(primary_channel_id, out volume); - long min_volume, max_volume; + long min_volume = 0, max_volume = 0; alsa_element.get_playback_volume_range(out min_volume, out max_volume); // Prevent division by zero @@ -67,15 +67,16 @@ namespace BrickManager { long min_volume, max_volume; alsa_element.get_playback_volume_range(out min_volume, out max_volume); - int constrained_volume = int.min(100, int.max(0, value)); + var constrained_volume = int.min(100, int.max(0, value)); float scaled_volume = constrained_volume * (max_volume - min_volume) / 100f + min_volume; long rounded_volume = (long)Math.round(scaled_volume); alsa_element.set_playback_volume_all(rounded_volume); bool should_mute = rounded_volume <= min_volume; - if(is_muted != should_mute) + if (is_muted != should_mute) { set_is_muted(should_mute); + } } } @@ -87,11 +88,14 @@ namespace BrickManager { public bool is_muted { get { - if(!can_mute) + if (!can_mute) { return false; + } - int mute_switch = 0; - alsa_element.get_playback_switch(primary_channel_id, out mute_switch); + int mute_switch = 1; + if(alsa_element.get_playback_switch(primary_channel_id, out mute_switch) != 0) { + critical("Error while getting mute switch state"); + } return mute_switch == 0; } @@ -102,4 +106,4 @@ namespace BrickManager { alsa_element.set_playback_switch_all(is_muted ? 0 : 1); } } -} \ No newline at end of file +} diff --git a/src/AlsaInterface.vala b/src/AlsaInterface.vala index 63e6010..7bcba93 100644 --- a/src/AlsaInterface.vala +++ b/src/AlsaInterface.vala @@ -81,6 +81,7 @@ namespace BrickManager { return _can_mute; } } + public bool is_muted { get { return _is_muted; @@ -107,4 +108,4 @@ namespace BrickManager { notify_property("is_muted"); } } -} \ No newline at end of file +} diff --git a/src/controller/AudioController.vala b/src/controller/SoundController.vala similarity index 62% rename from src/controller/AudioController.vala rename to src/controller/SoundController.vala index 4aadbbe..2bfeb74 100644 --- a/src/controller/AudioController.vala +++ b/src/controller/SoundController.vala @@ -1,7 +1,7 @@ /* * brickman -- Brick Manager for LEGO MINDSTORMS EV3/ev3dev * - * Copyright 2014-2015 David Lechner + * Copyright 2016 Kaelin Laundry * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,15 +19,15 @@ * MA 02110-1301, USA. */ -/* BatteryController.vala - Controller for monitoring battery */ +/* SoundController.vala - Controller for sound volume control */ using Ev3devKit.Devices; using Ev3devKit.Ui; using Alsa; namespace BrickManager { - public class AudioController : Object, IBrickManagerModule { - private const int VOLUME_STEP = 10; + public class SoundController : Object, IBrickManagerModule { + const int VOLUME_STEP = 10; Mixer mixer; MixerElementSelectorWindow mixer_select_window; @@ -35,12 +35,32 @@ namespace BrickManager { public string display_name { get { return "Sound"; } } - protected void initialize_mixer() { + private void initialize_mixer() { mixer = null; - Mixer.open(out mixer); - mixer.attach(); - mixer.register(); - mixer.load(); + + int err = Mixer.open(out mixer); + if(err != 0) { + critical("Failed to open mixer: %s", Alsa.strerror(err)); + return; + } + + err = mixer.attach(); + if(err != 0) { + critical("Failed to attach mixer: %s", Alsa.strerror(err)); + return; + } + + err = mixer.register(); + if(err != 0) { + critical("Failed to register mixer: %s", Alsa.strerror(err)); + return; + } + + err = mixer.load(); + if(err != 0) { + critical("Failed to load mixer: %s", Alsa.strerror(err)); + return; + } } void create_main_window () { @@ -59,15 +79,16 @@ namespace BrickManager { void create_volume_window() { volume_window = new MixerElementVolumeWindow(); + weak MixerElementVolumeWindow weak_volume_window = volume_window; // Wire up handlers for volume window signals volume_window.volume_up.connect(() => - volume_window.current_element.volume += VOLUME_STEP); + weak_volume_window.current_element.volume += VOLUME_STEP); volume_window.volume_down.connect(() => - volume_window.current_element.volume -= VOLUME_STEP); + weak_volume_window.current_element.volume -= VOLUME_STEP); volume_window.volume_min.connect(() => - volume_window.current_element.volume = IMixerElementViewModel.MIN_VOLUME); + weak_volume_window.current_element.volume = IMixerElementViewModel.MIN_VOLUME); } public void show_main_window () { @@ -75,25 +96,25 @@ namespace BrickManager { create_main_window (); } - // Whenever the audio item is launched from the main menu, + // Whenever the sound item is launched from the main menu, // repopulate the mixer list mixer_select_window.clear_elements(); // Re-initializing will return updated data, including volume initialize_mixer(); - for(MixerElement element = mixer.first_elem(); element != null; element = element.next()) { + for (MixerElement element = mixer.first_elem(); element != null; element = element.next()) { mixer_select_window.add_element(new AlsaBackedMixerElement(element)); } - if(mixer_select_window.has_single_element) { - if(volume_window == null) + if (mixer_select_window.has_single_element) { + if (volume_window == null) create_volume_window(); volume_window.current_element = mixer_select_window.first_element; volume_window.show_element_details = false; volume_window.show(); - } - else + } else { mixer_select_window.show (); + } } } } diff --git a/src/main.vala b/src/main.vala index 175a9d8..54c74a3 100644 --- a/src/main.vala +++ b/src/main.vala @@ -67,8 +67,8 @@ namespace BrickManager { var bluetooth_controller = new BluetoothController (); network_controller.add_controller (bluetooth_controller); network_controller.add_controller (network_controller.wifi_controller); - var audio_controller = new AudioController (); - home_window.add_controller (audio_controller); + var sound_controller = new SoundController (); + home_window.add_controller (sound_controller); var battery_controller = new BatteryController (); home_window.add_controller (battery_controller); var open_roberta_controller = new OpenRobertaController (); diff --git a/src/view/MixerElementSelectorWindow.vala b/src/view/MixerElementSelectorWindow.vala index ec0dbe8..065151d 100644 --- a/src/view/MixerElementSelectorWindow.vala +++ b/src/view/MixerElementSelectorWindow.vala @@ -29,16 +29,10 @@ namespace BrickManager { public class MixerElementSelectorWindow : BrickManagerWindow { Ui.Menu element_menu; - public signal void mixer_elem_selected (IMixerElementViewModel selected_element); - - private class MixerElementWithSignalIds: Object { - public IMixerElementViewModel element; - public ulong notify_signal_handler_id = 0; - public ulong button_press_signal_handler_id = 0; - } + public signal void mixer_element_selected (IMixerElementViewModel selected_element); public MixerElementSelectorWindow () { - title = "Audio mixer elements"; + title = "Sound mixer elements"; element_menu = new Ui.Menu (); content_vbox.add (element_menu); } @@ -53,8 +47,8 @@ namespace BrickManager { // the item in the correct place instead of sorting the entire list // each time an item is inserted. element_menu.sort_menu_items ((a, b) => { - IMixerElementViewModel element_a = (a.represented_object as MixerElementWithSignalIds).element; - IMixerElementViewModel element_b = (b.represented_object as MixerElementWithSignalIds).element; + IMixerElementViewModel element_a = a.represented_object as IMixerElementViewModel; + IMixerElementViewModel element_b = b.represented_object as IMixerElementViewModel; // Group by name, and sort by index within the same name if(element_a.name == element_b.name) @@ -65,40 +59,33 @@ namespace BrickManager { } public void add_element (IMixerElementViewModel element) { - var menu_item = new Ui.MenuItem (get_element_label_text(element)); - - var represented_object = new MixerElementWithSignalIds() { - element = element + var menu_item = new Ui.MenuItem (get_element_label_text(element)) { + represented_object = (Object)element }; + weak IMixerElementViewModel weak_element = element; // Update the menu item whenever the represented element changes - represented_object.notify_signal_handler_id = element.notify.connect((sender, property) => { - menu_item.label.text = get_element_label_text(element); + element.notify.connect((sender, property) => { + menu_item.label.text = get_element_label_text(weak_element); sort_element_menu(); }); // Emit a selection signal for this element when its menu item is selected - represented_object.button_press_signal_handler_id = menu_item.button.pressed.connect (() => - mixer_elem_selected (element)); - - menu_item.represented_object = (Object)represented_object; + menu_item.button.pressed.connect (() => + mixer_element_selected (weak_element)); + element_menu.add_menu_item (menu_item); } protected void remove_menu_item (Ui.MenuItem menu_item) { if (menu_item != null) { - var represented_object = menu_item.represented_object as MixerElementWithSignalIds; - represented_object.element.disconnect(represented_object.notify_signal_handler_id); - menu_item.button.disconnect(represented_object.button_press_signal_handler_id); - element_menu.remove_menu_item (menu_item); } } public void remove_element (IMixerElementViewModel element) { var menu_item = element_menu.find_menu_item (element, (menu_item, target_element) => { - var other_element = (menu_item.represented_object as MixerElementWithSignalIds).element; - return target_element == other_element; + return target_element == (menu_item.represented_object as IMixerElementViewModel); }); remove_menu_item(menu_item); @@ -106,8 +93,9 @@ namespace BrickManager { public void clear_elements () { var iter = element_menu.menu_item_iter (); - while (iter.size > 0) + while (iter.size > 0) { remove_menu_item(iter[0]); + } } public bool has_single_element { @@ -121,7 +109,7 @@ namespace BrickManager { if(element_menu.menu_item_iter().size <= 0) return null; - return (element_menu.menu_item_iter().get(0).represented_object as MixerElementWithSignalIds).element; + return element_menu.menu_item_iter().get(0).represented_object as IMixerElementViewModel; } } } diff --git a/src/view/MixerElementVolumeWindow.vala b/src/view/MixerElementVolumeWindow.vala index eca2f9e..3272399 100644 --- a/src/view/MixerElementVolumeWindow.vala +++ b/src/view/MixerElementVolumeWindow.vala @@ -28,9 +28,9 @@ namespace BrickManager { public class MixerElementVolumeWindow : BrickManagerWindow { public signal void volume_up (); public signal void volume_down (); - public signal void volume_min (); + public signal void mute (); - IMixerElementViewModel _current_element = null; + IMixerElementViewModel _current_element; bool _show_element_details = true; Ui.Label element_label; @@ -57,12 +57,12 @@ namespace BrickManager { }); controls_menu.add_menu_item(volume_down_item); - var volume_min_item = new Ui.MenuItem("Mute"); - volume_min_item.button.pressed.connect(() => { + var mute_item = new Ui.MenuItem("Mute"); + mute_item.button.pressed.connect(() => { if(_current_element != null) - volume_min(); + mute(); }); - controls_menu.add_menu_item(volume_min_item); + controls_menu.add_menu_item(mute_item); content_vbox.add (controls_menu); diff --git a/test/ControlPanel.vala b/test/ControlPanel.vala index 3be62c8..2191941 100644 --- a/test/ControlPanel.vala +++ b/test/ControlPanel.vala @@ -33,7 +33,7 @@ namespace BrickManager { public FakeFileBrowserController file_browser_controller; public FakeDeviceBrowserController device_browser_controller; public FakeNetworkController network_controller; - public FakeAudioController audio_controller; + public FakeSoundController sound_controller; public FakeBluetoothController bluetooth_controller; public FakeBatteryController battery_controller; public FakeAboutController about_controller; @@ -42,7 +42,7 @@ namespace BrickManager { enum Tab { DEVICE_BROWSER, NETWORK, - AUDIO, + SOUND, BLUETOOTH, BATTERY, OPEN_ROBERTA, @@ -131,7 +131,7 @@ namespace BrickManager { COLUMN_COUNT; } - enum AudioMixerElementsColumn { + enum SoundMixerElementsColumn { NAME, INDEX, VOLUME, @@ -158,7 +158,7 @@ namespace BrickManager { file_browser_controller = new FakeFileBrowserController (builder); device_browser_controller = new FakeDeviceBrowserController (builder); network_controller = new FakeNetworkController (builder); - audio_controller = new FakeAudioController(builder); + sound_controller = new FakeSoundController (builder); bluetooth_controller = new FakeBluetoothController (builder); battery_controller = new FakeBatteryController (builder); about_controller = new FakeAboutController (builder); diff --git a/test/controller/FakeAudioController.vala b/test/controller/FakeSoundController.vala similarity index 86% rename from test/controller/FakeAudioController.vala rename to test/controller/FakeSoundController.vala index 429b147..c7d73d4 100644 --- a/test/controller/FakeAudioController.vala +++ b/test/controller/FakeSoundController.vala @@ -19,13 +19,13 @@ * MA 02110-1301, USA. */ -/* FakeAudioController.vala - Fake Audio controller for testing */ +/* FakeSoundController.vala - Fake sound controller for testing */ using Ev3devKit; using Ev3devKit.Ui; namespace BrickManager { - public class FakeAudioController : Object, IBrickManagerModule { + public class FakeSoundController : Object, IBrickManagerModule { private const int VOLUME_STEP = 10; MixerElementSelectorWindow mixer_select_window; @@ -33,7 +33,7 @@ namespace BrickManager { public string display_name { get { return "Sound"; } } - public FakeAudioController (Gtk.Builder builder) { + public FakeSoundController (Gtk.Builder builder) { // Initialize windows that the controller needs mixer_select_window = new MixerElementSelectorWindow (); volume_window = new MixerElementVolumeWindow(); @@ -42,9 +42,9 @@ namespace BrickManager { // the first time either window is invoked var control_panel_notebook = builder.get_object ("control-panel-notebook") as Gtk.Notebook; mixer_select_window.shown.connect (() => - control_panel_notebook.page = (int)ControlPanel.Tab.AUDIO); + control_panel_notebook.page = (int)ControlPanel.Tab.SOUND); volume_window.shown.connect (() => - control_panel_notebook.page = (int)ControlPanel.Tab.AUDIO); + control_panel_notebook.page = (int)ControlPanel.Tab.SOUND); // Initialize items in brickman for all current elements var mixer_elems_liststore = builder.get_object ("mixer-elements-liststore") as Gtk.ListStore; @@ -61,19 +61,19 @@ namespace BrickManager { // Link liststore and control panel GUI (builder.get_object ("mixer-element-name-cellrenderertext") as Gtk.CellRendererText) .edited.connect ((path, new_text) => ControlPanel.update_listview_text_item ( - mixer_elems_liststore, path, new_text, ControlPanel.AudioMixerElementsColumn.NAME)); + mixer_elems_liststore, path, new_text, ControlPanel.SoundMixerElementsColumn.NAME)); (builder.get_object ("mixer-element-index-cellrenderertext") as Gtk.CellRendererText) .edited.connect ((path, new_text) => ControlPanel.update_listview_text_item ( - mixer_elems_liststore, path, new_text, ControlPanel.AudioMixerElementsColumn.INDEX)); + mixer_elems_liststore, path, new_text, ControlPanel.SoundMixerElementsColumn.INDEX)); (builder.get_object ("mixer-element-volume-cellrenderertext") as Gtk.CellRendererText) .edited.connect ((path, new_text) => ControlPanel.update_listview_text_item ( - mixer_elems_liststore, path, new_text, ControlPanel.AudioMixerElementsColumn.VOLUME)); + mixer_elems_liststore, path, new_text, ControlPanel.SoundMixerElementsColumn.VOLUME)); (builder.get_object ("mixer-element-can-mute-cellrenderertoggle") as Gtk.CellRendererToggle) .toggled.connect ((toggle, path) => ControlPanel.update_listview_toggle_item ( - mixer_elems_liststore, toggle, path, ControlPanel.AudioMixerElementsColumn.CAN_MUTE)); + mixer_elems_liststore, toggle, path, ControlPanel.SoundMixerElementsColumn.CAN_MUTE)); (builder.get_object ("mixer-element-mute-cellrenderertoggle") as Gtk.CellRendererToggle) .toggled.connect ((toggle, path) => ControlPanel.update_listview_toggle_item ( - mixer_elems_liststore, toggle, path, ControlPanel.AudioMixerElementsColumn.MUTE)); + mixer_elems_liststore, toggle, path, ControlPanel.SoundMixerElementsColumn.MUTE)); // Configure the add button (builder.get_object ("mixer-element-add-button") as Gtk.Button).clicked.connect (() => { @@ -83,11 +83,11 @@ namespace BrickManager { // Doing this all at once ensures that the row_changed handler is only called once, // and never called with partial data. mixer_elems_liststore.set_valuesv(iter, new int[] { - ControlPanel.AudioMixerElementsColumn.NAME, - ControlPanel.AudioMixerElementsColumn.INDEX, - ControlPanel.AudioMixerElementsColumn.VOLUME, - ControlPanel.AudioMixerElementsColumn.CAN_MUTE, - ControlPanel.AudioMixerElementsColumn.MUTE + ControlPanel.SoundMixerElementsColumn.NAME, + ControlPanel.SoundMixerElementsColumn.INDEX, + ControlPanel.SoundMixerElementsColumn.VOLUME, + ControlPanel.SoundMixerElementsColumn.CAN_MUTE, + ControlPanel.SoundMixerElementsColumn.MUTE }, new Value[] { "New Element", 0, IMixerElementViewModel.HALF_VOLUME, true, false }); }); @@ -102,7 +102,7 @@ namespace BrickManager { if (mixer_element_treeview_selection.get_selected (out model, out iter)) { Value user_data; - model.get_value (iter, ControlPanel.AudioMixerElementsColumn.USER_DATA, out user_data); + model.get_value (iter, ControlPanel.SoundMixerElementsColumn.USER_DATA, out user_data); var mixer_element = (FakeMixerElement)user_data.get_pointer (); if (mixer_element != null) @@ -121,10 +121,10 @@ namespace BrickManager { mixer_element_treeview_selection.changed (); // Configure the direct window link buttons - (builder.get_object ("audio-mixer-select-window-button") as Gtk.Button).clicked.connect (() => + (builder.get_object ("sound-mixer-select-window-button") as Gtk.Button).clicked.connect (() => mixer_select_window.show()); - (builder.get_object ("audio-volume-window-button") as Gtk.Button).clicked.connect (() => { + (builder.get_object ("sound-volume-window-button") as Gtk.Button).clicked.connect (() => { if(mixer_select_window.first_element == null) return; @@ -144,13 +144,13 @@ namespace BrickManager { update_liststore_for_element(mixer_elems_liststore, volume_window.current_element); }); - volume_window.volume_min.connect(() => { + volume_window.mute.connect(() => { volume_window.current_element.volume = IMixerElementViewModel.MIN_VOLUME; update_liststore_for_element(mixer_elems_liststore, volume_window.current_element); }); // Show volume window when mixer element is selected - mixer_select_window.mixer_elem_selected.connect ((selected_element) => { + mixer_select_window.mixer_element_selected.connect ((selected_element) => { volume_window.current_element = selected_element; volume_window.show_element_details = true; volume_window.show(); @@ -163,11 +163,11 @@ namespace BrickManager { * already exist. */ private void update_fake_element_from_liststore(Gtk.TreeIter iter, Gtk.ListStore mixer_elems_liststore) { - Value name = get_liststore_value(mixer_elems_liststore, iter, ControlPanel.AudioMixerElementsColumn.NAME); - Value index = get_liststore_value(mixer_elems_liststore, iter, ControlPanel.AudioMixerElementsColumn.INDEX); - Value volume = get_liststore_value(mixer_elems_liststore, iter, ControlPanel.AudioMixerElementsColumn.VOLUME); - Value can_mute = get_liststore_value(mixer_elems_liststore, iter, ControlPanel.AudioMixerElementsColumn.CAN_MUTE); - Value user_data = get_liststore_value(mixer_elems_liststore, iter, ControlPanel.AudioMixerElementsColumn.USER_DATA); + Value name = get_liststore_value(mixer_elems_liststore, iter, ControlPanel.SoundMixerElementsColumn.NAME); + Value index = get_liststore_value(mixer_elems_liststore, iter, ControlPanel.SoundMixerElementsColumn.INDEX); + Value volume = get_liststore_value(mixer_elems_liststore, iter, ControlPanel.SoundMixerElementsColumn.VOLUME); + Value can_mute = get_liststore_value(mixer_elems_liststore, iter, ControlPanel.SoundMixerElementsColumn.CAN_MUTE); + Value user_data = get_liststore_value(mixer_elems_liststore, iter, ControlPanel.SoundMixerElementsColumn.USER_DATA); // The mixer elements will make sure that these numbers are within proper bounds later int parsed_index = (int)parse_double_with_default(index.get_string(), 0); @@ -180,7 +180,7 @@ namespace BrickManager { mixer_element = new FakeMixerElement(name.get_string(), parsed_index, parsed_volume, can_mute.get_boolean()); mixer_select_window.add_element(mixer_element); - mixer_elems_liststore.set(iter, ControlPanel.AudioMixerElementsColumn.USER_DATA, mixer_element.ref ()); + mixer_elems_liststore.set(iter, ControlPanel.SoundMixerElementsColumn.USER_DATA, mixer_element.ref ()); } else { mixer_element.freeze_notify(); @@ -206,7 +206,7 @@ namespace BrickManager { // Find the iter pointing to this element if one was not supplied if(iter == null) { mixer_elems_liststore.foreach((model, path, current_iter) => { - Value user_data = get_liststore_value(mixer_elems_liststore, current_iter, ControlPanel.AudioMixerElementsColumn.USER_DATA); + Value user_data = get_liststore_value(mixer_elems_liststore, current_iter, ControlPanel.SoundMixerElementsColumn.USER_DATA); FakeMixerElement other_element = (FakeMixerElement)user_data.get_pointer(); if(other_element == element) { @@ -220,11 +220,11 @@ namespace BrickManager { mixer_elems_liststore.set_valuesv (iter, new int[] { - ControlPanel.AudioMixerElementsColumn.NAME, - ControlPanel.AudioMixerElementsColumn.INDEX, - ControlPanel.AudioMixerElementsColumn.VOLUME, - ControlPanel.AudioMixerElementsColumn.CAN_MUTE, - ControlPanel.AudioMixerElementsColumn.MUTE + ControlPanel.SoundMixerElementsColumn.NAME, + ControlPanel.SoundMixerElementsColumn.INDEX, + ControlPanel.SoundMixerElementsColumn.VOLUME, + ControlPanel.SoundMixerElementsColumn.CAN_MUTE, + ControlPanel.SoundMixerElementsColumn.MUTE }, new Value[] { element.name, element.index.to_string(), diff --git a/test/glade/ControlPanel.glade b/test/glade/ControlPanel.glade index ed46ee8..ee09653 100644 --- a/test/glade/ControlPanel.glade +++ b/test/glade/ControlPanel.glade @@ -2880,7 +2880,7 @@ - + True False vertical @@ -3053,7 +3053,7 @@ True False - + Mixer select True True @@ -3066,7 +3066,7 @@ - + Volume control True True @@ -3102,10 +3102,10 @@ - + True False - Audio + Sound 2 diff --git a/test/main.vala b/test/main.vala index 23fe015..972be72 100644 --- a/test/main.vala +++ b/test/main.vala @@ -51,7 +51,7 @@ namespace BrickManager { control_panel.network_controller.add_controller (control_panel.network_controller.wifi_controller); control_panel.bluetooth_controller.show_network_connection_requested.connect ((name) => control_panel.network_controller.show_connection (name)); - home_window.add_controller (control_panel.audio_controller); + home_window.add_controller (control_panel.sound_controller); home_window.add_controller (control_panel.battery_controller); home_window.add_controller (control_panel.open_roberta_controller); home_window.add_controller (control_panel.about_controller); From a426fe485536a72ce5b87996a99784ad375c7c44 Mon Sep 17 00:00:00 2001 From: wasabifan Date: Sat, 25 Jun 2016 16:50:45 -0700 Subject: [PATCH 5/7] Format audio code to follow spacing style conventions --- src/AlsaBackedMixerElement.vala | 40 +++++------ src/AlsaInterface.vala | 30 ++++---- src/controller/SoundController.vala | 54 +++++++------- src/view/MixerElementSelectorWindow.vala | 28 ++++---- src/view/MixerElementVolumeWindow.vala | 56 +++++++-------- test/controller/FakeSoundController.vala | 92 ++++++++++++------------ 6 files changed, 150 insertions(+), 150 deletions(-) diff --git a/src/AlsaBackedMixerElement.vala b/src/AlsaBackedMixerElement.vala index e830006..12ab468 100644 --- a/src/AlsaBackedMixerElement.vala +++ b/src/AlsaBackedMixerElement.vala @@ -30,59 +30,59 @@ namespace BrickManager { unowned MixerElement alsa_element; SimpleElementId alsa_id; - public AlsaBackedMixerElement(MixerElement element) { + public AlsaBackedMixerElement (MixerElement element) { this.alsa_element = element; - SimpleElementId.alloc(out alsa_id); - element.get_id(alsa_id); + SimpleElementId.alloc (out alsa_id); + element.get_id (alsa_id); } public string name { get { - return alsa_id.get_name(); + return alsa_id.get_name (); } } public uint index { get { - return alsa_id.get_index(); + return alsa_id.get_index (); } } public int volume { get { long volume = 0; - alsa_element.get_playback_volume(primary_channel_id, out volume); + alsa_element.get_playback_volume (primary_channel_id, out volume); long min_volume = 0, max_volume = 0; - alsa_element.get_playback_volume_range(out min_volume, out max_volume); + alsa_element.get_playback_volume_range (out min_volume, out max_volume); // Prevent division by zero - if(max_volume == min_volume) + if (max_volume == min_volume) return 0; // Do calculations as floats so avoid odd-looking increments from truncation - return (int)Math.round((volume - min_volume) * 100 / (float)(max_volume - min_volume)); + return (int)Math.round ((volume - min_volume) * 100 / (float)(max_volume - min_volume)); } set { long min_volume, max_volume; - alsa_element.get_playback_volume_range(out min_volume, out max_volume); + alsa_element.get_playback_volume_range (out min_volume, out max_volume); - var constrained_volume = int.min(100, int.max(0, value)); + var constrained_volume = int.min (100, int.max (0, value)); float scaled_volume = constrained_volume * (max_volume - min_volume) / 100f + min_volume; - long rounded_volume = (long)Math.round(scaled_volume); + long rounded_volume = (long)Math.round (scaled_volume); - alsa_element.set_playback_volume_all(rounded_volume); + alsa_element.set_playback_volume_all (rounded_volume); bool should_mute = rounded_volume <= min_volume; if (is_muted != should_mute) { - set_is_muted(should_mute); + set_is_muted (should_mute); } } } public bool can_mute { get { - return alsa_element.has_playback_switch(); + return alsa_element.has_playback_switch (); } } @@ -93,17 +93,17 @@ namespace BrickManager { } int mute_switch = 1; - if(alsa_element.get_playback_switch(primary_channel_id, out mute_switch) != 0) { - critical("Error while getting mute switch state"); + if (alsa_element.get_playback_switch (primary_channel_id, out mute_switch) != 0) { + critical ("Error while getting mute switch state"); } return mute_switch == 0; } } - protected void set_is_muted(bool is_muted) { - if(can_mute) - alsa_element.set_playback_switch_all(is_muted ? 0 : 1); + protected void set_is_muted (bool is_muted) { + if (can_mute) + alsa_element.set_playback_switch_all (is_muted ? 0 : 1); } } } diff --git a/src/AlsaInterface.vala b/src/AlsaInterface.vala index 7bcba93..77722f3 100644 --- a/src/AlsaInterface.vala +++ b/src/AlsaInterface.vala @@ -55,12 +55,12 @@ namespace BrickManager { } } - public FakeMixerElement(string name, uint index, int volume, bool can_mute) { - set_name(name); - set_index(index); + public FakeMixerElement (string name, uint index, int volume, bool can_mute) { + set_name (name); + set_index (index); this.volume = volume; - set_can_mute(can_mute); + set_can_mute (can_mute); } public int volume { @@ -68,11 +68,11 @@ namespace BrickManager { return _volume; } set { - _volume = int.min(100, int.max(0, value)); + _volume = int.min (100, int.max (0, value)); bool should_mute = _volume <= 0; - if(_is_muted != should_mute) - set_is_muted(should_mute); + if (_is_muted != should_mute) + set_is_muted (should_mute); } } @@ -88,24 +88,24 @@ namespace BrickManager { } } - public void set_name(string new_name) { + public void set_name (string new_name) { this._name = new_name; - notify_property("name"); + notify_property ("name"); } - public void set_index(uint new_index) { + public void set_index (uint new_index) { this._index = new_index; - notify_property("index"); + notify_property ("index"); } - public void set_can_mute(bool can_mute) { + public void set_can_mute (bool can_mute) { this._can_mute = can_mute; - notify_property("can_mute"); + notify_property ("can_mute"); } - public void set_is_muted(bool is_muted) { + public void set_is_muted (bool is_muted) { this._is_muted = is_muted; - notify_property("is_muted"); + notify_property ("is_muted"); } } } diff --git a/src/controller/SoundController.vala b/src/controller/SoundController.vala index 2bfeb74..ded9011 100644 --- a/src/controller/SoundController.vala +++ b/src/controller/SoundController.vala @@ -35,30 +35,30 @@ namespace BrickManager { public string display_name { get { return "Sound"; } } - private void initialize_mixer() { + private void initialize_mixer () { mixer = null; - int err = Mixer.open(out mixer); - if(err != 0) { - critical("Failed to open mixer: %s", Alsa.strerror(err)); + int err = Mixer.open (out mixer); + if (err != 0) { + critical ("Failed to open mixer: %s", Alsa.strerror (err)); return; } - err = mixer.attach(); - if(err != 0) { - critical("Failed to attach mixer: %s", Alsa.strerror(err)); + err = mixer.attach (); + if (err != 0) { + critical ("Failed to attach mixer: %s", Alsa.strerror (err)); return; } - err = mixer.register(); - if(err != 0) { - critical("Failed to register mixer: %s", Alsa.strerror(err)); + err = mixer.register (); + if (err != 0) { + critical ("Failed to register mixer: %s", Alsa.strerror (err)); return; } - err = mixer.load(); - if(err != 0) { - critical("Failed to load mixer: %s", Alsa.strerror(err)); + err = mixer.load (); + if (err != 0) { + critical ("Failed to load mixer: %s", Alsa.strerror (err)); return; } } @@ -67,27 +67,27 @@ namespace BrickManager { mixer_select_window = new MixerElementSelectorWindow (); mixer_select_window.mixer_elem_selected.connect ((selected_element) => { - if(volume_window == null) - create_volume_window(); + if (volume_window == null) + create_volume_window (); volume_window.current_element = selected_element; volume_window.show_element_details = true; - volume_window.show(); + volume_window.show (); }); } - void create_volume_window() { - volume_window = new MixerElementVolumeWindow(); + void create_volume_window () { + volume_window = new MixerElementVolumeWindow (); weak MixerElementVolumeWindow weak_volume_window = volume_window; // Wire up handlers for volume window signals - volume_window.volume_up.connect(() => + volume_window.volume_up.connect (() => weak_volume_window.current_element.volume += VOLUME_STEP); - volume_window.volume_down.connect(() => + volume_window.volume_down.connect (() => weak_volume_window.current_element.volume -= VOLUME_STEP); - volume_window.volume_min.connect(() => + volume_window.volume_min.connect (() => weak_volume_window.current_element.volume = IMixerElementViewModel.MIN_VOLUME); } @@ -98,20 +98,20 @@ namespace BrickManager { // Whenever the sound item is launched from the main menu, // repopulate the mixer list - mixer_select_window.clear_elements(); + mixer_select_window.clear_elements (); // Re-initializing will return updated data, including volume - initialize_mixer(); - for (MixerElement element = mixer.first_elem(); element != null; element = element.next()) { - mixer_select_window.add_element(new AlsaBackedMixerElement(element)); + initialize_mixer (); + for (MixerElement element = mixer.first_elem (); element != null; element = element.next ()) { + mixer_select_window.add_element (new AlsaBackedMixerElement (element)); } if (mixer_select_window.has_single_element) { if (volume_window == null) - create_volume_window(); + create_volume_window (); volume_window.current_element = mixer_select_window.first_element; volume_window.show_element_details = false; - volume_window.show(); + volume_window.show (); } else { mixer_select_window.show (); } diff --git a/src/view/MixerElementSelectorWindow.vala b/src/view/MixerElementSelectorWindow.vala index 065151d..4452b45 100644 --- a/src/view/MixerElementSelectorWindow.vala +++ b/src/view/MixerElementSelectorWindow.vala @@ -37,12 +37,12 @@ namespace BrickManager { content_vbox.add (element_menu); } - protected string get_element_label_text(IMixerElementViewModel element) { + protected string get_element_label_text (IMixerElementViewModel element) { string mute_string = (element.can_mute && element.is_muted) ? ", muted" : ""; - return "[%u] %s (%ld%%%s)".printf(element.index, element.name, element.volume, mute_string); + return "[%u] %s (%ld%%%s)".printf (element.index, element.name, element.volume, mute_string); } - protected void sort_element_menu() { + protected void sort_element_menu () { // TODO: we would get much better performance if we just inserted // the item in the correct place instead of sorting the entire list // each time an item is inserted. @@ -51,23 +51,23 @@ namespace BrickManager { IMixerElementViewModel element_b = b.represented_object as IMixerElementViewModel; // Group by name, and sort by index within the same name - if(element_a.name == element_b.name) + if (element_a.name == element_b.name) return (int)element_a.index - (int)element_b.index; else - return element_a.name.ascii_casecmp(element_b.name); + return element_a.name.ascii_casecmp (element_b.name); }); } public void add_element (IMixerElementViewModel element) { - var menu_item = new Ui.MenuItem (get_element_label_text(element)) { + var menu_item = new Ui.MenuItem (get_element_label_text (element)) { represented_object = (Object)element }; weak IMixerElementViewModel weak_element = element; // Update the menu item whenever the represented element changes - element.notify.connect((sender, property) => { - menu_item.label.text = get_element_label_text(weak_element); - sort_element_menu(); + element.notify.connect ((sender, property) => { + menu_item.label.text = get_element_label_text (weak_element); + sort_element_menu (); }); // Emit a selection signal for this element when its menu item is selected @@ -88,28 +88,28 @@ namespace BrickManager { return target_element == (menu_item.represented_object as IMixerElementViewModel); }); - remove_menu_item(menu_item); + remove_menu_item (menu_item); } public void clear_elements () { var iter = element_menu.menu_item_iter (); while (iter.size > 0) { - remove_menu_item(iter[0]); + remove_menu_item (iter[0]); } } public bool has_single_element { get { - return element_menu.menu_item_iter().size == 1; + return element_menu.menu_item_iter ().size == 1; } } public IMixerElementViewModel? first_element { get { - if(element_menu.menu_item_iter().size <= 0) + if (element_menu.menu_item_iter ().size <= 0) return null; - return element_menu.menu_item_iter().get(0).represented_object as IMixerElementViewModel; + return element_menu.menu_item_iter ().get (0).represented_object as IMixerElementViewModel; } } } diff --git a/src/view/MixerElementVolumeWindow.vala b/src/view/MixerElementVolumeWindow.vala index 3272399..02421f3 100644 --- a/src/view/MixerElementVolumeWindow.vala +++ b/src/view/MixerElementVolumeWindow.vala @@ -38,35 +38,35 @@ namespace BrickManager { public MixerElementVolumeWindow () { title = "Volume control"; - element_label = new Ui.Label(); - content_vbox.add(element_label); + element_label = new Ui.Label (); + content_vbox.add (element_label); - var controls_menu = new Ui.Menu(); + var controls_menu = new Ui.Menu (); - var volume_up_item = new Ui.MenuItem("+ Volume up"); - volume_up_item.button.pressed.connect(() => { - if(_current_element != null) - volume_up(); + var volume_up_item = new Ui.MenuItem ("+ Volume up"); + volume_up_item.button.pressed.connect (() => { + if (_current_element != null) + volume_up (); }); - controls_menu.add_menu_item(volume_up_item); + controls_menu.add_menu_item (volume_up_item); - var volume_down_item = new Ui.MenuItem("- Volume down"); - volume_down_item.button.pressed.connect(() => { - if(_current_element != null) - volume_down(); + var volume_down_item = new Ui.MenuItem ("- Volume down"); + volume_down_item.button.pressed.connect (() => { + if (_current_element != null) + volume_down (); }); - controls_menu.add_menu_item(volume_down_item); + controls_menu.add_menu_item (volume_down_item); - var mute_item = new Ui.MenuItem("Mute"); - mute_item.button.pressed.connect(() => { - if(_current_element != null) - mute(); + var mute_item = new Ui.MenuItem ("Mute"); + mute_item.button.pressed.connect (() => { + if (_current_element != null) + mute (); }); - controls_menu.add_menu_item(mute_item); + controls_menu.add_menu_item (mute_item); content_vbox.add (controls_menu); - update_from_element(); + update_from_element (); } public IMixerElementViewModel current_element { @@ -75,13 +75,13 @@ namespace BrickManager { } set { - if(_current_element != null) { - _current_element.notify.disconnect(update_from_element); + if (_current_element != null) { + _current_element.notify.disconnect (update_from_element); } _current_element = value; - _current_element.notify.connect(update_from_element); - update_from_element(); + _current_element.notify.connect (update_from_element); + update_from_element (); } } @@ -91,17 +91,17 @@ namespace BrickManager { } set { _show_element_details = value; - update_from_element(); + update_from_element (); } } - private void update_from_element() { - if(_current_element == null) { + private void update_from_element () { + if (_current_element == null) { element_label.text = "???"; } else { - string elem_details_string = show_element_details ? " ([%u] %s)".printf(_current_element.index, _current_element.name) : ""; - string volume_string = _current_element.is_muted ? "muted" : "%ld%%".printf(_current_element.volume); + string elem_details_string = show_element_details ? " ([%u] %s)".printf (_current_element.index, _current_element.name) : ""; + string volume_string = _current_element.is_muted ? "muted" : "%ld%%".printf (_current_element.volume); element_label.text = volume_string + elem_details_string; } } diff --git a/test/controller/FakeSoundController.vala b/test/controller/FakeSoundController.vala index c7d73d4..012213b 100644 --- a/test/controller/FakeSoundController.vala +++ b/test/controller/FakeSoundController.vala @@ -36,7 +36,7 @@ namespace BrickManager { public FakeSoundController (Gtk.Builder builder) { // Initialize windows that the controller needs mixer_select_window = new MixerElementSelectorWindow (); - volume_window = new MixerElementVolumeWindow(); + volume_window = new MixerElementVolumeWindow (); // Register for callback so that we can focus on the correct control panel tab // the first time either window is invoked @@ -49,13 +49,13 @@ namespace BrickManager { // Initialize items in brickman for all current elements var mixer_elems_liststore = builder.get_object ("mixer-elements-liststore") as Gtk.ListStore; mixer_elems_liststore.foreach ((model, path, iter) => { - update_fake_element_from_liststore(iter, mixer_elems_liststore); + update_fake_element_from_liststore (iter, mixer_elems_liststore); return false; }); // Propagate changes from liststore to views mixer_elems_liststore.row_changed.connect ((path, iter) => { - update_fake_element_from_liststore(iter, mixer_elems_liststore); + update_fake_element_from_liststore (iter, mixer_elems_liststore); }); // Link liststore and control panel GUI @@ -82,7 +82,7 @@ namespace BrickManager { // Doing this all at once ensures that the row_changed handler is only called once, // and never called with partial data. - mixer_elems_liststore.set_valuesv(iter, new int[] { + mixer_elems_liststore.set_valuesv (iter, new int[] { ControlPanel.SoundMixerElementsColumn.NAME, ControlPanel.SoundMixerElementsColumn.INDEX, ControlPanel.SoundMixerElementsColumn.VOLUME, @@ -122,38 +122,38 @@ namespace BrickManager { // Configure the direct window link buttons (builder.get_object ("sound-mixer-select-window-button") as Gtk.Button).clicked.connect (() => - mixer_select_window.show()); + mixer_select_window.show ()); (builder.get_object ("sound-volume-window-button") as Gtk.Button).clicked.connect (() => { - if(mixer_select_window.first_element == null) + if (mixer_select_window.first_element == null) return; volume_window.current_element = mixer_select_window.first_element; volume_window.show_element_details = !mixer_select_window.has_single_element; - volume_window.show(); + volume_window.show (); }); // Wire up handlers for volume window signals - volume_window.volume_up.connect(() => { + volume_window.volume_up.connect (() => { volume_window.current_element.volume += VOLUME_STEP; - update_liststore_for_element(mixer_elems_liststore, volume_window.current_element); + update_liststore_for_element (mixer_elems_liststore, volume_window.current_element); }); - volume_window.volume_down.connect(() => { + volume_window.volume_down.connect (() => { volume_window.current_element.volume -= VOLUME_STEP; - update_liststore_for_element(mixer_elems_liststore, volume_window.current_element); + update_liststore_for_element (mixer_elems_liststore, volume_window.current_element); }); - volume_window.mute.connect(() => { + volume_window.mute.connect (() => { volume_window.current_element.volume = IMixerElementViewModel.MIN_VOLUME; - update_liststore_for_element(mixer_elems_liststore, volume_window.current_element); + update_liststore_for_element (mixer_elems_liststore, volume_window.current_element); }); // Show volume window when mixer element is selected mixer_select_window.mixer_element_selected.connect ((selected_element) => { volume_window.current_element = selected_element; volume_window.show_element_details = true; - volume_window.show(); + volume_window.show (); }); } @@ -162,38 +162,38 @@ namespace BrickManager { * GUI with data from the backing ListStore. Will create the mixer element if one does not * already exist. */ - private void update_fake_element_from_liststore(Gtk.TreeIter iter, Gtk.ListStore mixer_elems_liststore) { - Value name = get_liststore_value(mixer_elems_liststore, iter, ControlPanel.SoundMixerElementsColumn.NAME); - Value index = get_liststore_value(mixer_elems_liststore, iter, ControlPanel.SoundMixerElementsColumn.INDEX); - Value volume = get_liststore_value(mixer_elems_liststore, iter, ControlPanel.SoundMixerElementsColumn.VOLUME); - Value can_mute = get_liststore_value(mixer_elems_liststore, iter, ControlPanel.SoundMixerElementsColumn.CAN_MUTE); - Value user_data = get_liststore_value(mixer_elems_liststore, iter, ControlPanel.SoundMixerElementsColumn.USER_DATA); + private void update_fake_element_from_liststore (Gtk.TreeIter iter, Gtk.ListStore mixer_elems_liststore) { + Value name = get_liststore_value (mixer_elems_liststore, iter, ControlPanel.SoundMixerElementsColumn.NAME); + Value index = get_liststore_value (mixer_elems_liststore, iter, ControlPanel.SoundMixerElementsColumn.INDEX); + Value volume = get_liststore_value (mixer_elems_liststore, iter, ControlPanel.SoundMixerElementsColumn.VOLUME); + Value can_mute = get_liststore_value (mixer_elems_liststore, iter, ControlPanel.SoundMixerElementsColumn.CAN_MUTE); + Value user_data = get_liststore_value (mixer_elems_liststore, iter, ControlPanel.SoundMixerElementsColumn.USER_DATA); // The mixer elements will make sure that these numbers are within proper bounds later - int parsed_index = (int)parse_double_with_default(index.get_string(), 0); - int parsed_volume = (int)parse_double_with_default(volume.get_string(), IMixerElementViewModel.HALF_VOLUME); + int parsed_index = (int)parse_double_with_default (index.get_string (), 0); + int parsed_volume = (int)parse_double_with_default (volume.get_string (), IMixerElementViewModel.HALF_VOLUME); // This is guaranteed to be a fake mixer element; as such, it is referenced by the concrete implementation name FakeMixerElement? mixer_element = (FakeMixerElement?)user_data.get_pointer (); - if(mixer_element == null) { - mixer_element = new FakeMixerElement(name.get_string(), parsed_index, parsed_volume, can_mute.get_boolean()); - mixer_select_window.add_element(mixer_element); + if (mixer_element == null) { + mixer_element = new FakeMixerElement (name.get_string (), parsed_index, parsed_volume, can_mute.get_boolean ()); + mixer_select_window.add_element (mixer_element); - mixer_elems_liststore.set(iter, ControlPanel.SoundMixerElementsColumn.USER_DATA, mixer_element.ref ()); + mixer_elems_liststore.set (iter, ControlPanel.SoundMixerElementsColumn.USER_DATA, mixer_element.ref ()); } else { - mixer_element.freeze_notify(); - mixer_element.set_name(name.get_string()); - mixer_element.set_index(parsed_index); + mixer_element.freeze_notify (); + mixer_element.set_name (name.get_string ()); + mixer_element.set_index (parsed_index); mixer_element.volume = parsed_volume; - mixer_element.set_can_mute(can_mute.get_boolean()); - mixer_element.thaw_notify(); + mixer_element.set_can_mute (can_mute.get_boolean ()); + mixer_element.thaw_notify (); // If the original value was invalid and the mixer object modified it, replace the entered text with the valid version // This will invoke any active signal handlers again; while not optimal, running them twice shouldn't be a problem. - if(index.get_string() != mixer_element.index.to_string() || volume.get_string() != mixer_element.volume.to_string()) { - update_liststore_for_element(mixer_elems_liststore, mixer_element, iter); + if (index.get_string () != mixer_element.index.to_string () || volume.get_string () != mixer_element.volume.to_string ()) { + update_liststore_for_element (mixer_elems_liststore, mixer_element, iter); } } } @@ -202,14 +202,14 @@ namespace BrickManager { * Updates the ListStore entry associated with the specified mixer element with data from * the mixer element. */ - private void update_liststore_for_element(Gtk.ListStore mixer_elems_liststore, IMixerElementViewModel element, Gtk.TreeIter? iter = null) { + private void update_liststore_for_element (Gtk.ListStore mixer_elems_liststore, IMixerElementViewModel element, Gtk.TreeIter? iter = null) { // Find the iter pointing to this element if one was not supplied - if(iter == null) { - mixer_elems_liststore.foreach((model, path, current_iter) => { - Value user_data = get_liststore_value(mixer_elems_liststore, current_iter, ControlPanel.SoundMixerElementsColumn.USER_DATA); - FakeMixerElement other_element = (FakeMixerElement)user_data.get_pointer(); + if (iter == null) { + mixer_elems_liststore.foreach ((model, path, current_iter) => { + Value user_data = get_liststore_value (mixer_elems_liststore, current_iter, ControlPanel.SoundMixerElementsColumn.USER_DATA); + FakeMixerElement other_element = (FakeMixerElement)user_data.get_pointer (); - if(other_element == element) { + if (other_element == element) { iter = current_iter; return true; } @@ -227,22 +227,22 @@ namespace BrickManager { ControlPanel.SoundMixerElementsColumn.MUTE }, new Value[] { element.name, - element.index.to_string(), - element.volume.to_string(), + element.index.to_string (), + element.volume.to_string (), element.can_mute, element.is_muted }); } - private Value get_liststore_value(Gtk.ListStore list_store, Gtk.TreeIter iter, int column) { + private Value get_liststore_value (Gtk.ListStore list_store, Gtk.TreeIter iter, int column) { Value ret_value; list_store.get_value (iter, column, out ret_value); return ret_value; } - private double parse_double_with_default(string str, int default_value) { + private double parse_double_with_default (string str, int default_value) { double parsed_result; - if(double.try_parse(str, out parsed_result)) { + if (double.try_parse (str, out parsed_result)) { return parsed_result; } @@ -250,10 +250,10 @@ namespace BrickManager { } public void show_main_window () { - if(mixer_select_window.has_single_element) { + if (mixer_select_window.has_single_element) { volume_window.current_element = mixer_select_window.first_element; volume_window.show_element_details = false; - volume_window.show(); + volume_window.show (); } else mixer_select_window.show (); From a745576f77d00e0588efa06aa33b4bce805d1b60 Mon Sep 17 00:00:00 2001 From: wasabifan Date: Sat, 25 Jun 2016 17:07:10 -0700 Subject: [PATCH 6/7] Only show sound mixer index when nonzero --- src/view/MixerElementSelectorWindow.vala | 3 ++- src/view/MixerElementVolumeWindow.vala | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/view/MixerElementSelectorWindow.vala b/src/view/MixerElementSelectorWindow.vala index 4452b45..21d1fba 100644 --- a/src/view/MixerElementSelectorWindow.vala +++ b/src/view/MixerElementSelectorWindow.vala @@ -39,7 +39,8 @@ namespace BrickManager { protected string get_element_label_text (IMixerElementViewModel element) { string mute_string = (element.can_mute && element.is_muted) ? ", muted" : ""; - return "[%u] %s (%ld%%%s)".printf (element.index, element.name, element.volume, mute_string); + string index_string = element.index == 0 ? "" : " [%u]".printf(element.index); + return "%s%s (%ld%%%s)".printf (element.name, index_string, element.volume, mute_string); } protected void sort_element_menu () { diff --git a/src/view/MixerElementVolumeWindow.vala b/src/view/MixerElementVolumeWindow.vala index 02421f3..d658e41 100644 --- a/src/view/MixerElementVolumeWindow.vala +++ b/src/view/MixerElementVolumeWindow.vala @@ -100,7 +100,8 @@ namespace BrickManager { element_label.text = "???"; } else { - string elem_details_string = show_element_details ? " ([%u] %s)".printf (_current_element.index, _current_element.name) : ""; + string index_string = _current_element.index == 0 ? "" : " [%u]".printf(_current_element.index); + string elem_details_string = show_element_details ? " (%s%s)".printf (_current_element.name, index_string) : ""; string volume_string = _current_element.is_muted ? "muted" : "%ld%%".printf (_current_element.volume); element_label.text = volume_string + elem_details_string; } From 982690e12a9116aac7a1e449196feab92ac496d0 Mon Sep 17 00:00:00 2001 From: wasabifan Date: Wed, 6 Jul 2016 18:38:42 -0700 Subject: [PATCH 7/7] More clean-up of audio code --- CMakeLists.txt | 5 +- src/controller/SoundController.vala | 7 +-- src/view/MixerElementSelectorWindow.vala | 37 ++++++++++----- src/view/MixerElementVolumeWindow.vala | 34 ++++++++++---- .../AlsaBackedMixerElement.vala | 3 +- src/view_model/IMixerElementViewModel.vala | 38 +++++++++++++++ test/controller/FakeSoundController.vala | 46 ++++++++++--------- .../view_model/FakeMixerElement.vala | 25 +++------- 8 files changed, 128 insertions(+), 67 deletions(-) rename src/{ => view_model}/AlsaBackedMixerElement.vala (98%) create mode 100644 src/view_model/IMixerElementViewModel.vala rename src/AlsaInterface.vala => test/view_model/FakeMixerElement.vala (78%) diff --git a/CMakeLists.txt b/CMakeLists.txt index b4b07af..3386539 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,6 +41,7 @@ if (BRICKMAN_TEST) test/controller/FakeNetworkController.vala test/controller/FakeOpenRobertaController.vala test/controller/FakeSoundController.vala + test/view_model/FakeMixerElement.vala ) else (BRICKMAN_TEST) set (BRICKMAN_SOURCE_FILES @@ -60,7 +61,7 @@ else (BRICKMAN_TEST) src/controller/NetworkController.vala src/controller/OpenRobertaController.vala src/controller/SoundController.vala - src/AlsaBackedMixerElement.vala + src/view_model/AlsaBackedMixerElement.vala src/GlobalManager.vala src/main.vala ) @@ -74,7 +75,6 @@ set (BRICKMAN_COMMON_SOURCE_FILES lib/systemd/Systemd.vala lib/systemd/logind-interfaces.vala lib/systemd/systemd-interfaces.vala - src/AlsaInterface.vala src/controller/IBrickManagerModule.vala src/dbus/Bluez5Agent.vala src/dbus/ConnmanAgent.vala @@ -120,6 +120,7 @@ set (BRICKMAN_COMMON_SOURCE_FILES src/view/WifiNetworkWindow.vala src/view/WifiStatusBarItem.vala src/view/WifiWindow.vala + src/view_model/IMixerElementViewModel.vala ) find_package(PkgConfig REQUIRED) diff --git a/src/controller/SoundController.vala b/src/controller/SoundController.vala index ded9011..5d32b68 100644 --- a/src/controller/SoundController.vala +++ b/src/controller/SoundController.vala @@ -66,9 +66,10 @@ namespace BrickManager { void create_main_window () { mixer_select_window = new MixerElementSelectorWindow (); - mixer_select_window.mixer_elem_selected.connect ((selected_element) => { - if (volume_window == null) + mixer_select_window.mixer_element_selected.connect ((selected_element) => { + if (volume_window == null) { create_volume_window (); + } volume_window.current_element = selected_element; volume_window.show_element_details = true; @@ -87,7 +88,7 @@ namespace BrickManager { volume_window.volume_down.connect (() => weak_volume_window.current_element.volume -= VOLUME_STEP); - volume_window.volume_min.connect (() => + volume_window.mute.connect (() => weak_volume_window.current_element.volume = IMixerElementViewModel.MIN_VOLUME); } diff --git a/src/view/MixerElementSelectorWindow.vala b/src/view/MixerElementSelectorWindow.vala index 21d1fba..83a37dd 100644 --- a/src/view/MixerElementSelectorWindow.vala +++ b/src/view/MixerElementSelectorWindow.vala @@ -32,15 +32,26 @@ namespace BrickManager { public signal void mixer_element_selected (IMixerElementViewModel selected_element); public MixerElementSelectorWindow () { - title = "Sound mixer elements"; + title = "Volume Controls"; element_menu = new Ui.Menu (); content_vbox.add (element_menu); } protected string get_element_label_text (IMixerElementViewModel element) { - string mute_string = (element.can_mute && element.is_muted) ? ", muted" : ""; - string index_string = element.index == 0 ? "" : " [%u]".printf(element.index); - return "%s%s (%ld%%%s)".printf (element.name, index_string, element.volume, mute_string); + var builder = new StringBuilder (); + builder.append (element.name); + + if (element.index != 0) { + builder.append_printf ("[%u]", element.index); + } + + if (element.can_mute && element.is_muted) { + builder.append (" (muted)"); + } else { + builder.append_printf (" (%ld%%)", element.volume); + } + + return builder.str; } protected void sort_element_menu () { @@ -48,14 +59,15 @@ namespace BrickManager { // the item in the correct place instead of sorting the entire list // each time an item is inserted. element_menu.sort_menu_items ((a, b) => { - IMixerElementViewModel element_a = a.represented_object as IMixerElementViewModel; - IMixerElementViewModel element_b = b.represented_object as IMixerElementViewModel; + var element_a = a.represented_object as IMixerElementViewModel; + var element_b = b.represented_object as IMixerElementViewModel; // Group by name, and sort by index within the same name - if (element_a.name == element_b.name) + if (element_a.name == element_b.name) { return (int)element_a.index - (int)element_b.index; - else + } else { return element_a.name.ascii_casecmp (element_b.name); + } }); } @@ -66,7 +78,7 @@ namespace BrickManager { weak IMixerElementViewModel weak_element = element; // Update the menu item whenever the represented element changes - element.notify.connect ((sender, property) => { + weak_element.notify.connect ((sender, property) => { menu_item.label.text = get_element_label_text (weak_element); sort_element_menu (); }); @@ -85,11 +97,11 @@ namespace BrickManager { } public void remove_element (IMixerElementViewModel element) { - var menu_item = element_menu.find_menu_item (element, (menu_item, target_element) => { + var item = element_menu.find_menu_item (element, (menu_item, target_element) => { return target_element == (menu_item.represented_object as IMixerElementViewModel); }); - remove_menu_item (menu_item); + remove_menu_item (item); } public void clear_elements () { @@ -107,8 +119,9 @@ namespace BrickManager { public IMixerElementViewModel? first_element { get { - if (element_menu.menu_item_iter ().size <= 0) + if (element_menu.menu_item_iter ().size <= 0) { return null; + } return element_menu.menu_item_iter ().get (0).represented_object as IMixerElementViewModel; } diff --git a/src/view/MixerElementVolumeWindow.vala b/src/view/MixerElementVolumeWindow.vala index d658e41..934cf92 100644 --- a/src/view/MixerElementVolumeWindow.vala +++ b/src/view/MixerElementVolumeWindow.vala @@ -45,22 +45,25 @@ namespace BrickManager { var volume_up_item = new Ui.MenuItem ("+ Volume up"); volume_up_item.button.pressed.connect (() => { - if (_current_element != null) + if (current_element != null) { volume_up (); + } }); controls_menu.add_menu_item (volume_up_item); var volume_down_item = new Ui.MenuItem ("- Volume down"); volume_down_item.button.pressed.connect (() => { - if (_current_element != null) + if (current_element != null) { volume_down (); + } }); controls_menu.add_menu_item (volume_down_item); var mute_item = new Ui.MenuItem ("Mute"); mute_item.button.pressed.connect (() => { - if (_current_element != null) + if (current_element != null) { mute (); + } }); controls_menu.add_menu_item (mute_item); @@ -98,12 +101,25 @@ namespace BrickManager { private void update_from_element () { if (_current_element == null) { element_label.text = "???"; - } - else { - string index_string = _current_element.index == 0 ? "" : " [%u]".printf(_current_element.index); - string elem_details_string = show_element_details ? " (%s%s)".printf (_current_element.name, index_string) : ""; - string volume_string = _current_element.is_muted ? "muted" : "%ld%%".printf (_current_element.volume); - element_label.text = volume_string + elem_details_string; + } else { + var builder = new StringBuilder(); + if(_current_element.can_mute && _current_element.is_muted) { + builder.append("muted"); + } else { + builder.append_printf("%ld%%", _current_element.volume); + } + + if(show_element_details) { + builder.append_printf(" (%s", _current_element.name); + + if(_current_element.index != 0) { + builder.append_printf(" [%u]", _current_element.index); + } + + builder.append(")"); + } + + element_label.text = builder.str; } } } diff --git a/src/AlsaBackedMixerElement.vala b/src/view_model/AlsaBackedMixerElement.vala similarity index 98% rename from src/AlsaBackedMixerElement.vala rename to src/view_model/AlsaBackedMixerElement.vala index 12ab468..a54fa31 100644 --- a/src/AlsaBackedMixerElement.vala +++ b/src/view_model/AlsaBackedMixerElement.vala @@ -102,8 +102,9 @@ namespace BrickManager { } protected void set_is_muted (bool is_muted) { - if (can_mute) + if (can_mute) { alsa_element.set_playback_switch_all (is_muted ? 0 : 1); + } } } } diff --git a/src/view_model/IMixerElementViewModel.vala b/src/view_model/IMixerElementViewModel.vala new file mode 100644 index 0000000..abe7539 --- /dev/null +++ b/src/view_model/IMixerElementViewModel.vala @@ -0,0 +1,38 @@ +/* + * brickman -- Brick Manager for LEGO MINDSTORMS EV3/ev3dev + * + * Copyright (C) 2016 Kaelin Laundry + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +/* IMixerElementViewModel.vala - Interface for object controlling a mixer element */ + +using Alsa; + +namespace BrickManager { + public interface IMixerElementViewModel : Object { + public const int MIN_VOLUME = 0; + public const int MAX_VOLUME = 100; + public const int HALF_VOLUME = (MAX_VOLUME + MIN_VOLUME) / 2; + + public abstract string name { get; } + public abstract uint index { get; } + public abstract int volume { get; set; } + public abstract bool can_mute { get; } + public abstract bool is_muted { get; } + } +} diff --git a/test/controller/FakeSoundController.vala b/test/controller/FakeSoundController.vala index 012213b..c9797cd 100644 --- a/test/controller/FakeSoundController.vala +++ b/test/controller/FakeSoundController.vala @@ -105,8 +105,9 @@ namespace BrickManager { model.get_value (iter, ControlPanel.SoundMixerElementsColumn.USER_DATA, out user_data); var mixer_element = (FakeMixerElement)user_data.get_pointer (); - if (mixer_element != null) + if (mixer_element != null) { mixer_select_window.remove_element (mixer_element); + } mixer_elems_liststore.remove (iter); } @@ -125,8 +126,9 @@ namespace BrickManager { mixer_select_window.show ()); (builder.get_object ("sound-volume-window-button") as Gtk.Button).clicked.connect (() => { - if (mixer_select_window.first_element == null) + if (mixer_select_window.first_element == null) { return; + } volume_window.current_element = mixer_select_window.first_element; volume_window.show_element_details = !mixer_select_window.has_single_element; @@ -134,19 +136,20 @@ namespace BrickManager { }); // Wire up handlers for volume window signals + weak MixerElementVolumeWindow weak_volume_window = volume_window; volume_window.volume_up.connect (() => { - volume_window.current_element.volume += VOLUME_STEP; - update_liststore_for_element (mixer_elems_liststore, volume_window.current_element); + weak_volume_window.current_element.volume += VOLUME_STEP; + update_liststore_for_element (mixer_elems_liststore, weak_volume_window.current_element); }); volume_window.volume_down.connect (() => { - volume_window.current_element.volume -= VOLUME_STEP; - update_liststore_for_element (mixer_elems_liststore, volume_window.current_element); + weak_volume_window.current_element.volume -= VOLUME_STEP; + update_liststore_for_element (mixer_elems_liststore, weak_volume_window.current_element); }); volume_window.mute.connect (() => { - volume_window.current_element.volume = IMixerElementViewModel.MIN_VOLUME; - update_liststore_for_element (mixer_elems_liststore, volume_window.current_element); + weak_volume_window.current_element.volume = IMixerElementViewModel.MIN_VOLUME; + update_liststore_for_element (mixer_elems_liststore, weak_volume_window.current_element); }); // Show volume window when mixer element is selected @@ -163,26 +166,25 @@ namespace BrickManager { * already exist. */ private void update_fake_element_from_liststore (Gtk.TreeIter iter, Gtk.ListStore mixer_elems_liststore) { - Value name = get_liststore_value (mixer_elems_liststore, iter, ControlPanel.SoundMixerElementsColumn.NAME); - Value index = get_liststore_value (mixer_elems_liststore, iter, ControlPanel.SoundMixerElementsColumn.INDEX); - Value volume = get_liststore_value (mixer_elems_liststore, iter, ControlPanel.SoundMixerElementsColumn.VOLUME); - Value can_mute = get_liststore_value (mixer_elems_liststore, iter, ControlPanel.SoundMixerElementsColumn.CAN_MUTE); - Value user_data = get_liststore_value (mixer_elems_liststore, iter, ControlPanel.SoundMixerElementsColumn.USER_DATA); + var name = get_liststore_value (mixer_elems_liststore, iter, ControlPanel.SoundMixerElementsColumn.NAME); + var index = get_liststore_value (mixer_elems_liststore, iter, ControlPanel.SoundMixerElementsColumn.INDEX); + var volume = get_liststore_value (mixer_elems_liststore, iter, ControlPanel.SoundMixerElementsColumn.VOLUME); + var can_mute = get_liststore_value (mixer_elems_liststore, iter, ControlPanel.SoundMixerElementsColumn.CAN_MUTE); + var user_data = get_liststore_value (mixer_elems_liststore, iter, ControlPanel.SoundMixerElementsColumn.USER_DATA); // The mixer elements will make sure that these numbers are within proper bounds later - int parsed_index = (int)parse_double_with_default (index.get_string (), 0); - int parsed_volume = (int)parse_double_with_default (volume.get_string (), IMixerElementViewModel.HALF_VOLUME); + var parsed_index = (int)parse_double_with_default (index.get_string (), 0); + var parsed_volume = (int)parse_double_with_default (volume.get_string (), IMixerElementViewModel.HALF_VOLUME); // This is guaranteed to be a fake mixer element; as such, it is referenced by the concrete implementation name - FakeMixerElement? mixer_element = (FakeMixerElement?)user_data.get_pointer (); + var mixer_element = (FakeMixerElement?)user_data.get_pointer (); if (mixer_element == null) { mixer_element = new FakeMixerElement (name.get_string (), parsed_index, parsed_volume, can_mute.get_boolean ()); mixer_select_window.add_element (mixer_element); mixer_elems_liststore.set (iter, ControlPanel.SoundMixerElementsColumn.USER_DATA, mixer_element.ref ()); - } - else { + } else { mixer_element.freeze_notify (); mixer_element.set_name (name.get_string ()); mixer_element.set_index (parsed_index); @@ -206,8 +208,8 @@ namespace BrickManager { // Find the iter pointing to this element if one was not supplied if (iter == null) { mixer_elems_liststore.foreach ((model, path, current_iter) => { - Value user_data = get_liststore_value (mixer_elems_liststore, current_iter, ControlPanel.SoundMixerElementsColumn.USER_DATA); - FakeMixerElement other_element = (FakeMixerElement)user_data.get_pointer (); + var user_data = get_liststore_value (mixer_elems_liststore, current_iter, ControlPanel.SoundMixerElementsColumn.USER_DATA); + var other_element = (FakeMixerElement)user_data.get_pointer (); if (other_element == element) { iter = current_iter; @@ -254,9 +256,9 @@ namespace BrickManager { volume_window.current_element = mixer_select_window.first_element; volume_window.show_element_details = false; volume_window.show (); - } - else + } else { mixer_select_window.show (); + } } } } diff --git a/src/AlsaInterface.vala b/test/view_model/FakeMixerElement.vala similarity index 78% rename from src/AlsaInterface.vala rename to test/view_model/FakeMixerElement.vala index 77722f3..3174bae 100644 --- a/src/AlsaInterface.vala +++ b/test/view_model/FakeMixerElement.vala @@ -19,23 +19,9 @@ * MA 02110-1301, USA. */ -/* AlsaInterface.vala - Definitions for interfacing with ALSA */ - -using Alsa; +/* FakeMixerElement.vala - Fake implementation of mixer element */ namespace BrickManager { - public interface IMixerElementViewModel : Object { - public const int MIN_VOLUME = 0; - public const int MAX_VOLUME = 100; - public const int HALF_VOLUME = (MAX_VOLUME + MIN_VOLUME) / 2; - - public abstract string name { get; } - public abstract uint index { get; } - public abstract int volume { get; set; } - public abstract bool can_mute { get; } - public abstract bool is_muted { get; } - } - public class FakeMixerElement: IMixerElementViewModel, Object { private string _name; private uint _index; @@ -70,9 +56,12 @@ namespace BrickManager { set { _volume = int.min (100, int.max (0, value)); - bool should_mute = _volume <= 0; - if (_is_muted != should_mute) - set_is_muted (should_mute); + if(_can_mute) { + bool should_mute = _volume <= 0; + if (_is_muted != should_mute) { + set_is_muted (should_mute); + } + } } }