Add music visualizations

This commit is contained in:
Rafostar
2020-09-11 20:33:06 +02:00
parent a01cc058cd
commit 5afe5149aa
4 changed files with 163 additions and 32 deletions

View File

@@ -5,7 +5,7 @@
- [X] Dragging player by video (MPV) - [X] Dragging player by video (MPV)
- [X] Switching video/audio/subtitles tracks from bottom bar (MPV) - [X] Switching video/audio/subtitles tracks from bottom bar (MPV)
- [X] Over-amplification supported by default (VLC) - [X] Over-amplification supported by default (VLC)
- [ ] Audio visualizations (VLC) - [X] Audio visualizations (VLC)
- [ ] Clock with current hour and "Ends at" time on top overlay (Kodi) - [ ] Clock with current hour and "Ends at" time on top overlay (Kodi)
- [ ] Auto select subtitles matching OS language (Totem) - [ ] Auto select subtitles matching OS language (Totem)
- [ ] Picture-in-Picture mode window - [ ] Picture-in-Picture mode window

View File

@@ -14,6 +14,9 @@ var Controls = GObject.registerClass({
'track-change-requested': { 'track-change-requested': {
param_types: [GObject.TYPE_STRING, GObject.TYPE_INT] param_types: [GObject.TYPE_STRING, GObject.TYPE_INT]
}, },
'visualization-change-requested': {
param_types: [GObject.TYPE_STRING]
},
} }
}, class ClapperControls extends Gtk.HBox }, class ClapperControls extends Gtk.HBox
{ {
@@ -32,6 +35,9 @@ var Controls = GObject.registerClass({
this._addTogglePlayButton(); this._addTogglePlayButton();
this._addPositionScale(); this._addPositionScale();
this.visualizationsButton = this.addPopoverButton(
'display-projector-symbolic'
);
this.videoTracksButton = this.addPopoverButton( this.videoTracksButton = this.addPopoverButton(
'emblem-videos-symbolic' 'emblem-videos-symbolic'
); );
@@ -58,8 +64,11 @@ var Controls = GObject.registerClass({
Gtk.IconSize.SMALL_TOOLBAR Gtk.IconSize.SMALL_TOOLBAR
); );
this.setDefaultWidgetBehaviour(this.openMenuButton); this.setDefaultWidgetBehaviour(this.openMenuButton);
this.forall(this.setDefaultWidgetBehaviour); this.forall(this.setDefaultWidgetBehaviour);
this.realizeSignal = this.connect(
'realize', this._onControlsRealize.bind(this)
);
} }
set fullscreenMode(isFullscreen) set fullscreenMode(isFullscreen)
@@ -135,7 +144,7 @@ var Controls = GObject.registerClass({
for(let i = 0; i < lastEl; i++) { for(let i = 0; i < lastEl; i++) {
if(i >= array.length) { if(i >= array.length) {
children[i].hide(); children[i].hide();
debug(`hiding unused ${children[i].trackType} radioButton nr: ${i}`); debug(`hiding unused ${children[i].type} radioButton nr: ${i}`);
continue; continue;
} }
@@ -153,19 +162,20 @@ var Controls = GObject.registerClass({
}); });
radioButton.connect( radioButton.connect(
'toggled', 'toggled',
this._onTrackRadioButtonToggled.bind(this, radioButton) this._onRadioButtonToggled.bind(this, radioButton)
); );
this.setDefaultWidgetBehaviour(radioButton); this.setDefaultWidgetBehaviour(radioButton);
box.add(radioButton); box.add(radioButton);
} }
radioButton.label = el.label; radioButton.label = el.label;
debug(`radioButton label: ${radioButton.label}`); debug(`radioButton label: ${radioButton.label}`);
radioButton.trackType = el.type; radioButton.type = el.type;
debug(`radioButton type: ${radioButton.trackType}`); debug(`radioButton type: ${radioButton.type}`);
radioButton.trackId = el.value; radioButton.activeId = el.activeId;
debug(`radioButton track id: ${radioButton.trackId}`); debug(`radioButton id: ${radioButton.activeId}`);
if(radioButton.trackId === activeId) { if(radioButton.activeId === activeId) {
radioButton.set_active(true); radioButton.set_active(true);
debug(`activated ${el.type} radioButton nr: ${i}`); debug(`activated ${el.type} radioButton nr: ${i}`);
} }
@@ -195,7 +205,7 @@ var Controls = GObject.registerClass({
_addTogglePlayButton() _addTogglePlayButton()
{ {
this.togglePlayButton = this.addButton( this.togglePlayButton = this.addButton(
'media-playback-pause-symbolic', 'media-playback-start-symbolic',
Gtk.IconSize.LARGE_TOOLBAR Gtk.IconSize.LARGE_TOOLBAR
); );
this.togglePlayButton.setPlayImage = () => this.togglePlayButton.setPlayImage = () =>
@@ -287,16 +297,30 @@ var Controls = GObject.registerClass({
button.popover.popup(); button.popover.popup();
} }
_onTrackRadioButtonToggled(self, radioButton) _onRadioButtonToggled(self, radioButton)
{ {
if(!radioButton.get_active()) if(!radioButton.get_active())
return; return;
this.emit( switch(radioButton.type) {
'track-change-requested', case 'video':
radioButton.trackType, case 'audio':
radioButton.trackId case 'subtitle':
); this.emit(
'track-change-requested',
radioButton.type,
radioButton.activeId
);
break;
case 'visualization':
this.emit(
`${radioButton.type}-change-requested`,
radioButton.activeId
);
break;
default:
break;
}
} }
_onPositionScaleFormatValue(self, value) _onPositionScaleFormatValue(self, value)
@@ -316,4 +340,19 @@ var Controls = GObject.registerClass({
this.isPositionSeeking = false; this.isPositionSeeking = false;
this.emit('position-seeking-changed', this.isPositionSeeking); this.emit('position-seeking-changed', this.isPositionSeeking);
} }
_onControlsRealize()
{
this.disconnect(this.realizeSignal);
let hiddenButtons = [
'visualizations',
'videoTracks',
'audioTracks',
'subtitleTracks'
];
for(let name of hiddenButtons)
this[`${name}Button`].hide();
}
}); });

View File

@@ -67,6 +67,9 @@ class ClapperInterface extends Gtk.Grid
this.controls.connect( this.controls.connect(
'track-change-requested', this._onTrackChangeRequested.bind(this) 'track-change-requested', this._onTrackChangeRequested.bind(this)
); );
this.controls.connect(
'visualization-change-requested', this._onVisualizationChangeRequested.bind(this)
);
this.overlay.add(this._player.widget); this.overlay.add(this._player.widget);
} }
@@ -177,28 +180,33 @@ class ClapperInterface extends Gtk.Grid
tracksArr[0] = { tracksArr[0] = {
label: 'Disabled', label: 'Disabled',
type: type, type: type,
value: -1 activeId: -1
}; };
} }
tracksArr.push({ tracksArr.push({
label: text, label: text,
type: type, type: type,
value: info.get_index(), activeId: info.get_index(),
}); });
} }
for(let type of ['video', 'audio', 'subtitle']) { for(let type of ['video', 'audio', 'subtitle']) {
let currStream = this._player[`get_current_${type}_track`](); let currStream = this._player[`get_current_${type}_track`]();
let activeId = (currStream) ? currStream.get_index() : -1; let activeId = (currStream) ? currStream.get_index() : -1;
let buttonBox = this.controls[`${type}TracksButton`].get_parent();
if(currStream && type !== 'subtitle') { if(currStream && type !== 'subtitle') {
let caps = currStream.get_caps(); let caps = currStream.get_caps();
debug(`${type} caps: ${caps.to_string()}`, 'LEVEL_INFO'); debug(`${type} caps: ${caps.to_string()}`, 'LEVEL_INFO');
} }
if(type === 'video') {
let isShowVis = (parsedInfo[`${type}Tracks`].length === 0);
this.showVisualizationsButton(isShowVis);
}
if(!parsedInfo[`${type}Tracks`].length) { if(!parsedInfo[`${type}Tracks`].length) {
debug(`hiding popover button without contents: ${type}`); if(this.controls[`${type}TracksButton`].visible) {
buttonBox.hide(); debug(`hiding popover button without contents: ${type}`);
this.controls[`${type}TracksButton`].hide();
}
continue; continue;
} }
this.controls.addRadioButtons( this.controls.addRadioButtons(
@@ -206,7 +214,10 @@ class ClapperInterface extends Gtk.Grid
parsedInfo[`${type}Tracks`], parsedInfo[`${type}Tracks`],
activeId activeId
); );
buttonBox.show(); if(!this.controls[`${type}TracksButton`].visible) {
debug(`showing popover button with contents: ${type}`);
this.controls[`${type}TracksButton`].show();
}
} }
} }
@@ -237,38 +248,104 @@ class ClapperInterface extends Gtk.Grid
this.headerBar.set_subtitle(subtitle); this.headerBar.set_subtitle(subtitle);
} }
_onTrackChangeRequested(self, trackType, trackId) showVisualizationsButton(isShow)
{
if(isShow && !this.controls.visualizationsButton.isVisList) {
debug('creating visualizations list');
let visArr = GstPlayer.Player.visualizations_get();
if(!visArr.length)
return;
let parsedVisArr = [{
label: 'Disabled',
type: 'visualization',
activeId: null
}];
visArr.forEach(vis => {
parsedVisArr.push({
label: vis.name[0].toUpperCase() + vis.name.substring(1),
type: 'visualization',
activeId: vis.name,
});
});
this.controls.addRadioButtons(
this.controls.visualizationsButton.popoverBox,
parsedVisArr,
null
);
this.controls.visualizationsButton.isVisList = true;
debug(`total visualizations: ${visArr.length}`);
}
if(this.controls.visualizationsButton.visible === isShow)
return debug('visualizations button is already visible');
let action = (isShow) ? 'show' : 'hide';
this.controls.visualizationsButton[action]();
debug(`show visualizations button: ${isShow}`);
}
_onTrackChangeRequested(self, type, activeId)
{ {
// reenabling audio is slow (as expected), // reenabling audio is slow (as expected),
// so it is better to toggle mute instead // so it is better to toggle mute instead
if(trackType === 'audio') { if(type === 'audio') {
if(trackId < 0) if(activeId < 0)
return this._player.set_mute(true); return this._player.set_mute(true);
if(this._player.get_mute()) if(this._player.get_mute())
this._player.set_mute(false); this._player.set_mute(false);
return this._player[`set_${trackType}_track`](trackId); return this._player[`set_${type}_track`](activeId);
} }
if(trackId < 0) { if(activeId < 0) {
// disabling video leaves last frame frozen, // disabling video leaves last frame frozen,
// so we also hide the widget // so we also hide the widget
if(trackType === 'video') if(type === 'video')
this._player.widget.hide(); this._player.widget.hide();
return this._player[`set_${trackType}_track_enabled`](false); return this._player[`set_${type}_track_enabled`](false);
} }
this._player[`set_${trackType}_track`](trackId); this._player[`set_${type}_track`](activeId);
this._player[`set_${trackType}_track_enabled`](true); this._player[`set_${type}_track_enabled`](true);
if(trackType === 'video' && !this._player.widget.get_visible()) { if(type === 'video' && !this._player.widget.get_visible()) {
this._player.widget.show(); this._player.widget.show();
this._player.renderer.expose(); this._player.renderer.expose();
} }
} }
_onVisualizationChangeRequested(self, visName)
{
let isEnabled = this._player.get_visualization_enabled();
if(!visName) {
if(isEnabled) {
this._player.set_visualization_enabled(false);
debug('disabled visualizations');
}
return;
}
let currVis = this._player.get_current_visualization();
if(currVis === visName)
return;
debug(`set visualization: ${visName}`);
this._player.set_visualization(visName);
if(!isEnabled) {
this._player.set_visualization_enabled(true);
debug('enabled visualizations');
}
}
_onPlayerStateChanged(player, state) _onPlayerStateChanged(player, state)
{ {
switch(state) { switch(state) {

View File

@@ -56,6 +56,7 @@ class ClapperPlayer extends GstPlayer.Player
this.run_loop = opts.run_loop || false; this.run_loop = opts.run_loop || false;
this.widget = gtkglsink.widget; this.widget = gtkglsink.widget;
this.state = GstPlayer.PlayerState.STOPPED; this.state = GstPlayer.PlayerState.STOPPED;
this.visualization_enabled = false;
this._playlist = []; this._playlist = [];
this._trackId = 0; this._trackId = 0;
@@ -103,6 +104,20 @@ class ClapperPlayer extends GstPlayer.Player
return this._playlist; return this._playlist;
} }
set_visualization_enabled(value)
{
if(value === this.visualization_enabled)
return;
super.set_visualization_enabled(value);
this.visualization_enabled = value;
}
get_visualization_enabled()
{
return this.visualization_enabled;
}
seek_seconds(position) seek_seconds(position)
{ {
this.seek(position * 1000000000); this.seek(position * 1000000000);