577 lines
16 KiB
Plaintext
577 lines
16 KiB
Plaintext
; WaveSurferSB - A wavesurfer.js v7 wrapper for SpiderBasic
|
|
;
|
|
; This library wraps the wavesurfer.js audio waveform player for use in SpiderBasic.
|
|
; Includes support for Timeline, Minimap, and Spectrogram plugins.
|
|
; wavesurfer.js v7: https://wavesurfer.xyz / https://github.com/katspaugh/wavesurfer.js
|
|
;
|
|
; Usage:
|
|
; IncludeFile "WaveSurferSB/WaveSurferSB.sbi"
|
|
;
|
|
; ; Load core + plugins (comma-separated: "timeline,minimap,spectrogram")
|
|
; WaveSurferSB::Download(@MyCallback(), "timeline,minimap,spectrogram")
|
|
;
|
|
; Procedure MyCallback(Success)
|
|
; If Success
|
|
; Define ws = WaveSurferSB::Create("#myContainer", WaveSurferSB::#DragToSeek)
|
|
; WaveSurferSB::TimelineCreate(ws)
|
|
; WaveSurferSB::MinimapCreate(ws, 50, "#777", "#333")
|
|
; WaveSurferSB::SpectrogramCreate(ws, "", 128, #True)
|
|
; WaveSurferSB::Load(ws, "audio.mp3")
|
|
; EndIf
|
|
; EndProcedure
|
|
;
|
|
; For wavesurfer.js documentation, see: https://wavesurfer.xyz/docs
|
|
|
|
DeclareModule WaveSurferSB
|
|
|
|
;{ Initialization
|
|
; Plugins.s: comma-separated plugin names to load, e.g. "timeline,minimap,spectrogram"
|
|
Declare Download(*Callback, Plugins.s = "", UseLocalFiles = #False)
|
|
;}
|
|
|
|
;{ Instance Management
|
|
Declare Create(Container.s, Flags = 0)
|
|
Declare Create_ex(Container.s, OptionsJSON.s)
|
|
Declare Destroy(Instance)
|
|
Declare Empty(Instance)
|
|
;}
|
|
|
|
;{ Audio Loading
|
|
Declare Load(Instance, URL.s)
|
|
Declare LoadWithPeaks(Instance, URL.s, PeaksJSON.s, Duration.d = 0.0)
|
|
;}
|
|
|
|
;{ Playback Control
|
|
Declare Play(Instance)
|
|
Declare PlayFrom(Instance, StartSeconds.d, EndSeconds.d = -1.0)
|
|
Declare Pause(Instance)
|
|
Declare PlayPause(Instance)
|
|
Declare Stop(Instance)
|
|
Declare IsPlaying(Instance)
|
|
;}
|
|
|
|
;{ Navigation
|
|
Declare SetTime(Instance, TimeSeconds.d)
|
|
Declare.d GetCurrentTime(Instance)
|
|
Declare.d GetDuration(Instance)
|
|
Declare SeekTo(Instance, Progress.d)
|
|
;}
|
|
|
|
;{ Volume & Speed
|
|
Declare SetVolume(Instance, Volume.d)
|
|
Declare.d GetVolume(Instance)
|
|
Declare SetMuted(Instance, Muted)
|
|
Declare SetPlaybackRate(Instance, Rate.d)
|
|
Declare.d GetPlaybackRate(Instance)
|
|
;}
|
|
|
|
;{ Visual Options
|
|
Declare SetOptions(Instance, OptionsJSON.s)
|
|
Declare Zoom(Instance, MinPxPerSec.d)
|
|
;}
|
|
|
|
;{ Generic Plugin Registration
|
|
Declare RegisterPlugin(Instance, PluginName.s, OptionsJSON.s = "{}")
|
|
Declare DestroyPlugin(PluginInstance)
|
|
;}
|
|
|
|
;{ Timeline Plugin
|
|
Declare TimelineCreate(Instance, Container.s = "", Height = 20)
|
|
Declare TimelineCreate_ex(Instance, OptionsJSON.s)
|
|
Declare TimelineDestroy(PluginInstance)
|
|
;}
|
|
|
|
;{ Minimap Plugin
|
|
Declare MinimapCreate(Instance, Height = 50, WaveColor.s = "#999", ProgressColor.s = "#555", OverlayColor.s = "")
|
|
Declare MinimapCreate_ex(Instance, OptionsJSON.s)
|
|
Declare MinimapDestroy(PluginInstance)
|
|
;}
|
|
|
|
;{ Spectrogram Plugin
|
|
Declare SpectrogramCreate(Instance, Container.s = "", Height = 128, Labels = #False)
|
|
Declare SpectrogramCreate_ex(Instance, OptionsJSON.s)
|
|
Declare SpectrogramDestroy(PluginInstance)
|
|
;}
|
|
|
|
;{ Events
|
|
Declare OnReady(Instance, *Callback)
|
|
Declare OnPlay(Instance, *Callback)
|
|
Declare OnPause(Instance, *Callback)
|
|
Declare OnFinish(Instance, *Callback)
|
|
Declare OnTimeUpdate(Instance, *Callback)
|
|
Declare OnSeeking(Instance, *Callback)
|
|
Declare OnInteraction(Instance, *Callback)
|
|
Declare OnClick(Instance, *Callback)
|
|
Declare OnDecode(Instance, *Callback)
|
|
Declare OnLoading(Instance, *Callback)
|
|
Declare OnDestroy(Instance, *Callback)
|
|
Declare OnEvent(Instance, EventName.s, *Callback)
|
|
;}
|
|
|
|
;{ Data Export
|
|
Declare.s GetMediaElement(Instance)
|
|
;}
|
|
|
|
;{ Creation Flag Constants
|
|
#Default = 0
|
|
#Normalize = 1 << 0
|
|
#AutoPlay = 1 << 1
|
|
#DragToSeek = 1 << 2
|
|
#HideScrollbar = 1 << 3
|
|
#AutoScroll = 1 << 4
|
|
#AutoCenter = 1 << 5
|
|
#SplitChannels = 1 << 6
|
|
#BarStyle = 1 << 7
|
|
;}
|
|
|
|
EndDeclareModule
|
|
|
|
Module WaveSurferSB
|
|
EnableExplicit
|
|
|
|
;{ Private Variables
|
|
Global download_callback
|
|
Global is_loaded = #False
|
|
Global plugins_to_load.s = ""
|
|
Global use_local.i = #False
|
|
;}
|
|
|
|
;{ Private Declarations
|
|
Declare Handler_Download(url.s, success)
|
|
Declare Handler_PluginDownload(url.s, success)
|
|
Declare _HideAMD()
|
|
Declare _RestoreAMD()
|
|
Declare _LoadNextPlugin()
|
|
;}
|
|
|
|
; AMD/UMD Helpers
|
|
Procedure _HideAMD()
|
|
!window._ws_saved_module = (typeof module !== 'undefined') ? module : undefined;
|
|
!window._ws_saved_exports = (typeof exports !== 'undefined') ? exports : undefined;
|
|
!window._ws_saved_define = (typeof define !== 'undefined') ? define : undefined;
|
|
!try { module = undefined; } catch(e) {}
|
|
!try { exports = undefined; } catch(e) {}
|
|
!try { define = undefined; } catch(e) {}
|
|
EndProcedure
|
|
|
|
Procedure _RestoreAMD()
|
|
!try { if (window._ws_saved_module !== undefined) module = window._ws_saved_module; } catch(e) {}
|
|
!try { if (window._ws_saved_exports !== undefined) exports = window._ws_saved_exports; } catch(e) {}
|
|
!try { if (window._ws_saved_define !== undefined) define = window._ws_saved_define; } catch(e) {}
|
|
EndProcedure
|
|
|
|
; Plugin Loading Chain
|
|
Procedure _LoadNextPlugin()
|
|
Protected plugin.s, url.s
|
|
|
|
; Pop the first plugin from the comma-separated list
|
|
!v_plugin = wavesurfersb$g_plugins_to_load.split(',')[0] || '';
|
|
!wavesurfersb$g_plugins_to_load = wavesurfersb$g_plugins_to_load.split(',').slice(1).join(',');
|
|
|
|
!v_plugin = v_plugin.trim();
|
|
|
|
Protected check
|
|
!v_check = (v_plugin === '');
|
|
|
|
If check
|
|
; All plugins loaded - restore AMD and call user callback
|
|
_RestoreAMD()
|
|
is_loaded = #True
|
|
!wavesurfersb$g_download_callback(1);
|
|
ProcedureReturn
|
|
EndIf
|
|
|
|
If use_local
|
|
url = "LocalFiles/JS/wavesurfer-" + plugin + ".min.js"
|
|
Else
|
|
url = "https://unpkg.com/wavesurfer.js@7/dist/plugins/" + plugin + ".min.js"
|
|
EndIf
|
|
|
|
LoadScript(url, @Handler_PluginDownload(), #PB_Script_JavaScript)
|
|
EndProcedure
|
|
|
|
; Initialization
|
|
Procedure Download(*Callback, Plugins.s = "", UseLocalFiles = #False)
|
|
download_callback = *Callback
|
|
plugins_to_load = Plugins
|
|
use_local = UseLocalFiles
|
|
|
|
_HideAMD()
|
|
|
|
If UseLocalFiles
|
|
LoadScript("LocalFiles/JS/wavesurfer.min.js", @Handler_Download(), #PB_Script_JavaScript)
|
|
Else
|
|
LoadScript("https://unpkg.com/wavesurfer.js@7/dist/wavesurfer.min.js", @Handler_Download(), #PB_Script_JavaScript)
|
|
EndIf
|
|
EndProcedure
|
|
|
|
; Instance Management
|
|
Procedure Create(Container.s, Flags = 0)
|
|
Protected Instance
|
|
|
|
; Build options object in JavaScript
|
|
!var _opts = { container: v_container };
|
|
|
|
If Flags & #Normalize
|
|
!_opts.normalize = true;
|
|
EndIf
|
|
|
|
If Flags & #AutoPlay
|
|
!_opts.autoplay = true;
|
|
EndIf
|
|
|
|
If Flags & #DragToSeek
|
|
!_opts.dragToSeek = true;
|
|
EndIf
|
|
|
|
If Flags & #HideScrollbar
|
|
!_opts.hideScrollbar = true;
|
|
EndIf
|
|
|
|
If Flags & #AutoScroll
|
|
!_opts.autoScroll = true;
|
|
EndIf
|
|
|
|
If Flags & #AutoCenter
|
|
!_opts.autoCenter = true;
|
|
EndIf
|
|
|
|
If Flags & #SplitChannels
|
|
!_opts.splitChannels = [{}];
|
|
EndIf
|
|
|
|
If Flags & #BarStyle
|
|
!_opts.barWidth = 2;
|
|
!_opts.barGap = 1;
|
|
!_opts.barRadius = 2;
|
|
EndIf
|
|
|
|
!v_instance = WaveSurfer.create(_opts);
|
|
ProcedureReturn Instance
|
|
EndProcedure
|
|
|
|
Procedure Create_ex(Container.s, OptionsJSON.s)
|
|
Protected Instance
|
|
|
|
!var _opts;
|
|
!try { _opts = JSON.parse(v_optionsjson); } catch(e) { _opts = {}; }
|
|
!_opts.container = v_container;
|
|
!v_instance = WaveSurfer.create(_opts);
|
|
|
|
ProcedureReturn Instance
|
|
EndProcedure
|
|
|
|
Procedure Destroy(Instance)
|
|
!if (v_instance && typeof v_instance.destroy === 'function') {
|
|
! v_instance.destroy();
|
|
!}
|
|
EndProcedure
|
|
|
|
Procedure Empty(Instance)
|
|
!if (v_instance && typeof v_instance.empty === 'function') {
|
|
! v_instance.empty();
|
|
!}
|
|
EndProcedure
|
|
|
|
; Audio Loading
|
|
Procedure Load(Instance, URL.s)
|
|
!if (v_instance) v_instance.load(v_url);
|
|
EndProcedure
|
|
|
|
Procedure LoadWithPeaks(Instance, URL.s, PeaksJSON.s, Duration.d = 0.0)
|
|
!if (v_instance) {
|
|
! var _peaks;
|
|
! try { _peaks = JSON.parse(v_peaksjson); } catch(e) { _peaks = undefined; }
|
|
! var _dur = v_duration > 0 ? v_duration : undefined;
|
|
! v_instance.load(v_url, _peaks, _dur);
|
|
!}
|
|
EndProcedure
|
|
|
|
; Playback Control
|
|
Procedure Play(Instance)
|
|
!if (v_instance) v_instance.play();
|
|
EndProcedure
|
|
|
|
Procedure PlayFrom(Instance, StartSeconds.d, EndSeconds.d = -1.0)
|
|
!if (v_instance) {
|
|
! if (v_endseconds >= 0) {
|
|
! v_instance.play(v_startseconds, v_endseconds);
|
|
! } else {
|
|
! v_instance.play(v_startseconds);
|
|
! }
|
|
!}
|
|
EndProcedure
|
|
|
|
Procedure Pause(Instance)
|
|
!if (v_instance) v_instance.pause();
|
|
EndProcedure
|
|
|
|
Procedure PlayPause(Instance)
|
|
!if (v_instance) v_instance.playPause();
|
|
EndProcedure
|
|
|
|
Procedure Stop(Instance)
|
|
!if (v_instance) v_instance.stop();
|
|
EndProcedure
|
|
|
|
Procedure IsPlaying(Instance)
|
|
Protected Result = #False
|
|
!if (v_instance) v_result = v_instance.isPlaying();
|
|
ProcedureReturn Result
|
|
EndProcedure
|
|
|
|
; Navigation
|
|
Procedure SetTime(Instance, TimeSeconds.d)
|
|
!if (v_instance) v_instance.setTime(v_timeseconds);
|
|
EndProcedure
|
|
|
|
Procedure.d GetCurrentTime(Instance)
|
|
Protected.d Result = 0.0
|
|
!if (v_instance) v_result = v_instance.getCurrentTime();
|
|
ProcedureReturn Result
|
|
EndProcedure
|
|
|
|
Procedure.d GetDuration(Instance)
|
|
Protected.d Result = 0.0
|
|
!if (v_instance) v_result = v_instance.getDuration();
|
|
ProcedureReturn Result
|
|
EndProcedure
|
|
|
|
Procedure SeekTo(Instance, Progress.d)
|
|
!if (v_instance) v_instance.seekTo(v_progress);
|
|
EndProcedure
|
|
|
|
; Volume & Speed
|
|
Procedure SetVolume(Instance, Volume.d)
|
|
!if (v_instance) v_instance.setVolume(v_volume);
|
|
EndProcedure
|
|
|
|
Procedure.d GetVolume(Instance)
|
|
Protected.d Result = 1.0
|
|
!if (v_instance) v_result = v_instance.getVolume();
|
|
ProcedureReturn Result
|
|
EndProcedure
|
|
|
|
Procedure SetMuted(Instance, Muted)
|
|
!if (v_instance) v_instance.setMuted(!!v_muted);
|
|
EndProcedure
|
|
|
|
Procedure SetPlaybackRate(Instance, Rate.d)
|
|
!if (v_instance) v_instance.setPlaybackRate(v_rate);
|
|
EndProcedure
|
|
|
|
Procedure.d GetPlaybackRate(Instance)
|
|
Protected.d Result = 1.0
|
|
!if (v_instance) v_result = v_instance.getPlaybackRate();
|
|
ProcedureReturn Result
|
|
EndProcedure
|
|
|
|
; Visual Options
|
|
Procedure SetOptions(Instance, OptionsJSON.s)
|
|
!if (v_instance) {
|
|
! var _opts;
|
|
! try { _opts = JSON.parse(v_optionsjson); } catch(e) { _opts = {}; }
|
|
! v_instance.setOptions(_opts);
|
|
!}
|
|
EndProcedure
|
|
|
|
Procedure Zoom(Instance, MinPxPerSec.d)
|
|
!if (v_instance) v_instance.zoom(v_minpxpersec);
|
|
EndProcedure
|
|
|
|
; Generic Plugin Registration
|
|
Procedure RegisterPlugin(Instance, PluginName.s, OptionsJSON.s = "{}")
|
|
Protected PluginInstance
|
|
!if (v_instance && WaveSurfer[v_pluginname]) {
|
|
! var _opts;
|
|
! try { _opts = JSON.parse(v_optionsjson); } catch(e) { _opts = {}; }
|
|
! v_plugininstance = v_instance.registerPlugin(WaveSurfer[v_pluginname].create(_opts));
|
|
!}
|
|
ProcedureReturn PluginInstance
|
|
EndProcedure
|
|
|
|
Procedure DestroyPlugin(PluginInstance)
|
|
!if (v_plugininstance && typeof v_plugininstance.destroy === 'function') {
|
|
! v_plugininstance.destroy();
|
|
!}
|
|
EndProcedure
|
|
|
|
; Timeline Plugin
|
|
Procedure TimelineCreate(Instance, Container.s = "", Height = 20)
|
|
Protected PluginInstance
|
|
!if (v_instance && WaveSurfer.Timeline) {
|
|
! var _opts = { height: v_height };
|
|
! if (v_container && v_container !== '') _opts.container = v_container;
|
|
! v_plugininstance = v_instance.registerPlugin(WaveSurfer.Timeline.create(_opts));
|
|
!}
|
|
ProcedureReturn PluginInstance
|
|
EndProcedure
|
|
|
|
Procedure TimelineCreate_ex(Instance, OptionsJSON.s)
|
|
Protected PluginInstance
|
|
!if (v_instance && WaveSurfer.Timeline) {
|
|
! var _opts;
|
|
! try { _opts = JSON.parse(v_optionsjson); } catch(e) { _opts = {}; }
|
|
! v_plugininstance = v_instance.registerPlugin(WaveSurfer.Timeline.create(_opts));
|
|
!}
|
|
ProcedureReturn PluginInstance
|
|
EndProcedure
|
|
|
|
Procedure TimelineDestroy(PluginInstance)
|
|
DestroyPlugin(PluginInstance)
|
|
EndProcedure
|
|
|
|
; Minimap Plugin
|
|
Procedure MinimapCreate(Instance, Height = 50, WaveColor.s = "#999", ProgressColor.s = "#555", OverlayColor.s = "")
|
|
Protected PluginInstance
|
|
!if (v_instance && WaveSurfer.Minimap) {
|
|
! var _opts = {
|
|
! height: v_height,
|
|
! waveColor: v_wavecolor,
|
|
! progressColor: v_progresscolor
|
|
! };
|
|
! if (v_overlaycolor && v_overlaycolor !== '') _opts.overlayColor = v_overlaycolor;
|
|
! v_plugininstance = v_instance.registerPlugin(WaveSurfer.Minimap.create(_opts));
|
|
!}
|
|
ProcedureReturn PluginInstance
|
|
EndProcedure
|
|
|
|
Procedure MinimapCreate_ex(Instance, OptionsJSON.s)
|
|
Protected PluginInstance
|
|
!if (v_instance && WaveSurfer.Minimap) {
|
|
! var _opts;
|
|
! try { _opts = JSON.parse(v_optionsjson); } catch(e) { _opts = {}; }
|
|
! v_plugininstance = v_instance.registerPlugin(WaveSurfer.Minimap.create(_opts));
|
|
!}
|
|
ProcedureReturn PluginInstance
|
|
EndProcedure
|
|
|
|
Procedure MinimapDestroy(PluginInstance)
|
|
DestroyPlugin(PluginInstance)
|
|
EndProcedure
|
|
|
|
; Spectrogram Plugin
|
|
Procedure SpectrogramCreate(Instance, Container.s = "", Height = 128, Labels = #False)
|
|
Protected PluginInstance
|
|
!if (v_instance && WaveSurfer.Spectrogram) {
|
|
! var _opts = {
|
|
! height: v_height,
|
|
! labels: !!v_labels
|
|
! };
|
|
! if (v_container && v_container !== '') _opts.container = v_container;
|
|
! v_plugininstance = v_instance.registerPlugin(WaveSurfer.Spectrogram.create(_opts));
|
|
!}
|
|
ProcedureReturn PluginInstance
|
|
EndProcedure
|
|
|
|
Procedure SpectrogramCreate_ex(Instance, OptionsJSON.s)
|
|
Protected PluginInstance
|
|
!if (v_instance && WaveSurfer.Spectrogram) {
|
|
! var _opts;
|
|
! try { _opts = JSON.parse(v_optionsjson); } catch(e) { _opts = {}; }
|
|
! v_plugininstance = v_instance.registerPlugin(WaveSurfer.Spectrogram.create(_opts));
|
|
!}
|
|
ProcedureReturn PluginInstance
|
|
EndProcedure
|
|
|
|
Procedure SpectrogramDestroy(PluginInstance)
|
|
DestroyPlugin(PluginInstance)
|
|
EndProcedure
|
|
|
|
; Events
|
|
Procedure OnReady(Instance, *Callback)
|
|
!if (v_instance) v_instance.on('ready', function(duration) { p_callback(duration); });
|
|
EndProcedure
|
|
|
|
Procedure OnPlay(Instance, *Callback)
|
|
!if (v_instance) v_instance.on('play', function() { p_callback(); });
|
|
EndProcedure
|
|
|
|
Procedure OnPause(Instance, *Callback)
|
|
!if (v_instance) v_instance.on('pause', function() { p_callback(); });
|
|
EndProcedure
|
|
|
|
Procedure OnFinish(Instance, *Callback)
|
|
!if (v_instance) v_instance.on('finish', function() { p_callback(); });
|
|
EndProcedure
|
|
|
|
Procedure OnTimeUpdate(Instance, *Callback)
|
|
!if (v_instance) v_instance.on('timeupdate', function(currentTime) { p_callback(currentTime); });
|
|
EndProcedure
|
|
|
|
Procedure OnSeeking(Instance, *Callback)
|
|
!if (v_instance) v_instance.on('seeking', function(currentTime) { p_callback(currentTime); });
|
|
EndProcedure
|
|
|
|
Procedure OnInteraction(Instance, *Callback)
|
|
!if (v_instance) v_instance.on('interaction', function(newTime) { p_callback(newTime); });
|
|
EndProcedure
|
|
|
|
Procedure OnClick(Instance, *Callback)
|
|
!if (v_instance) v_instance.on('click', function(relativeX) { p_callback(relativeX); });
|
|
EndProcedure
|
|
|
|
Procedure OnDecode(Instance, *Callback)
|
|
!if (v_instance) v_instance.on('decode', function(duration) { p_callback(duration); });
|
|
EndProcedure
|
|
|
|
Procedure OnLoading(Instance, *Callback)
|
|
!if (v_instance) v_instance.on('loading', function(percent) { p_callback(percent); });
|
|
EndProcedure
|
|
|
|
Procedure OnDestroy(Instance, *Callback)
|
|
!if (v_instance) v_instance.on('destroy', function() { p_callback(); });
|
|
EndProcedure
|
|
|
|
Procedure OnEvent(Instance, EventName.s, *Callback)
|
|
!if (v_instance) v_instance.on(v_eventname, function() { p_callback(); });
|
|
EndProcedure
|
|
|
|
; Data Export
|
|
Procedure.s GetMediaElement(Instance)
|
|
Protected Result.s = ""
|
|
!if (v_instance) {
|
|
! var _media = v_instance.getMediaElement();
|
|
! if (_media) v_result = _media.outerHTML || '';
|
|
!}
|
|
ProcedureReturn Result
|
|
EndProcedure
|
|
|
|
; Private Procedures
|
|
Procedure Handler_Download(url.s, success)
|
|
If success
|
|
; Core loaded - now load plugins if any
|
|
If plugins_to_load <> ""
|
|
_LoadNextPlugin()
|
|
Else
|
|
; No plugins requested - all done
|
|
_RestoreAMD()
|
|
is_loaded = #True
|
|
!wavesurfersb$g_download_callback(1);
|
|
EndIf
|
|
Else
|
|
_RestoreAMD()
|
|
!wavesurfersb$g_download_callback(0);
|
|
EndIf
|
|
EndProcedure
|
|
|
|
Procedure Handler_PluginDownload(url.s, success)
|
|
If success
|
|
; Plugin loaded - continue chain with next plugin
|
|
_LoadNextPlugin()
|
|
Else
|
|
_RestoreAMD()
|
|
!wavesurfersb$g_download_callback(0);
|
|
EndIf
|
|
EndProcedure
|
|
|
|
EndModule
|
|
|
|
; IDE Options = SpiderBasic 3.20 (Windows - x86)
|
|
; Folding = BAAAAAAAAAAg
|
|
; iOSAppOrientation = 0
|
|
; AndroidAppCode = 0
|
|
; AndroidAppOrientation = 0
|
|
; EnableXP
|
|
; DPIAware
|
|
; CompileSourceDirectory |