@@ -35,8 +35,10 @@ function strip(s) {
3535}
3636
3737let g_context ;
38+ let g_gainNode ;
3839let g_byteBeat ;
3940let g_filter ;
41+ let g_localSettings ;
4042const g_analyzers = [ ] ;
4143let g_splitter ;
4244let g_merger ;
@@ -90,9 +92,11 @@ function reconnect() {
9092 const lastNode = connectFor2Channels ( ) ;
9193 if ( g_filter ) {
9294 lastNode . connect ( g_filter ) ;
93- g_filter . connect ( g_context . destination ) ;
95+ g_filter . connect ( g_gainNode ) ;
96+ g_gainNode . connect ( g_context . destination ) ;
9497 } else {
95- lastNode . connect ( g_context . destination ) ;
98+ lastNode . connect ( g_gainNode ) ;
99+ g_gainNode . connect ( g_context . destination ) ;
96100 }
97101 g_context . resume ( ) ;
98102}
@@ -131,12 +135,36 @@ const setVisualizer = ndx => {
131135 setSelectOption ( visualTypeElem , ndx ) ;
132136} ;
133137
138+ try {
139+ g_localSettings = JSON . parse ( localStorage . getItem ( 'localSettings' ) ) ;
140+ } catch {
141+ }
142+
143+ {
144+ if ( ! g_localSettings || typeof g_localSettings !== 'object' ) {
145+ g_localSettings = { } ;
146+ }
147+ const defaultSettings = {
148+ volume : 100 ,
149+ } ;
150+ for ( const [ key , value ] of Object . entries ( defaultSettings ) ) {
151+ if ( typeof g_localSettings [ key ] != typeof value ) {
152+ g_localSettings [ key ] = value ;
153+ }
154+ }
155+ }
156+
157+ function saveSettings ( ) {
158+ localStorage . setItem ( 'localSettings' , JSON . stringify ( g_localSettings ) ) ;
159+ }
160+
134161async function main ( ) {
135162 canvas = $ ( 'visualization' ) ;
136163 controls = $ ( 'controls' ) ;
137164
138165 g_context = new AudioContext ( ) ;
139166 g_context . resume ( ) ; // needed for safari
167+ g_gainNode = new GainNode ( g_context ) ;
140168 await ByteBeatNode . setup ( g_context ) ;
141169 g_byteBeat = new ByteBeatNode ( g_context ) ;
142170
@@ -204,6 +232,22 @@ async function main() {
204232 return select ;
205233 }
206234
235+ function addVerticalRange ( options , props ) {
236+ const fn = props . onChange ;
237+ const valueElem = el ( 'div' , { textContent : options . value ?? 0 } ) ;
238+ return el ( 'div' , { className : 'vertical-range' , tabIndex : 0 } , [
239+ valueElem ,
240+ el ( 'div' , { className : 'vertical-range-holder' } , [
241+ el ( 'input' , { ...options , type : 'range' , onInput : ( e ) => {
242+ valueElem . textContent = e . target . value ;
243+ if ( fn ) {
244+ fn ( e ) ;
245+ }
246+ } , } ) ,
247+ ] ) ,
248+ ] )
249+ }
250+
207251 beatTypeElem = addSelection ( s_beatTypes , 0 , {
208252 onChange ( event ) {
209253 g_byteBeat . setType ( event . target . selectedIndex ) ;
@@ -229,6 +273,15 @@ async function main() {
229273 } ) ;
230274 controls . appendChild ( sampleRateElem ) ;
231275
276+ const volumeElem = addVerticalRange ( { min : 1 , max : 100 , step : 1 , value : g_localSettings . volume } , {
277+ onChange ( event ) {
278+ g_gainNode . gain . value = event . target . value / 100 ;
279+ g_localSettings . volume = parseInt ( event . target . value ) ;
280+ saveSettings ( ) ;
281+ } ,
282+ } ) ;
283+ controls . appendChild ( volumeElem ) ;
284+
232285 if ( g_slow ) {
233286 g_visualizers = [
234287 { name : 'none' , visualizer : new NullVisualizer ( ) } ,
0 commit comments