-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
390 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,390 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<meta charset="UTF-8"> | ||
<style> | ||
:root | ||
{ | ||
color-scheme: light dark; /* Opt the entire page into the user's color scheme preferences */ | ||
} | ||
|
||
html | ||
{ | ||
margin: 0; | ||
padding: 0; | ||
} | ||
body | ||
{ | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
height: 100vh; | ||
margin: 0 auto; | ||
padding: 0; | ||
font: 90% Calibri, Sans-serif; | ||
line-height: 1.2; | ||
color: #555753; | ||
/* background-color: #e0e0e0; *//* Don't set a background color to use the theme background color of the foobar2000 panel */ | ||
} | ||
div | ||
{ | ||
display: none; | ||
margin-top: 0; | ||
text-align: center; | ||
display: inline; | ||
} | ||
.track | ||
{ | ||
font: 1.75em Cambria; | ||
color: crimson; | ||
} | ||
.artist | ||
{ | ||
font: 1.00em Cambria; | ||
color: crimson; | ||
} | ||
.value | ||
{ | ||
font-weight: 800; | ||
} | ||
</style> | ||
</head> | ||
<body> | ||
<div id="Info"> | ||
<span id="Album"></span><br/> | ||
<span id="AlbumArtist"></span><br/> | ||
<span id="AlbumDate"></span><br/> | ||
<span id="AlbumPublisher"></span><br/> | ||
<span id="AlbumGenre"></span><br/> | ||
<div> | ||
<span id="TrackTitle" class="track"></span><br/> | ||
<span class="artist"> | ||
<span id="TrackArtist"></span><span id="TrackCountry"></span> | ||
<span id="TrackFeaturing"></span> | ||
</span> | ||
</div><br/> | ||
<span id="TrackDate"></span><br/> | ||
<span id="TrackNumber"></span><br/> | ||
<span id="TrackGenre"></span><br/> | ||
<span id="TrackLanguage"></span><br/> | ||
<span id="TrackTime"></span><br/> | ||
<span id="TrackCodec"></span><br/> | ||
<span id="TrackInfo"></span><br/> | ||
<br/> | ||
<span id="TrackComposer"></span><br/> | ||
<span id="TrackLyricist"></span><br/> | ||
<span id="TrackComposed"></span><br/> | ||
<span id="TrackConductor"></span><br/> | ||
<span id="TrackOrchestra"></span><br/> | ||
<span id="TrackArranger"></span><br/> | ||
<span id="OriginalAlbum"></span><br/> | ||
<span id="Medium"></span><br/> | ||
<span id="Comment"></span><br/> | ||
<span id="MIDI"></span><br/> | ||
<div width="256px" height="256px" style="overflow: hidden;"> | ||
<img id="Artwork" style="max-width: 256px; height: auto; object-fit: cover;"/> | ||
</div> | ||
<p style="display: flow"> | ||
<strong>Events</strong><br/> | ||
onStarting: <span id="Starting">?</span><br/> | ||
onPaused: <span id="Paused">?</span><br/> | ||
onStop: <span id="StopReason">?</span><br/> | ||
onSeek: <span id="Time">?</span>s<br/> | ||
onVolumeChange: <span id="Volume">?</span>dBFS<br/> | ||
</p> | ||
<p> | ||
<strong>Properties</strong><br/> | ||
foo_uie_webview <span id="VersionText"></span> (<span id="Version"></span>)<br/> | ||
volume: <span id="Volume2">?</span>dBFS<br/> | ||
isPlaying: <span id="IsPlaying">?</span><br/> | ||
isPaused: <span id="IsPaused">?</span><br/> | ||
stopAfterCurrent: <span id="StopAfterCurrent">?</span><br/> | ||
length: <span id="Length">?</span><br/> | ||
position: <span id="Position">?</span><br/> | ||
canSeek: <span id="CanSeek">?</span><br/> | ||
</p> | ||
<p> | ||
<div id="Controls"> | ||
<button id="Stop" onclick="chrome.webview.hostObjects.sync.foo_uie_webview.stop(); RefreshProperties();"></button> | ||
<button id="Play" onclick="chrome.webview.hostObjects.sync.foo_uie_webview.play(false); RefreshProperties();"></button> | ||
<button id="Pause" onclick="chrome.webview.hostObjects.sync.foo_uie_webview.togglePause(); RefreshProperties();"></button> | ||
<button id="Prev" onclick="chrome.webview.hostObjects.sync.foo_uie_webview.previous(); RefreshProperties();"></button> | ||
<button id="Next" onclick="chrome.webview.hostObjects.sync.foo_uie_webview.next(); RefreshProperties();"></button> | ||
<button id="Random" onclick="chrome.webview.hostObjects.sync.foo_uie_webview.random(); RefreshProperties();"></button> | ||
<button id="VolumeDown" onclick="chrome.webview.hostObjects.sync.foo_uie_webview.volumeDown(); RefreshProperties();"></button> | ||
<button id="VolumeUp" onclick="chrome.webview.hostObjects.sync.foo_uie_webview.volumeUp(); RefreshProperties();"></button> | ||
<button id="Mute" onclick="chrome.webview.hostObjects.sync.foo_uie_webview.toggleMute(); RefreshProperties();"></button> | ||
<button id="Seek" onclick="chrome.webview.hostObjects.sync.foo_uie_webview.seek(Math.random() * chrome.webview.hostObjects.sync.foo_uie_webview.length); RefreshProperties();">Seek</button> | ||
<button id="Delta" onclick="chrome.webview.hostObjects.sync.foo_uie_webview.seekDelta((Math.random() - 0.5) * 5.0); RefreshProperties();">Delta</button> | ||
<br/> | ||
<input type="checkbox" id="ToggleStopAfterCurrent" onclick="chrome.webview.hostObjects.sync.foo_uie_webview.toggleStopAfterCurrent(); RefreshProperties();"/><label for="ToggleStopAfterCurrent">Stop after current item</label> | ||
</div> | ||
</p> | ||
<div>Timestamp: <span id="Timestamp"></span>, <span id="SampleCount"></span> samples, <span id="SampleRate"></span>Hz, <span id="ChannelCount"></span> channels (<span id="ChannelConfig"></span>)<br/> | ||
<span id="Timer"></span><br/> | ||
</div> | ||
</div> | ||
<script type="text/javascript"> | ||
var WebView; | ||
|
||
window.onload = function() | ||
{ | ||
window.chrome.webview.addEventListener("sharedbufferreceived", e => | ||
{ | ||
chrome.webview.hostObjects.sync.foo_uie_webview.print("foo_uie_webview JavaScript says hello."); | ||
|
||
document.getElementById("VersionText").textContent = chrome.webview.hostObjects.sync.foo_uie_webview.componentVersionText; | ||
|
||
var Version = chrome.webview.hostObjects.sync.foo_uie_webview.componentVersion; | ||
|
||
document.getElementById("Version").textContent = 'v' + ((Version >> 24) & 255) + '.' + ((Version >> 16) & 255) + '.' + ((Version >> 8) & 255) + '.' + (Version & 255); | ||
|
||
document.getElementById("Volume2").textContent = chrome.webview.hostObjects.sync.foo_uie_webview.volume; | ||
|
||
// chrome.webview.hostObjects.sync.foo_uie_webview.volume = 0; // The volume can be set from code. | ||
|
||
document.getElementById("Stop").textContent = '\u23F9'; | ||
document.getElementById("Play").textContent = '\u23F5'; | ||
document.getElementById("Pause").textContent = '\u23F8'; | ||
document.getElementById("Prev").textContent = '\u23EE'; | ||
document.getElementById("Next").textContent = '\u23ED'; | ||
document.getElementById("Random").textContent = '\u{1F500}'; | ||
document.getElementById("VolumeDown").textContent = '\u{1F509}'; | ||
document.getElementById("VolumeUp").textContent = '\u{1F50A}'; | ||
document.getElementById("Mute").textContent = chrome.webview.hostObjects.sync.foo_uie_webview.isMuted ? '\u{1F508}' : '\u{1F507}'; | ||
|
||
document.getElementById("ToggleStopAfterCurrent").checked = chrome.webview.hostObjects.sync.foo_uie_webview.stopAfterCurrent; | ||
|
||
OnSharedBufferReceived(e); | ||
}); | ||
|
||
window.chrome.webview.addEventListener("playlistItemFocusChanged", e => | ||
{ | ||
alert("Focus changed"); | ||
}); | ||
|
||
Refresh(); | ||
} | ||
|
||
let SharedBuffer; | ||
let Samples; | ||
|
||
// Called when playback is being initialized. | ||
function onPlaybackStarting(command, paused) | ||
{ | ||
document.getElementById("Info").style.display = 'inline'; | ||
document.getElementById("Starting").textContent = command + " (" + (paused ? "Paused" : "Playing") + ")"; | ||
|
||
RefreshProperties(); | ||
} | ||
|
||
// Called when playback advances to a new track. | ||
function onPlaybackNewTrack() | ||
{ | ||
document.getElementById("Info").style.display = 'inline'; | ||
|
||
Refresh() | ||
|
||
const TestDataURI = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="; | ||
|
||
const DataURI = chrome.webview.hostObjects.sync.foo_uie_webview.getArtwork("front"); // "front", "back", "disc", "icon", "artist" | ||
|
||
document.getElementById("Artwork").src = (DataURI.length != 0) ? DataURI : TestDataURI; | ||
} | ||
|
||
// Called when playback stops. | ||
function onPlaybackStop(reason) | ||
{ | ||
// document.getElementById("Info").style.display = 'none'; | ||
document.getElementById("StopReason").textContent = reason; // "User" / "EOF" / "Starting another" / "Shutting down" | ||
|
||
RefreshProperties(); | ||
} | ||
|
||
// Called when the user seeks to a specific time. | ||
function onPlaybackSeek(time) | ||
{ | ||
document.getElementById("Time").textContent = time; // in seconds | ||
} | ||
|
||
// Called when playback pauses or resumes. | ||
function onPlaybackPause(paused) | ||
{ | ||
document.getElementById("Paused").textContent = paused; // true / false | ||
|
||
RefreshProperties(); | ||
} | ||
|
||
// Called when the currently played file gets edited. | ||
function onPlaybackEdited() | ||
{ | ||
Refresh(); | ||
} | ||
|
||
// Called when dynamic info (VBR bitrate etc...) changes. | ||
function onPlaybackDynamicInfo() | ||
{ | ||
Refresh(); | ||
} | ||
|
||
// Called when the per-track dynamic info (stream track titles etc...) change. Happens less often than OnPlaybackDynamicInfo(). | ||
function onPlaybackDynamicTrackInfo() | ||
{ | ||
Refresh(); | ||
} | ||
|
||
// Called, every second, for time display. | ||
function onPlaybackTime(time) | ||
{ | ||
document.getElementById("Time").textContent = time; // in seconds | ||
document.getElementById("TrackTime").textContent = chrome.webview.hostObjects.sync.foo_uie_webview.getFormattedText("[%playback_time%[/%length%]]"); | ||
} | ||
|
||
// Called when the user changes the volume. | ||
function OnVolumeChange(newValue) | ||
{ | ||
document.getElementById("Volume").textContent = newValue; // in dBFS | ||
} | ||
|
||
// Refreshes the content of all elements. | ||
function Refresh() | ||
{ | ||
document.getElementById("Album").textContent = chrome.webview.hostObjects.sync.foo_uie_webview.getFormattedText("[%album%[: %subtitle%]]"); | ||
document.getElementById("AlbumArtist").textContent = chrome.webview.hostObjects.sync.foo_uie_webview.getFormattedText("[%album artist%]"); | ||
document.getElementById("AlbumDate").textContent = chrome.webview.hostObjects.sync.foo_uie_webview.getFormattedText("[%album recorded%]['/'%album released%]"); | ||
document.getElementById("AlbumPublisher").textContent = chrome.webview.hostObjects.sync.foo_uie_webview.getFormattedText("[%publisher%[' ('%album country%')']]"); | ||
document.getElementById("AlbumGenre").textContent = chrome.webview.hostObjects.sync.foo_uie_webview.getFormattedText("[%album genre%]"); | ||
|
||
document.getElementById("TrackTitle").textContent = chrome.webview.hostObjects.sync.foo_uie_webview.getFormattedText("%title%[' ['%remix%']']"); | ||
document.getElementById("TrackArtist").textContent = chrome.webview.hostObjects.sync.foo_uie_webview.getFormattedText("[%artist%]"); | ||
document.getElementById("TrackCountry").textContent = chrome.webview.hostObjects.sync.foo_uie_webview.getFormattedText("[' ('%country%')']"); | ||
document.getElementById("TrackFeaturing").textContent = chrome.webview.hostObjects.sync.foo_uie_webview.getFormattedText("['ft. '%featuring%]"); | ||
document.getElementById("TrackDate").textContent = chrome.webview.hostObjects.sync.foo_uie_webview.getFormattedText("[%date%]"); | ||
document.getElementById("TrackNumber").textContent = chrome.webview.hostObjects.sync.foo_uie_webview.getFormattedText("[%tracknumber%[/%totaltracks%]]"); | ||
document.getElementById("TrackGenre").textContent = chrome.webview.hostObjects.sync.foo_uie_webview.getFormattedText("%genre%[/%subgenre%]"); | ||
document.getElementById("TrackLanguage").textContent = chrome.webview.hostObjects.sync.foo_uie_webview.getFormattedText("[ %language%]"); | ||
|
||
document.getElementById("TrackTime").textContent = chrome.webview.hostObjects.sync.foo_uie_webview.getFormattedText("[%playback_time%[/%length%]]"); | ||
|
||
document.getElementById("TrackCodec").textContent = chrome.webview.hostObjects.sync.getFormattedText("%codec_long%[, $info(codec_profile)], $caps($info(encoding))"); | ||
document.getElementById("TrackInfo").textContent = chrome.webview.hostObjects.sync.foo_uie_webview.getFormattedText("%samplerate%Hz, %bitrate% kbps[, $info(bitspersample) bit], $caps(%channels%)[, $caps($info(channel_mode))]"); | ||
|
||
document.getElementById("TrackComposer").textContent = chrome.webview.hostObjects.sync.foo_uie_webview.getFormattedText("['Composer: '%composer%]"); | ||
document.getElementById("TrackLyricist").textContent = chrome.webview.hostObjects.sync.foo_uie_webview.getFormattedText("['Lyricist: '%lyricist%]"); | ||
document.getElementById("TrackComposed").textContent = chrome.webview.hostObjects.sync.foo_uie_webview.getFormattedText("['Composed in: ' %composed%]"); | ||
document.getElementById("TrackConductor").textContent = chrome.webview.hostObjects.sync.foo_uie_webview.getFormattedText("['Conductor: '%conductor%]"); | ||
document.getElementById("TrackOrchestra").textContent = chrome.webview.hostObjects.sync.foo_uie_webview.getFormattedText("['Orchestra: '%orchestra%]"); | ||
document.getElementById("TrackArranger").textContent = chrome.webview.hostObjects.sync.foo_uie_webview.getFormattedText("['Arranger: ' %arranger%]"); | ||
|
||
document.getElementById("OriginalAlbum").textContent = chrome.webview.hostObjects.sync.foo_uie_webview.getFormattedText("['Original Album: '%original album%]"); | ||
|
||
document.getElementById("Medium").textContent = chrome.webview.hostObjects.sync.foo_uie_webview.getFormattedText("['Medium: '%medium%]"); | ||
document.getElementById("Comment").textContent = chrome.webview.hostObjects.sync.foo_uie_webview.getFormattedText("['Comments: '%comment%]"); | ||
document.getElementById("MIDI").textContent = chrome.webview.hostObjects.sync.foo_uie_webview.getFormattedText("['MIDI: '$info(midi_player)][, $info(midi_active_voices) voices '(peak ' $info(midi_peak_voices)')'][, extra percussion channel $info(midi_extra_percussion_channel)]"); | ||
|
||
RefreshProperties(); | ||
} | ||
|
||
// Refreshes the content of the property elements. | ||
function RefreshProperties() | ||
{ | ||
document.getElementById("IsPlaying").textContent = chrome.webview.hostObjects.sync.foo_uie_webview.isPlaying ? 'True' : 'False'; | ||
document.getElementById("IsPaused").textContent = chrome.webview.hostObjects.sync.foo_uie_webview.isPaused ? 'True' : 'False'; | ||
document.getElementById("StopAfterCurrent").textContent = chrome.webview.hostObjects.sync.foo_uie_webview.stopAfterCurrent ? 'True' : 'False'; | ||
|
||
document.getElementById("Length").textContent = (Math.round(chrome.webview.hostObjects.sync.foo_uie_webview.length * 100) / 100).toFixed(2) + 's'; | ||
document.getElementById("CanSeek").textContent = chrome.webview.hostObjects.sync.foo_uie_webview.canSeek ? 'True' : 'False'; | ||
|
||
document.getElementById("Mute").textContent = chrome.webview.hostObjects.sync.foo_uie_webview.isMuted ? '\u{1F508}' : '\u{1F507}'; | ||
} | ||
|
||
// Called when the shared buffer does not exist yet or when the channel configuration changes. | ||
function OnSharedBufferReceived(e) | ||
{ | ||
if (SharedBuffer) | ||
{ | ||
window.chrome.webview.releaseBuffer(SharedBuffer); | ||
SharedBuffer = null; | ||
Samples = null; | ||
} | ||
|
||
if (!e.additionalData) | ||
return; | ||
|
||
SharedBuffer = e.getBuffer(); // as an ArrayBuffer | ||
Samples = new Float64Array(SharedBuffer); | ||
|
||
document.getElementById("Timestamp").textContent = Date.now(); | ||
document.getElementById("SampleCount").textContent = e.additionalData.SampleCount; | ||
document.getElementById("SampleRate").textContent = e.additionalData.SampleRate; | ||
document.getElementById("ChannelCount").textContent = e.additionalData.ChannelCount; | ||
document.getElementById("ChannelConfig").textContent = GetChannelConfigurationText(e.additionalData.ChannelConfig); | ||
} | ||
|
||
// Called when the visualisation timer ticks. | ||
function OnTimer(sampleCount, sampleRate, channelCount, channelConfig) | ||
{ | ||
document.getElementById("Position").textContent = (Math.round(chrome.webview.hostObjects.sync.foo_uie_webview.position * 100) / 100).toFixed(2) + 's'; | ||
|
||
var L = 0, R = 0; | ||
|
||
if (Samples && Samples.length >= 2) | ||
{ | ||
for (i = 0; i < sampleCount; i += channelCount) | ||
{ | ||
if ((channelConfig & 3) == 3) // Front Left + Front Right | ||
{ | ||
L = Math.max(L, Samples[i]); | ||
R = Math.max(R, Samples[i + 1]); | ||
} | ||
else | ||
if ((channelConfig & 4) == 4) // Front Center (Mono) | ||
{ | ||
L = R = Math.max(L, Samples[i]); | ||
} | ||
} | ||
} | ||
else | ||
document.getElementById("Timer").textContent = "N/A"; | ||
|
||
const NormalizeValue = (value) => | ||
{ | ||
const dB = 20 * Math.log10(value); | ||
|
||
return Math.min(Math.max((dB + 60) / 60, 0.0), 1.0); | ||
}; | ||
|
||
L = NormalizeValue(L); | ||
R = NormalizeValue(R); | ||
|
||
document.getElementById("Timer").textContent = Date.now() + ": " + sampleCount + " samples, " + sampleRate + "Hz, " + channelCount + " channels (0x" + ("00000000" + channelConfig.toString(16)).toUpperCase().slice(-8) + "), Left: " + L.toFixed(2) + "%, Right: " + R.toFixed(2) + "%"; | ||
} | ||
|
||
// Converts the channel configuration mask to text. | ||
function GetChannelConfigurationText(channelConfig) | ||
{ | ||
var ChannelNames = | ||
[ | ||
"FL", "FR", "FC", | ||
"LFE", | ||
"BL", "BR", | ||
"FCL", "FCR", | ||
"BC", "SL", "SR", "TC", | ||
"TFL", "TFC", "TFR", "TBL", "TBC", "TBR", | ||
]; | ||
|
||
return ChannelNames.filter | ||
( | ||
function(x) | ||
{ | ||
var b = (channelConfig & 1); | ||
|
||
channelConfig >>= 1; | ||
|
||
return b; | ||
} | ||
).join(" / "); | ||
} | ||
</script> | ||
</body> | ||
</html> |