'+ ico( kv.mute ? 'volume mute bl' : 'volume' ) + ico( 'add' )
+ + ico( kv.mute ? 'volume mute' : 'volume' ) + iconadd( chout === kv.sources.length )
+'';
kv.sources.forEach( ( s, si ) => {
var source = data[ i ].sources[ si ];
var channel = source.channel;
var opts = optin.replace( '>'+ channel, ' selected>'+ channel );
- var val = util.dbRound( source.gain );
- var disabled = ( source.mute ? ' disabled' : '' );
- li += '
'+ ico( 'input liicon' ) +''+ opts +' '
- + ico( source.mute ? 'volume mute bl' : 'volume' ) +''+ val +' '
- +' '
- +''+ ico( 'minus' ) + ico( 'set0' ) + ico( 'plus' ) +'
'
+ var gain = source.gain || 0;
+ var disabled = source.mute ? ' disabled' : '';
+ var linear = source.scale === 'linear';
+ li += ' '+ ico( 'input liicon' ) +''+ opts +' '
+ + render.htmlRange( linear ? 10 : 100, gain, disabled )
+ + ico( source.mute ? 'volume mute' : 'volume' )
+ ico( source.inverted ? 'inverted bl' : 'inverted' )
+ + ico( linear ? 'linear bl' : 'linear' )
+' ';
} );
} );
- render.toggle( li, 'sub' );
+ $( '#'+ V.tab +' .entries.sub' ).html( li );
+ render.toggle( 'sub' );
selectSet( $( '#mixers select' ) );
- } //---------------------------------------------------------------------------------------------
+ } //-----------------------------------------------------------------------------------
+ , processors : () => {
+ if ( ! PRO || ! Object.keys( PRO ).length ) return
+
+ var data = render.dataSort( 'processors' );
+ var li = '';
+ $.each( data, ( k, v ) => {
+ var param = jsonClone( v.parameters );
+ [ 'channels', 'monitor_channels', 'process_channels' ].forEach( k => delete param[ k ] );
+ li += '
'+ ico( 'processors liicon edit' )
+ +''+ k +'
'
+ +''+ v.type +' · '+ render.json2string( param )+'
'
+ +' '
+ } );
+ $( '#'+ V.tab +' .entries.main' ).html( li );
+ render.toggle();
+ } //-----------------------------------------------------------------------------------
, pipeline : () => {
- graph.list();
+ $( '.i-flowchart' ).toggleClass( 'disabled', PIP.length === 0 );
+ if ( ! PIP.length ) return
+
var li = '';
PIP.forEach( ( el, i ) => li += render.pipe( el, i ) );
- render.toggle( li );
+ $( '#'+ V.tab +' .entries.main' ).html( li );
+ render.toggle();
render.sortable( 'main' );
- graph.refresh();
}
, pipe : ( el, i ) => {
+ var icon = 'pipeline liicon';
if ( el.type === 'Filter' ) {
- var graph = 'graph';
- var each = '
' + el.type +'
'
- +'
channel '+ el.channel +': '+ el.names.join( ', ' ) +'
';
- if ( i in V.graphlist ) each += V.graphlist[ i ];
+ icon += ' graph';
+ var li = '
' + el.type +'
'
+ +'
channel '+ el.channel +': '+ el.names.join( ', ' ) +'
';
} else {
- var graph = '';
- var each = '
Mixer: '+ el.name;
+ var li = '
Mixer: '+ el.name;
}
- return '
'+ ico( 'pipeline liicon '+ graph ) + each +' '
+ var $graph = $( '#filters .entries.main li[data-index="'+ i +'"]' ).find( '.divgraph' );
+ if ( $graph.length ) li += $graph[ 0 ].outerHTML;
+ return '
'+ ico( icon ) + li +' '
}
- , pipelineSub : ( index ) => {
+ , pipelineSub : index => {
var data = PIP[ index ];
var li = '
'+ ico( 'pipeline' ) +'Channel '+ data.channel + ico( 'add' ) + ico( 'back' ) +' ';
data.names.forEach( ( name, i ) => li += render.pipeFilter( name, i ) );
- render.toggle( li, 'sub' );
+ $( '#'+ V.tab +' .entries.sub' ).html( li );
+ render.toggle( 'sub' );
render.sortable( 'sub' );
}
, pipeFilter : ( name, i ) => {
@@ -768,7 +941,7 @@ var render = {
+'
'+ FIL[ name ].type +' · '+ render.json2string( FIL[ name ].parameters ) +'
'
+''
}
- , sortable : ( el ) => {
+ , sortable : el => {
if ( el in V.sortable ) return
V.sortable[ el ] = new Sortable( $( '#pipeline .entries.'+ el )[ 0 ], {
@@ -791,7 +964,7 @@ var render = {
graph.pipeline();
}
} );
- } //---------------------------------------------------------------------------------------------
+ } //-----------------------------------------------------------------------------------
, devices : () => {
var li = '';
[ 'playback', 'capture' ].forEach( d => {
@@ -799,43 +972,55 @@ var render = {
var data = jsonClone( dev );
[ 'device', 'type' ].forEach( k => delete data[ k ] );
li += '
'+ ico( d === 'capture' ? 'input' : 'output' )
- +''+ util.key2label( d ) +' · '+ render.typeReplace( dev.type )
- + ( 'device' in dev ? ' · '+ dev.device +'
' : '' )
- +''+ render.json2string( data ) +'
'
- +' ';
+ +'
'+ common.key2label( d ) +' · '+ render.typeReplace( dev.type )
+ + ( 'device' in dev ? ' · '+ dev.device +'
' : '' )
+ +'
'+ render.json2string( data ) +'
'
+ +'';
} );
$( '#devices .entries.main' ).html( li );
- [ 'enable_rate_adjust', 'stop_on_rate_change', 'enable_resampling' ].forEach( k => S[ k ] = DEV[ k ] );
var labels = '';
var values = '';
- C.sampling.forEach( k => {
- labels += util.key2label( k ) +'
';
- values += DEV[ k ].toLocaleString() +'
';
+ D0.main.forEach( k => {
+ if ( k in DEV ) {
+ labels += common.key2label( k ) +'
';
+ values += DEV[ k ].toLocaleString() +'
';
+ }
} );
var keys = [];
- if ( DEV.enable_rate_adjust ) keys.push( 'adjust_period', 'target_level' );
- if ( DEV.enable_resampling ) keys.push( 'resampler_type', 'capture_samplerate' );
- if ( DEV.stop_on_rate_change ) keys.push( 'rate_measure_interval' );
+ if ( S.enable_rate_adjust ) keys.push( 'adjust_period', 'target_level' );
+ if ( S.stop_on_rate_change ) keys.push( 'rate_measure_interval' );
if ( keys.length ) {
labels += '
';
values += '
';
keys.forEach( k => {
- labels += util.key2label( k ) +'
';
+ labels += common.key2label( k ) +'
';
values += DEV[ k ] +'
';
} );
}
+ if ( S.resampler ) {
+ labels += 'Resampler
'
+ values += DEV.resampler.type +'
';
+ if ( 'profile' in DEV.resampler ) {
+ labels += 'Profile
'
+ values += DEV.resampler.profile +'
';
+ }
+ if ( S.capture_samplerate ) {
+ labels += 'Capture samplerate
'
+ values += DEV.capture_samplerate +'
';
+ }
+ }
$( '#divsampling .label' ).html( labels );
$( '#divsampling .value' ).html( values.replace( /bluealsa|Bluez/, 'BlueALSA' ) );
switchSet();
- $( '#divenable_rate_adjust input' ).toggleClass( 'disabled', DEV.enable_resampling && DEV.resampler_type === 'Synchronous' );
- } //---------------------------------------------------------------------------------------------
+ $( '#divenable_rate_adjust input' ).toggleClass( 'disabled', S.resampler && DEV.resampler.type === 'Synchronous' );
+ } //-----------------------------------------------------------------------------------
, config : () => {
var li = '';
S.lsconfigs.forEach( f => {
li += '
'+ ico( 'file liicon' ) +''+ f +' ';
} );
- $( '#config .entries.main' ).html( li );
- } //---------------------------------------------------------------------------------------------
+ $( '#'+ V.tab +' .entries.main' ).html( li );
+ } //-----------------------------------------------------------------------------------
, dataSort : () => {
var kv = S.config[ V.tab ];
var data = {};
@@ -843,7 +1028,26 @@ var render = {
keys.sort().forEach( k => data[ k ] = kv[ k ] );
return data
}
- , json2string : ( json ) => {
+ , htmlRange : ( scale, gain, disabled ) => {
+ if ( scale === 100 ) { // filter - Gain / mixer - dB
+ var db = gain;
+ var value = gain;
+ } else {
+ var db = gain.toFixed( 1 );
+ var value = gain * 10
+ }
+ var disabled = {
+ range : disabled
+ , min : disabled || ( value === -100 ? ' disabled' : '' )
+ , max : disabled || ( value === 100 ? ' disabled' : '' )
+ , db : disabled || ( value === 0 ? ' disabled' : '' )
+ }
+ return '
'
+ +'
'
+ +'
'
+ +'
'+ db +' '
+ }
+ , json2string : json => {
return JSON.stringify( json )
.replace( /[{"}]/g, '' )
.replace( /type:|filename:.*\/|format:TEXT,|skip_bytes_lines:.*|read_bytes_lines:.*/g, '' )
@@ -851,143 +1055,111 @@ var render = {
.replace( /([:,])/g, '$1 ' )
}
, prevconfig : () => V.prevconfig[ V.tab ] = jsonClone( S.config[ V.tab ] )
- , toggle : ( li, sub ) => {
- var ms = sub ? [ 'main', 'sub' ] : [ 'sub', 'main' ];
- $( '#'+ V.tab +' .entries.'+ ms[ 0 ] ).addClass( 'hide' );
- $( '#'+ V.tab +' .entries.'+ ms[ 1 ] )
- .html( li )
- .removeClass( 'hide' );
+ , toggle : ( sub ) => {
+ var $main = $( '#'+ V.tab +' .entries.main' );
+ var $sub = $( '#'+ V.tab +' .entries.sub' );
+ if ( sub || $main.hasClass( 'hide' ) ) {
+ $main.addClass( 'hide' );
+ $sub.removeClass( 'hide' );
+ } else {
+ $main.removeClass( 'hide' );
+ $sub.addClass( 'hide' );
+ }
$( '#menu' ).addClass( 'hide' );
+ if ( [ 'filters', 'pipeline' ].includes( V.tab ) && V.graph[ V.tab ].length ) {
+ var val = V.tab === 'filters' ? 'name' : 'index';
+ $( '#'+ V.tab +' .entries.main li' ).each( ( i, el ) => {
+ var $el = $( el );
+ if ( V.graph[ V.tab ].includes( $el.data( val ) ) ) graph.plot( $el );
+ } );
+ }
}
- , typeReplace : ( str ) => {
+ , typeReplace : str => {
return str
.replace( 'Alsa', 'ALSA' )
.replace( 'Std', 'std' )
}
}
-var setting = {
- filter : ( type, subtype, name, newname ) => {
- if ( name ) {
- var ekv = { type : type }
- $.each( FIL[ name ].parameters, ( k, v ) => ekv[ k === 'type' ? 'subtype' : k ] = v );
- }
- // select
- var selectlabel = [ 'type' ];
- var select = [ jsonClone( C.type ) ];
- var values = { type: type }
- if ( subtype ) {
- selectlabel.push( 'subtype' )
- select.push( jsonClone( C.subtype[ type ] ) );
- values.subtype = subtype;
- var key_val = subtype in F ? jsonClone( F[ subtype ] ) : jsonClone( F[ type ] );
- if ( subtype === 'Uniform' ) key_val.amplitude = 1;
- }
- if ( ! key_val ) var key_val = jsonClone( F[ type ] );
- if ( 'select' in key_val ) {
- var kv = key_val.select;
- var k = Object.keys( kv );
- selectlabel = [ ...selectlabel, ...k ];
- if ( [ 'Raw', 'Wav' ].includes( subtype ) ) {
- var lscoef = jsonClone( S[ subtype === 'Raw' ? 'lscoefraw' : 'lscoefwav' ] );
- select = [ ...select, lscoef ];
+var setting = {
+ filter : ( type, subtype, name ) => {
+ var list = subtype ? F[ type ][ subtype ] : F[ type ];
+ if ( type === 'Biquad' ) {
+ if ( [ 'Hig', 'Low' ].includes( subtype.slice( 0, 3 ) ) ) {
+ var vsubtype = subtype.replace( /High|Low/, '' );
+ } else if ( subtype.slice( -4 ) === 'pass' ) {
+ var vsubtype = 'notch';
+ } else if ( subtype === 'AllpassFO' ) {
+ var vsubtype = 'passFO';
+ } else {
+ var vsubtype = type;
}
- if ( name ) k.forEach( key => kv[ key ] = ekv[ key ] );
- values = { ...values, ...kv };
- }
- selectlabel = util.labels2array( selectlabel );
- // text
- var textlabel = [ 'name' ];
- values.name = name || newname;
- if ( 'text' in key_val ) {
- var kv = key_val.text;
- var k = Object.keys( kv );
- textlabel = [ ...textlabel, ...k ];
- if ( name ) k.forEach( key => kv[ key ] = ekv[ key ] );
- values = { ...values, ...kv };
+ } else if ( type === 'BiquadCombo' ) {
+ var vsubtype = [ 'Tilt', 'FivePointPeq', 'GraphicEqualizer' ].includes( subtype ) ? subtype : type;
+ } else {
+ var vsubtype = type;
}
- textlabel = util.labels2array( textlabel );
- // number
- var numberlabel = false;
- if ( 'number' in key_val ) {
- var kv = key_val.number;
- var k = Object.keys( kv );
- numberlabel = k;
- if ( name ) {
- k.forEach( key => {
- if ( [ 'q', 'samples' ].includes( key ) ) {
- if ( ! ( 'q' in ekv ) ) {
- delete kv.q;
- key = 'samples';
- }
- numberlabel[ numberlabel.length - 1 ] = key;
- }
- kv[ key ] = ekv[ key ];
+ var values = F.values[ vsubtype ];
+ values.type = type;
+ if ( subtype ) values.subtype = subtype;
+ if ( name ) {
+ values.name = name;
+ if ( subtype === 'FivePointPeq' ) {
+ Object.keys( F0.FivePointPeq ).forEach( k => {
+ values[ k ] = [];
+ F0.FivePointPeq[ k ].forEach( key => values[ k ].push( FIL[ name ].parameters[ key ] ) );
+ } );
+ } else if ( subtype === 'GraphicEqualizer' ) {
+ $.each( FIL[ name ].parameters, ( k, v ) => {
+ if ( k === 'type' ) return
+
+ k === 'gains' ? values.bands = v.length : values[ k ] = v;
+ } );
+ } else {
+ $.each( FIL[ name ].parameters, ( k, v ) => {
+ if ( k === 'type' ) return
+
+ values[ k ] = v;
} );
}
- values = { ...values, ...kv };
- numberlabel = util.labels2array( numberlabel );
}
- // radio - q / samples
- var radio = false;
- if ( 'radio' in key_val ) {
- radio = key_val.radio;
- values = { ...values, radio: numberlabel[ numberlabel.length - 1 ] };
- }
- // checkbox
- var checkbox = false;
- if ( 'checkbox' in key_val ) {
- var kv = key_val.checkbox;
- var k = Object.keys( kv );
- checkbox = util.labels2array( k );
- if ( name ) k.forEach( key => kv[ key ] = ekv[ key ] );
- values = { ...values, ...kv };
- }
- if ( 'filename' in values ) values.filename = values.filename.split( '/' ).pop();
var title = name ? 'Filter' : 'Add Filter';
info( {
icon : V.tab
, title : title
- , selectlabel : selectlabel
- , select : select
- , textlabel : textlabel
- , numberlabel : numberlabel
- , radio : radio
- , radiosingle : true
- , checkbox : checkbox
+ , list : list
, boxwidth : 198
- , order : [ 'select', 'text', 'number', 'radio', 'checkbox' ]
, values : values
, checkblank : true
- , checkchanged : name
+ , checkchanged : name in FIL
, beforeshow : () => {
- $( '#infoContent td:first-child' ).css( 'min-width', '125px' );
- var $tdname = $( '#infoContent td' ).filter( function() {
- return $( this ).text() === 'Name'
- } );
- $( '#infoContent tr' ).eq( 0 ).before( $tdname.parent() );
- var $select = $( '#infoContent select' );
- var $selecttype = $select.eq( 0 );
- $selecttype.on( 'input', function() {
- var type = $( this ).val();
- var subtype = type in C.subtype ? C.subtype[ type ][ 0 ] : '';
- setting.filter( type, subtype, '', infoVal().name );
+ if ( name ) $( '#infoList select' ).slice( 0, 2 ).prop( 'disabled', true );
+ $( '#infoList td:first-child' ).css( 'min-width', '125px' );
+ var $select = $( '#infoList select' );
+ $select.eq( 0 ).on( 'input', function() {
+ var val = infoVal();
+ var subtype = val.type in F0.subtype ? F0.subtype[ val.type ][ 2 ][ 0 ] : '';
+ setting.filter( val.type, subtype, val.name );
} );
- if ( $select.length > 1 ) {
+ if ( subtype ) {
$select.eq( 1 ).on( 'input', function() {
- var type = $selecttype.val();
- var subtype = $( this ).val();
- setting.filter( type, subtype, '', infoVal().name );
+ var val = infoVal();
+ if ( val.type === 'Conv' && [ 'Raw', 'Wav' ].includes( val.subtype ) && ! S.lscoeffs.length ) {
+ info( {
+ icon : V.tab
+ , title : title
+ , message : 'Filter files not available.'
+ , ok : () => setting.filter( 'Conv', subtype, val.name )
+ } );
+ } else {
+ setting.filter( val.type, val.subtype, val.name );
+ }
} );
}
- if ( radio ) {
- var $tr = $( '#infoContent .trradio' ).prev();
- var itr = $tr.index()
- var $label = $tr.find( 'td' ).eq( 0 );
- var $radio = $( '#infoContent input:radio' );
+ var $radio = $( '#infoList input:radio' );
+ if ( $radio.length ) {
+ var $label = $radio.parents( 'tr' ).prev().find( 'td' ).eq( 0 );
$radio.on( 'input', function() {
- var val = $( this ).filter( ':checked' ).val();
- I.keys[ itr ] = val.toLowerCase();
- $label.text( val );
+ $label.text( $( this ).filter( ':checked' ).parent().text() );
} );
}
}
@@ -996,15 +1168,39 @@ var setting = {
var newname = val.name;
type = val.type;
subtype = val.subtype;
- var param = { type: subtype };
- [ 'name', 'type', 'subtype', 'radio' ].forEach( k => delete val[ k ] );
+ if ( type === 'DiffEq' ) {
+ [ 'a', 'b' ].forEach( k => val[ k ] = common.list2array( val[ k ] ) );
+ } else if ( subtype === 'FivePointPeq' ) {
+ Object.keys( F0.FivePointPeq ).forEach( k => {
+ var v = common.list2array( val[ k ] );
+ F0.FivePointPeq[ k ].forEach( ( key, i ) => {
+ val[ key ] = v[ i ];
+ } );
+ delete val[ k ];
+ } );
+ } else if ( subtype === 'GraphicEqualizer' ) {
+ var bands = val.bands;
+ delete val.bands;
+ val.gains = Array( bands ).fill( 0 );
+ } else if ( subtype === 'Values' ) {
+ val.values = common.list2array( val.values );
+ }
+ var param = {}
+ if ( 'subtype' in val ) param.type = subtype;
+ [ 'name', 'type', 'subtype' ].forEach( k => delete val[ k ] );
+ if ( 'q' in values && 'unit' in values ) {
+ var q = val.q;
+ var unit = val.unit;
+ [ 'q', 'bandwidth', 'slope', 'unit' ].forEach( k => delete val[ k ] );
+ val[ unit ] = q;
+ }
$.each( val, ( k, v ) => param[ k ] = v );
if ( 'filename' in param ) {
param.filename = '/srv/http/data/camilladsp/coeffs/'+ param.filename;
if ( subtype === 'Raw' ) param.format = 'TEXT';
}
FIL[ newname ] = { type: type, parameters : param }
- if ( name !== newname ) {
+ if ( name in FIL && name !== newname ) {
delete FIL[ name ];
PIP.forEach( p => {
if ( p.type === 'Filter' ) {
@@ -1018,14 +1214,14 @@ var setting = {
render.filters();
}
} );
- } //---------------------------------------------------------------------------------------------
- , mixer : ( name ) => {
+ } //-----------------------------------------------------------------------------------
+ , mixer : name => {
var title = name ? 'Mixer' : 'Add Mixer'
info( {
icon : V.tab
, title : title
, message : name ? 'Rename
'+ name +' to:' : ''
- , textlabel : 'Name'
+ , list : [ 'Name', 'text' ]
, values : name
, checkblank : true
, checkchanged : name
@@ -1071,46 +1267,25 @@ var setting = {
} );
}
, mixerMap : ( name, index ) => {
- var option = {
- dest : htmlOption( DEV.playback.channels )
- , source : htmlOption( DEV.capture.channels )
- }
- var trdest = `
-
- ${ option.dest }
-
-
- `;
- var trsource = `
-
- Source Gain Mute Invert
-
-
-
- ${ option.source }
-
-
- `;
-
- if ( index === '' ) {
+ if ( index === 'dest' ) {
var title = 'Add Destination';
info( {
- icon : V.tab
- , title : title
- , content : '
'
- , contentcssno : true
- , values : [ MIX[ name ].mapping.length, 0, 0, false, false ]
- , checkblank : true
- , ok : () => {
- var s = {}
- $( '.trsource' ).find( 'select, input' ).each( ( i, el ) => {
- var $this = $( el )
- s[ $this.data( 'k' ) ] = $this.is( ':checkbox' ) ? $this.prop( 'checked' ) : +$this.val();
- } );
+ icon : V.tab
+ , title : title
+ , list : [ 'Playback channel', 'select', DEV.playback.channels ]
+ , boxwidth : 70
+ , ok : () => {
var mapping = {
- dest : +$( '.trsource select' ).val()
+ dest : infoVal()
, mute : false
- , sources : [ s ]
+ , sources : [
+ {
+ channel : 0
+ , gain : 0
+ , inverted : false
+ , mute : false
+ }
+ ]
}
MIX[ name ].mapping.push( mapping );
setting.save( title, 'Save ...' );
@@ -1120,46 +1295,82 @@ var setting = {
} else {
var title = 'Add Source';
info( {
- icon : V.tab
- , title : title
- , content : '
'
- , contentcssno : true
- , values : [ 0, 0, false, false ]
- , checkblank : true
- , ok : () => {
- var s = {}
- $( '.trsource' ).find( 'select, input' ).each( ( i, el ) => {
- var $this = $( el )
- s[ $this.data( 'k' ) ] = $this.is( ':checkbox' ) ? $this.prop( 'checked' ) : +$this.val();
- } );
- MIX[ name ].mapping[ index ].sources.push( s );
+ icon : V.tab
+ , title : title
+ , list : [ 'Capture channel', 'select', DEV.capture.channels ]
+ , boxwidth : 70
+ , ok : () => {
+ var source = {
+ channel : infoVal()
+ , gain : 0
+ , inverted : false
+ , mute : false
+ }
+ MIX[ name ].mapping[ index ].sources.push( source );
setting.save( title, 'Save ...' );
render.mixersSub( name );
}
} );
}
- } //---------------------------------------------------------------------------------------------
+ } //-----------------------------------------------------------------------------------
+ , processor : name => {
+ var type = name ? PRO[ name ].type : 'Compressor';
+ var values = jsonClone( P.values[ type ] );
+ if ( name ) {
+ $.each( PRO[ name ].parameters, ( k, v ) => values[ k ] = v );
+ values.name = name;
+ }
+ var title = name ? 'Processor' : 'Add Processor'
+ info( {
+ icon : V.tab
+ , title : title
+ , list : name ? P[ PRO[ name ].type ] : P.Compressor
+ , boxwidth : 150
+ , values : values
+ , checkblank : true
+ , checkchanged : name
+ , beforeshow : () => {
+ if ( name ) $( '#infoList select' ).eq( 0 ).prop( 'disabled', true );
+ }
+ , ok : () => {
+ var val = infoVal();
+ var typenew = val.type;
+ var namenew = val.name;
+ [ 'name', 'type' ].forEach( k => delete val[ k ] );
+ [ 'monitor_channels', 'process_channels' ].forEach( k => val[ k ] = common.list2array( val[ k ] ) );
+ if ( ! PRO ) {
+ S.config.processors = {}
+ PRO = S.config.processors;
+ }
+ PRO[ namenew ] = { type: v.type, parameters: val }
+ if ( name in PRO && name !== namenew ) delete PRO[ name ];
+ setting.save( title, name ? 'Change ...' : 'Save ...' );
+ render.processors();
+ }
+ } );
+ } //-----------------------------------------------------------------------------------
, pipeline : () => {
var filters = Object.keys( FIL );
info( {
- icon : V.tab
- , title : 'Add Pipeline'
- , tablabel : [ ico( 'filters' ) +' Filter', ico( 'mixers' ) +' Mixer' ]
- , tab : [ '', setting.pipelineMixer ]
- , selectlabel : [ 'Channel', 'Filters' ]
- , select : [ [ ...Array( DEV.playback.channels ).keys() ], filters ]
- , beforeshow : () => {
- $( '#infoContent .select2-container' ).eq( 0 ).addClass( 'channel' )
- $( '#infoContent td' ).last().append( ico( 'add' ) );
- var tradd = '
'+ ico( 'remove' ) +'';
- $( '#infoContent' ).on( 'click', '.i-add', function() {
- $( '#infoContent table' ).append( tradd.replace( 'VALUE', $( '#infoContent select' ).eq( 1 ).val() ) );
+ icon : V.tab
+ , title : 'Add Pipeline'
+ , tablabel : [ ico( 'filters' ) +' Filter', ico( 'mixers' ) +' Mixer' ]
+ , tab : [ '', setting.pipelineMixer ]
+ , list : [
+ [ 'Channel', 'select', [ ...Array( DEV.playback.channels ).keys() ] ]
+ , [ 'Filters', 'select', filters, ico( 'add' ) ]
+ ]
+ , beforeshow : () => {
+ $( '#infoList .select2-container' ).eq( 0 ).attr( 'style', 'width: 70px !important' );
+ var tradd = '
'+ ico( 'remove' ) +' ';
+ $( '#infoList' ).on( 'click', '.i-add', function() {
+ $( '#infoList table' ).append( tradd.replace( 'VALUE', $( '#infoList select' ).eq( 1 ).val() ) );
} ).on( 'click', '.i-remove', function() {
$( this ).parents( 'tr' ).remove();
} );
}
- , ok : () => {
- var $input = $( '#infoContent input' );
+ , ok : () => {
+ var $input = $( '#infoList input' );
if ( $input.length ) {
var names = [];
$input.each( ( i, el ) => names.push( $( el ).val() ) );
@@ -1168,7 +1379,7 @@ var setting = {
}
PIP.push( {
type : 'Filter'
- , channel : +$( '#infoContent select' ).eq( 0 ).val()
+ , channel : +$( '#infoList select' ).eq( 0 ).val()
, names : names
} );
setting.pipelineSave();
@@ -1176,14 +1387,23 @@ var setting = {
} );
}
, pipelineMixer : () => {
+ if ( ! Object.keys( MIX ).length ) {
+ info( {
+ icon : V.tab
+ , title : 'Add Pipeline'
+ , message : 'No mixers found.'
+ , ok : setting.pipeline
+ } );
+ return
+ }
+
info( {
- icon : V.tab
- , title : 'Add Pipeline'
- , tablabel : [ ico( 'filters' ) +' Filter', ico( 'mixers' ) +' Mixer' ]
- , tab : [ setting.pipeline, '' ]
- , selectlabel : 'Mixers'
- , select : Object.keys( MIX )
- , ok : () => {
+ icon : V.tab
+ , title : 'Add Pipeline'
+ , tablabel : [ ico( 'filters' ) +' Filter', ico( 'mixers' ) +' Mixer' ]
+ , tab : [ setting.pipeline, '' ]
+ , list : [ 'Mixers', 'select', Object.keys( MIX ) ]
+ , ok : () => {
PIP.push( {
type : 'Mixer'
, name : infoVal()
@@ -1196,94 +1416,41 @@ var setting = {
setting.save( 'Add Pipeline', 'Save ...' );
render.pipeline();
}
- , sortRefresh : ( k ) => {
+ , sortRefresh : k => {
V.sortable[ k ].destroy();
delete V.sortable[ k ];
render.sortable( k );
- } //---------------------------------------------------------------------------------------------
+ } //-----------------------------------------------------------------------------------
, device : ( dev, type ) => {
- var key_val, kv, k, v;
- var data = jsonClone( DEV[ dev ] );
- var type = type || data.type;
- // select
- var selectlabel = [ 'type' ];
- var select = [ jsonClone( C.devicetype[ dev ] ) ];
- var values = { type: type }
- key_val = jsonClone( CP[ dev ][ type ] );
- if ( 'select' in key_val ) {
- kv = key_val.select;
- k = Object.keys( kv );
- k.forEach( key => {
- if ( key === 'format' ) {
- var s = jsonClone( dev === 'capture' ? C.format : S.format );
- var v = { format: data.format };
- } else if ( key === 'device' ) {
- var s = jsonClone( C.devices[ dev ] );
- var v = { device: data.device };
- } else if ( key === 'filename' ) {
- var s = S.lscoef.length ? S.lscoeffs : [ '(n/a)' ];
- var v = { filename: data.filename };
- }
- selectlabel = [ ...selectlabel, key ];
- select = [ ...select, s ];
- values = { ...values, ...v };
- } );
- }
- selectlabel = util.labels2array( selectlabel );
- // text
- var textlabel = false;
- if ( 'text' in key_val ) {
- kv = key_val.text;
- k = Object.keys( kv );
- textlabel = util.labels2array( k );
- k.forEach( key => {
- if ( key in data ) kv[ key ] = data[ key ];
- } );
- values = { ...values, ...kv };
- }
- // number
- var numberlabel = false;
- if ( 'number' in key_val ) {
- kv = key_val.number;
- k = Object.keys( kv );
- numberlabel = util.labels2array( k );
- k.forEach( key => {
- if ( key in data ) kv[ key ] = data[ key ];
- } );
- values = { ...values, ...kv };
- }
- // checkbox
- var checkbox = false;
- if ( 'checkbox' in key_val ) {
- kv = key_val.checkbox;
- k = Object.keys( kv );
- checkbox = util.labels2array( k );
- k.forEach( key => {
- if ( key in data ) kv[ key ] = data[ key ];
- } );
- values = { ...values, ...kv };
- }
- $.each( v, ( k, v ) => values[ k ] = v );
- var title = util.key2label( dev );
+ var type = type || 'Alsa';
+ var vtype = type === 'File' && dev === 'playback' ? 'FileP' : type;
+ var values = jsonClone( D.values[ vtype ] );
+ values.type = type;
+ if ( DEV[ dev ].type === type ) $.each( values, ( k, v ) => values[ k ] = DEV[ dev ][ k ] );
+ var title = common.key2label( dev );
info( {
icon : V.tab
, title : title
- , selectlabel : selectlabel
- , select : select
- , textlabel : textlabel
- , numberlabel : numberlabel
- , checkbox : checkbox
+ , list : D[ dev ][ type ]
, boxwidth : 198
- , order : [ 'select', 'text', 'number', 'checkbox' ]
, values : values
, checkblank : true
- , checkchanged : type === data.type
+ , checkchanged : true
, beforeshow : () => {
- $( '#infoContent input[type=number]' ).css( 'width', '70px' );
- $( '#infoContent td:first-child' ).css( 'width', '128px' );
- var $select = $( '#infoContent select' );
- $select.eq( 0 ).on( 'input', function() {
- setting.device( dev, $( this ).val() );
+ $( '#infoList input[type=number]' ).css( 'width', '70px' );
+ $( '#infoList td:first-child' ).css( 'width', '128px' );
+ $( '#infoList select' ).eq( 0 ).on( 'input', function() {
+ var typenew = $( this ).val();
+ if ( typenew === 'File' && ! S.lsraw.length ) {
+ info( {
+ icon : V.tab
+ , title : title
+ , message : 'No raw files available.'
+ , ok : () => setting.device( dev, type )
+ } );
+ } else {
+ setting.device( dev, typenew );
+ }
} );
}
, ok : () => {
@@ -1292,31 +1459,30 @@ var setting = {
}
} );
}
- , devicesampling : () => {
- var textlabel = [ ...C.sampling ].slice( 1 );
- textlabel.push( 'Other' );
- var values = {};
- C.sampling.forEach( k => values[ k ] = DEV[ k ] );
- if ( ! C.samplerate.includes( DEV.samplerate ) ) values.samplerate = 'Other';
- values.other = values.samplerate;
- var title = util.key2label( V.tab );
+ , main : () => {
+ var values = {};
+ D0.main.forEach( k => {
+ values[ k ] = DEV[ k ];
+ if ( k === 'samplerate' ) values.other = DEV.samplerate;
+ } );
+ if ( ! D0.samplerate.includes( DEV.samplerate ) ) values.samplerate = 'Other';
+ var title = common.tabTitle();
info( {
icon : V.tab
, title : title
- , selectlabel : 'Sample Rate'
- , select : C.samplerate
- , textlabel : util.labels2array( textlabel )
+ , list : D.main
, boxwidth : 120
- , order : [ 'select', 'text' ]
, values : values
, checkblank : true
, checkchanged : true
, beforeshow : () => {
- $( '.trselect' ).after( $( 'tr' ).last() );
- var $trother = $( '.trtext' ).eq( 0 );
+ var $trother = $( '#infoList tr' ).eq( 1 );
$trother.toggleClass( 'hide', values.samplerate !== 'Other' );
- $( '.trselect select' ).on( 'input', function() {
- setting.hidetrinfo( $trother, $( this ).val() );
+ $( '#infoList select' ).on( 'input', function() {
+ var rate = $( this ).val();
+ var other = rate === 'Other';
+ $trother.toggleClass( 'hide', ! other );
+ if ( ! other ) $trother.find( 'input' ).val( rate );
} );
}
, ok : () => {
@@ -1328,103 +1494,158 @@ var setting = {
render.devices();
}
} );
- } //---------------------------------------------------------------------------------------------
- , resampling : ( freeasync ) => {
- var rateadjust = DEV.enable_rate_adjust;
- var samplerate = DEV.capture_samplerate;
- var selectlabel = [ 'Resampler type', 'Capture samplerate' ];
- var select = [ rateadjust ? C.sampletype.slice( 0, -1 ) : C.sampletype, C.samplerate ];
- var numberlabel = [ 'Other' ];
- var capturerate = C.samplerate.includes( samplerate ) ? samplerate : 'Other';
- if ( freeasync ) {
- selectlabel.push( 'interpolation', 'window' );
- select.push( C.freeasync.interpolation, C.freeasync.window );
- numberlabel.push( 'Sinc length', 'Oversampling ratio', 'Frequency cutoff' );
- var f = jsonClone( DEV.resampler_type.FreeAsync ) || {};
- var values = {
- resampler_type : 'FreeAsync'
- , capture_samplerate : capturerate
- , interpolation : f.interpolation || 'Linear'
- , window : f.window || 'Blackman2'
- , other : capturerate
- , sinc_len : f.sinc_len || 128
- , oversampling_ratio : f.oversampling_ratio || 1024
- , f_cutoff : f.f_cutoff || 0.925
- }
- } else {
- var values = {
- resampler_type : rateadjust && DEV.resampler_type === 'Synchronous' ? 'BalancedAsync' : DEV.resampler_type
- , capture_samplerate : capturerate
- , other : capturerate
- }
- }
+ } //-----------------------------------------------------------------------------------
+ , resampler : ( type, profile ) => {
+ var Dtype = D.resampler[ type ];
+ var values = D.resampler.values[ type ];
+ if (profile ) values.profile = profile;
+ if ( S.resampler ) DEV.resample.each( ( k, v ) => values[ k ] = v );
+ values.capture_samplerate = DEV.capture_samplerate || DEV.samplerate;
info( {
icon : V.tab
, title : SW.title
- , selectlabel : selectlabel
- , select : select
- , numberlabel : numberlabel
+ , list : Dtype
, boxwidth : 160
- , order : [ 'select', 'number' ]
, values : values
- , checkchanged : DEV.enable_resampling
+ , checkblank : true
+ , checkchanged : S.resampler
, beforeshow : () => {
- var $trnumber = $( '.trnumber' );
- var $trother = $trnumber.eq( 0 );
- var indextr = freeasync ? [ 2, 1, 0 ] : [ 0 ]
- indextr.forEach( i => $( '.trselect' ).eq( 1 ).after( $trnumber.eq( i ) ) );
- $trother.toggleClass( 'hide', values.capture_samplerate !== 'Other' );
- $( '.trselect select' ).eq( 0 ).on( 'input', function() {
- if ( $( this ).val() === 'FreeAsync' ) {
- setting.resampling( 'freeasync' );
- } else if ( $trnumber.length > 1 ) {
- setting.resampling();
- }
- } );
- $( '.trselect select' ).eq( 1 ).on( 'input', function() {
- setting.hidetrinfo( $trother, $( this ).val() );
+ $( 'select' ).eq( 0 ).on( 'input', function() {
+ setting.resampler( $( this ).val() );
} );
+ if ( values.type === 'AsyncSinc' ) {
+ $( 'select' ).eq( 1 ).on( 'input', function() {
+ var profile = $( this ).val();
+ if ( type === 'Custom' ) {
+ setting.resampler( 'AsyncSinc', profile );
+ } else {
+ if ( profile === 'Custom' ) setting.resampler( 'Custom' );
+ }
+ } );
+ }
}
, cancel : switchCancel
, ok : () => {
var val = infoVal();
- if ( val.capture_samplerate === 'Other' ) val.capture_samplerate = val.other;
- [ 'resampler_type', 'capture_samplerate' ].forEach( k => DEV[ k ] = val[ k ] );
- if ( freeasync ) {
- var v = {}
- C.freeasync.keys.forEach( k => v[ k ] = val[ k ] );
- DEV.resampler_type = { FreeAsync: v }
- }
- setting.switchSave( 'enable_resampling' );
+ DEV.capture_samplerate = val.capture_samplerate === DEV.samplerate ? null : val.capture_samplerate;
+ delete val.capture_samplerate;
+ if ( val.type === 'Synchronous' && S.enable_rate_adjust ) DEV.enable_rate_adjust = false;
+ DEV.resampler = val;
+ setting.switchSave( 'resampler' );
}
} );
- } //---------------------------------------------------------------------------------------------
- , hidetrinfo : ( $trother, rate ) => {
- var other = rate === 'Other';
- $trother.toggleClass( 'hide', ! other );
- if ( ! other ) $trother.find( 'input' ).val( rate );
+ } //-----------------------------------------------------------------------------------
+ , mixerGet : $this => {
+ var $li = $this.parents( 'li' );
+ return {
+ $li : $li
+ , name : $li.data( 'name' )
+ , index : $li.hasClass( 'lihead' ) ? 'dest' : $li.data( 'index' )
+ , si : $li.data( 'si' )
+ , checked : ! ( $this.hasClass( 'bl' ) || $this.hasClass( 'mute' ) )
+ }
+ }
+ , muteToggle : ( $this, checked ) => {
+ $this.toggleClass( 'mute', checked );
+ $this.siblings( 'input' ).prop( 'disabled', checked );
+ $this.parent().find( '.i-minus, .i-plus, .db' ).toggleClass( 'disabled', checked );
+ }
+ , rangeGet : ( $this, type ) => {
+ var input = type === 'input';
+ var $li = $this.parents( 'li' );
+ var $gain = input ? $this : $this.siblings( 'input' );
+ R = {
+ $gain : $gain
+ , $db : $li.find( 'c' )
+ , val : +$gain.val()
+ , cmd : input ? '' : $this.prop( 'class' ).replace( 'i-', '' )
+ , up : input ? '' : $this.hasClass( 'i-plus' )
+ , scale : $gain.data( 'scale' )
+ , name : $li.data( 'name' )
+ , index : $li.data( 'index' )
+ , si : $li.data( 'si' )
+ }
+ if ( input ) {
+ setting.rangeSet();
+ } else if ( type === 'press' ) {
+ V.intervalgain = setInterval( () => {
+ R.up ? R.val++ : R.val--;
+ setting.rangeSet();
+ }, 100 );
+ } else {
+ switch ( R.cmd ) {
+ case 'minus':
+ R.val--;
+ break;
+ case 'plus':
+ R.val++;
+ break;
+ default: // db
+ R.val = 0;
+ }
+ setting.rangeSet();
+ }
}
- , switchSave : ( id, disable ) => {
- if ( disable === 'disable' ) {
- var msg = 'Disable ...';
- DEV[ id ] = false;
+ , rangeSet : () => {
+ if ( R.val === -100 || R.val === 100 ) clearTimeout( V.timeoutgain );
+ R.$gain.val( R.val );
+ if ( R.scale === 100 ) { // filter - Gain dB / mixer - dB
+ var val = R.val;
+ var db = R.val;
} else {
- var msg = DEV[ id ] ? 'Change ...' : 'Enable ...';
- DEV[ id ] = true;
+ var val = R.val / 10;
+ var db = val.toFixed( 1 );
+ }
+ R.$db
+ .text( db )
+ .toggleClass( 'disabled', R.val === 0 );
+ if ( V.tab === 'filters' ) {
+ FIL[ R.name ].parameters.gain = val;
+ } else {
+ MIX[ R.name ].mapping[ R.index ].sources[ R.si ].gain = val;
+ }
+ setting.save();
+ }
+ , scaleSet : ( checked, key, $this ) => {
+ var $db = $this.siblings( '.db' );
+ if ( checked ) {
+ var gain = key.gain / 10;
+ key.scale = 'linear';
+ key.gain = gain;
+ $db.text( gain.toFixed( 1 ) );
+ } else {
+ var gain = key.gain * 10;
+ key.scale = 'dB';
+ key.gain = gain;
+ $db.text( gain );
}
- setting.save( SW.title, msg );
- render.devices();
}
, save : ( titlle, msg ) => {
setTimeout( () => {
var config = JSON.stringify( S.config ).replace( /"/g, '\\"' );
wscamilla.send( '{ "SetConfigJson": "'+ config +'" }' );
- wscamilla.send( '"Reload"' );
- setTimeout( util.save2file, 300 );
+ if ( ! V.press ) setting.save2file();
}, wscamilla ? 0 : 300 );
- util.webSocket(); // websocket migth be closed by setting.filter()
if ( msg ) banner( V.tab, titlle, msg );
}
+ , save2file : () => {
+ clearTimeout( V.timeoutsave );
+ V.timeoutsave = setTimeout( () => {
+ local();
+ bash( [ 'saveconfig' ] );
+ }, 1000 );
+ }
+ , switchSave : ( id, disable ) => {
+ if ( disable === 'disable' ) {
+ var msg = 'Disable ...';
+ DEV[ id ] = null;
+ } else {
+ var msg = DEV[ id ] ? 'Change ...' : 'Enable ...';
+ DEV[ id ] = true;
+ }
+ setting.save( SW.title, msg );
+ render.devices();
+ }
, upload : () => {
var filters = V.tab === 'filters';
var title = 'Add File';
@@ -1436,15 +1657,12 @@ var setting = {
var message = 'Upload
configuration file:'
}
info( {
- icon : V.tab
- , title : title
- , message : message
- , fileoklabel : ico( 'file' ) +'Upload'
- , filetype : dir === 'coeffs' ? '.dbl,.pcm,.raw,.wav' : '.yml'
- , cancel : () => {
- util.webSocket();
- }
- , ok : () => {
+ icon : V.tab
+ , title : title
+ , message : message
+ , file : { oklabel: ico( 'file' ) +'Upload', type: dir === 'coeffs' ? '.dbl,.pcm,.raw,.wav' : '.yml' }
+ , cancel : common.webSocket
+ , ok : () => {
notify( V.tab, title, 'Upload ...' );
var formdata = new FormData();
formdata.append( 'cmd', 'camilla' );
@@ -1458,27 +1676,13 @@ var setting = {
infoWarning( V.tab, title, message );
}
} );
- util.webSocket();
+ common.webSocket();
}
} );
}
}
-var util = {
- db2percent : ( v ) => {
- var value = 0;
- if ( v >= -12 ) {
- value = 81.25 + 12.5 * v / 6
- } else if ( v >= -24 ) {
- value = 68.75 + 12.5 * v / 12
- } else {
- value = 56.25 + 12.5 * v / 24
- }
- return value < 0 ? 0 : ( value > 100 ? 100 : value )
- }
- , dbRound : ( num ) => {
- return Math.round( num * 10 ) / 10
- }
- , inUse : ( name ) => {
+var common = {
+ inUse : name => {
var filters = V.tab === 'filters';
var inuse = [];
if ( filters && ! ( name in FIL ) ) { // file
@@ -1512,7 +1716,7 @@ var util = {
return false
}
- , key2label : ( key ) => {
+ , key2label : key => {
if ( key === 'ms' ) return 'ms'
var str = key[ 0 ].toUpperCase();
@@ -1531,13 +1735,16 @@ var util = {
.slice( 1 )
return str + key
}
- , labels2array : ( array ) => {
- var capitalized = array.map( el => util.key2label( el ) );
+ , labels2array : array => {
+ if ( ! array ) return false
+
+ var capitalized = array.map( el => common.key2label( el ) );
return capitalized
}
- , save2file : () => {
- bash( [ 'saveconfig' ] );
+ , list2array : list => { // '1, 2, 3' > [ 1, 2, 3 ]
+ return list.replace( /[ \]\[]/g, '' ).split( ',' ).map( Number )
}
+ , tabTitle : () => V.tab[ 0 ].toUpperCase() + V.tab.slice( 1 )
, volume : ( pageX, type ) => {
var bandW = $( '#volume .slide' ).width();
if ( V.start ) {
@@ -1550,26 +1757,33 @@ var util = {
}
if ( V.drag ) {
S.volume = vol;
- util.volumeThumb();
+ common.volumeThumb();
+ local();
volumeSetAt();
+ render.volume();
} else {
var diff = V.dragpress ? 3 : Math.abs( vol - S.volume );
- $( '#volume, #divvolume .divgain i' ).addClass( 'disabled' );
+ $master.addClass( 'noclick' );
$( '#volume .thumb' ).animate(
{ 'margin-left': posX }
, {
duration : diff * 40
, easing : 'linear'
- , complete : () => $( '#volume, #divvolume .divgain i' ).removeClass( 'disabled' )
+ , complete : () => {
+ $master
+ .removeClass( 'noclick' )
+ .toggleClass( 'disabled', S.volumemute > 0 );
+ render.volume();
+ }
}
);
S.volume = vol;
if ( ! type ) { // not from push
+ volumeSetAt();
volumePush( vol );
volumeSetAt();
}
}
- render.volume();
}
, volumeThumb : () => {
$( '#volume .thumb' ).css( 'margin-left', $( '#volume .slide' ).width() / 100 * S.volume );
@@ -1577,12 +1791,22 @@ var util = {
, webSocket : () => {
if ( wscamilla && wscamilla.readyState < 2 ) return
+ var cmd_el = {
+ GetBufferLevel : 'buffer'
+ , GetCaptureRate : 'capture'
+ , GetClippedSamples : 'clipped'
+ , GetProcessingLoad : 'load'
+ , GetRateAdjust : 'rate'
+ }
wscamilla = new WebSocket( 'ws://'+ window.location.host +':1234' );
wscamilla.onready = () => { // custom
- util.wsGetConfig();
- S.status = { GetState: ' '+ blinkdot }
+ common.wsGetState();
+ common.wsGetConfig();
V.intervalstatus = setInterval( () => {
- if ( ! V.local ) V.statusget.forEach( k => wscamilla.send( '"'+ k +'"' ) );
+ if ( V.local ) return
+
+ common.wsGetState();
+ if ( S.enable_rate_adjust ) wscamilla.send( '"GetRateAdjust"' );
}, 1000 );
}
wscamilla.onopen = () => {
@@ -1591,119 +1815,93 @@ var util = {
wscamilla.onclose = () => {
render.vuClear();
clearInterval( V.intervalstatus );
- util.save2file();
- $( '#divstate .value' ).html( ' · · ·' );
}
wscamilla.onmessage = response => {
var data = JSON.parse( response.data );
var cmd = Object.keys( data )[ 0 ];
var value = data[ cmd ].value;
- var cl, cp, css, v;
+ var cp, p, v;
switch ( cmd ) {
- case 'GetCaptureSignalPeak':
- case 'GetCaptureSignalRms':
- case 'GetPlaybackSignalPeak':
- case 'GetPlaybackSignalRms':
- cp = cmd[ 3 ];
- if ( cmd.slice( -1 ) === 'k' ) {
- if ( V.clipped ) break;
-
- cl = '.peak';
- css = 'left';
- V[ cmd ] = value;
- V[ cp ] = [];
- } else {
- cl = '.rms'
- css = 'width';
- }
- value.forEach( ( v, i ) => {
- v = S.volumemute && cp === 'P' ? 0 : util.db2percent( v );
- V[ cp ].push( v );
- $( '.'+ cmd[ 3 ] + i + cl ).css( css, v +'%' );
- } );
- break;
- case 'GetClippedSamples':
- if ( ! ( 'status' in S ) ) return
-
- if ( V.clipped ) {
- S.status.GetClippedSamples = value;
- break;
+ case 'GetSignalLevels':
+ if ( S.state !== 'play' ) {
+ render.vuClear();
+ return
}
- if ( value > S.status.GetClippedSamples ) {
- V.clipped = true;
- clearTimeout( V.timeoutclipped );
- $( '.peak, .clipped' )
- .css( 'transition-duration', 0 )
- .addClass( 'red' );
- V.timeoutclipped = setTimeout( () => {
- V.clipped = false;
- $( '.peak, .clipped' )
- .removeClass( 'red' )
- .css( 'transition-duration', '' );
- // set clipped value to previous peak
- [ 'C', 'P' ].forEach( ( k, i ) => $( '.peak.'+ k + i ).css( 'left', util.db2percent( V[ k ][ i ] ) +'%' ) );
- }, 1000 );
+ if ( ! V.signal ) { // restore after 1st set
+ V.signal = true;
+ $( '.peak' ).css( 'width', '3px' );
+ $( '.rms' ).css( 'transition-duration', '' );
+ setTimeout( () => $( '.peak' ).css( 'transition-duration', '' ), 200 );
}
- S.status.GetClippedSamples = value;
+ [ 'playback_peak', 'playback_rms', 'capture_peak', 'capture_rms' ].forEach( k => {
+ cp = k[ 0 ];
+ value[ k ].forEach( ( db, i ) => {
+ if ( S.volumemute && cp === 'p' ) db = -99;
+ render.vuLevel( k.slice( -1 ) === 's', cp + i, db );
+ } );
+ } );
break;
- case 'GetState':
- case 'GetCaptureRate':
case 'GetBufferLevel':
+ case 'GetCaptureRate':
+ case 'GetProcessingLoad':
case 'GetRateAdjust':
- if ( ! ( 'status' in S ) ) S.status = { GetState: blinkdot }
- if ( cmd === 'GetState' ) {
- if ( value !== 'Running' ) {
- render.vuClear();
- if ( S.status.GetState !== value ) {
- S.status.GetState = value;
- render.statusValue();
- }
- } else {
- S.status.GetState = value;
- if ( ! ( 'intervalvu' in V ) ) {
- $( '.peak' ).css( 'background', '' );
- render.vu();
- }
- }
+ if ( S.state !== 'play' ) {
+ render.vuClear();
+ return
+ }
+
+ v = cmd === 'GetProcessingLoad' ? value.toLocaleString( undefined, { minimumFractionDigits: 3 } ) : value.toLocaleString();
+ $( '#divstate .'+ cmd_el[ cmd ] ).text( v );
+ break;
+ case 'GetClippedSamples':
+ if ( V.local ) return
+
+ if ( value ) {
+ $( '.divclipped' )
+ .removeClass( 'hide' )
+ .find( '.clipped' ).text( value.toLocaleString() );
} else {
- S.status[ cmd ] = value;
- if ( cmd === V.statuslast ) render.statusValue();
+ $( '.divclipped' ).addClass( 'hide' );
}
break;
- // config
+ case 'GetState':
+ if ( 'intervalvu' in V || S.state !== 'play' ) return
+
+ V.intervalvu = setInterval( () => wscamilla.send( '"GetSignalLevels"' ), 100 );
+ break;
case 'GetConfigJson':
S.config = JSON.parse( value );
- DEV = S.config.devices;
- FIL = S.config.filters;
- MIX = S.config.mixers;
- PIP = S.config.pipeline;
- [ 'enable_rate_adjust', 'enable_resampling', 'stop_on_rate_change' ].forEach( k => S[ k ] = DEV[ k ] );
+ DEV = S.config.devices;
+ FIL = S.config.filters;
+ MIX = S.config.mixers;
+ PIP = S.config.pipeline;
+ PRO = S.config.processors;
+ [ 'capture_samplerate', 'enable_rate_adjust', 'resampler', 'stop_on_rate_change' ].forEach( k => {
+ S[ k ] = ! [ null, false ].includes( DEV[ k ] );
+ } );
+ if ( ! $( '#data' ).hasClass( 'hide' ) ) $( '#data' ).html( highlightJSON( S ) );
render.page();
render.tab();
break;
- case 'GetConfigName':
+ case 'GetConfigFilePath':
S.configname = value.split( '/' ).pop();
break;
case 'GetSupportedDeviceTypes':
- S.devicetype = {
- capture : value[ 1 ].sort()
- , playback : value[ 0 ].sort()
- };
- [ 'devices', 'devicetype' ].forEach( k => C[ k ] = { capture: {}, playback: {} } );
- [ 'capture', 'playback' ].forEach( k => {
- S.devices[ k ].forEach( d => {
- v = d.replace( /bluealsa|Bluez/, 'BlueALSA' );
- C.devices[ k ][ v ] = d;
- } );
- S.devicetype[ k ].forEach( t => {
+ var type = {};
+ [ 'playback', 'capture' ].forEach( ( k, i ) => {
+ type[ k ] = {};
+ value[ i ].forEach( t => {
v = render.typeReplace( t );
- C.devicetype[ k ][ v ] = t; // [ 'Alsa', 'Bluez' 'CoreAudio', 'Pulse', 'Wasapi', 'Jack', 'Stdin/Stdout', 'File' ]
+ type[ k ][ v ] = t; // [ 'Alsa', 'Bluez' 'CoreAudio', 'Pulse', 'Wasapi', 'Jack', 'Stdin/Stdout', 'File' ]
} );
} );
+ Dlist.typeC[ 2 ] = type.capture;
+ Dlist.typeP[ 2 ] = type.playback;
+ Dlist.deviceC[ 2 ] = S.devices.capture;
+ Dlist.deviceP[ 2 ] = S.devices.playback;
+ $( '#divvolume .col-l gr' ).text( S.control );
showContent();
- render.status();
- render.tab();
break;
case 'Invalid':
info( {
@@ -1717,134 +1915,124 @@ var util = {
}
, wsGetConfig : () => {
setTimeout( () => {
- [ 'GetConfigName', 'GetConfigJson', 'GetSupportedDeviceTypes' ].forEach( cmd => wscamilla.send( '"'+ cmd +'"' ) );
+ [ 'GetConfigFilePath', 'GetConfigJson', 'GetSupportedDeviceTypes' ].forEach( cmd => wscamilla.send( '"'+ cmd +'"' ) );
}, wscamilla.readyState === 1 ? 0 : 300 );
}
+ , wsGetState : () => {
+ [ 'GetState', 'GetBufferLevel', 'GetCaptureRate', 'GetClippedSamples', 'GetProcessingLoad' ].forEach( k => {
+ wscamilla.send( '"'+ k +'"' );
+ } );
+ }
}
$( function() { // document ready start >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
-$( '.close' ).on( 'click', function() {
- util.save2file();
-} );
+// volume ---------------------------------------------------------------------------------
$( '#volume' ).on( 'touchstart mousedown', function( e ) {
V.start = true;
} ).on( 'touchmove mousemove', function( e ) {
if ( ! V.start ) return
V.drag = true;
- util.volume( e.pageX || e.changedTouches[ 0 ].pageX );
+ common.volume( e.pageX || e.changedTouches[ 0 ].pageX );
} ).on( 'touchend mouseup', function( e ) {
if ( ! V.start ) return
- V.drag ? volumePush() : util.volume( e.pageX || e.changedTouches[ 0 ].pageX );
+ V.drag ? volumePush() : common.volume( e.pageX || e.changedTouches[ 0 ].pageX );
V.start = false;
setTimeout( () => V.drag = false, 1000 );
} ).on( 'mouseleave', function() {
if ( V.start ) $( '#volume' ).trigger( 'mouseup' );
} );
-$( '#voldn, #volup' ).on( 'click', function() {
- var up = this.id === 'volup';
+$( '#divvolume' ).on( 'click', '.i-minus, .i-plus', function() {
+ var up = $( this ).hasClass( 'i-plus' );
if ( ( ! up && S.volume === 0 ) || ( up && S.volume === 100 ) ) return
up ? S.volume++ : S.volume--;
+ render.volume();
volumePush( S.volume );
volumeSetAt();
- $( '#volume-text' ).text( S.volume );
-} ).on( 'touchend mouseup', function() {
+} ).on( 'touchend mouseup mouseleave', function() {
+ if ( ! V.press ) return
+
clearInterval( V.intervalvolume );
volumePush();
-} ).on( 'mouseleave', function() {
- if ( V.press ) $( '#voldn' ).trigger( 'mouseup' );
-} ).press( function( e ) {
- var up = e.target.id === 'volup';
+} ).press( '.i-minus, .i-plus', function( e ) {
+ var up = $( e.target ).hasClass( 'i-plus' );
V.intervalvolume = setInterval( () => {
up ? S.volume++ : S.volume--;
volumeSetAt();
- util.volumeThumb();
- $( '#volume-text' ).text( S.volume );
+ common.volumeThumb();
+ $( '#divvolume .level' ).text( S.volume );
if ( S.volume === 0 || S.volume === 100 ) {
clearInterval( V.intervalvolume );
volumePush();
}
}, 100 );
-} );
-$( '#volmute' ).on( 'click', function() {
+} ).on( 'click', '.i-volume, .db', function() {
S.volumemute ? volumePush( S.volumemute, 'unmute' ) : volumePush( S.volume, 'mute' );
volumeSet( S.volumemute, 'toggle' );
+ $( '#out .peak' ).css( 'transition-duration', '0s' );
+ setTimeout( () => $( '#out .peak' ).css( 'transition-duration', '' ), 100 );
+
} );
-$( '#filters, #mixers' ).on( 'click', '.divgain i', function() {
- var $this = $( this );
- if ( $this.parent().hasClass( 'disabled' ) ) return
+// common ---------------------------------------------------------------------------------
+$( '.entries' ).on( 'click', '.i-minus, .i-plus, .db', function() { // filters, mixersSub
+ setting.rangeGet( $( this ), 'click' );
+} ).on( 'touchend mouseup mouseleave', '.i-minus, .i-plus, .db', function() {
+ if ( ! V.press ) return
- clearTimeout( V.timeoutgain );
- var $gain = $this.parent().prev();
- var $db = $gain.prev();
- var val = +$gain.val();
- if ( $this.hasClass( 'i-set0' ) ) {
- if ( val === 0 ) return
-
- val = 0;
- } else if ( $this.hasClass( 'i-minus' ) ) {
- if ( val === $gain.prop( 'min' ) ) return
-
- val -= 0.1;
- } else if ( $this.hasClass( 'i-plus' ) ) {
- if ( val === $gain.prop( 'max' ) ) return
-
- val += 0.1;
- }
- $gain
- .val( val )
- .trigger( 'input' );
- if ( V.li.find( '.divgraph' ).length || $( '#pipeline .divgraph' ).length ) graph.gain();
-} ).on( 'touchend mouseup mouseleave', function() {
clearInterval( V.intervalgain );
-} ).press( '.divgain i', function( e ) {
- var $this = $( e.currentTarget );
- var $gain = $this.parent().prev();
- var val = +$gain.val();
- var up = $this.hasClass( 'i-plus' );
- V.intervalgain = setInterval( () => {
- val = up ? val + 0.1 : val - 0.1;
- $gain.val( val ).trigger( 'input' );
- }, 100 );
+ if ( $( this ).parents( 'li' ).find( '.divgraph' ).length || $( '#pipeline .divgraph' ).length ) graph.gain();
+ setting.save2file();
+} ).press( '.i-minus, .i-plus', function( e ) {
+ setting.rangeGet( $( e.currentTarget ), 'press' );
} );
$( '#divstate' ).on( 'click', '.clipped', function() {
- S.clipped = S.status.GetClippedSamples;
- bash( [ 'clippedreset', S.clipped, 'CMD CLIPPED' ] );
- render.status();
+ local( 2000 );
+ $( '.divclipped' ).addClass( 'hide' );
+ wscamilla.send( '"ResetClippedSamples"' );
} );
$( '#configuration' ).on( 'input', function() {
if ( V.local ) return
var name = $( this ).val();
- bash( [ 'confswitch', name, 'CMD NAME' ], () => {
- wscamilla.send( '{ "SetConfigName": "/srv/http/data/camilladsp/configs/'+ name +'" }' );
+ var path = '/srv/http/data/camilladsp/configs'+ ( S.bluetooth ? '-bt/' : '/' ) + name;
+ bash( [ 'confswitch', path, 'CMD PATH' ], () => {
+ wscamilla.send( '{ "SetConfigFilePath": "'+ path +'" }' );
wscamilla.send( '"Reload"' );
S.configname = name;
- setTimeout( () => util.wsGetConfig(), 300 );
+ setTimeout( () => common.wsGetConfig(), 300 );
} );
notify( 'camilladsp', 'Configuration', 'Switch ...' );
- V.graph = { filters: {}, pipeline: {} }
} );
$( '#setting-configuration' ).on( 'click', function() {
- $( '#tabconfig' ).trigger( 'click' );
+ if ( $( '#divconfig' ).hasClass( 'hide' ) ) {
+ V.tabprev = V.tab;
+ $( '#tabconfig' ).trigger( 'click' );
+ } else {
+ $( '#tab'+ V.tabprev ).trigger( 'click' );
+ }
+} );
+$( '.tab' ).on( 'click', '.graphclose', function() {
+ var $this = $( this );
+ var $li = $this.parents( 'li' );
+ $this.parent().remove();
+ var val = $li.data( V.tab === 'filters' ? 'name' : 'index' );
+ V.graph[ V.tab ] = V.graph[ V.tab ].filter( v => v !== val );
} );
-$( '#divtabs' ).on( 'click', '.graphclose', function() {
- $( this ).parent().addClass( 'hide' );
+$( '.tab .headtitle' ).on( 'click', function() {
+ if ( $( '#'+ V.tab +' .entries.main' ).hasClass( 'hide' ) ) $( '#'+ V.tab +' .i-back' ).trigger( 'click' );
} );
-$( '.headtitle' ).on( 'click', '.i-folder-filter', function() {
+$( 'heading' ).on( 'click', '.i-folder-filter', function() {
render.filtersSub();
} ).on( 'click', '.i-add', function() {
if ( V.tab === 'filters' ) {
setting.filter( 'Biquad', 'Lowpass' );
- } else if ( V.tab === 'mixers' ) {
- setting.mixer();
- } else if ( V.tab === 'pipeline' ) {
- setting.pipeline();
} else if ( V.tab === 'config' ) {
setting.upload();
+ } else {
+ setting[ V.tab.replace( /s$/, '' ) ]();
}
} ).on( 'click', '.i-flowchart', function() {
var $flowchart = $( '.flowchart' );
@@ -1861,48 +2049,9 @@ $( '.headtitle' ).on( 'click', '.i-folder-filter', function() {
} else {
$flowchart.addClass( 'hide' );
}
-} ).on( 'click', '.i-gear', function() {
- if ( V.tab === 'devices' ) {
- setting.devicesampling();
- return
- }
-
- var TAB = V.tab.toUpperCase();
- var values = {};
- [ 'MAX', 'MIN' ].forEach( k => values[ TAB + k ] = S.range[ TAB + k ] );
- info( {
- icon : V.tab
- , title : 'Gain Slider Range'
- , numberlabel : [ 'Max', 'Min' ]
- , footer : '(50 ... -50)'
- , boxwidth : 110
- , values : values
- , beforeshow : () => {
- var $input = $( '#infoContent input' );
- var $max = $input.eq( 0 );
- var $min = $input.eq( 1 );
- $( '#infoContent' ).on( 'blur', 'input', function() {
- if ( $max.val() > 50 ) {
- $max.val( 50 );
- } else if ( $max.val() < 0 ) {
- $max.val( 0 );
- }
- if ( $min.val() < -50 ) {
- $min.val( -50 );
- } else if ( $min.val() > 0 ) {
- $min.val( 0 );
- }
- } );
- }
- , ok : () => {
- var val = infoVal();
- [ 'MAX', 'MIN' ].forEach( k => S.range[ TAB + k ] = val[ TAB + k ] );
- $( '#'+ V.tab +' input[type=range]' ).prop( { min: S.range[ TAB +'MIN' ], max: S.range[ TAB +'MAX' ] } );
- bash( [ 'camilla', ...Object.values( S.range ), 'CFG '+ Object.keys( S.range ).join( ' ' ) ] );
- }
- } );
} );
-$( '.entries' ).on( 'click', '.liicon', function() {
+$( '.entries' ).on( 'click', '.liicon', function( e ) {
+ e.stopPropagation();
var $this = $( this );
V.li = $this.parent();
var active = V.li.hasClass( 'active' );
@@ -1920,15 +2069,16 @@ $( '.entries' ).on( 'click', '.liicon', function() {
var name = $( '#mixers .lihead' ).text();
if ( ! MIX[ name ].mapping.length ) { // no mapping left
delete MIX[ name ];
- setting.save( title, 'Remove ...' );
+ setting.save( 'Mixer', 'Remove ...' );
}
} else if ( V.tab === 'pipeline' ) {
if ( ! $( '#pipeline .i-filters' ).length ) {
var pi = $( '#pipeline .lihead' ).data( 'index' );
PIP.splice( pi, 1 );
- setting.save( title, 'Remove filter ...' );
+ setting.save( 'Pipeline', 'Remove filter ...' );
}
}
+ $( '#'+ V.tab +' .entries.main' ).removeClass( 'hide' );
render[ V.tab ]();
} );
$( 'body' ).on( 'click', function( e ) {
@@ -1941,7 +2091,7 @@ $( '#menu a' ).on( 'click', function( e ) {
var $this = $( this );
var cmd = $this.prop( 'class' );
if ( cmd === 'graph' ) {
- graph.toggle();
+ graph.plot();
return
}
@@ -1957,7 +2107,7 @@ $( '#menu a' ).on( 'click', function( e ) {
icon : V.tab
, title : title
, message : 'Rename
'+ name +' to:'
- , textlabel : 'Name'
+ , list : [ 'Name', 'text' ]
, values : name
, checkblank : true
, checkchanged : true
@@ -1977,7 +2127,7 @@ $( '#menu a' ).on( 'click', function( e ) {
}
break;
case 'delete':
- if ( util.inUse( name ) ) return
+ if ( common.inUse( name ) ) return
info( {
icon : V.tab
@@ -1995,7 +2145,6 @@ $( '#menu a' ).on( 'click', function( e ) {
}
break;
case 'mixers':
- var title = 'Mixer';
var name = V.li.data( 'name' );
var main = $( '#mixers .entries.sub' ).hasClass( 'hide' );
switch ( cmd ) {
@@ -2005,22 +2154,27 @@ $( '#menu a' ).on( 'click', function( e ) {
case 'delete':
var dest = V.li.hasClass( 'liinput main' );
if ( main ) {
- if ( util.inUse( name ) ) return
+ if ( common.inUse( name ) ) return
+ var title = 'Mixer';
var msg = name;
} else if ( dest ) {
- var mi = V.li.data( 'index' );
- var msg = 'output #'+ mi;
+ var mi = V.li.data( 'index' );
+ var title = 'Destination';
+ var msg = '#'+ mi;
} else {
- var mi = V.li.siblings( '.main' ).data( 'index' );
- var si = V.li.data( 'index' );
- var msg = 'input #'+ si;
+ var mi = V.li.siblings( '.main' ).data( 'index' );
+ var title = 'Source';
+ var si = V.li.data( 'index' );
+ var msg = '#'+ si;
}
var message = 'Delete
'+ msg +' ?';
info( {
icon : V.tab
, title : title
, message : message
+ , oklabel : ico( 'remove' ) +'Delete'
+ , okcolor : red
, ok : () => {
if ( main ) {
delete MIX[ name ];
@@ -2042,12 +2196,34 @@ $( '#menu a' ).on( 'click', function( e ) {
break;
}
break;
+ case 'processors':
+ var title = 'Processors';
+ var name = V.li.data( 'name' );
+ switch ( cmd ) {
+ case 'edit':
+ setting.processor( name );
+ break;
+ case 'delete':
+ info( {
+ icon : V.tab
+ , title : title
+ , message : 'Delete
'+ name +' ?'
+ , ok : () => {
+ delete PRO[ name ];
+ setting.save( title, 'Remove ...' );
+ V.li.remove();
+ }
+ } );
+ break;
+ }
+ break;
case 'pipeline':
+ var title = 'Pipeline';
var main = $( '#pipeline .entries.sub' ).hasClass( 'hide' );
var type = main ? V.li.data( 'type' ).toLowerCase() : 'filter';
info( {
icon : V.tab
- , title : 'Pipeline'
+ , title : title
, message : main ? 'Delete this '+ type +'?' : 'Delete
'+ V.li.data( 'name' ) +' ?'
, ok : () => {
if ( main ) {
@@ -2058,7 +2234,7 @@ $( '#menu a' ).on( 'click', function( e ) {
var ni = V.li.data( 'index' );
PIP[ pi ].names.splice( ni, 1 );
}
- setting.save( 'Pipeline', 'Remove '+ type +' ...' );
+ setting.save( title, 'Remove '+ type +' ...' );
V.li.remove();
setting.sortRefresh( main ? 'main' : 'sub' );
graph.pipeline();
@@ -2076,7 +2252,7 @@ $( '#menu a' ).on( 'click', function( e ) {
icon : V.tab
, title : 'Configuration'
, message : 'File:
'+ name +' '
- , textlabel : 'Copy as'
+ , list : [ 'Copy as', 'text' ]
, values : [ name ]
, checkchanged : true
, ok : () => {
@@ -2090,7 +2266,7 @@ $( '#menu a' ).on( 'click', function( e ) {
info( {
icon : V.tab
, title : 'Configuration'
- , textlabel : 'Rename to'
+ , list : [ 'Rename to', 'text' ]
, values : [ name ]
, checkchanged : true
, ok : () => {
@@ -2117,85 +2293,138 @@ $( '#menu a' ).on( 'click', function( e ) {
}
}
} );
+// filters --------------------------------------------------------------------------------
$( '#filters' ).on( 'click', '.i-add', function() {
setting.upload();
} ).on( 'input', 'input[type=range]', function() {
- var $this = $( this );
- var val = +$this.val();
- $this.prev().text( util.dbRound( val ) );
- V.li = $this.parents( 'li' );
- var name = V.li.data( 'name' );
- FIL[ name ].parameters.gain = val;
- setting.save();
+ setting.rangeGet( $( this ), 'input' );
} ).on( 'touchend mouseup keyup', 'input[type=range]', function() {
graph.gain();
+} ).on( 'click', '.i-volume', function() {
+ var $this = $( this );
+ var name = $this.parents( 'li' ).data( 'name' );
+ var checked = ! $this.hasClass( 'mute' );
+ setting.muteToggle( $this, checked );
+ FIL[ name ].parameters.mute = checked;
+ setting.save();
+} ).on( 'click', '.i-inverted, .i-linear', function() {
+ var $this = $( this );
+ var name = $this.parents( 'li' ).data( 'name' );
+ var checked = ! $this.hasClass( 'bl' );
+ $this.toggleClass( 'bl', checked );
+ var param = FIL[ name ].parameters;
+ $this.hasClass( 'i-inverted' ) ? param.inverted = checked : setting.scaleSet( checked, param, $this );
+ setting.save();
+} ).on( 'click', 'li.eq', function( e ) {
+ if ( $( e.target ).parents( '.divgraph' ).length ) return
+
+ var name = $( this ).data( 'name' );
+ var param = FIL[ name ].parameters;
+ var bands = param.gains.length;
+ var min = Math.log10( param.freq_min ); // Hz > log10 : 20 > 1.3
+ var max = Math.log10( param.freq_max ); // Hz > log10 : 20000 > 4.3
+ var width = ( max - min ) / bands; // log10 / band
+ var v0 = min + width / 2; // log10 midband
+ var v = [ v0 ];
+ for ( i = 0; i < bands - 1; i++ ) {
+ v0 += width;
+ v.push( v0 );
+ }
+ var labelhz = '';
+ v.forEach( val => {
+ var hz = Math.round( Math.pow( 10, val ) ); // log10 > Hz
+ if ( hz > 999 ) hz = Math.round( hz / 1000 ) +'k'
+ labelhz += '
'+ hz +' ';
+ } );
+ var list = `
+
+
${ labelhz }
+
+
${ ' '.repeat( bands ) }
+
`;
+ var flatButton = () => $( '#infoOk' ).toggleClass( 'disabled', param.gains.reduce( ( a, b ) => a + b, 0 ) === 0 );
+ info( {
+ icon : 'equalizer'
+ , title : name
+ , list : list
+ , width : 50 * bands + 40
+ , values : param.gains
+ , beforeshow : () => {
+ flatButton();
+ $( '.inforange input' ).on( 'input', function() {
+ var $this = $( this );
+ param.gains[ $this.index() ] = +$this.val();
+ setting.save();
+ flatButton();
+ } );
+ $( '#eq .label a' ).on( 'click', function() {
+ var $this = $( this );
+ var i = $this.index();
+ var gain = param.gains[ i ];
+ $this.parent().hasClass( 'up' ) ? gain++ : gain--;
+ $( '.inforange input' ).eq( i ).val( gain );
+ param.gains[ i ] = gain;
+ setting.save();
+ flatButton();
+ } );
+ }
+ , oklabel : ico( 'set0' ) +'Flat'
+ , oknoreset : true
+ , ok : () => {
+ param.gains = Array( bands ).fill( 0 );
+ setting.save();
+ $( '.inforange input' ).val( 0 );
+ $( '#infoOk' ).addClass( 'disabled' );
+ }
+ } );
} );
+// mixers ---------------------------------------------------------------------------------
$( '#mixers' ).on( 'click', 'li', function( e ) {
- var $this = $( this );
+ var $this = $( this );
if ( $( e.target ).is( 'i' ) || $this.parent().hasClass( 'sub' ) ) return
- var name = $this.find( '.li1' ).text();
+ var name = $this.find( '.li1' ).text();
render.mixersSub( name );
-} ).on( 'click', 'li i', function() {
+} ).on( 'input', 'input[type=range]', function() {
+ setting.rangeGet( $( this ), 'input' );
+} ).on( 'touchend mouseup keyup', 'input[type=range]', function() {
+ graph.gain();
+} ).on( 'click', '.i-volume', function() {
+ var $this = $( this );
+ var M = setting.mixerGet( $this );
+ var mapping = MIX[ M.name ].mapping[ M.index ];
+ setting.muteToggle( $this, M.checked );
+ typeof M.si === 'number' ? mapping.sources[ M.si ].mute = M.checked : mapping.mute = M.checked;
+ setting.save();
+} ).on( 'click', '.i-inverted, .i-linear', function() {
var $this = $( this );
- if ( $this.is( '.liicon, .i-mixers, .i-back' ) || $this.parent().hasClass( 'divgain' ) ) return
-
- V.li = $this.parents( 'li' );
- var name = V.li.data( 'name' );
- var title = util.key2label( V.tab );
- if ( $this.hasClass( 'i-add' ) ) {
- var index = V.li.hasClass( 'lihead' ) ? '' : V.li.data( 'index' );
- setting.mixerMap( name, index );
- } else {
- var index = V.li.data( 'index' );
- var si = V.li.data( 'si' );
- var mapping = MIX[ name ].mapping[ index ];
- var source = mapping.sources[ si ];
- var checked = ! $this.hasClass( 'bl' );
- if ( $this.hasClass( 'i-volume' ) ) {
- if ( V.li.hasClass( 'main' ) ) {
- mapping.mute = checked;
- } else {
- source.mute = checked;
- V.li.find( 'input[type=range]' ).prop( 'disabled', checked );
- V.li.find( '.divgain' ).toggleClass( 'disabled', checked );
- }
- $this.toggleClass( 'mute bl', checked );
- } else if ( $this.hasClass( 'i-inverted' ) ) {
- $this.toggleClass( 'bl', checked );
- source.inverted = checked;
- }
- setting.save( 'Mixer', 'Change ...' );
- }
+ var M = setting.mixerGet( $this );
+ var source = MIX[ M.name ].mapping[ M.index ].sources[ M.si ];
+ $this.toggleClass( 'bl', M.checked );
+ $this.hasClass( 'i-inverted' ) ? source.inverted = M.checked : setting.scaleSet( M.checked, source, $this );
+ setting.save();
} ).on( 'input', 'select', function() {
- var $this = $( this );
- V.li = $this.parents( 'li' );
- var name = V.li.data( 'name' );
- var mi = V.li.data( 'index' );
- var val = +$this.val();
- if ( V.li.hasClass( 'main' ) ) {
- MIX[ name ].mapping[ mi ].dest = val;
+ var M = setting.mixerGet( $( this ) );
+ var val = +$this.val();
+ if ( typeof M.si === 'number' ) {
+ MIX[ M.name ].mapping[ M.index ].sources[ M.si ].channel = val;
} else {
- var si = V.li.data( 'si' );
- MIX[ name ].mapping[ mi ].sources[ si ].channel = val;
+ MIX[ M.name ].mapping[ M.index ].dest = val;
}
- setting.save( 'Mixer', 'Change ...');
-} ).on( 'input', 'input[type=range]', function() {
- var $this = $( this );
- var val = +$this.val();
- $this.prev().text( util.dbRound( val ) );
- V.li = $( this ).parents( 'li' );
- var name = V.li.data( 'name' );
- var index = V.li.data( 'index' );
- var si = V.li.data( 'si' );
- MIX[ name ].mapping[ index ].sources[ si ].gain = val;
- setting.save();
-} ).on( 'touchend mouseup keyup', 'input[type=range]', function() {
- graph.gain();
+ setting.save( M.name, 'Change ...' );
+} ).on( 'click', '.i-add', function() {
+ var M = setting.mixerGet( $( this ) );
+ setting.mixerMap( M.name, M.index );
+} );
+// processors ---------------------------------------------------------------------------------------
+$( '#processors' ).on( 'click', 'li', function( e ) {
+ e.stopPropagation();
+ $( this ).find( '.liicon' ).trigger( 'click' );
} );
-$( '#pipeline' ).on( 'click', 'li', function( e ) {
+// pipeline -------------------------------------------------------------------------------
+$( '#pipeline' ).on( 'click', 'li', function( e ) {
var $this = $( this );
- if ( $( e.target ).is( 'i' ) || $this.parent().is( '.sub, .divgain' ) ) return
+ if ( $( e.target ).is( 'i' ) || $this.parent().is( '.sub' ) ) return
var index = $this.data( 'index' );
if ( $this.data( 'type' ) === 'Filter' ) {
@@ -2209,7 +2438,7 @@ $( '#pipeline' ).on( 'click', 'li', function( e ) {
icon : V.tab
, title : 'Pipeline'
, message : values
- , select : names
+ , list : [ '', 'select', names ]
, values : values
, ok : () => {
PIP[ index ].name = infoVal();
@@ -2221,17 +2450,15 @@ $( '#pipeline' ).on( 'click', 'li', function( e ) {
var $this = $( this );
if ( $this.is( '.liicon, .i-back' ) ) return
- V.li = $this.parents( 'li' );
- var title = util.key2label( V.tab );
- var index = V.li.data( 'index' );
+ var title = common.tabTitle();
+ var index = $this.parents( 'li' ).data( 'index' );
if ( $this.hasClass( 'i-add' ) ) {
var title = 'Add Filter';
info( {
- icon : V.tab
- , title : title
- , selectlabel : 'Filter'
- , select : Object.keys( FIL )
- , ok : () => {
+ icon : V.tab
+ , title : title
+ , list : [ 'Filter', 'select', Object.keys( FIL ) ]
+ , ok : () => {
PIP[ index ].names.push( infoVal() );
setting.save( title, 'Save ...' );
setting.sortRefresh( 'sub' );
@@ -2241,9 +2468,11 @@ $( '#pipeline' ).on( 'click', 'li', function( e ) {
} );
}
} );
+// devices --------------------------------------------------------------------------------
$( '#devices' ).on( 'click', 'li', function() {
setting.device( $( this ).data( 'type' ) );
} );
+// config ---------------------------------------------------------------------------------
$( '#config' ).on( 'click', '.i-add', function() {
setting.upload();
} ).on( 'click', 'li', function( e ) {
@@ -2261,6 +2490,7 @@ $( '#config' ).on( 'click', '.i-add', function() {
} );
}
} );
+// ----------------------------------------------------------------------------------------
$( '.switch' ).on( 'click', function() {
var id = this.id;
var $setting = $( '#setting-'+ id );
@@ -2272,7 +2502,7 @@ $( '#setting-enable_rate_adjust' ).on( 'click', function() {
info( {
icon : V.tab
, title : SW.title
- , message : 'Resampling type is
Synchronous '
+ , message : 'Resampler type is
Synchronous '
} );
switchCancel();
return
@@ -2281,13 +2511,16 @@ $( '#setting-enable_rate_adjust' ).on( 'click', function() {
info( {
icon : V.tab
, title : SW.title
- , numberlabel : [ 'Adjust period', 'Target level' ]
+ , list : [
+ [ 'Adjust period', 'number' ]
+ , [ 'Target level', 'number' ]
+ ]
, boxwidth : 100
, values : {
- adjust_period : DEV.adjust_period
- , target_level : DEV.target_level
+ adjust_period : DEV.adjust_period
+ , target_level : DEV.target_level
}
- , checkchanged : DEV.enable_rate_adjust
+ , checkchanged : S.enable_rate_adjust
, cancel : switchCancel
, ok : () => {
var val = infoVal();
@@ -2300,10 +2533,10 @@ $( '#setting-stop_on_rate_change' ).on( 'click', function() {
info( {
icon : V.tab
, title : SW.title
- , numberlabel : 'Rate mearsure interval'
+ , list : [ 'Rate mearsure interval', 'number' ]
, boxwidth : 65
, values : DEV.rate_measure_interval
- , checkchanged : DEV.stop_on_rate_change
+ , checkchanged : S.stop_on_rate_change
, cancel : switchCancel
, ok : () => {
DEV.rate_measure_interval = infoVal();
@@ -2311,8 +2544,8 @@ $( '#setting-stop_on_rate_change' ).on( 'click', function() {
}
} );
} );
-$( '#setting-enable_resampling' ).on( 'click', function() {
- setting.resampling( DEV.resampler_type === 'FreeAsync' );
+$( '#setting-resampler' ).on( 'click', function() {
+ setting.resampler( S.resampler ? DEV.resampler.type : 'AsyncSinc' );
} );
$( '#bar-bottom div' ).on( 'click', function() {
V.tab = this.id.slice( 3 );
diff --git a/srv/http/assets/js/common.js b/srv/http/assets/js/common.js
index b399f3138..f20e38a3c 100644
--- a/srv/http/assets/js/common.js
+++ b/srv/http/assets/js/common.js
@@ -202,9 +202,9 @@ function highlightJSON( json ) {
var json = Object.keys( json )
.sort()
.reduce( ( r, k ) => ( r[ k ] = json[ k ], r ), {} ); // https://stackoverflow.com/a/29622653
- json = '\n\n'+ JSON.stringify( json, null, '\t' );
- json = json.replace( /'+ match +''
}
- } ); // source: https://stackoverflow.com/a/7220510
+ } );
+ return '\n\n'+ json.replace( /: null,/g, ':
null ,' );
}
function ico( cls, id ) {
return '
'
@@ -230,7 +231,7 @@ function ico( cls, id ) {
$( '#infoOverlay' ).press( '#infoIcon', function() { // usage
window.open( 'https://github.com/rern/js/blob/master/info/README.md#infojs', '_blank' );
} );
-$( '#infoOverlay' ).on( 'click', '#infoContent', function() {
+$( '#infoOverlay' ).on( 'click', '#infoList', function() {
$( '.infobtn, .filebtn' ).removeClass( 'active' );
} );
$( '#infoOverlay' ).on( 'keydown', function( e ) {
@@ -268,12 +269,10 @@ select: [U] [D] - check
} );
I = { active: false }
-var $infocontent;
function info( json ) {
local(); // flag for consecutive info
- I = json;
- V.iwidth = I.width;
+ I = json;
if ( 'values' in I ) {
if ( ! Array.isArray( I.values ) ) {
@@ -295,15 +294,13 @@ function info( json ) {
${ ico( 'close', 'infoX' ) }
-
+
` );
- $infocontent = $( '#infoContent' );
-
// title
- if ( I.width ) $( '#infoBox' ).css( 'width', I.width );
- if ( I.height ) $( '#infoContent' ).css( 'height', I.height );
+ if ( I.width ) $( '#infoBox' ).css( 'min-width', I.width );
+ if ( I.height ) $( '#infoList' ).css( 'height', I.height );
if ( I.icon ) {
I.icon.charAt( 0 ) !== '<' ? $( '#infoIcon' ).addClass( 'i-'+ I.icon ) : $( '#infoIcon' ).html( I.icon );
} else {
@@ -349,17 +346,14 @@ function info( json ) {
infoButtonCommand( I.ok );
} );
- if ( I.fileoklabel ) { // file api
- var htmlfile = '