diff --git a/README.md b/README.md
index a689aff..5396b85 100644
--- a/README.md
+++ b/README.md
@@ -3,25 +3,26 @@ A Sound Visualizer for Gnome Shell based on Gstreamer specially for Wayland

-For Desktop Widgets I'm Using [Circular Widgets](https://extensions.gnome.org/extension/5530/circular-widgets/) Extension.
+For the Desktop Widgets, I'm using the [Circular Widgets](https://extensions.gnome.org/extension/5530/circular-widgets/) extension.
# Features
-- Drag and Drop Supports
-- Change Audio source from Menu (To change right/left click on Visualizer)
-- Change Visualizer size
+- Drag and Drop Support
+- Change Audio Source from the Menu (To change right/left click on Visualizer)
+- Change Visualizer Size
- Increase or Decrease Bands
-- Choose how many bands will appear on display
-- Now you can Flip Visualizer
-- Added older version Gnome Shell v3.36 to v43.0
+- Flip Visualizer Horizontally or Vertically
+- Ability to Change the FPS of the Visualizer
+- Ability to choose how many bands will appear on display
+- Added older version Gnome Shell support, from v3.36 to v43.0
-More Feature will be added in Future
+More features will be added in the future
# Installation
-1. Download zip file : https://github.com/raihan2000/visualizer/archive/refs/heads/main.zip
+1. Download the zip file : https://github.com/raihan2000/visualizer/archive/refs/heads/main.zip
2. Extract to visualizer-main
-4. make install
+3. `make install`
or
@@ -33,4 +34,4 @@ make install
# Credits
-This Extension is inspired by [Glava](https://github.com/jarcode-foss/glava)
+This extension is inspired by [Glava](https://github.com/jarcode-foss/glava).
diff --git a/src/metadata.json b/src/metadata.json
index 7991f79..2325d8f 100644
--- a/src/metadata.json
+++ b/src/metadata.json
@@ -1,6 +1,6 @@
{
"_generated": "Generated by SweetTooth, do not edit",
- "description": "A Real Time Sound Visualizer Based On Gstreamer",
+ "description": "A Real Time Sound Visualizer Based On Gstreamer\nFor any Issues,Bugs and Suggestions please open an issue on Github",
"name": "Sound Visualizer",
"settings-schema": "org.gnome.shell.extensions.visualizer",
"shell-version": [
@@ -13,5 +13,5 @@
],
"url": "https://github.com/raihan2000/visualizer",
"uuid": "visualizer@sound.org",
- "version": 4
-}
+ "version": 5
+}
\ No newline at end of file
diff --git a/src/prefs.js b/src/prefs.js
index c9e5226..f9196a2 100644
--- a/src/prefs.js
+++ b/src/prefs.js
@@ -1,13 +1,23 @@
'use strict';
const { Gio, Gtk, Gdk, GLib, GObject } = imports.gi;
-const Params = imports.misc.params;
const ExtensionUtils = imports.misc.extensionUtils;
-const Me = ExtensionUtils.getCurrentExtension();
+const Params = imports.misc.params;
const Config = imports.misc.config;
-const [major, minor] = Config.PACKAGE_VERSION.split('.').map(s => Number(s));
+const Me = ExtensionUtils.getCurrentExtension();
+
+const [MajorVersion, MinorVersion] = Config.PACKAGE_VERSION.split('.').map(s => Number(s));
let Adw;
+const DEFAULT_SPIN_MIN = 1; // Minimum pixel size
+const DEFAULT_SPIN_MAX = 200; // Maximum pixel size
+const VISUALIZER_WIDTH_MAX = 1920; // Maximum pixel size for width
+const SPECTS_LINE_WIDTH_MAX = 20; // Maximum pixel size for spect line widths
+const TOTAL_SPECTS_BAND_MAX = 256; // Maximum # of spect bands possible to be chosen
+const FPS_OPTIONS = ["15", "30", "60", "90", "120"]; // Frame counts; going from 15 to 120 fps
+const GRID_COLUMN_SPACING = 200; // Spacing between columns
+const GRID_ROW_SPACING = 25; // Spacing between rows
+
function init() {
}
@@ -19,44 +29,68 @@ function fillPreferencesWindow(window) {
function buildPrefsWidget() {
let widget = new prefsWidget();
- (major < 40) ? widget.show_all(): widget.show();
+ (MajorVersion < 40) ? widget.show_all(): widget.show();
return widget;
}
+function attachItems(grid, label, widget, row) {
+ grid.set_column_spacing(GRID_COLUMN_SPACING);
+ grid.set_row_spacing(GRID_ROW_SPACING);
+ grid.attach(label, 0, row, 1, 1);
+ grid.attach(widget, 1, row, 1, 1);
+}
+
+
const prefsWidget = GObject.registerClass(
- class prefsWidget extends Gtk.Notebook {
-
- _init(params) {
- super._init(params);
- this._settings = ExtensionUtils.getSettings('org.gnome.shell.extensions.visualizer');
- this.margin = 20;
-
- let grid = new Gtk.Grid();
- attachItems(grid, new Gtk.Label({ label: 'Flip the Visualizer' }), getSwitch('flip-visualizer', this._settings), 0);
- attachItems(grid, new Gtk.Label({ label: 'Visualizer Height' }), getSpinButton(false, 'visualizer-height', 1, 200, 1, this._settings), 1);
- attachItems(grid, new Gtk.Label({ label: 'Visualizer Width' }), getSpinButton(false, 'visualizer-width', 1, 1920, 1, this._settings), 2);
- attachItems(grid, new Gtk.Label({ label: 'Spects Line Width' }), getSpinButton(false, 'spects-line-width', 1, 20, 1, this._settings), 3);
- attachItems(grid, new Gtk.Label({ label: 'Change Spects Band to Get' }), getSpinButton(false, 'total-spects-band', 1, 256, 1, this._settings), 4);
- this.attachHybridRow(grid, new Gtk.Label({ label: 'Override Spect Value' }), new Gtk.Label({ label: 'Set Spects Value' }), getSwitch('spect-over-ride-bool', this._settings), getSpinButton(false, 'spect-over-ride', 1, 256, 1, this._settings), 5);
- this.append_page(grid, new Gtk.Label({ label: 'Visualizer' }));
- let aboutBox = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL });
- if (major < 40) {
- aboutBox.add(new Gtk.Label({ label: Me.metadata.name }));
- aboutBox.add(new Gtk.Label({ label: 'Version: ' + Me.metadata.version.toString() }));
- } else {
- aboutBox.append(new Gtk.Label({ label: Me.metadata.name }));
- aboutBox.append(new Gtk.Label({ label: 'Version: ' + Me.metadata.version.toString() }));
+ class prefsWidget extends Gtk.Notebook {
+
+ _init(params) {
+ super._init(params);
+ let grid = new Gtk.Grid();
+ this._settings = ExtensionUtils.getSettings('org.gnome.shell.extensions.visualizer');
+ this.setupFpsOptions();
+ this.margin = 20;
+
+ attachItems(grid, new Gtk.Label({ label: 'Flip the Visualizer Vertically' }), getSwitch('flip-visualizer', this._settings), 0);
+ attachItems(grid, new Gtk.Label({ label: 'Flip the Visualizer Horizontally' }), getSwitch('horizontal-flip', this._settings), 1);
+ attachItems(grid, new Gtk.Label({ label: 'Visualizer Height' }), getSpinButton(false, 'visualizer-height', DEFAULT_SPIN_MIN, DEFAULT_SPIN_MAX, 1, this._settings), 2);
+ attachItems(grid, new Gtk.Label({ label: 'Visualizer Width' }), getSpinButton(false, 'visualizer-width', DEFAULT_SPIN_MIN, VISUALIZER_WIDTH_MAX, 1, this._settings), 3);
+ attachItems(grid, new Gtk.Label({ label: 'Spects Line Width' }), getSpinButton(false, 'spects-line-width', DEFAULT_SPIN_MIN, SPECTS_LINE_WIDTH_MAX, 1, this._settings), 5);
+ attachItems(grid, new Gtk.Label({ label: 'Change Spects Band to Get' }), getSpinButton(false, 'total-spects-band', DEFAULT_SPIN_MIN, TOTAL_SPECTS_BAND_MAX, 1, this._settings), 4);
+ attachItems(grid, new Gtk.Label({ label: 'Frames Per Second (FPS)' }), getDropDown(this._settings), 7);
+ attachItems(grid, new Gtk.Label({ label: 'Visualizer Color' }), getColorButton(this._settings), 8);
+ this.attachHybridRow(grid, new Gtk.Label({ label: 'Override Spect Value' }), new Gtk.Label({ label: 'Set Spects Value' }), getSwitch('spect-over-ride-bool', this._settings), getSpinButton(false, 'spect-over-ride', 1, 256, 1, this._settings), 6);
+ this.append_page(grid, new Gtk.Label({ label: 'Visualizer' }));
+
+ let aboutBox = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL });
+ if (MajorVersion < 40) {
+ aboutBox.add(new Gtk.Label({ label: Me.metadata.name }));
+ aboutBox.add(new Gtk.Label({ label: 'Version: ' + Me.metadata.version.toString() }));
+ } else {
+ aboutBox.append(new Gtk.Label({ label: Me.metadata.name }));
+ aboutBox.append(new Gtk.Label({ label: 'Version: ' + Me.metadata.version.toString() }));
+ }
+ this.append_page(aboutBox, new Gtk.Label({ label: 'About' }));
}
- this.append_page(aboutBox, new Gtk.Label({ label: 'About' }));
- }
- attachHybridRow(grid, label, label1, button, button1, row) {
- grid.attach(label, 0, row, 1, 1);
- grid.attach(button, 1, row, 1, 1);
- grid.attach(label1, 0, row + 1, 1, 1);
- grid.attach(button1, 1, row + 1, 1, 1);
- }
- });
+ setupFpsOptions() {
+ let fpsOptions = new Gtk.ComboBoxText();
+ FPS_OPTIONS.forEach(fps => fpsOptions.append_text(fps));
+ fpsOptions.connect('changed', (widget) => {
+ let fps = widget.get_active_text();
+ this._settings.set_int('fps', parseInt(fps, 10));
+ });
+ let currentFps = this._settings.get_int('fps');
+ fpsOptions.set_active_id(currentFps.toString());
+ }
+
+ attachHybridRow(grid, label, label1, button, button1, row) {
+ grid.attach(label, 0, row, 1, 1);
+ grid.attach(button, 1, row, 1, 1);
+ grid.attach(label1, 0, row + 1, 1, 1);
+ grid.attach(button1, 1, row + 1, 1, 1);
+ }
+ });
class PrefsWindow {
constructor(window) {
@@ -65,10 +99,12 @@ class PrefsWindow {
}
create_page(title) {
- let page = new Adw.PreferencesPage({
- title: title,
- //icon_name: icon,
- });
+ let page = new Adw.PreferencesPage(
+ {
+ title: title,
+ //icon_name: icon,
+ }
+ );
this._window.add(page);
// get the headerbar
@@ -99,9 +135,7 @@ class PrefsWindow {
}
append_row(group, title, widget) {
- let row = new Adw.ActionRow({
- title: title,
- });
+ let row = new Adw.ActionRow({ title: title });
group.add(row);
row.add_suffix(widget);
row.activatable_widget = widget;
@@ -114,12 +148,10 @@ class PrefsWindow {
expanded: this._settings.get_boolean(key),
enable_expansion: this._settings.get_boolean(key)
});
- let row = new Adw.ActionRow({
- title: title,
- });
+
+ let row = new Adw.ActionRow({ title: title });
expand_row.connect("notify::enable-expansion", (widget) => {
- let settingArray = this._settings.get_boolean(key);
- settingArray = widget.enable_expansion;
+ let settingArray = widget.enable_expansion;
this._settings.set_value(key, new GLib.Variant('b', settingArray));
});
row.add_suffix(key1);
@@ -129,20 +161,9 @@ class PrefsWindow {
append_info_group(group, name, title) {
let adw_group = new Adw.PreferencesGroup();
- let infoBox = new Gtk.Box({
- orientation: Gtk.Orientation.VERTICAL,
- hexpand: false,
- vexpand: false
- });
-
- let name_label = new Gtk.Label({
- label: name,
- });
-
- let version = new Gtk.Label({
- label: 'Version: ' + title,
- });
-
+ let infoBox = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL, hexpand: false, vexpand: false});
+ let name_label = new Gtk.Label({ label: name });
+ let version = new Gtk.Label({ label: 'Version: ' + title });
infoBox.append(name_label);
infoBox.append(version);
adw_group.add(infoBox);
@@ -152,12 +173,15 @@ class PrefsWindow {
fillPrefsWindow() {
let visualWidget = this.create_page('Visualizer'); {
let groupVisual = this.create_group(visualWidget);
- this.append_row(groupVisual, 'Flip the Visualizer', getSwitch('flip-visualizer', this._settings));
- this.append_row(groupVisual, 'Visualizer Height', getSpinButton(false, 'visualizer-height', 1, 200, 1, this._settings));
- this.append_row(groupVisual, 'Visualizer Width', getSpinButton(false, 'visualizer-width', 1, 1920, 1, this._settings));
- this.append_row(groupVisual, 'Spects Line Width', getSpinButton(false, 'spects-line-width', 1, 20, 1, this._settings));
- this.append_row(groupVisual, 'Change Spects Band to Get', getSpinButton(false, 'total-spects-band', 1, 256, 1, this._settings));
- this.append_expander_row(groupVisual, 'Override Spect Value', 'Set Spects Value', 'spect-over-ride-bool', getSpinButton(false, 'spect-over-ride', 1, 256, 1, this._settings));
+ this.append_row(groupVisual, 'Flip the Visualizer Vertically', getSwitch('flip-visualizer', this._settings));
+ this.append_row(groupVisual, 'Flip the Visualizer Horizontally', getSwitch('horizontal-flip', this._settings));
+ this.append_row(groupVisual, 'Visualizer Height (px)', getSpinButton(false, 'visualizer-height', 1, 200, 1, this._settings));
+ this.append_row(groupVisual, 'Visualizer Width (px)', getSpinButton(false, 'visualizer-width', 1, 1920, 1, this._settings));
+ this.append_row(groupVisual, 'Spects Line Width (px)', getSpinButton(false, 'spects-line-width', 1, 20, 1, this._settings));
+ this.append_row(groupVisual, 'Change # of Spect Bands to Get', getSpinButton(false, 'total-spects-band', 1, 256, 1, this._settings));
+ this.append_row(groupVisual, 'Frames Per Second (FPS)', getDropDown(this._settings));
+ this.append_row(groupVisual, 'Visualizer Color', getColorButton(this._settings));
+ this.append_expander_row(groupVisual, 'Override Spect Value', 'Set Spect Value', 'spect-over-ride-bool', getSpinButton(false, 'spect-over-ride', 1, 256, 1, this._settings));
}
let aboutPage = this.create_page('About'); {
@@ -167,13 +191,6 @@ class PrefsWindow {
}
}
-function attachItems(grid, label, widget, row) {
- grid.set_column_spacing(200);
- grid.set_row_spacing(25);
- grid.attach(label, 0, row, 1, 1);
- grid.attach(widget, 1, row, 1, 1);
-}
-
function getSwitch(key, settings) {
let button = new Gtk.Switch({ active: key, valign: Gtk.Align.CENTER });
settings.bind(key, button, 'active', Gio.SettingsBindFlags.DEFAULT);
@@ -181,10 +198,41 @@ function getSwitch(key, settings) {
}
function getSpinButton(is_double, key, min, max, step, settings) {
- let v = 0;
- (is_double) ? v = settings.get_double(key) : v = settings.get_int(key);
+ let value = is_double ? settings.get_double(key) : settings.get_int(key);
let spin = Gtk.SpinButton.new_with_range(min, max, step);
- spin.set_value(v);
+ spin.set_value(value);
settings.bind(key, spin, 'value', Gio.SettingsBindFlags.DEFAULT);
return spin;
}
+
+function getDropDown(settings) {
+ let dropDown = new Gtk.ComboBoxText();
+ FPS_OPTIONS.forEach(fps => dropDown.append_text(fps));
+
+ dropDown.connect('changed', (widget) => {
+ let fps = widget.get_active_text();
+ settings.set_int('fps', parseInt(fps, 10));
+ });
+
+ let currentFps = settings.get_int('fps').toString();
+ let currentIndex = FPS_OPTIONS.indexOf(currentFps);
+ if (currentIndex !== -1) {
+ dropDown.set_active(currentIndex);
+ }
+ return dropDown;
+}
+
+function getColorButton(settings) {
+ let button = new Gtk.ColorButton();
+ let rgbaString = settings.get_string('visualizer-color');
+ let rgbaParts = rgbaString.split(',').map(parseFloat);
+ let gdkRGBA = new Gdk.RGBA({red: rgbaParts[0], green: rgbaParts[1], blue: rgbaParts[2], alpha: rgbaParts[3]});
+ button.set_rgba(gdkRGBA);
+ button.connect('color-set', () => {
+ let gdkRGBA = button.get_rgba();
+ let rgbaString = `${gdkRGBA.red},${gdkRGBA.green},${gdkRGBA.blue},${gdkRGBA.alpha}`;
+ settings.set_string('visualizer-color', rgbaString);
+ });
+
+ return button;
+}
\ No newline at end of file
diff --git a/src/schemas/gschemas.compiled b/src/schemas/gschemas.compiled
new file mode 100644
index 0000000..ea68312
Binary files /dev/null and b/src/schemas/gschemas.compiled differ
diff --git a/src/schemas/org.gnome.shell.extensions.visualizer.gschema.xml b/src/schemas/org.gnome.shell.extensions.visualizer.gschema.xml
index c20986b..9129dc8 100644
--- a/src/schemas/org.gnome.shell.extensions.visualizer.gschema.xml
+++ b/src/schemas/org.gnome.shell.extensions.visualizer.gschema.xml
@@ -8,25 +8,18 @@
Location of visualizer
-
- 150
- Vertical Size of DrawingArea
-
-
- 720
- Horizontal Size of DrawingArea
-
-
- 5
- All Spect Bands width
-
-
- 64
- Count Total Spects Bands
+
+ '1.0,0.0,1.0,1.0'
+ Visualizer color
+ The color of the visualizer
false
- Flip Visualizer
+ Flip Visualizer Vertically
+
+
+ false
+ Flip Visualizer Horizontally
false
@@ -38,5 +31,26 @@
Override Spects Bands
Override Spects Bands
+
+ 30
+ Frames Per Second
+ The number of frames per second for the visualizer. Possible values are 15, 30, 60, 90, and 120.
+
+
+ 5
+ All Spect Bands width
+
+
+ 64
+ Count Total Spects Bands
+
+
+ 150
+ Vertical Size of DrawingArea
+
+
+ 720
+ Horizontal Size of DrawingArea
+
diff --git a/src/visual.js b/src/visual.js
index cd335d8..30ff45a 100644
--- a/src/visual.js
+++ b/src/visual.js
@@ -5,48 +5,127 @@ const ExtensionUtils = imports.misc.extensionUtils;
const Main = imports.ui.main;
const PopupMenu = imports.ui.popupMenu;
const Config = imports.misc.config;
-const [major, minor] = Config.PACKAGE_VERSION.split('.').map(s => Number(s));
+const [MajorVersion, MinorVersion] = Config.PACKAGE_VERSION.split('.').map(s => Number(s));
+
+// General Constants
+const REFRESH_RATE_BASE = 1000; // Base ms count for calculating refresh rate
+const INTERVAL_BASE = 1000000000; // Base ns count for calculating interval
+const SPECTRUM_THRESHOLD = -80; // Default threshold for the spectrum
+const MENU_POSITION_Y = 0.5; // Y position for popup menu
+const MENU_SIDE = St.Side.TOP; // Side for popup menu
+const POPUP_TIMEOUT = 600; // Timeout for popup in milliseconds
+
+// drawStuff Constants
+const MAX_FREQUENCY = 80; // Maximum frequency value
+const MIN_INTENSITY = 0.2; // Minimum intensity
+const VERTICAL_FLIP_FACTOR = 80; // Factor used in calculating yPosition for vertical flip
+const SQRT_VALUE = 0.5; // Square root exponent used in calculating intensity
+const MAX_COLOR_VALUE = 1.0; // Maximum color value for unit rgb used in calculations
+const MIN_RANGE = 0; // Minimum range value when normalizing frequency
+const MAX_RANGE = 1; // Maximum range value when normalizing frequency
+const MIRROR_VALUE = 1; // Mirror value used to invert factors for horizontal flipping
+const MIDDLE_DIVISOR = 2; // Divisor used in calculating xPosition, used for finding the middle of the line width of lineW
+const START_DRAW_Y_VALUE = 0; // Value used for drawing line segments; marks the start of the drawn line
+const END_DRAW_Y_VALUE = 1; // Value used for drawing line segments; marks the end of the drawn line
var Visualizer = GObject.registerClass(
class musicVisualizer extends St.BoxLayout {
+ /*
+ * Initialization and Destruction Methods
+ */
_init() {
+ this._settings = ExtensionUtils.getSettings('org.gnome.shell.extensions.visualizer');
super._init({
reactive: true,
track_hover: true,
can_focus: true
});
+ this._initializeSettings();
this._visualMenuManager = new PopupMenu.PopupMenuManager(this);
this._freq = [];
this._actor = new St.DrawingArea();
this.add_child(this._actor);
- this._settings = ExtensionUtils.getSettings();
this.settingsChanged();
+ this.setupDraggable();
+ this.setupActor();
+ this.setupGst();
+ this.setDefaultSrc();
+ this.getMenuItems();
+ this._update();
+ this.setPosition();
+ this._refreshLoopId = null;
+ this.startRefreshLoop();
+ Main.layoutManager._backgroundGroup.add_child(this);
+ }
+
+ setupDraggable() {
this._draggable = DND.makeDraggable(this);
this._draggable._animateDragEnd = (eventTime) => {
this._draggable._animationInProgress = true;
this._draggable._onAnimationComplete(this._draggable._dragActor, eventTime);
};
+
this._draggable.connect('drag-begin', this._onDragBegin.bind(this));
this._draggable.connect('drag-end', this._onDragEnd.bind(this));
+
this.connect('notify::hover', () => this._onHover());
+
+ this.isDragging = false;
+ this._dragMonitor = null;
+ this.startX = 0;
+ this.startY = 0;
+ this.oldX = 0;
+ this.oldY = 0;
+ this.deltaX = 0;
+ this.deltaY = 0;
+ }
+
+ actorInit() {
+ this._spectBands = this._settings.get_int('total-spects-band');
+ this._spectHeight = this._settings.get_int('visualizer-height');
+ this._spectWidth = this._settings.get_int('visualizer-width');
+ this._actor.height = this._spectHeight;
+ this._actor.width = this._spectWidth;
+ }
+
+ setupActor() {
this.actorInit();
this._actor.connect('repaint', (area) => this.drawStuff(area));
- this.setupGst();
- this.setDefaultSrc();
- this.getMenuItems();
- this._update();
- this.setPosition();
- Main.layoutManager._backgroundGroup.add_child(this);
}
+ _initializeSettings() {
+ this._settings = ExtensionUtils.getSettings('org.gnome.shell.extensions.visualizer');
+ this._connectSetting('fps', this._updateRefreshRate.bind(this));
+ this._connectSetting('horizontal-flip', this._updateFlipSettings.bind(this));
+ this._connectSetting('visualizer-pos-x', this.setPosition.bind(this));
+ this._connectSetting('visualizer-pos-y', this.setPosition.bind(this));
+ this._connectSetting('visualizer-color', this._update.bind(this));
+ this._refreshRate = REFRESH_RATE_BASE / this._settings.get_int('fps');
+ }
+
+ onDestroy() {
+ if (this._refreshLoopId !== null) {
+ GLib.Source.remove(this._refreshLoopId);
+ }
+ this._removeSource(this._menuTimeoutId);
+ this._removeSource(this._streamId);
+ this._removeSource(this._defaultSrcId);
+ this._pipeline.set_state(Gst.State.NULL);
+ Main.layoutManager._backgroundGroup.remove_child(this);
+ }
+
+ /*
+ * GStreamer Methods
+ */
setupGst() {
Gst.init(null);
this._pipeline = Gst.Pipeline.new("bin");
this._src = Gst.ElementFactory.make("pulsesrc", "src");
this._spectrum = Gst.ElementFactory.make("spectrum", "spectrum");
this._spectrum.set_property("bands", this._spectBands);
- this._spectrum.set_property("threshold", -80);
+ this._spectrum.set_property("threshold", SPECTRUM_THRESHOLD);
this._spectrum.set_property("post-messages", true);
+ this.updateGstInterval();
let _sink = Gst.ElementFactory.make("fakesink", "sink");
this._pipeline.add(this._src);
this._pipeline.add(this._spectrum);
@@ -60,6 +139,14 @@ var Visualizer = GObject.registerClass(
this._pipeline.set_state(Gst.State.PLAYING);
}
+ updateGstInterval() {
+ let fps = this._settings.get_int('fps');
+ let interval = INTERVAL_BASE / fps;
+ if (this._spectrum) {
+ this._spectrum.set_property("interval", interval);
+ }
+ }
+
onMessage(bus, msg) {
let struct = msg.get_structure();
let [magbool, magnitudes] = struct.get_list("magnitude");
@@ -72,34 +159,94 @@ var Visualizer = GObject.registerClass(
}
}
- actorInit() {
- this._spectBands = this._settings.get_int('total-spects-band');
- this._spectHeight = this._settings.get_int('visualizer-height');
- this._spectWidth = this._settings.get_int('visualizer-width');
- this._actor.height = this._spectHeight;
- this._actor.width = this._spectWidth;
+ /*
+ * Event Handlers
+ */
+ _onDragBegin() {
+ this.isDragging = true;
+ this._dragMonitor = {
+ dragMotion: this._onDragMotion.bind(this)
+ };
+ DND.addDragMonitor(this._dragMonitor);
+ let p = this.get_transformed_position();
+ this.startX = this.oldX = p[0];
+ this.startY = this.oldY = p[1];
+ this.get_allocation_box();
+ this.rowHeight = this.height;
+ this.rowWidth = this.width;
+ }
+
+ _onDragEnd() {
+ if (this._dragMonitor) {
+ DND.removeDragMonitor(this._dragMonitor);
+ this._dragMonitor = null;
+ }
+ this.set_position(this.deltaX, this.deltaY);
+ this.ignoreUpdatePosition = true;
+ this._settings.set_value('visualizer-location', new GLib.Variant('(ii)', [this.deltaX, this.deltaY]));
+ this.ignoreUpdatePosition = false;
+ }
+
+ _onHover() {
+ if (!this.hover)
+ this._removeMenuTimeout();
+ }
+
+ _onDragMotion(dragEvent) {
+ this.deltaX = dragEvent.x - (dragEvent.x - this.oldX);
+ this.deltaY = dragEvent.y - (dragEvent.y - this.oldY);
+ let p = this.get_transformed_position();
+ this.oldX = p[0];
+ this.oldY = p[1];
+ return DND.DragMotionResult.CONTINUE;
+ }
+
+ vfunc_button_press_event() {
+ let event = Clutter.get_current_event();
+ if (event.get_button() === 1)
+ this._setPopupTimeout();
+ else if (event.get_button() === 3) {
+ this._popupMenu();
+ return Clutter.EVENT_STOP;
+ }
+ return Clutter.EVENT_PROPAGATE;
}
+ /*
+ * Drawing and Rendering Methods
+ */
drawStuff(area) {
- let values = this.getSpectBands();
let [width, height] = area.get_surface_size();
let cr = area.get_context();
+ let values = this.getSpectBands();
let lineW = this._settings.get_int('spects-line-width');
- let flip = this._settings.get_boolean('flip-visualizer');
+ let horizontal_flip = this._settings.get_boolean('horizontal-flip');
+ let vertical_flip = this._settings.get_boolean('flip-visualizer');
+ let [r, g, b, a] = this._settings.get_string('visualizer-color').split(',').map(parseFloat);
+ cr.setLineWidth(lineW);
+
for (let i = 0; i < values; i++) {
- cr.setSourceRGBA(1, this._freq[i] / 80, 1, 1);
- cr.setLineWidth(lineW);
- if (!flip) {
- cr.moveTo(lineW / 2 + i * width / values, height);
- cr.lineTo(lineW / 2 + i * width / values, height - 1);
- cr.lineTo(lineW / 2 + i * width / values, height * this._freq[i] / 80);
- } else {
- cr.moveTo(lineW / 2 + i * width / values, 0);
- cr.lineTo(lineW / 2 + i * width / values, 1);
- cr.lineTo(lineW / 2 + i * width / values, height / 80 * (80 - this._freq[i]));
- }
+ let normalizedFreq = Math.max(Math.min(this._freq[i] / MAX_FREQUENCY, MAX_RANGE), MIN_RANGE);
+ let intensity = Math.pow(normalizedFreq, SQRT_VALUE);
+ intensity = Math.max(intensity, MIN_INTENSITY);
+
+ let horizontalFlip = this._settings.get_boolean('horizontal-flip');
+ let positionFactor = horizontalFlip ? MIRROR_VALUE - (i / (values - MIRROR_VALUE)) : i / (values - MIRROR_VALUE);
+ let blendFactor = horizontalFlip ? MIRROR_VALUE - positionFactor : positionFactor;
+ let blendedR = r + blendFactor * (MAX_COLOR_VALUE - r);
+ let blendedG = g + blendFactor * (MAX_COLOR_VALUE - g);
+ let blendedB = b + blendFactor * (MAX_COLOR_VALUE - b);
+ cr.setSourceRGBA(blendedR * intensity, blendedG * intensity, blendedB * intensity, a);
+
+ let xPosition = horizontal_flip ? width - (lineW / MIDDLE_DIVISOR + i * width / values) : lineW / MIDDLE_DIVISOR + i * width / values;
+ let yPosition = vertical_flip ? height / VERTICAL_FLIP_FACTOR * (VERTICAL_FLIP_FACTOR - this._freq[i]) : height * this._freq[i] / MAX_FREQUENCY;
+
+ cr.moveTo(xPosition, vertical_flip ? START_DRAW_Y_VALUE : height);
+ cr.lineTo(xPosition, vertical_flip ? END_DRAW_Y_VALUE : height - END_DRAW_Y_VALUE);
+ cr.lineTo(xPosition, yPosition);
cr.stroke();
}
+
cr.$dispose();
}
@@ -107,12 +254,14 @@ var Visualizer = GObject.registerClass(
this._actor.queue_repaint();
}
- getSpectBands() {
- let override = this._settings.get_boolean('spect-over-ride-bool');
- let values = this._settings.get_int('spect-over-ride');
- return (!override) ? this._spectBands : (values <= this._spectBands) ? values : this._spectBands
+ _updateFlipSettings() {
+ this._horizontalFlip = this._settings.get_boolean('horizontal-flip');
+ this._update();
}
+ /*
+ * Utility Methods
+ */
_getMetaRectForCoords(x, y) {
this.get_allocation_box();
let rect = new Meta.Rectangle();
@@ -126,12 +275,6 @@ var Visualizer = GObject.registerClass(
return Main.layoutManager.getWorkAreaForMonitor(monitorIndex);
}
- _isOnScreen(x, y) {
- let rect = this._getMetaRectForCoords(x, y);
- let monitorWorkArea = this._getWorkAreaForRect(rect);
- return monitorWorkArea.contains_rect(rect);
- }
-
_keepOnScreen(x, y) {
let rect = this._getMetaRectForCoords(x, y);
let monitorWorkArea = this._getWorkAreaForRect(rect);
@@ -142,6 +285,72 @@ var Visualizer = GObject.registerClass(
return [x, y];
}
+ _removeMenuTimeout() {
+ if (this._menuTimeoutId > 0) {
+ GLib.source_remove(this._menuTimeoutId);
+ this._menuTimeoutId = 0;
+ }
+ }
+
+ _setPopupTimeout() {
+ this._removeMenuTimeout();
+ this._menuTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, POPUP_TIMEOUT, () => {
+ this._menuTimeoutId = 0;
+ this._popupMenu();
+ return GLib.SOURCE_REMOVE;
+ });
+ GLib.Source.set_name_by_id(this._menuTimeoutId, '[visualizer] this.popupMenu');
+ }
+
+ _connectSetting(key, callback) {
+ this._settings.connect(`changed::${key}`, callback);
+ }
+
+ _updateRefreshRate() {
+ let fps = this._settings.get_int('fps');
+ this._refreshRate = REFRESH_RATE_BASE / fps;
+ this.updateGstInterval();
+ this.startRefreshLoop();
+ }
+
+ /*
+ * Getters, Setters
+ */
+ getStreams() {
+ return new Promise((resolve, reject) => {
+ this._streamId = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 1, () => {
+ let control = (MajorVersion < 43) ? Main.panel.statusArea.aggregateMenu._volume._control : Main.panel.statusArea.quickSettings._volume._control;
+ if (control.get_state() == Gvc.MixerControlState.READY) {
+ let streams = control.get_streams();
+ (streams.length > 0) ? resolve(streams): reject(Error('failure'))
+ }
+ return GLib.SOURCE_REMOVE;
+ });
+ });
+ }
+
+ getDefaultSrc() {
+ return new Promise((resolve, reject) => {
+ this._defaultSrcId = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 1, () => {
+ let stream = (MajorVersion < 43) ? Main.panel.statusArea.aggregateMenu._volume._volumeMenu._output.stream : Main.panel.statusArea.quickSettings._volume._output.stream;
+ (stream !== null) ? resolve(stream.get_name() + '.monitor'): reject(Error('failure'));
+ return GLib.SOURCE_REMOVE;
+ });
+ });
+ }
+
+ getSpectBands() {
+ let override = this._settings.get_boolean('spect-over-ride-bool');
+ let values = this._settings.get_int('spect-over-ride');
+ return (!override) ? this._spectBands : (values <= this._spectBands) ? values : this._spectBands
+ }
+
+ getDragActor() {}
+
+ getDragActorSource() {
+ return this;
+ }
+
setPosition() {
if (this._ignorePositionUpdate)
return;
@@ -163,46 +372,49 @@ var Visualizer = GObject.registerClass(
}
}
- _onDragBegin() {
- this.isDragging = true;
- this._dragMonitor = {
- dragMotion: this._onDragMotion.bind(this)
- };
- DND.addDragMonitor(this._dragMonitor);
- let p = this.get_transformed_position();
- this.startX = this.oldX = p[0];
- this.startY = this.oldY = p[1];
- this.get_allocation_box();
- this.rowHeight = this.height;
- this.rowWidth = this.width;
+ /*
+ * Lifecycle-related Methods
+ */
+ startRefreshLoop() {
+ if (this._refreshLoopId !== null) {
+ GLib.Source.remove(this._refreshLoopId);
+ }
+ this._refreshLoopId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, this._refreshRate, () => {
+ this._actor.queue_repaint();
+ return true;
+ });
}
- _onDragMotion(dragEvent) {
- this.deltaX = dragEvent.x - (dragEvent.x - this.oldX);
- this.deltaY = dragEvent.y - (dragEvent.y - this.oldY);
- let p = this.get_transformed_position();
- this.oldX = p[0];
- this.oldY = p[1];
- return DND.DragMotionResult.CONTINUE;
+ settingsChanged() {
+ this._settings.connect('changed::visualizer-location', () => this.setPosition());
+ this._settings.connect('changed::total-spects-band', () => {
+ this.actorInit();
+ this._spectrum.set_property("bands", this._spectBands);
+ this._update();
+ });
+ this._settings.connect('changed::visualizer-height', () => {
+ this.actorInit();
+ this._update();
+ });
+ this._settings.connect('changed::visualizer-width', () => {
+ this.actorInit();
+ this._update();
+ });
+ this._settings.connect('changed::spect-over-ride', () => this.getSpectBands());
+ this._settings.connect('changed::spect-over-ride-bool', () => this.getSpectBands());
+ this._settings.connect('changed::spects-line-width', () => this._update());
}
- _onDragEnd() {
- if (this._dragMonitor) {
- DND.removeDragMonitor(this._dragMonitor);
- this._dragMonitor = null;
+ _removeSource(src) {
+ if (src) {
+ GLib.Source.remove(src);
+ src = null;
}
- this.set_position(this.deltaX, this.deltaY);
- this.ignoreUpdatePosition = true;
- this._settings.set_value('visualizer-location', new GLib.Variant('(ii)', [this.deltaX, this.deltaY]));
- this.ignoreUpdatePosition = false;
- }
-
- getDragActor() {}
-
- getDragActorSource() {
- return this;
}
+ /*
+ * Async Methods
+ */
async getMenuItems() {
try {
this._menuItems = [];
@@ -234,67 +446,14 @@ var Visualizer = GObject.registerClass(
}
}
- getDefaultSrc() {
- return new Promise((resolve, reject) => {
- this._defaultSrcId = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 1, () => {
- let stream = (major < 43) ? Main.panel.statusArea.aggregateMenu._volume._volumeMenu._output.stream : Main.panel.statusArea.quickSettings._volume._output.stream;
- (stream !== null) ? resolve(stream.get_name() + '.monitor'): reject(Error('failure'));
- return GLib.SOURCE_REMOVE;
- });
- });
- }
-
- getStreams() {
- return new Promise((resolve, reject) => {
- this._streamId = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 1, () => {
- let control = (major < 43) ? Main.panel.statusArea.aggregateMenu._volume._control : Main.panel.statusArea.quickSettings._volume._control;
- if (control.get_state() == Gvc.MixerControlState.READY) {
- let streams = control.get_streams();
- (streams.length > 0) ? resolve(streams): reject(Error('failure'))
- }
- return GLib.SOURCE_REMOVE;
- });
- });
- }
-
- vfunc_button_press_event() {
- let event = Clutter.get_current_event();
- if (event.get_button() === 1)
- this._setPopupTimeout();
- else if (event.get_button() === 3) {
- this._popupMenu();
- return Clutter.EVENT_STOP;
- }
- return Clutter.EVENT_PROPAGATE;
- }
-
- _onHover() {
- if (!this.hover)
- this._removeMenuTimeout();
- }
-
- _removeMenuTimeout() {
- if (this._menuTimeoutId > 0) {
- GLib.source_remove(this._menuTimeoutId);
- this._menuTimeoutId = 0;
- }
- }
-
- _setPopupTimeout() {
- this._removeMenuTimeout();
- this._menuTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 600, () => {
- this._menuTimeoutId = 0;
- this._popupMenu();
- return GLib.SOURCE_REMOVE;
- });
- GLib.Source.set_name_by_id(this._menuTimeoutId, '[visualizer] this.popupMenu');
- }
-
+ /*
+ * Miscellaneous Methods
+ */
_popupMenu() {
this._removeMenuTimeout();
if (!this._menu) {
this._subMenuItem = [];
- this._menu = new PopupMenu.PopupMenu(this, 0.5, St.Side.TOP);
+ this._menu = new PopupMenu.PopupMenu(this, MENU_POSITION_Y, MENU_SIDE);
let srcDevice = new PopupMenu.PopupSubMenuMenuItem('Change Audio Source');
this._menu.addMenuItem(srcDevice);
for (let i = 0; i < this._menuItems.length; i++) {
@@ -322,38 +481,9 @@ var Visualizer = GObject.registerClass(
return false;
}
- onDestroy() {
- this._removeSource(this._menuTimeoutId);
- this._removeSource(this._streamId);
- this._removeSource(this._defaultSrcId);
- this._pipeline.set_state(Gst.State.NULL);
- Main.layoutManager._backgroundGroup.remove_child(this);
- }
-
- settingsChanged() {
- this._settings.connect('changed::visualizer-location', () => this.setPosition());
- this._settings.connect('changed::total-spects-band', () => {
- this.actorInit();
- this._spectrum.set_property("bands", this._spectBands);
- this._update();
- });
- this._settings.connect('changed::visualizer-height', () => {
- this.actorInit();
- this._update();
- });
- this._settings.connect('changed::visualizer-width', () => {
- this.actorInit();
- this._update();
- });
- this._settings.connect('changed::spect-over-ride', () => this.getSpectBands());
- this._settings.connect('changed::spect-over-ride-bool', () => this.getSpectBands());
- this._settings.connect('changed::spects-line-width', () => this._update());
- }
-
- _removeSource(src) {
- if (src) {
- GLib.Source.remove(src);
- src = null;
- }
+ _isOnScreen(x, y) {
+ let rect = this._getMetaRectForCoords(x, y);
+ let monitorWorkArea = this._getWorkAreaForRect(rect);
+ return monitorWorkArea.contains_rect(rect);
}
});