Форум программистов, компьютерный форум, киберфорум
JavaScript
Войти
Регистрация
Восстановить пароль
 
Рейтинг 4.89/9: Рейтинг темы: голосов - 9, средняя оценка - 4.89
3 / 3 / 0
Регистрация: 29.06.2018
Сообщений: 1,137
1

Генератор звуковых частот.

14.12.2020, 01:32. Просмотров 1690. Ответов 14
Метки нет (Все метки)

Генератор ЗЧ на основе HTML, JS( на основе https://onlinetonegenerator.com/dtmf.html ):
file.html
PHP/HTML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<!DOCTYPE html>
 
<html>
 
<div id="SoundGen">
 
 <script src="generator.js"></script>
                                 
 <p class="tagline"><strong>The Sound  generator </strong></p>
 
<form id="generatorForm"   >
 <div class="input-group">
<p>Input frequency</p>
<input type="text" class="beginGen" id="freq" name="freq" value="432" autocomplete="off">
<label for="freq">Hz</label>
<p id="units">Input frequency</p>
</div>
 
<div class="generatorbuttons">
    <button  type="button"  id="Start" class="beginGen"   onclick="doStart()" >Start</button>
    <button  type="button"  id="Stop"  class="beginGen"   onclick="stop()"     >Stop</button>
</div>
</form>
 
 
<script type="text/javascript">
document.getElementById("freq").value =1000;
</script>
</html>
generator.js
Javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
      var contextClass = (window.AudioContext ||
        window.webkitAudioContext ||
        window.mozAudioContext ||
        window.oAudioContext ||
        window.msAudioContext);
 
      if (contextClass) {
        // Web Audio API is available.
        var context = new contextClass();
      }
 
      var oscillator1 ;
      var  Tone = function(freq1 )
     {
        // merger = context.createChannelMerger(2);
        oscillator1 = context.createOscillator();
        oscillator1.type = 0;
        oscillator1.frequency.value = freq1;
        gainNode = context.createGain ? context.createGain() : context.createGainNode();
        oscillator1.connect(gainNode,0,0);
        gainNode.connect(context.destination);
        gainNode.gain.value = .1;
        oscillator1.start ? oscillator1.start(0) : oscillator1.noteOn(0)
        // merger.connect(context.destination);
      };
 
      function start() {
        if (typeof oscillator1 != 'undefined') oscillator1.disconnect();
        oscOn(parseFloat(document.getElementById("freq").value) );
      }
 
 
function doStart()
{
Tone( freq.value);
document.getElementById('units').innerHTML =document.getElementById("freq").value+' Hz';
}
 
      function stop() {
        oscillator1.disconnect();
      }
Добавлено через 3 минуты
Вариант с DTMF (пример двухчастотного , более оффлайновая редакция на основе https://onlinetonegenerator.com/dtmf.html ):
file.html
PHP/HTML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
<!DOCTYPE html>
 
<html>
 
<div id="tuningNotes">
 
      <script src="DTMFgenerator.js"></script>
 
 
<table class="dialpad">
    <tbody><tr>
        <td></td>
        <td>1209Hz</td>
        <td>1336Hz</td>
        <td>1477Hz</td>
        <td>1633Hz</td>
    </tr>
    <tr>
        <td>697Hz</td>
        <td><button id="one" class="beginDial" onmousedown="dialTone(697.0, 1209.0)" onmouseup="stop()" data-freq="697.0, 1209.0">1</button></td>
        <td><button id="two" class="beginDial" onmousedown="dialTone(697.0, 1336.0)" onmouseup="stop()" data-freq="697.0, 1336.0">2</button></td>
        <td><button id="three" class="beginDial" onmousedown="dialTone(697.0, 1477.0)" onmouseup="stop()" data-freq="697.0, 1477.0">3</button></td>
        <td><button id="dtmf-a" class="beginDial" onmousedown="dialTone(697.0, 1633.0)" onmouseup="stop()" data-freq="697.0, 1633.0">A</button></td>
    </tr>
    <tr>
        <td>770Hz</td>
        <td><button id="four" class="beginDial" onmousedown="dialTone(770.0, 1209.0)" onmouseup="stop()" data-freq="770.0, 1209.0">4</button></td>
        <td><button id="five" class="beginDial" onmousedown="dialTone(770.0, 1336.0)" onmouseup="stop()" data-freq="770.0, 1336.0">5</button></td>
        <td><button id="six" class="beginDial" onmousedown="dialTone(770.0, 1477.0)" onmouseup="stop()" data-freq="770.0, 1477.0">6</button></td>
        <td><button id="dtmf-b" class="beginDial" onmousedown="dialTone(770.0, 1633.0)" onmouseup="stop()" data-freq="770.0, 1633.0">B</button></td>
    </tr>
    <tr>
        <td>852Hz</td>
        <td><button id="seven" class="beginDial" onmousedown="dialTone(852.0, 1209.0)" onmouseup="stop()" data-freq="852.0, 1209.0">7</button></td>
        <td><button id="eight" class="beginDial" onmousedown="dialTone(852.0, 1336.0)" onmouseup="stop()" data-freq="852.0, 1336.0">8</button></td>
        <td><button id="nine" class="beginDial" onmousedown="dialTone(852.0, 1477.0)" onmouseup="stop()" data-freq="852.0, 1477.0">9</button></td>
        <td><button id="dtmf-c" class="beginDial" onmousedown="dialTone(852.0, 1633.0)" onmouseup="stop()" data-freq="852.0, 1633.0">C</button></td>
 
    </tr>
    <tr>
        <td>941Hz</td>
        <td><button class="beginDial" onmousedown="dialTone(941.0, 1209.0)" onmouseup="stop()" data-freq="941.0, 1209.0">*</button></td>
        <td><button id="zero" class="beginDial" onmousedown="dialTone(941.0, 1336.0)" onmouseup="stop()" data-freq="941.0, 1336.0">0</button></td>
        <td><button class="beginDial" onmousedown="dialTone(941.0, 1477.0)" onmouseup="stop()" data-freq="941.0, 1477.0">#</button></td>
        <td><button id="dtmf-d" class="beginDial" onmousedown="dialTone(941.0, 1633.0)" onmouseup="stop()" data-freq="941.0, 1633.0">D</button></td>
 
    </tr>
</tbody></table>
 
<script type="text/javascript">
 
$('.dialpad button').on("touchstart", function(e){
    e.preventDefault();
    freqs = $(this).data("freq").split(",");
    dialTone(parseInt(freqs[0]), parseInt(freqs[1]));
});
 
 
$('body').on("touchend", function(e){
    e.preventDefault();
    stop();
});
 
$('body').on("mouseup", function(e){
    // e.preventDefault();
    stop();
});
 
</script>
 
 
 
</html>

DTMFgenerator.js
Javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
      var contextClass = (window.AudioContext ||
        window.webkitAudioContext ||
        window.mozAudioContext ||
        window.oAudioContext ||
        window.msAudioContext);
 
      if (contextClass) {
        // Web Audio API is available.
        var context = new contextClass();
      }
 
      var oscillator1, oscillator2;
 
      var dialTone = function(freq1, freq2){
 
        // merger = context.createChannelMerger(2);
 
        oscillator1 = context.createOscillator();
        oscillator1.type = 0;
        oscillator1.frequency.value = freq1;
        gainNode = context.createGain ? context.createGain() : context.createGainNode();
        oscillator1.connect(gainNode,0,0);
        gainNode.connect(context.destination);
        gainNode.gain.value = .1;
        oscillator1.start ? oscillator1.start(0) : oscillator1.noteOn(0)
 
        // gainNode.connect(merger,0,1);
 
        oscillator2 = context.createOscillator();
        oscillator2.type = 0;
        oscillator2.frequency.value = freq2;
        gainNode = context.createGain ? context.createGain() : context.createGainNode();
        oscillator2.connect(gainNode);
        gainNode.connect(context.destination);
        // gainNode.connect(merger,0,0);
 
 
        gainNode.gain.value = .1;
        oscillator2.start ? oscillator2.start(0) : oscillator2.noteOn(0)
 
        // merger.connect(context.destination);
 
 
      };
 
      function start() {
        if (typeof oscillator1 != 'undefined') oscillator1.disconnect();
        if (typeof oscillator2 != 'undefined') oscillator2.disconnect();
        oscOn(parseFloat(document.getElementById("freq").value),parseFloat(document.getElementById("freq2").value));
      }
 
 
      function stop() {
        oscillator1.disconnect();
        oscillator2.disconnect();
      }
Добавлено через 7 минут
В Chrome,Windows 10 работало оффлайново.

Добавлено через 1 час 18 минут
Как на основе этого скрипта и https://ourcodeworld.com/artic... javascript сделать программу измерения АЧХ и свип-генератор?

Добавлено через 1 час 53 минуты
некоторые скрипты для свип-генератора (немного глючит перестройка и ограничение, не имплементовано переключение формы(может не использоваться, для Chrome ,Edge))
HTML5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
<!DOCTYPE html>
 
<html><head>
 
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 
    
 <meta name="viewport" content="initial-scale=1">
 
<div id="mainContent" style="height: auto !important;">
<p class="tagline"><strong> frequency sweep generator.</strong></p>
 
         
<div class="generator-ad-wrapper">
<div class="frequency-sweeper">
 
        <div class="frequency-options-wrapper">
        <div class="input-group">
            <p>Start frequency<br>(in Hz)<br>
            <input type="text" class="input--frequency-start" id="freq-start" name="freq-start" value="100">
            </p>
        </div>
 
        <div class="input-group">
            <p>End frequency<br>(in Hz)<br>
            <input type="text" class="input--frequency-end" id="freq-end" name="freq-end" value="1000">
            </p>
        </div>
 
        <div class="input-group">
            <p>Duration<br>(in seconds)<br>
            <input type="text" class="input--duration" id="duration" name="duration" value="10">
            </p>
        </div>
        </div>
 
        <div class="advanced-options">
            <div class="waveform-wrapper">
                <input type="radio" name="waveform" value="sine" id="sine" checked="">
                <label for="sine">Sine</label>
                <div class="waveform-image waveform-image--sine"></div>
            </div>
            <div class="waveform-wrapper">
                <input type="radio" name="waveform" value="square" id="square">
                <label for="square">Square</label>
                <div class="waveform-image waveform-image--square"></div>
            </div>
            <div class="waveform-wrapper">
                <input type="radio" name="waveform" value="sawtooth" id="sawtooth">
                <label for="sawtooth">Sawtooth</label>
                <div class="waveform-image waveform-image--sawtooth"></div>
            </div>
            <div class="waveform-wrapper">
                <input type="radio" name="waveform" value="triangle" id="triangle">
                <label for="triangle">Triangle</label>
                <div class="waveform-image waveform-image--triangle"></div>
            </div>
        </div>
 
        <div class="clear"></div>
 
    <p>
    <label for="ramp-type-linear">
        Linear sweep
        <input id="ramp-type-linear" type="radio" checked="" name="ramp" value="linear">
    </label>
    <label for="ramp-type-exponential">
        Exponential sweep
        <input id="ramp-type-exponential" type="radio" name="ramp" value="exp">
    </label>
    <br>
    <label for="maintain-tone">Continue playing tone after sweep has finished? <input id="maintain-tone" type="checkbox" checked="" value="1"></label>
    </p>
    <p>
        <label for="start-volume">
            Start Volume:
            <input id="start-volume" type="text" value="40">
        </label><br>
        <label for="end-volume">
            End Volume:
            <input id="end-volume" type="text" value="40">
        </label>
    </p>
 
    <p>
        Current Frequency: <span id="current-frequency">128</span>Hz<br>
        Current Time: <span id="current-time">1.3</span>s<br>
        Current Volume: <span id="current-volume">40.0</span>%<br>
    </p>
 
 
    <div class="generator-buttons">
        <button type="button" class="beginTuning generatorButton"onclick="start1()">Play</button>
        <button type="button" class="stopTuning generatorButton" onclick="stop()">Stop</button>
                 </div>
 
        </div>  
 
 
</div>
 
<script src="frequency-sweeper.js"></script>
 
 
 
 
 
 
     </html>
frequency-sweeper.js
Javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
var contextClass = (window.AudioContext ||
  window.webkitAudioContext ||
  window.mozAudioContext ||
  window.oAudioContext ||
  window.msAudioContext);
 
var messageDisplayed = false;
 
if (contextClass) {
  // Web Audio API is available.
  var context = new contextClass();
    var gainValue = 0.1;
    var gainNode;
    var oscillator;
} else {
    $(".beginTuning").click(function(e){
        e.stopImmediatePropagation();
        ga('send', 'event', 'Error caught', "No context available");
        
    });
}
 
var oscs = {sine:0, square:1, sawtooth:2, triangle:3 };
 
 
function createSource(buffer) {
  var source = context.createBufferSource();
  var gainNode = context.createGainNode();
  source.buffer = buffer;
  // Connect source to gain.
  source.connect(gainNode);
  // Connect gain to destination.
  gainNode.connect(context.destination);
 
  return {
    source: source,
    gainNode: gainNode
  };
}
 
 
var freq1,freq2,duration;
 
//var animationID;
 var currTime;
 
 
 
 
function sweeplinear(freq1, freq2, duration){
 
    //clearInterval(animationID);
      currTime = context.currentTime;
                // var currTime;
 
    oscillator = context.createOscillator();
    gainNode = context.createGain ? context.createGain() : context.createGainNode();
 
    // oscillator.type = 0;
    oscillator.frequency.value = freq1;
    oscillator.connect(gainNode);
    gainNode.connect(context.destination);
    //gainNode.gain.value = 0.15;
                  oscillator.type = "sine"; //sine , square , sawtooth , triangle  
    oscillator.start ? oscillator.start(0) : oscillator.noteOn(0);
    vol1 =0.25; 
    vol2 =0.25; 
    vol1 = Math.min(vol1, 0.5);
    vol2 = Math.min(vol2, 0.5);
    gainNode.gain.value = vol1;
     
        oscillator.frequency.linearRampToValueAtTime(freq1, currTime);
        gainNode.gain.linearRampToValueAtTime(vol1, currTime);
        oscillator.frequency.linearRampToValueAtTime(freq2, currTime + duration*1);
        gainNode.gain.linearRampToValueAtTime(vol2, currTime + duration*1);
 
         oscillator.stop ? oscillator.stop(currTime + duration*1) : oscillator.noteOff(currTime + duration*1);
    // newTime =  context.currentTime;  
    // oscillator.start(0);
};
 
 
function sweepexp(freq1, freq2, duration){
 
    //clearInterval(animationID);
                  //var currTime;
 
     currTime = context.currentTime;
    oscillator = context.createOscillator();
    gainNode = context.createGain ? context.createGain() : context.createGainNode();
 
    // oscillator.type = 0;
    oscillator.frequency.value = freq1;
    oscillator.connect(gainNode);
    gainNode.connect(context.destination);
    //gainNode.gain.value = 0.15;
                  oscillator.type = "sine"; //sine , square , sawtooth , triangle  
    oscillator.start ? oscillator.start(0) : oscillator.noteOn(0);
    vol1 =0.25; 
    vol2 =0.25; 
    vol1 = Math.min(vol1, 0.5);
    vol2 = Math.min(vol2, 0.5);
    gainNode.gain.value = vol1;
     
     
        oscillator.frequency.exponentialRampToValueAtTime(freq1, currTime);
        gainNode.gain.exponentialRampToValueAtTime(vol1, currTime);
        oscillator.frequency.exponentialRampToValueAtTime(freq2, currTime + duration*1);
        gainNode.gain.exponentialRampToValueAtTime(vol2, currTime + duration*1);
     
     
         oscillator.stop ? oscillator.stop(currTime + duration*1) : oscillator.noteOff(currTime + duration*1);  
    //  newTime =  context.currentTime;
    // oscillator.start(0);
};
 
 
 
 
 
 
 
 
function start1()
{
freq1 =200; //исправить 
freq2 =500;
duration =10;//исправить
sweeplinear(freq1, freq2, duration);
//sweepexp(freq1, freq2, duration);
 
}
function stop() {
    oscillator.disconnect();
     $(".frequency-sweeper .beginTuning").removeClass("active");
    //clearInterval(animationID);
 
}

https://developer.mozilla.org/... Node/start
1
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
14.12.2020, 01:32
Ответы с готовыми решениями:

3D-аудиовизуализация. (Преобразование звуковых частот в графику)
Как реализовать дискретное преобразование вейвлет для аудиофайла? Какие вейвлет функции...

Меняю значение высоких и низких звуковых частот
Здравствуйте! Работаю над одним генератором звуковых частот, вернее над генератором шума. Нашёл...

Генератор звуковых волн
Есть статья, описывающая, как сделать генератор синуса, прямоугольного импульса, пилы и шума:...

Генератор звуковых сигналов
Всем доброго времени суток. Дали мне задание: сделать на ардуино генератор звуковых сигналов. Что...

14
3 / 3 / 0
Регистрация: 29.06.2018
Сообщений: 1,137
14.12.2020, 20:09  [ТС] 2
HTML5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
<!DOCTYPE html>
 
<html><head>
 
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 <title>Frequency Sweep Generator | OnlineToneGenerator.com</title>
    
 <meta name="viewport" content="initial-scale=1">
 
<div id="mainContent" style="height: auto !important;">
<p class="tagline"><strong>Free online frequency sweep generator.</strong></p>
 
         
<div class="generator-ad-wrapper">
<div class="frequency-sweeper">
 
        <div class="frequency-options-wrapper">
        <div class="input-group">
            <p>Start frequency<br>(in Hz)<br>
            <input type="text" class="input--frequency-start" id="freqstart" name="freqstart" value="100">
            </p>
        </div>
 
        <div class="input-group">
            <p>End frequency<br>(in Hz)<br>
            <input type="text" class="input--frequency-end" id="freqend" name="freqend" value="1000">
            </p>
        </div>
 
        <div class="input-group">
            <p>Duration<br>(in seconds)<br>
            <input type="text" class="input--duration" id="duration1" name="duration" value="10">
            </p>
        </div>
        </div>
 
        <div class="advanced-options">
            <div class="waveform-wrapper">
                <input type="radio" name="waveform" value="sine" id="sine" checked="">
                <label for="sine">Sine</label>
                <div class="waveform-image waveform-image--sine"></div>
            </div>
            <div class="waveform-wrapper">
                <input type="radio" name="waveform" value="square" id="square">
                <label for="square">Square</label>
                <div class="waveform-image waveform-image--square"></div>
            </div>
            <div class="waveform-wrapper">
                <input type="radio" name="waveform" value="sawtooth" id="sawtooth">
                <label for="sawtooth">Sawtooth</label>
                <div class="waveform-image waveform-image--sawtooth"></div>
            </div>
            <div class="waveform-wrapper">
                <input type="radio" name="waveform" value="triangle" id="triangle">
                <label for="triangle">Triangle</label>
                <div class="waveform-image waveform-image--triangle"></div>
            </div>
        </div>
 
        <div class="clear"></div>
 
    <p>
    <label for="ramp-type-linear">
        Linear sweep
        <input    type="radio" id="ramptypelinear" checked="" name="ramp" value="linear">
    </label>
    <label for="ramp-type-exponential">
        Exponential sweep
        <input type="radio"   id="ramptypeexponential"  name="ramp" value="exp">
    </label>
    <br>
    <label for="maintain-tone">Continue playing tone after sweep has finished? <input id="maintaintone" type="checkbox" checked="" value="1"></label>
    </p>
    <p>
        <label for="start-volume">
            Start Volume:
            <input type="text" id="startvolume"  value="40">
        </label><br>
        <label for="end-volume">
            End Volume:
            <input  type="text" id="endvolume"value="40">
        </label>
    </p>
<!--
    <p>
        Current Frequency: <span id="current-frequency">128</span>Hz<br>
        Current Time: <span id="current-time">1.3</span>s<br>
        Current Volume: <span id="current-volume">00.0</span>%<br>
    </p>
-->
 
    <div class="generator-buttons">
        <button type="button" class="beginTuning generatorButton"onclick="start1()">Play</button>
        <button type="button" class="stopTuning generatorButton" onclick="stop()">Stop</button>
                 </div>
 
        </div>  
 
 
</div>
 
<script src="frequency-sweeper.js"></script>
 
 
     </html>
Добавлено через 50 секунд
frequency-sweeper.js

Javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
var contextClass = (window.AudioContext ||
  window.webkitAudioContext ||
  window.mozAudioContext ||
  window.oAudioContext ||
  window.msAudioContext);
 
var messageDisplayed = false;
 
if (contextClass) {
  // Web Audio API is available.
  var context = new contextClass();
    var gainValue = 0.1;
    var gainNode;
    var oscillator;
} else {
    $(".beginTuning").click(function(e){
        e.stopImmediatePropagation();
        ga('send', 'event', 'Error caught', "No context available");
        
    });
}
 
var oscs = {sine:0, square:1, sawtooth:2, triangle:3 };
 
 
function createSource(buffer) {
  var source = context.createBufferSource();
  var gainNode = context.createGainNode();
  source.buffer = buffer;
  // Connect source to gain.
  source.connect(gainNode);
  // Connect gain to destination.
  gainNode.connect(context.destination);
 
  return {
    source: source,
    gainNode: gainNode
  };
}
 
 
var freq1,freq2,duration;
 
 //var animationID;
 var currTime;
 
 
 
 
function sweep(freq1, freq2, duration){
 
    // clearInterval(animationID);
      currTime = context.currentTime;
               
 
    oscillator = context.createOscillator();
    gainNode = context.createGain ? context.createGain() : context.createGainNode();
 
    // oscillator.type = 0;
    oscillator.frequency.value = freq1;
    oscillator.connect(gainNode);
    gainNode.connect(context.destination);
    //gainNode.gain.value = 0.15;
                  oscillator.type = "sine"; //sine , square , sawtooth , triangle  
if (document.getElementById('sine').checked ) { oscillator.type = "sine";}
if (document.getElementById('square').checked ) { oscillator.type = "square";}
if (document.getElementById('sawtooth').checked ) { oscillator.type = "sawtooth";}
if (document.getElementById('triangle').checked ) { oscillator.type = "triangle";}
 
 
    oscillator.start ? oscillator.start(0) : oscillator.noteOn(0);
    vol1 =document.getElementById('startvolume').value/400; 
    vol2 =document.getElementById('endvolume').value/400; 
    vol1 = Math.min(vol1, 0.5);
    vol2 = Math.min(vol2, 0.5);
    gainNode.gain.value = vol1;
 
 
if (document.getElementById('ramptypelinear').checked )
     {
         oscillator.frequency.linearRampToValueAtTime(freq1, currTime);
         gainNode.gain.linearRampToValueAtTime(vol1, currTime);
         oscillator.frequency.linearRampToValueAtTime(freq2, (currTime + 1*duration ));
         gainNode.gain.linearRampToValueAtTime(vol2, (currTime + 1*duration ));
                  }
if (document.getElementById('ramptypeexponential').checked )
{
                       oscillator.frequency.exponentialRampToValueAtTime(freq1, currTime);
         gainNode.gain.exponentialRampToValueAtTime(vol1, currTime);
 
         oscillator.frequency.exponentialRampToValueAtTime(freq2, (currTime + duration*1 ));
         gainNode.gain.exponentialRampToValueAtTime(vol2, (currTime +  duration*1 ));
 
}
 
if (!document.getElementById('maintaintone').checked )
{
         oscillator.stop ? oscillator.stop(currTime + duration*1) : oscillator.noteOff(currTime + (duration*1));
}
     newTime =  context.currentTime;
    
    // oscillator.start(0);
};
 
 
 
 
 
 
 
 
function start1()
{
freq1 =freqstart.value;
freq2 =freqend.value;
duration =duration1.value;
 
 
 sweep(freq1, freq2, duration);
 
 
}
function stop() {
    oscillator.disconnect();
     $(".frequency-sweeper .beginTuning").removeClass("active");
    //clearInterval(animationID);
 
}
0
3 / 3 / 0
Регистрация: 29.06.2018
Сообщений: 1,137
16.12.2020, 00:55  [ТС] 3
Javascript
1
2
    vol1 =0.25;//document.getElementById('startvolume').value/400; 
    vol2 =0.25;//document.getElementById('endvolume').value/400;
немного устранить проблему с конверсией типов и вводом с полей ввода( пока пример заглушки ).
0
3 / 3 / 0
Регистрация: 29.06.2018
Сообщений: 1,137
24.12.2020, 19:46  [ТС] 4
Из примера измерителя уровня
main.js
Javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
/*
The MIT License (MIT)
 
Copyright (c) 2014 Chris Wilson
 
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
 
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
 
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
var audioContext = null;
var meter = null;
var canvasContext = null;
var WIDTH=500;
var HEIGHT=50;
var rafID = null;
 
window.onload = function() {
 
    // grab our canvas
    canvasContext = document.getElementById( "meter" ).getContext("2d");
    
    // monkeypatch Web Audio
    window.AudioContext = window.AudioContext || window.webkitAudioContext;
    
    // grab an audio context
    audioContext = new AudioContext();
 
    // Attempt to get audio input
    try {
        // monkeypatch getUserMedia
        navigator.getUserMedia = 
            navigator.getUserMedia ||
            navigator.webkitGetUserMedia ||
            navigator.mozGetUserMedia;
 
        // ask for an audio input
        navigator.getUserMedia(
        {
            "audio": {
                "mandatory": {
                    "googEchoCancellation": "false",
                    "googAutoGainControl": "false",
                    "googNoiseSuppression": "false",
                    "googHighpassFilter": "false"
                },
                "optional": []
            },
        }, gotStream, didntGetStream);
    } catch (e) {
        alert('getUserMedia threw exception :' + e);
    }
 
}
 
 
function didntGetStream() {
    alert('Stream generation failed.');
}
 
var mediaStreamSource = null;
 
function gotStream(stream) {
    // Create an AudioNode from the stream.
    mediaStreamSource = audioContext.createMediaStreamSource(stream);
 
    // Create a new volume meter and connect it.
    meter = createAudioMeter(audioContext);
    mediaStreamSource.connect(meter);
 
    // kick off the visual updating
    drawLoop();
}
 
function drawLoop( time ) {
    // clear the background
    canvasContext.clearRect(0,0,WIDTH,HEIGHT);
 
    // check if we're currently clipping
    if (meter.checkClipping())
        canvasContext.fillStyle = "red";
    else
        canvasContext.fillStyle = "green";
 
    // draw a bar based on the current volume
    canvasContext.fillRect(0, 0, meter.volume*WIDTH*1.4, HEIGHT);
 
    // set up the next visual callback
    rafID = window.requestAnimationFrame( drawLoop );
}
Добавлено через 42 секунды
volume-meter.js

Javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
/*
The MIT License (MIT)
 
Copyright (c) 2014 Chris Wilson
 
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
 
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
 
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
 
/*
 
Usage:
audioNode = createAudioMeter(audioContext,clipLevel,averaging,clipLag);
 
audioContext: the AudioContext you're using.
clipLevel: the level (0 to 1) that you would consider "clipping".
   Defaults to 0.98.
averaging: how "smoothed" you would like the meter to be over time.
   Should be between 0 and less than 1.  Defaults to 0.95.
clipLag: how long you would like the "clipping" indicator to show
   after clipping has occured, in milliseconds.  Defaults to 750ms.
 
Access the clipping through node.checkClipping(); use node.shutdown to get rid of it.
*/
 
function createAudioMeter(audioContext,clipLevel,averaging,clipLag) {
    var processor = audioContext.createScriptProcessor(512);
    processor.onaudioprocess = volumeAudioProcess;
    processor.clipping = false;
    processor.lastClip = 0;
    processor.volume = 0;
    processor.clipLevel = clipLevel || 0.98;
    processor.averaging = averaging || 0.95;
    processor.clipLag = clipLag || 750;
 
    // this will have no effect, since we don't copy the input to the output,
    // but works around a current Chrome bug.
    processor.connect(audioContext.destination);
 
    processor.checkClipping =
        function(){
            if (!this.clipping)
                return false;
            if ((this.lastClip + this.clipLag) < window.performance.now())
                this.clipping = false;
            return this.clipping;
        };
 
    processor.shutdown =
        function(){
            this.disconnect();
            this.onaudioprocess = null;
        };
 
    return processor;
}
 
function volumeAudioProcess( event ) {
    var buf = event.inputBuffer.getChannelData(0);
    var bufLength = buf.length;
    var sum = 0;
    var x;
 
    // Do a root-mean-square on the samples: sum up the squares...
    for (var i=0; i<bufLength; i++) {
        x = buf[i];
        if (Math.abs(x)>=this.clipLevel) {
            this.clipping = true;
            this.lastClip = window.performance.now();
        }
        sum += x * x;
    }
 
    // ... then take the square root of the sum.
    var rms =  Math.sqrt(sum / bufLength);
 
    // Now smooth this out with the averaging factor applied
    // to the previous sample - take the max here because we
    // want "fast attack, slow release."
    this.volume = Math.max(rms, this.volume*this.averaging);
}
Добавлено через 56 секунд
index.html

HTML5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<!doctype html>
<html>
    <head>
        <title>Volume Meter Sample</title>
        <style>
        </style>
        <!-- Include the volume meter component -->
        <script src="volume-meter.js"></script>
        <!-- Include the main app logic -->
        <script src="main.js"></script>
    </head>
    <body>
        <p>This sample shows how to implement a clip-indicating volume meter in Web Audio, using a ScriptProcessor.  It's necessary to use a ScriptProcessor in order to not miss any clipping samples - otherwise you could implement this using a RealtimeAnalyser to only grab samples when necessary.</p>
        <canvas id="meter" width="500" height="50"></canvas>
 
        <p>Check out the <a href="http://github.com/cwilso/volume-meter/">source on Github</a>.</p>
        <p>The usage is quite easy:
        <pre>var meter = createAudioMeter(audioContext,clipLevel,averaging,clipLag);
 
audioContext: the AudioContext you're using.
clipLevel: the level (0 to 1) that you would consider "clipping".  Defaults to 0.98.
averaging: how "smoothed" you would like the meter to be over time.  Should be between 0 and less than 1.  Defaults to 0.95.
clipLag: how long you would like the "clipping" indicator to show after clipping has occured, in milliseconds.  Defaults to 750ms.
 
meter.checkClipping();
 
returns true if the node has clipped in the last clipLag milliseconds.
 
meter.shutdown();
 
used to destroy the node (it's important to disconnect and remove the event handler for any ScriptProcessor).
</pre>
    </body>
</html>
0
3 / 3 / 0
Регистрация: 29.06.2018
Сообщений: 1,137
28.12.2020, 11:26  [ТС] 5
Пробный вариант на основе интернета и этих двух сайтов, упомянутых в начале темы:
HTML5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
<!doctype html>
<html>
    <head>
                  <title>AFR</title>
        <title>Volume Meter Sample</title>
        <style>
        </style>
        <!-- Include the volume meter component -->
        <!-- <script src="volume-meter.js"></script>  -->
        <!-- Include the main app logic -->
        <script src="main.js"></script>
 
 
 
 
 
    </head>
    <body>
 
<p>This sample shows how to implement an AFR meter 
 using volume meter in Web Audio, using a ScriptProcessor. </p>
        
<canvas id="meter" width="1000" height="300"></canvas>
    
 
 
 <p>Current frequency: <output id="curfreq1"  >0</output> Hz </p>
 <p>Current Input Level : <output id="Vinp1"  >0</output>   </p>
 <p>Current Input Level , dB: <output id="Vinp2"  >0</output>   </p>
 
  Input  level : 
<input type="range" id="LevelInput"    min="0" max="100" step="1"  oninput="SetLevelIn()" value="50"   > 
<output id="LevelIn1"  >50</output>
 <br>
<!--
   Output  level : 
<input type="range" id="LevelOutput"    min="0" max="100" step="1"  oninput="SetLevelOut()" value="50"   > 
<output id="LevelOut1"  >0</output>
 <br>
           
-->          
<form   action="">   
 
 
         <label for="FminIn"  >Fmin:</label>
         <input type="text"  name="FminIn"  id="FminIn" value="100"    >  Hz     
         <input type="button" value="Input Fmin" onclick="OnChangeFmin()" >   <br>   
 
         <label for="FmaxIn" >Fmax:</label>
         <input type="text"  name="FmaxIn"  id="FmaxIn" value="10000"    >  Hz
         <input type="button" value="Input Fmax" onclick="OnChangeFmax()" >   <br> 
      
        <label for="Duration" >Duration:</label>
         <input type="text"  name="DuratIn" id="DuratIn" value="300"    > ms 
         <input type="button" value="Input Duration" onclick="OnChangeDuration()" ><br>     
        
         <label for="Step" >Step:</label>
         <input type="text"   name="StepIn"  id="StepIn" value="100"    >  Hz
         <input type="button" value="Input step" onclick="OnChangeStep()" >  <br>   
         
          <p>Fmin: <output id="minfreq"  >0</output> Hz</p>
          <p>Fmax: <output id="maxfreq"  >0</output> Hz</p>
          <p>Duration: <output id="duration1"  >0</output> s</p>
           <p>Step: <output id="step1"  >0</output>Hz</p>
 
 
 </form>
 
 
<p> 
 <input type="button"  value="Run" onclick="RunOnClick()" > 
 <input type="button" value="Stop" onclick="StopOnClick()" >
 <input type="button" value="Clear" onclick="ClearOnClick()" >
</p>
        
 </body>
</html>
Добавлено через 3 минуты
main.js

Javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
/*
The MIT License (MIT)
Using sources from http://github.com/cwilso/volume-meter/ 
(Copyright (c) 2014 Chris Wilson)
and from https://onlinetonegenerator.com/ 
 
*/
 
/*   fix  level control problem */
 
function createAudioMeter(audioContext,clipLevel,averaging,clipLag) {
    var processor = audioContext.createScriptProcessor(512);
    processor.onaudioprocess = volumeAudioProcess;
    processor.clipping = false;
    processor.lastClip = 0;
    processor.volume = 0;
    processor.clipLevel = clipLevel || 0.98;
    processor.averaging = averaging || 0.95;
    processor.clipLag = clipLag || 750;
 
    // this will have no effect, since we don't copy the input to the output,
    // but works around a current Chrome bug.
    processor.connect(audioContext.destination);
 
    processor.checkClipping =
        function(){
            if (!this.clipping)
                return false;
            if ((this.lastClip + this.clipLag) < window.performance.now())
                this.clipping = false;
            return this.clipping;
        };
 
    processor.shutdown =
        function(){
            this.disconnect();
            this.onaudioprocess = null;
        };
 
    return processor;
}
 
function volumeAudioProcess( event ) {
    var buf = event.inputBuffer.getChannelData(0);
    var bufLength = buf.length;
    var sum = 0;
    var x;
 
    // Do a root-mean-square on the samples: sum up the squares...
    for (var i=0; i<bufLength; i++) {
        x = buf[i];
        if (Math.abs(x)>=this.clipLevel) {
            this.clipping = true;
            this.lastClip = window.performance.now();
        }
        sum += x * x;
    }
 
    // ... then take the square root of the sum.
    var rms =  Math.sqrt(sum / bufLength);
 
    // Now smooth this out with the averaging factor applied
    // to the previous sample - take the max here because we
    // want "fast attack, slow release."
    this.volume = Math.max(rms, this.volume*this.averaging);
}
 
 
 
 
 
 
var audioContext = null;
var meter = null;
var canvasContext = null;
var WIDTH=1000;
var HEIGHT=300;
var rafID = null;
var xPos=0;
var freqmin=100;
var freqmax=10000;
var currfreq= freqmin;
var fstep =100;
var duration=300;
var LevelIn=50;
var LevelOut=50;
 
var audioCtxGen =null;
var oscillator =null;
 
function  SetLevelIn()
{
LevelIn=document.getElementById("LevelInput").value;
document.getElementById("LevelIn1").innerHTML =LevelIn;
}
 
/*
function  SetLevelOut()
{
LevelOut=document.getElementById("LevelOutput").value;
document.getElementById("LevelOut1").innerHTML =LevelOut;
}
*/
 
 function UpdateLabels()
{
 document.getElementById("minfreq").innerHTML =freqmin;
 document.getElementById("maxfreq").innerHTML =freqmax;
 document.getElementById("duration1").innerHTML =duration;
 document.getElementById("step1").innerHTML =fstep;
}
 
 
function OnChangeFmin()
{
 freqmin=document.getElementById("FminIn").value;
 UpdateLabels();
}
 
 
function OnChangeFmax()
{
 freqmax =document.getElementById("FmaxIn").value;
 UpdateLabels()
}
 
 
function OnChangeDuration()
{
 duration =document.getElementById("DuratIn").value;
 UpdateLabels();
  
}
 
function OnChangeStep()
{
  fstep =document.getElementById("StepIn").value;
 UpdateLabels();
  
}
 
 
//window.onload = functionOnClick() 
 
 
function RunOnClick() 
{
UpdateLabels();
    // grab our canvas
    canvasContext = document.getElementById( "meter" ).getContext("2d");
   //canvasContext.clearRect(0,0,WIDTH, HEIGHT);
    // monkeypatch Web Audio
    window.AudioContext = window.AudioContext || window.webkitAudioContext; 
    // grab an audio context
    audioContext = new AudioContext();
 
   
    
 
    // Attempt to get audio input
    try {
        // monkeypatch getUserMedia
        navigator.getUserMedia = 
            navigator.getUserMedia ||
            navigator.webkitGetUserMedia ||
            navigator.mozGetUserMedia;
 
        // ask for an audio input
        navigator.getUserMedia(
        {
            "audio": {
                "mandatory": {
                    "googEchoCancellation": "false",
                    "googAutoGainControl": "false",
                    "googNoiseSuppression": "false",
                    "googHighpassFilter": "false"
                },
                "optional": []
            },
        }, gotStream, didntGetStream);
 
   
    } catch (e) {
        alert('getUserMedia threw exception :' + e);
    }
 
 
 
 
 
}
 
 
function didntGetStream() {
    alert('Stream generation failed.');
}
 
var mediaStreamSource = null;
 
 /*
function CreateGainFun( )
{
var gainNode = null;
 var AudioContext = window.AudioContext || window.webkitAudioContext;
var audioCtx = new AudioContext();
 
var myAudio = document.querySelector('audio');
 
 gainNode =audioCtx.createGain();
 gainNode.gain.setValueAtTime(0, audioCtx.currentTime);
 source.connect(gainNode);
 gainNode.connect(audioCtx.destination);
 gainNode.gain.linearRampToValueAtTime(0.5, audioCtx.currentTime );
}
 
*/
 
function CreateOsc( )
{
currfreq=freqmin;
audioCtxGen = new (window.AudioContext || window.webkitAudioContext)();
 
oscillator = audioCtxGen.createOscillator();
oscillator.type = 'sine';
oscillator.frequency.setValueAtTime(currfreq, audioCtxGen.currentTime); // value in hertz
oscillator.connect(audioCtxGen.destination);
oscillator.start();
}
 
 
function CreateInputLevelMeter(stream)
{
 
    mediaStreamSource = audioContext.createMediaStreamSource(stream);
    meter = createAudioMeter(audioContext);
    mediaStreamSource.connect(meter);
}
 
 
 
 
var timeoutID=null;
 
function gotStream(stream) 
{
  CreateInputLevelMeter(stream);
    
    
    CreateOsc( ); 
    
    drawLoop(  );
    timeoutID=setInterval( drawLoop , duration );   
 } 
 
 
//function doNothing() {    }
 
 function guard_delay()
{
//var tmpDelayID=setTimeout(doNothing,500);
//clearTimeout(tmpDelayID);
}
 
 
 function drawLoop(  ) 
//function drawLoop( time ) 
{
 
 if (currfreq<=freqmax)
{
  // var now  =  audioCtxGen.currentTime; 
 
   document.getElementById("curfreq1").innerHTML =currfreq.toFixed(2);  
   oscillator.frequency.value=currfreq; 
//oscillator.frequency.setValueAtTime(currfreq, audioCtxGen.currentTime); 
// oscillator.frequency.linearRampToValueAtTime(currfreq, audioCtxGen.currentTime);
 
   //guard_delay();
   var inputV=(meter.volume*1.4)  *(LevelIn*0.2) ;
   document.getElementById("Vinp1").innerHTML =inputV.toExponential(4); 
   var InputVdB=20*Math.log10(inputV);
   document.getElementById("Vinp2").innerHTML =InputVdB.toFixed(1); 
 
 
 
    canvasContext.beginPath();
    canvasContext.moveTo( xPos, HEIGHT);
   canvasContext.lineTo( xPos, HEIGHT*(1-inputV ));  //fix
 
  //canvasContext.lineTo( xPos, HEIGHT*(1-0.7)- InputVdB*(HEIGHT/120)    );  //fix
 
    //canvasContext.lineTo( xPos, 0 );
    if (meter.checkClipping()){  canvasContext.strokeStyle ="red"; } // тёмно-синий цвет
     else  { canvasContext.strokeStyle ="green"; }
     canvasContext.lineWidth = 2;
     canvasContext.stroke();
     
   
     currfreq =currfreq+ fstep;   
     xPos= xPos+2;
}
 
else
{
 xPos= WIDTH;
oscillator.stop();
clearInterval( timeoutID);
StopOnClick();
}
 
 //rafID = window.requestAnimationFrame( drawLoop );
 
}
 
//fix exit problem 
function StopOnClick()
{
clearInterval( timeoutID);
oscillator.stop();
oscillator.disconnect();
currfreq=freqmin;
rafID = null;
 meter =null;
mediaStreamSource = null;
audioCtxGen =null;
xPos=0;
 
}
 
 
function ClearOnClick()
{
 StopOnClick();
 canvasContext.clearRect(0,0,WIDTH, HEIGHT);
 
}
Добавлено через 3 минуты
Пофиксать программы индикации, ввести шкалу, ввести нормирование , вести программу защитной задержки при переключении частоты с точки на точку, ввести регулировку громкости , подправить способ выделения аудиопотоков, подачу сигнала с микрофона(линейного входа, пока пробовал на веб-камере) на выход во избежание самовозбуждения при работе программы блокировать.

Добавлено через 1 минуту
Javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
function createAudioMeter(audioContext,clipLevel,averaging,clipLag) {
    var processor = audioContext.createScriptProcessor(512);
    processor.onaudioprocess = volumeAudioProcess;
    processor.clipping = false;
    processor.lastClip = 0;
    processor.volume = 0;
    processor.clipLevel = clipLevel || 0.98;
    processor.averaging = averaging || 0.95;
    processor.clipLag = clipLag || 750;
 
    // this will have no effect, since we don't copy the input to the output,
    // but works around a current Chrome bug.
    processor.connect(audioContext.destination);
 
    processor.checkClipping =
        function(){
            if (!this.clipping)
                return false;
            if ((this.lastClip + this.clipLag) < window.performance.now())
                this.clipping = false;
            return this.clipping;
        };
 
    processor.shutdown =
        function(){
            this.disconnect();
            this.onaudioprocess = null;
        };
 
    return processor;
}
 
function volumeAudioProcess( event ) {
    var buf = event.inputBuffer.getChannelData(0);
    var bufLength = buf.length;
    var sum = 0;
    var x;
 
    // Do a root-mean-square on the samples: sum up the squares...
    for (var i=0; i<bufLength; i++) {
        x = buf[i];
        if (Math.abs(x)>=this.clipLevel) {
            this.clipping = true;
            this.lastClip = window.performance.now();
        }
        sum += x * x;
    }
 
    // ... then take the square root of the sum.
    var rms =  Math.sqrt(sum / bufLength);
 
    // Now smooth this out with the averaging factor applied
    // to the previous sample - take the max here because we
    // want "fast attack, slow release."
    this.volume = Math.max(rms, this.volume*this.averaging);
}
поменять на правильную для АЧХ и стабильных отсчетов.

Добавлено через 2 минуты
Аудиопоток
Javascript
1
2
3
4
5
6
7
function CreateOsc( )
{
currfreq=freqmin;
audioCtxGen = new (window.AudioContext || window.webkitAudioContext)();
 
oscillator = audioCtxGen.createOscillator();
oscillator.type = 'sine';
адаптировать под Node createGain() ( https://developer.mozilla.org/... I/GainNode )
с регулировкой громкости.

Добавлено через 1 час 45 минут
Javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
...
if (currfreq<=freqmax)
{
  // var now  =  audioCtxGen.currentTime; 
 
   document.getElementById("curfreq1").innerHTML =currfreq.toFixed(2);  
   oscillator.frequency.value=currfreq; 
//oscillator.frequency.setValueAtTime(currfreq, audioCtxGen.currentTime); 
// oscillator.frequency.linearRampToValueAtTime(currfreq, audioCtxGen.currentTime);
 
   //guard_delay();
   var inputV=(meter.volume*1.4)  *(LevelIn*0.2) ; //исправить управление множителем на адекватное вместо условного 
   document.getElementById("Vinp1").innerHTML =inputV.toExponential(4); 
   var InputVdB=20*Math.log10(inputV/1); //исправить знаменатель под переключатель 
   document.getElementById("Vinp2").innerHTML =InputVdB.toFixed(1);
Доработать обработчик(пока он условный). Можно рисовать точки, а не столбики(пока так удобно для дебага). Добавить кнопку автонастройки на собственную АЧХ, автошкалирователь , нормировать в знаменателе формулы "логарифматора" по правильному настраиваемому уровню (можно использовать переключатель пределов ) .
Ввести массив коэффициентов передачи собственной АЧХ . Избегать перегрузки входа для некоторых устройств .

Добавлено через 1 минуту
Обрабатывать правильное количество периодов(отсчетов) для низкочастотных и высокочастотных сигналов (влияет на время анализа).
0
3 / 3 / 0
Регистрация: 29.06.2018
Сообщений: 1,137
29.12.2020, 10:27  [ТС] 6
Javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
function OnChangeFmin()
{
 freqmin=parseFloat(document.getElementById("FminIn").value);
 UpdateLabels();
}
 
 
function OnChangeFmax()
{
 freqmax =parseFloat(document.getElementById("FmaxIn").value);
 UpdateLabels()
}
 
 
function OnChangeDuration()
{
 duration =parseFloat(document.getElementById("DuratIn").value) ;
 
UpdateLabels();
  
}
 
function OnChangeStep()
{
  fstep =parseFloat(document.getElementById("StepIn").value) ;
 UpdateLabels();
  
}
Добавлено через 13 секунд
так стабильнее

Добавлено через 3 часа 33 минуты
index.html
HTML5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
<!doctype html>
<html>
    <head>
                  <title>AFR</title>
        <title>Volume Meter Sample</title>
        <style>
        </style>
        <!-- Include the volume meter component -->
        <!-- <script src="volume-meter.js"></script>  -->
        <!-- Include the main app logic -->
        <script src="main.js"></script>
 
 
 
 
 
    </head>
    <body>
 
<p>This sample shows how to implement an AFR meter 
 using volume meter in Web Audio, using a ScriptProcessor. </p>
        
<canvas id="meter" width="1000" height="300"></canvas>
    
 
 
 <p>Current frequency: <output id="curfreq1"  >0</output> Hz </p>
 <p>Current Input Level : <output id="Vinp1"  >0</output>   </p>
 <p>Current Input Level , dB: <output id="Vinp2"  >0</output>   </p>
  <p>Current Num : <output id="nstep"  >0</output>   </p>
 
  Input  level : 
<input type="range" id="LevelInput"    min="0" max="100" step="1"  oninput="SetLevelIn()" value="50"   > 
<output id="LevelIn1"  >50</output>
 <br>
<!--
   Output  level : 
<input type="range" id="LevelOutput"    min="0" max="100" step="1"  oninput="SetLevelOut()" value="50"   > 
<output id="LevelOut1"  >0</output>
 <br>
           
-->          
<form   action="">   
 
 
         <label for="FminIn"  >Fmin:</label>
         <input type="text"  name="FminIn"  id="FminIn" value="100"    >  Hz     
         <input type="button" value="Input Fmin" onclick="OnChangeFmin()" >   <br>   
 
         <label for="FmaxIn" >Fmax:</label>
         <input type="text"  name="FmaxIn"  id="FmaxIn" value="10000"    >  Hz
         <input type="button" value="Input Fmax" onclick="OnChangeFmax()" >   <br> 
      
        <label for="Duration" >Duration:</label>
         <input type="text"  name="DuratIn" id="DuratIn" value="300"    > ms 
         <input type="button" value="Input Duration" onclick="OnChangeDuration()" ><br>     
        
         <label for="Step" >Step:</label>
         <input type="text"   name="StepIn"  id="StepIn" value="100"    >  Hz
         <input type="button" value="Input step" onclick="OnChangeStep()" >  <br>   
         
          <p>Fmin: <output id="minfreq"  >0</output> Hz</p>
          <p>Fmax: <output id="maxfreq"  >0</output> Hz</p>
          <p>Duration: <output id="duration1"  >0</output> s</p>
           <p>Step: <output id="step1"  >0</output>Hz</p>
           
 
 </form>
 
 
<p> 
 <input type="button"  value="Normalize" onclick="NormOnClick()" > 
 <input type="button"  value="Run" onclick="RunOnClick()" > 
 <input type="button" value="Stop" onclick="StopOnClick()" >
 <input type="button" value="Clear" onclick="ClearOnClick()" >
</p>
        
 </body>
</html>
Добавлено через 4 минуты
main.js
Javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
/*
The MIT License (MIT)
 
Copyright (c) 2014 Chris Wilson
 
*/
 
/*   fix  level control problem */
 
function createAudioMeter(audioContext,clipLevel,averaging,clipLag) {
    var processor = audioContext.createScriptProcessor(512);
    processor.onaudioprocess = volumeAudioProcess;
    processor.clipping = false;
    processor.lastClip = 0;
    processor.volume = 0;
    processor.clipLevel = clipLevel || 0.98;
    processor.averaging = averaging || 0.95;
    processor.clipLag = clipLag || 750;
 
    // this will have no effect, since we don't copy the input to the output,
    // but works around a current Chrome bug.
    processor.connect(audioContext.destination);
 
    processor.checkClipping =
        function(){
            if (!this.clipping)
                return false;
            if ((this.lastClip + this.clipLag) < window.performance.now())
                this.clipping = false;
            return this.clipping;
        };
 
    processor.shutdown =
        function(){
            this.disconnect();
            this.onaudioprocess = null;
        };
 
    return processor;
}
 
function volumeAudioProcess( event ) {
    var buf = event.inputBuffer.getChannelData(0);
    var bufLength = buf.length;
    var sum = 0;
    var x;
 
    // Do a root-mean-square on the samples: sum up the squares...
    for (var i=0; i<bufLength; i++) {
        x = buf[i];
        if (Math.abs(x)>=this.clipLevel) {
            this.clipping = true;
            this.lastClip = window.performance.now();
        }
        sum += x * x;
    }
 
    // ... then take the square root of the sum.
    var rms =  Math.sqrt(sum / bufLength);
 
    // Now smooth this out with the averaging factor applied
    // to the previous sample - take the max here because we
    // want "fast attack, slow release."
    this.volume = Math.max(rms, this.volume*this.averaging);
}
 
 
 
 
 
 
var audioContext = null;
var meter = null;
var canvasContext = null;
var audioCtxGen =null;
var oscillator =null;
 
var freqmin=50.0;
var freqmax=10000.0;
var currfreq= freqmin;
var fstep =100.0;
 
var WIDTH=1000;
var HEIGHT=300;
var timeoutID=null;
 
var duration=300;
var LevelIn=50;
var LevelOut=50;
 
var stepnum=0;
 
 
 
function  SetLevelIn()
{
LevelIn=document.getElementById("LevelInput").value;
document.getElementById("LevelIn1").innerHTML =LevelIn;
}
 
/*
function  SetLevelOut()
{
LevelOut=document.getElementById("LevelOutput").value;
document.getElementById("LevelOut1").innerHTML =LevelOut;
}
*/
 
 function UpdateLabels()
{
 document.getElementById("minfreq").innerHTML =freqmin;
 document.getElementById("maxfreq").innerHTML =freqmax;
 document.getElementById("duration1").innerHTML =duration;
 document.getElementById("step1").innerHTML =fstep;
}
 
 
function OnChangeFmin(){freqmin=parseFloat(document.getElementById("FminIn").value);UpdateLabels();}
function OnChangeFmax(){ freqmax =parseFloat(document.getElementById("FmaxIn").value); UpdateLabels(); }
function OnChangeDuration(){ duration =parseFloat(document.getElementById("DuratIn").value) ; UpdateLabels(); }
function OnChangeStep(){ fstep =parseFloat(document.getElementById("StepIn").value) ;UpdateLabels(); }
 
 
//window.onload = functionOnClick() 
function didntGetStream() { alert('Stream generation failed.');  }
 
function RunOnClick() 
{
UpdateLabels();
 canvasContext = document.getElementById( "meter" ).getContext("2d");
    // monkeypatch Web Audio
    window.AudioContext = window.AudioContext || window.webkitAudioContext; 
    // grab an audio context
    audioContext = new AudioContext();
    // Attempt to get audio input
    try {
        // monkeypatch getUserMedia
        navigator.getUserMedia = 
            navigator.getUserMedia ||
            navigator.webkitGetUserMedia ||
            navigator.mozGetUserMedia;
        // ask for an audio input
        navigator.getUserMedia(
        {
            "audio": {
                "mandatory": {
                    "googEchoCancellation": "false",
                    "googAutoGainControl": "false",
                    "googNoiseSuppression": "false",
                    "googHighpassFilter": "false"
                },
                "optional": []
            },
        }, gotStream, didntGetStream);
  } catch (e) { alert('getUserMedia threw exception :' + e);  }
}
 
 
function NormOnClick() 
{
UpdateLabels();
    // grab our canvas
   canvasContext = document.getElementById( "meter" ).getContext("2d");
   //canvasContext.clearRect(0,0,WIDTH, HEIGHT);
    // monkeypatch Web Audio
    window.AudioContext = window.AudioContext || window.webkitAudioContext; 
    // grab an audio context
    audioContext = new AudioContext();
    // Attempt to get audio input
    try {
        // monkeypatch getUserMedia
        navigator.getUserMedia = 
            navigator.getUserMedia ||
            navigator.webkitGetUserMedia ||
            navigator.mozGetUserMedia;
        // ask for an audio input
        navigator.getUserMedia(
        {
            "audio": {
                "mandatory": {
                    "googEchoCancellation": "false",
                    "googAutoGainControl": "false",
                    "googNoiseSuppression": "false",
                    "googHighpassFilter": "false"
                },
                "optional": []
            },
        }, gotStream1, didntGetStream);
 
   
    } catch (e) { alert('getUserMedia threw exception :' + e);  }
 
}
 
 
 
var mediaStreamSource = null;
 
 /*
function CreateGainFun( )
{
var gainNode = null;
 var AudioContext = window.AudioContext || window.webkitAudioContext;
var audioCtx = new AudioContext();
 
var myAudio = document.querySelector('audio');
 gainNode =audioCtx.createGain();
 gainNode.gain.setValueAtTime(0, audioCtx.currentTime);
 source.connect(gainNode);
 gainNode.connect(audioCtx.destination);
 gainNode.gain.linearRampToValueAtTime(0.5, audioCtx.currentTime );
}
 
*/
 
function CreateOsc( )
{
currfreq=freqmin;
audioCtxGen = new (window.AudioContext || window.webkitAudioContext)();
oscillator = audioCtxGen.createOscillator();
oscillator.type = 'sine';
oscillator.frequency.setValueAtTime(currfreq, audioCtxGen.currentTime); // value in hertz
oscillator.connect(audioCtxGen.destination);
oscillator.start();
}
 
 
function CreateInputLevelMeter(stream)
{
    mediaStreamSource = audioContext.createMediaStreamSource(stream);
    meter = createAudioMeter(audioContext);
    mediaStreamSource.connect(meter);
}
 
 var Coeffs= new Float64Array(2048);
 Coeffs.fill(1);
  
 
function GetLevel(){  return (meter.volume*1.4)  *(LevelIn*0.2); }
function GetValue(Ldata , nstep1, modedB, mode1  )
{
     
var datadBIn=20*Math.log10( Ldata );
var dataIn=Ldata ;
if (mode1==1) 
{  
datadBIn+= Coeffs [nstep1] ; 
dataIn*= Coeffs[ nstep1] ; 
} 
      document.getElementById("Vinp1").innerHTML =dataIn.toExponential(4); 
      document.getElementById("Vinp2").innerHTML =datadBIn.toFixed(1); 
 
if (modedB==1)  { return datadBIn; } else { return dataIn; }  
 
    return dataIn ;  
}
 
 
 
 
// =new Array(arrayLength);
 
function CreateNormCoefsArray(stepnum,  mode, indata )
{  
var coeftmp=1;
var i=stepnum;
 
if (mode==0){ coeftmp=1/(indata+0.00000001);   } //obtain coeff.   for     Level*Coeffs=1return; 
if (mode==1){ coeftmp=(-indata);  } //obtain coeff.   for     Level+Coeffs =return;
Coeffs[i]=coeftmp;
 
}
 
 
function doNothing() {    }
 
function guard_delay()
{
var tmpDelayID=setTimeout(doNothing, 500);
clearTimeout(tmpDelayID);
}
 
function UpdateFreqValue()
{   
 
document.getElementById("curfreq1").innerHTML =currfreq.toFixed(2);  
oscillator.frequency.value=currfreq; 
//oscillator.frequency.setValueAtTime(currfreq, audioCtxGen.currentTime); 
// oscillator.frequency.linearRampToValueAtTime(currfreq, audioCtxGen.currentTime);
}
 
 
 
 
 
 
 
function SetPoint(numstep,  V ,  num)
{
var dxPos=2;
var xPos=numstep*dxPos;
 
//var yPos=HEIGHT*(1-0.7)-  VdB*(HEIGHT/120);
  var yPos=HEIGHT*(1-V);
//yPos=0;
 
      canvasContext.beginPath();
     canvasContext.moveTo( xPos, HEIGHT);
     canvasContext.lineTo( xPos, yPos);  //fix
     //canvasContext.moveTo( xPos, yPos);
    // canvasContext.lineTo( xPos, yPos+1);  //fix
      if (meter.checkClipping()){  canvasContext.strokeStyle ="red"; } // тёмно-синий цвет
     else  { canvasContext.strokeStyle ="green"; }
     canvasContext.lineWidth = dxPos;
     canvasContext.stroke();
}
 
 
var dBmode=0;
 
 function NormLoop(  ) 
{
var tmpdata;
 
 if (currfreq<=freqmax)
{
document.getElementById("nstep").innerHTML =stepnum ;
 UpdateFreqValue(); 
    guard_delay();
    tmpdata=GetLevel();   
   
   CreateNormCoefsArray(stepnum,   dBmode  , GetValue(tmpdata,  stepnum, dBmode, 0 ) );
    SetPoint( stepnum, GetValue(tmpdata, stepnum , dBmode , 1 )  );
    currfreq +=fstep;   
    stepnum++;
}
else{ oscillator.stop(); clearInterval( timeoutID); StopOnClick();  }
}
 
 
 function drawLoop(  ) 
{
 var tmpdata;
 if (currfreq<=freqmax)
{
   document.getElementById("nstep").innerHTML =stepnum ;
   UpdateFreqValue(); 
   
    guard_delay();
    tmpdata=GetLevel();  
     tmpdata =GetValue(tmpdata, stepnum , dBmode , 1 ) 
   SetPoint( stepnum,  tmpdata);
    currfreq +=fstep;   
    stepnum++;
}
else{ oscillator.stop();clearInterval( timeoutID);StopOnClick(); }
}
 
 
 
function gotStream(stream) 
{
    CreateInputLevelMeter(stream); 
    CreateOsc( ); 
    Coeffs.length=2048;
  if (dBmode==1) { Coeffs.fill(0) ; } else { Coeffs.fill(1) ; }    
    drawLoop(  );
   timeoutID=setInterval( drawLoop , duration );   
 } 
 
function gotStream1(stream) 
{
    CreateInputLevelMeter(stream); 
    CreateOsc( );  
    Coeffs.length=2048 ;
    if (dBmode==1) { Coeffs.fill(0) ; } else { Coeffs.fill(1) ; }    
    NormLoop(  );
    timeoutID=setInterval( NormLoop , duration );   
 } 
 
function StopOnClick()
{
clearInterval( timeoutID);
oscillator.stop();
oscillator.disconnect();
currfreq=freqmin;
timeoutID=null  
 meter =null;
mediaStreamSource = null;
audioCtxGen =null;
stepnum=0;
}
 
 
function ClearOnClick()
{
 
 StopOnClick();
 canvasContext.clearRect(0,0,WIDTH, HEIGHT);
}
для настоящей работы программы измерителя АЧХ , самокалибровки (вместо нормализации ) его надо доработать , сделать правильный защитный интервал между установкой частоты и выборкой уровня (при повторном включении дает ложный режим) , ввести переключатель дБ/разы, пределов, отключения коэффициентов самокалибровки для заданных уровней (возможности ограничены), выбрать правильный метод индикации .

Добавлено через 2 минуты
Длительность тонального сигнала 100-300 мс дает плохо завершившийся переходной процесс . Можно ввести более правильные термины (пока это проверка простых функций и не дает возможностей измерения АЧХ ).

Добавлено через 31 секунду
Проблема с guard_delay.

Добавлено через 1 минуту
При более научном подходе это делается иначе и на основе другого шаблона. Ввести автовычисление количества точек по пределам и шагу (можно другие варианты).

Добавлено через 25 минут
Возможный вариант автоопределения количества точек вариант , еще определяюсь с форматом некоторых меню и переменных . Еще смотрю по количеству точек или по частоте останавливать .
Javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
...
function GetNumberofPoints()
{
return 1+Math.ceil ((freqmax-freqmin)/fstep);
}
 
 
 
 
function gotStream(stream) 
{
    CreateInputLevelMeter(stream); 
    CreateOsc( ); 
    Coeffs.length=GetNumberofPoints();
  //if (dBmode==1) { Coeffs.fill(0) ; } else { Coeffs.fill(1) ; }    
    drawLoop(  );
   timeoutID=setInterval( drawLoop , duration );   
 } 
 
function gotStream1(stream) 
{
    CreateInputLevelMeter(stream); 
    CreateOsc( );  
    Coeffs.length=GetNumberofPoints() ;
    if (dBmode==1) { Coeffs.fill(0) ; } else { Coeffs.fill(1) ; }    
    NormLoop(  );
    timeoutID=setInterval( NormLoop , duration );   
 } 
 
function StopOnClick()
{
clearInterval( timeoutID);
oscillator.stop();
oscillator.disconnect();
currfreq=freqmin;
timeoutID=null  
 meter =null;
mediaStreamSource = null;
audioCtxGen =null;
stepnum=0;
}
 
 
function ClearOnClick()
{
 
 StopOnClick();
 canvasContext.clearRect(0,0,WIDTH, HEIGHT);
}
 
 
 
 
 
function ClearCoefsOnClick()
{
 
 Coeffs.length=GetNumberofPoints() ;
 if (dBmode==1) { Coeffs.fill(0) ; } else { Coeffs.fill(1) ; }
}
Добавлено через 2 минуты
Вводить защиту от нуля или минус бесконечности в дБ (можно и режим с дбВт предусмотреть кроме дБВ ).

Добавлено через 36 минут
Проблема с разделением потоков при чтении .

Добавлено через 2 минуты
Javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...
function RunOnClick() 
{
 
(document.getElementById("LogMode").checked==true)  ? dBmode=1 : dBmode=0; 
 
if (dBmode==1)  { document.getElementById("currmode").innerHTML ="Log. Mode " ;}
if (dBmode==0)  { document.getElementById("currmode").innerHTML ="Lin.  Mode " ; }
 
 
 
UpdateLabels();
...
 
function NormOnClick() 
{
 
(document.getElementById("LogMode").checked==true)  ? dBmode=1 : dBmode=0; 
 
if (dBmode==1)  { document.getElementById("currmode").innerHTML ="Log. Mode " ;}
if (dBmode==0)  { document.getElementById("currmode").innerHTML ="Lin.  Mode " ;}
 
...

HTML5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
...
 
    <form   action="">   
 
 
         <label for="FminIn"  >Fmin:&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp</label>
         <input type="text"  name="FminIn"  id="FminIn" value="100"    >  Hz     
         <input type="button" value="Input Fmin" onclick="OnChangeFmin()" >  
         &nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp
         <label for="FmaxIn" >Fmax:</label>
         <input type="text"  name="FmaxIn"  id="FmaxIn" value="10000"    >  Hz
         <input type="button" value="Input Fmax" onclick="OnChangeFmax()" >   <br> 
      
        <label for="Duration" >Duration:</label>
         <input type="text"  name="DuratIn" id="DuratIn" value="300"    > ms 
         <input type="button" value="Input Duration" onclick="OnChangeDuration()" >     
        &nbsp&nbsp&nbsp
         <label for="Step" >Step:&nbsp&nbsp</label>
         <input type="text"   name="StepIn"  id="StepIn" value="100"    >  Hz
         <input type="button" value="Input step" onclick="OnChangeStep()" >  <br>   
         
          <p>Fmin: <output id="minfreq"  >0</output> Hz &nbsp
           Fmax: <output id="maxfreq"  >0</output> Hz</p>
          <p>Duration: <output id="duration1"  >0</output> ms &nbsp
            FStep: <output id="step1"  >0</output> Hz</p>
           
 
 </form>
 
<p> 
 
   <input type="radio" id="LinearMode" name="SelectMode" value="LinearMode" checked >
    <label for="LinearMode">Linear Mode </label>
    <input type="radio" id="LogMode" name="SelectMode" value="LogMode">
    <label for="LogMode">Log. Mode</label> <br>
    <output id="currmode"  ></output> </p>
 <input type="button"  value="Normalize" onclick="NormOnClick()" > 
 <input type="button" value="ClearCoeffs" onclick="ClearCoefsOnClick()" >
 <input type="button"  value="Run" onclick="RunOnClick()" > 
 <input type="button" value="Stop" onclick="StopOnClick()" >
 <input type="button" value="Clear" onclick="ClearOnClick()" >
 
</p>
 
 
         
 </body>
</html>
Добавлено через 40 секунд
Можно иначе с помощью стилей табуляцию кнопок и полей приделать.
0
3 / 3 / 0
Регистрация: 29.06.2018
Сообщений: 1,137
29.12.2020, 10:33  [ТС] 7
Пробные файлы (с логарифмическим недоделано, сверху от нуля дБ децибел 10...30 (множитель HEIGHT(1-0.1...0.3)*... * scalecoef ) , снизу поменять , масштабировать , 0дБ можно поменять на +6... -10...-30 дБ , переключать ).
0
Вложения
Тип файла: zip volume-meter2afr.zip (4.3 Кб, 0 просмотров)
3 / 3 / 0
Регистрация: 29.06.2018
Сообщений: 1,137
29.12.2020, 12:16  [ТС] 8
Еще нужно учитывать время реакции детектора (еще ищу боле подходящие альтернативные программы ), а также задержку ( ГВЗ , время запаздывания ) от момента подачи сигнала на вход до расшифровки отклика и вычисления его амплитуды (уровня СКЗ или для синусоиды Urms_sin= 0,707*Um_sin, Um_sin=1.41*Urms_sin ), которая не превышает 0,08...1,5 с , зависит от Windows, других процессов в системе , самих аудионадстроек браузера, драйверов, тактовой частоты, антивируса.
0
3 / 3 / 0
Регистрация: 29.06.2018
Сообщений: 1,137
30.12.2020, 08:35  [ТС] 9
Хотел организовать задержку на setTimeout , в Chrome не идет , задержку игнорирует.

Javascript
1
2
3
4
5
6
7
8
9
var tmpDelayID=null;
function doNothing() {   }
 
 function guard_delay()
{
   tmpDelayID= setTimeout(doNothing, 300);
   clearTimeout(tmpDelayID);   
   tmpDelayID=null;
}
https://developer.mozilla.org/... setTimeout

Добавлено через 1 минуту
https://www.w3schools.com/jsre... terval.asp setInterval() работает.

Добавлено через 7 минут
Javascript
1
2
3
4
5
6
7
8
9
var timeoutID;
function doNothing() {  var tmp=0;   }
 
 function guard_delay()
{
   timeoutID=window.setTimeout(doNothing, 300 );
   window.clearTimeout(timeoutID); 
 //  timeoutID=null;
}
тоже не работает, очевидно, мы внутри функции , вызываемой открытием потока.Пример задержки на сайте работает , при встраивании не хочет работать.

Добавлено через 2 минуты
Даже если заменить setInterval на

Javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function gotStream(stream) 
{
    CreateInputLevelMeter(stream); 
    CreateOsc( ); 
    Coeffs.length=GetNumberofPoints();
  //if (dBmode==1) { Coeffs.fill(0) ; } else { Coeffs.fill(1) ; }    
    //drawLoop(  );
   //timeoutID=setInterval( drawLoop , duration );   
 
 while (currfreq<=freqmax)
{
drawLoop(  );
} 
 
 
 }
Добавлено через 1 минуту
Если поставить alert("msg") вместо setTimeout, функция вызывается.

Добавлено через 44 секунды
даже с действием внутри функции.

Добавлено через 10 минут
Пробовал , как на https://stackoverflow.com/ques... javascript

Javascript
1
2
3
4
5
6
7
8
9
10
 function sleep(milliseconds) {
  var start = new Date().getTime();
  for (var i = 0; i < 1e7; i++) { if ((new Date().getTime() - start) > milliseconds) {   break; } }
}
 
 
 function guard_delay()
{
   sleep(300);
}

Работает, но не обновляется индикация .

Добавлено через 28 минут
Javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
...
 
 
 
 function dsleep(milliseconds) {
 var start = new Date().getTime();
 for (var i = 0; i < 1e7; i++) { if ((new Date().getTime() - start) > milliseconds) {   break; } }
}
 
 function guard_delay()
{
    dsleep(500 ); //set to 80...150 , 500 ms for debug 
}
 
function GetLevel(){    guard_delay(); return (meter.volume*1.4)  *(LevelIn*0.2); }
...
 
function drawLoop(  ) 
{
 var tmpdata;
 if (currfreq<=freqmax)
{
    document.getElementById("nstep").innerHTML =stepnum ;
    UpdateFreqValue(); 
     tmpdata=GetLevel(); 
...
 
 
function gotStream(stream) 
{
    CreateInputLevelMeter(stream); 
    CreateOsc( ); 
    Coeffs.length=GetNumberofPoints();
   drawLoop(  );
   timeoutID=setInterval( drawLoop , duration );   
 } 
...
 
//и в программе нормализации собственной АЧХ тоже, если работает
Добавлено через 1 минуту
Если ввести функцию в вычислитель отсчетов, то работает, с setTimeOut не работает даже там . Может с async/await посмотреть (пробовал, не работает).
0
3 / 3 / 0
Регистрация: 29.06.2018
Сообщений: 1,137
30.12.2020, 09:50  [ТС] 10
Изменил количество семплов
0
Вложения
Тип файла: zip test_simpleafr_jshtml.zip (4.0 Кб, 6 просмотров)
3 / 3 / 0
Регистрация: 29.06.2018
Сообщений: 1,137
30.12.2020, 12:27  [ТС] 11
Реакция на уменьшение сигнала на линейном выходе слишком медленная. Можно перенести длительность в программу задержки, переименовать в задержку (100...500 мс), а периодичность вызова функции отрисовки делать 50 мс с учетом внутренней задержки. Встроить индикатор уровня для настройки для предотвращения перегрузки .

Добавлено через 2 часа 10 минут
HTML5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
<!doctype html>
<html>
    <head>
                  <title>AFR and Volume Meter Sample</title>
         
        <style>
        </style>
        <!-- Include the volume meter component -->
        <!-- <script src="volume-meter.js"></script>  -->
        <!-- Include the main app logic -->
        <script src="main.js"></script>
 
    </head>
    <body>
 
<p>This sample shows how to implement an AFR meter 
 using volume meter in Web Audio, using a ScriptProcessor. </p>
        
<canvas id="meter" width="1000" height="300"></canvas>
    
<table>
<tr>
<td>Current frequency: <output id="curfreq1"  >0</output> Hz  </td>
<td>Current Num : <output id="nstep"  >0</output>    </td>
</tr>
<tr>
 <td>Current Input Level : <output id="Vinp1"  >0</output>   </td>
 <td>Current Input Level , dB: <output id="Vinp2"  >0</output></td>
</tr>
 
</table>
  Input  level : 
<input type="range" id="LevelInput"    min="0" max="100" step="1"  oninput="SetLevelIn()" value="50"   > 
<output id="LevelIn1"  >50</output>
 <br>
<!--
   Output  level : 
<input type="range" id="LevelOutput"    min="0" max="100" step="1"  oninput="SetLevelOut()" value="50"   > 
<output id="LevelOut1"  >0</output>
 <br>
           
-->          
<table>
 
<tr>
<td>
         <label for="FminIn"  >Fmin: </label>
         <input type="text"  name="FminIn"  id="FminIn" value="50"    >  Hz     
         <input type="button" value="Input Fmin" onclick="OnChangeFmin()" >  
</td>
<td>    
        <label for="Delay" >Delay:</label>
         <input type="text"  name="DelayIn" id="DelayIn" value="100"    > ms 
         <input type="button" value="Input Delay" onclick="OnChangeDelay()" >       
</td>
</tr>  
<tr>
<td>        
         <label for="FmaxIn" >Fmax:</label>
         <input type="text"  name="FmaxIn"  id="FmaxIn" value="10000"    >  Hz
         <input type="button" value="Input Fmax" onclick="OnChangeFmax()" >   <br> 
</td> 
<td>
         <label for="Step" >FStep: </label>
         <input type="text"   name="StepIn"  id="StepIn" value="50"    >  Hz
         <input type="button" value="Input step" onclick="OnChangeStep()" >  <br>   
</td> 
</tr>
<tr>
<td> Fmin: <output id="minfreq"  >0</output> Hz  </td>
<td> Delay: <output id="delay1"  >0</output> ms </td>
 </tr>
 <tr> 
<td> Fmax:  <output id="maxfreq" >0</output> Hz </td>
<td> FStep: <output id="step1"  >0</output> Hz</td>
</tr>
</table>           
 
 
 
 
<p> 
  
   <input type="radio" id="LinearMode" name="SelectMode" value="LinearMode" checked >
    <label for="LinearMode">Linear Mode </label>
    <input type="radio" id="LogMode" name="SelectMode" value="LogMode">
    <label for="LogMode">Log. Mode</label> 
    <input type="checkbox" id="NormCoefs" name="NormCoefs"  value="check" checked >
    <label for="NormCoef">Using coefficients </label>
<br>
    <output id="currmode"  ></output> </p>
 
 
 <input type="button"  value="Level" onclick="LevelOnClick()" > 
 <input type="button"  value="Normalize" onclick="NormOnClick()" > 
 <input type="button" value="ClearCoeffs" onclick="ClearCoefsOnClick()" >
 <input type="button"  value="Run" onclick="RunOnClick()" > 
 <input type="button" value="Stop" onclick="StopOnClick()" >
 <input type="button" value="Clear" onclick="ClearOnClick()" >
 
</p>
 
 
         
 </body>
</html>

main.js
Javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
function createAudioMeter(audioContext, clipLevel, averaging, clipLag) 
{
 var processor = audioContext.createScriptProcessor(256); //512,fix
 processor.onaudioprocess = volumeAudioProcess;
 processor.clipping = false;
 processor.lastClip = 0;
 processor.volume = 0;
 processor.clipLevel = clipLevel || 0.98;
 processor.averaging = averaging || 0.95;
 processor.clipLag = clipLag || 750;
 
 // this will have no effect, since we don't copy the input to the output,
 // but works around a current Chrome bug.
processor.connect(audioContext.destination);
 
processor.checkClipping =function()
{
if (!this.clipping) return false;
if ((this.lastClip + this.clipLag) < window.performance.now())  this.clipping = false;
return this.clipping;
};
 
processor.shutdown =function(){ this.disconnect(); this.onaudioprocess = null;  };
return processor;
}
 
function volumeAudioProcess( event ) {
 var buf = event.inputBuffer.getChannelData(0);
 var bufLength = buf.length;
 var sum = 0;
 var x;
   // Do a root-mean-square on the samples: sum up the squares...
 for (var i=0; i<bufLength; i++) {
 x = buf[i];
 if (Math.abs(x)>=this.clipLevel) { this.clipping = true; this.lastClip = window.performance.now();  }
 sum += x * x;
 }
 
 
     var rms =  Math.sqrt(sum / bufLength);
     this.volume =  Math.max(rms, this.volume*this.averaging);
}
 
 
 
 
 
 
var audioContext = null;
var meter = null;
var canvasContext = null;
var audioCtxGen =null;
var oscillator =null;
 
var freqmin=50.0;
var freqmax=10000.0;
var currfreq= 50.0;
var fstep =50.0;
 
var WIDTH=1000;
var HEIGHT=300;
var timeoutID=null;
 
var delay=100;
var LevelIn=50;
var LevelOut=50;
 
var stepnum=0;
var dBmode=0; 
var streammode=0;
 
 
function  SetLevelIn()
{
LevelIn=document.getElementById("LevelInput").value;
document.getElementById("LevelIn1").innerHTML =LevelIn;
}
 
/*
function  SetLevelOut()
{
LevelOut=document.getElementById("LevelOutput").value;
document.getElementById("LevelOut1").innerHTML =LevelOut;
}
*/
 
 function UpdateLabels()
{
 document.getElementById("minfreq").innerHTML =freqmin;
 document.getElementById("maxfreq").innerHTML =freqmax;
 document.getElementById("delay1").innerHTML =delay;
 document.getElementById("step1").innerHTML =fstep;
}
window.onload = function Init() {UpdateLabels(); }
  
function OnChangeFmin(){freqmin=parseFloat(document.getElementById("FminIn").value);UpdateLabels();}
function OnChangeFmax(){ freqmax =parseFloat(document.getElementById("FmaxIn").value); UpdateLabels(); }
function OnChangeDelay(){ delay =parseFloat(document.getElementById("DelayIn").value) ; UpdateLabels(); }
function OnChangeStep(){ fstep =parseFloat(document.getElementById("StepIn").value) ;UpdateLabels(); }
 
 
//window.onload = functionOnClick() 
function didntGetStream() { alert('Stream generation failed.');  }
 
 
 
function CreateStream()
{
    window.AudioContext = window.AudioContext || window.webkitAudioContext; 
    audioContext = new AudioContext();
    try {
        navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
        navigator.getUserMedia(
        {
            "audio": {
                "mandatory": {
                    "googEchoCancellation": "false",
                    "googAutoGainControl": "false",
                    "googNoiseSuppression": "false",
                    "googHighpassFilter": "false"
                },
                "optional": []
            },
        }, gotStream, didntGetStream);
  } catch (e) { alert('getUserMedia threw exception :' + e);  }
}
 
 
var mediaStreamSource = null;
 /*
function CreateGainFun( )
{
var gainNode = null;
 var AudioContext = window.AudioContext || window.webkitAudioContext;
var audioCtx = new AudioContext();
 
var myAudio = document.querySelector('audio');
 gainNode =audioCtx.createGain();
 gainNode.gain.setValueAtTime(0, audioCtx.currentTime);
 source.connect(gainNode);
 gainNode.connect(audioCtx.destination);
 gainNode.gain.linearRampToValueAtTime(0.5, audioCtx.currentTime );
}
 
*/
 
function CreateOsc(  )
{
audioCtxGen = new (window.AudioContext || window.webkitAudioContext)();
oscillator = audioCtxGen.createOscillator();
oscillator.type = 'sine';
oscillator.frequency.setValueAtTime(currfreq, audioCtxGen.currentTime); // value in hertz
oscillator.connect(audioCtxGen.destination);
oscillator.start();
}
 
 
function CreateInputLevelMeter(stream)
{
    mediaStreamSource = audioContext.createMediaStreamSource(stream);
    meter = createAudioMeter(audioContext);
    mediaStreamSource.connect(meter);
}
 
 var Coeffs= new Float64Array(2048);
 Coeffs.fill(1);
  
 
function GetLevel(){    guard_delay(); return (meter.volume *1.41)  *(LevelIn*0.01); }
 
 
 
function GetValue(Ldata , nstep1, modedB, mode1  )
{
     
var datadBIn=20*Math.log10(Ldata );
var dataIn=Ldata ;
if (mode1==1) {  datadBIn+= Coeffs [nstep1] ; dataIn*= Coeffs[ nstep1] ; } 
      document.getElementById("Vinp1").innerHTML =dataIn.toExponential(4); 
      document.getElementById("Vinp2").innerHTML =datadBIn.toFixed(1); 
 if (modedB==1)   { return datadBIn} else { return dataIn ; }
    return dataIn ;  
}
 
function CreateNormCoefsArray(stepnum,  mode, indata )
{  
var coeftmp=1;
var i=stepnum;
 
if (mode==0){ coeftmp=1/(indata+(1e-12));   } //obtain coeff.   for     Level*Coeffs=1return; 
if (mode==1){ coeftmp=(-indata);  } //obtain coeff.   for     Level+Coeffs =return;
Coeffs[i]=coeftmp;
 
}
 
 function dsleep(milliseconds) {
 var start = new Date().getTime();
 for (var i = 0; i < 1e7; i++) { if ((new Date().getTime() - start) > milliseconds) {   break; } }
}
 
 function guard_delay(){ dsleep(delay); }
 
function UpdateFreqValue()
{   
document.getElementById("curfreq1").innerHTML =currfreq.toFixed(2);  
oscillator.frequency.value=currfreq; 
//oscillator.frequency.setValueAtTime(currfreq, audioCtxGen.currentTime); 
// oscillator.frequency.linearRampToValueAtTime(currfreq, audioCtxGen.currentTime);
}
 
 
 
function SetPoint(numstep,  V ,  num)
{
var dxPos=2;
var xPos=numstep*dxPos;
var yPos ;
if (dBmode==0) { yPos=HEIGHT*(1-V); } else  { yPos=HEIGHT*(1-0.7)-  V *(HEIGHT/120);  }
 
    canvasContext.beginPath();
       
      canvasContext.moveTo( xPos, HEIGHT);
     canvasContext.lineTo( xPos, yPos);  //fix
       // canvasContext.moveTo( xPos, yPos);
       //canvasContext.lineTo( xPos+1, yPos);  //fix
    if (meter.checkClipping()){  canvasContext.strokeStyle ="red"; } // тёмно-синий цвет
    else  { canvasContext.strokeStyle ="green"; }
 canvasContext.lineWidth =dxPos;
  canvasContext.stroke();
}
 
 
 
 function NormLoop(  ) 
{
 var tmpdata;
 if (currfreq<=freqmax)
{
   document.getElementById("nstep").innerHTML =stepnum ;
   UpdateFreqValue(); 
    tmpdata=GetLevel();   
   
   CreateNormCoefsArray(stepnum,   dBmode  , GetValue(tmpdata,  stepnum, dBmode, 0 ) );
 
   if  ( document.getElementById("NormCoefs").checked )  { tmpdata =GetValue(tmpdata, stepnum , dBmode , 1 ) ; } else 
  {  tmpdata =GetValue(tmpdata, stepnum , dBmode , 0 ) ; }
 
    SetPoint( stepnum,  tmpdata);
    currfreq +=fstep;   
    stepnum++;
  }
  else{ StopOnClick();  }
}
 
 
 function drawLoop(  ) 
{
 var tmpdata;
 if (currfreq<=freqmax)
{
    document.getElementById("nstep").innerHTML =stepnum ;
     UpdateFreqValue(); 
     tmpdata=GetLevel();  
 
   if  ( document.getElementById("NormCoefs").checked )  { tmpdata =GetValue(tmpdata, stepnum , dBmode , 1 ) ;
} else 
 
 {  tmpdata =GetValue(tmpdata, stepnum , dBmode , 0 ) ; }
     SetPoint( stepnum,  tmpdata);
    currfreq  += fstep;   
    stepnum++;
}
else{  StopOnClick(); }
}
 
 
 function drawLoop1(  ) 
{
    var tmpdata;
 
     document.getElementById("nstep").innerHTML =0 ;
     UpdateFreqValue(); 
     tmpdata=(meter.volume *1.41)*(LevelIn*0.01);  
     tmpdata =GetValue(tmpdata, 0 , 0 , 0 ) ;
     var yPos1=HEIGHT*(1-tmpdata);
 
       canvasContext.clearRect(0,0,WIDTH, HEIGHT);
       canvasContext.beginPath();
       canvasContext.moveTo( 100, HEIGHT);
       canvasContext.lineTo(100, yPos1);  //fix
    
       if (meter.checkClipping()){  canvasContext.strokeStyle ="red"; } // тёмно-синий цвет
       else  { canvasContext.strokeStyle ="green"; }
  canvasContext.lineWidth =20;
  canvasContext.stroke();
 
}
 
 
function GetNumberofPoints(){ return 1+Math.ceil ((freqmax-freqmin)/fstep); }
function gotStream(stream) 
{
    CreateInputLevelMeter(stream); 
    CreateOsc( ); 
    Coeffs.length=GetNumberofPoints();
if (streammode==0){   timeoutID=setInterval( drawLoop , 50);   }
if (streammode==1){     if (dBmode==1) { Coeffs.fill(0) ; } else { Coeffs.fill(1) ; }    timeoutID=setInterval( NormLoop , 50 );  } 
if (streammode==2){     timeoutID=setInterval(  drawLoop1  , 50 );  } 
 
 } 
 
function StopOnClick()
{
clearInterval( timeoutID);
timeoutID=null  ;
oscillator.stop();
oscillator.disconnect();
currfreq=freqmin;
 
 meter =null;
mediaStreamSource = null;
audioCtxGen =null;
 audioContext =null;
stepnum=0;
if(streammode==2) canvasContext.clearRect(0,0,WIDTH, HEIGHT);
}
 
 
function ClearOnClick(){ StopOnClick(); canvasContext.clearRect(0,0,WIDTH, HEIGHT);}
function ClearCoefsOnClick(){ Coeffs.length=GetNumberofPoints() ; if (dBmode==1) { Coeffs.fill(0) ; } else { Coeffs.fill(1) ; } }
 
 
function RunOnClick() 
{
(document.getElementById("LogMode").checked==true)  ? dBmode=1 : dBmode=0; 
if (dBmode==1)  { document.getElementById("currmode").innerHTML ="Log. Mode " ;}
if (dBmode==0)  { document.getElementById("currmode").innerHTML ="Lin.  Mode " ; }
currfreq=freqmin;
streammode=0;
UpdateLabels();
 canvasContext = document.getElementById( "meter" ).getContext("2d");
 canvasContext.clearRect(0,0,WIDTH, HEIGHT);
CreateStream();
}
 
function NormOnClick() 
{
(document.getElementById("LogMode").checked==true)  ? dBmode=1 : dBmode=0; 
if (dBmode==1)  { document.getElementById("currmode").innerHTML ="Log. Mode " ;}
if (dBmode==0)  { document.getElementById("currmode").innerHTML ="Lin.  Mode " ; }
currfreq=freqmin;
streammode=1;
UpdateLabels();
 canvasContext = document.getElementById( "meter" ).getContext("2d");
 canvasContext.clearRect(0,0,WIDTH, HEIGHT);
 
CreateStream();
 
}
function LevelOnClick(){
 
currfreq=parseFloat(prompt('Input frequency, Hz ?', 1000.0) );
 streammode=2;
UpdateLabels();
document.getElementById("currmode").innerHTML ="Log. Mode ";
 canvasContext = document.getElementById( "meter" ).getContext("2d");
 canvasContext.clearRect(0,0,WIDTH, HEIGHT);
CreateStream();
 
}
0
3 / 3 / 0
Регистрация: 29.06.2018
Сообщений: 1,137
30.12.2020, 14:08  [ТС] 12
Регулировка уровня сигнала генератора (подключить через звено регулировки, а не непосредственно , добавив ползунок )
Javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
...
var gainNode=null;
var LevelOut=50;
 
function  SetLevelOut()
{
LevelOut=document.getElementById("LevelOutput").value;
document.getElementById("LevelOut1").innerHTML =LevelOut;
try {
gainNode.gain.value =0.01*LevelOut ;
} catch (e) { }
 
}
 
 
var mediaStreamSource = null;
 
 /*
function createSource(buffer) {
  var source = context.createBufferSource();
  var gainNode = context.createGainNode();
  source.buffer = buffer;
 
  source.connect(gainNode);
 
  gainNode.connect(context.destination);
 
  return {
    source: source,
    gainNode: gainNode
  };
}  
 
*/
function CreateOsc( )
{
 
//audioCtxGen = new (window.AudioContext || window.webkitAudioContext)();
 oscillator = audioContext.createOscillator();
 oscillator.type = 'sine';
 gainNode = audioContext.createGain ? audioContext.createGain() : audioContext.createGainNode();
 oscillator.connect(gainNode);
//oscillator.connect(audioContext.destination);
 gainNode.connect(audioContext.destination);
 gainNode.gain.value =0.01*LevelOut ;
 //gainNode.gain.setTargetAtTime(0.01*LevelOut, audioContext.currentTime, 1);
 oscillator.frequency.setValueAtTime(currfreq, audioContext.currentTime); // value in hertz
 oscillator.start();
}
 
...
 
 
function GetNumberofPoints(){ return 1+Math.ceil ((freqmax-freqmin)/fstep); }
function gotStream(stream) 
{
 
    CreateInputLevelMeter(stream); 
    CreateOsc( ); 
    Coeffs.length=GetNumberofPoints();
if (streammode==0){   timeoutID=setInterval( drawLoop , 50);   }
if (streammode==1){     if (dBmode==1) { Coeffs.fill(0) ; } else { Coeffs.fill(1) ; }    timeoutID=setInterval( NormLoop , 50 );  } 
if (streammode==2){     timeoutID=setInterval(  drawLoop1  , 50 );  } 
 
 } 
 
function StopOnClick()
{
clearInterval( timeoutID);
timeoutID=null  ;
oscillator.stop();
oscillator.disconnect();
currfreq=freqmin;
gainNode=null;
meter =null;
mediaStreamSource = null;
audioContext =null;
 
stepnum=0;
if(streammode==2) canvasContext.clearRect(0,0,WIDTH, HEIGHT);
}
...
HTML5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
...
<table>
<tr>
<td>
  Input  level : 
<input type="range" id="LevelInput"    min="0" max="100" step="1"  oninput="SetLevelIn()" value="50"   > 
<output id="LevelIn1"  >50 </output>%
</td>
<td> 
   Output  level : 
<input type="range" id="LevelOutput"    min="0" max="100" step="1"  oninput="SetLevelOut()" value="50"   > 
<output id="LevelOut1"  >50  </output>%
</td>
</tr>
 
  </table>      
...
0
Вложения
Тип файла: zip test_afr_volumereg.zip (4.2 Кб, 0 просмотров)
3 / 3 / 0
Регистрация: 29.06.2018
Сообщений: 1,137
30.12.2020, 19:26  [ТС] 13
Убрал в аттаче лишние аудиоконтексты.

Добавлено через 8 минут
Защищать вход звуковой карты от перегрузки, предсказывая усиление, защищать вход исследуемого устройства от перегрузки. Правильное ослабление и усиление, а также уровень выхода еще зависят от Windows и драйверов.
Некоторые масштабные коэффициенты можно заменить на более правильные, регулировку чувствительности по входу сделать правильнее .

Добавлено через 35 секунд
Масштаб пока только для тенденции , а не для конкретных значений.

Добавлено через 1 час 33 минуты
Способ приделать регулировку отсчетов входного потока через gainNodeIn (может и неадекватно влиять на детектор при слабых сигналах, на всякий случай, лучше по результату ):

Javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
var gainNodeIn=null;
...
 
function  SetLevelIn()
{
LevelIn=document.getElementById("LevelInput").value;
document.getElementById("LevelIn1").innerHTML =LevelIn;
try {
gainNodeIn.gain.value =0.01*LevelIn ; //перенесено из множителя в формулах сюда. Можно иначе .
//Зависит от детектора сигнала , когда поток не открыт , не обрабатывается , при остановке удаляется 
} catch (e) { }
 
 
}
 
function CreateInputLevelMeter(stream)
{
 gainNodeIn = audioContext.createGain ? audioContext.createGain() : audioContext.createGainNode();
 gainNodeIn.gain.value=0;
 
    mediaStreamSource = audioContext.createMediaStreamSource(stream);
    meter = createAudioMeter(audioContext);
  //  mediaStreamSource.connect(meter); //without  Gain
 
    mediaStreamSource.connect(gainNodeIn); //connect input stream to  the input node
    gainNodeIn.connect(meter);//подключает выходной поток  к детектору сигнала
    gainNodeIn.gain.value =0.01*LevelIn ;
 
}
Добавлено через 26 минут
Javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
function createAudioMeter(audioContext, clipLevel, averaging, clipLag) 
{
 var processor = audioContext.createScriptProcessor(512); //512, 256, 1024, другое количество отсчетов в буфере (см. на НЧ)
 processor.onaudioprocess = volumeAudioProcess;
 processor.clipping = false;
 processor.lastClip = 0;
 processor.volume = 0;
 processor.clipLevel = clipLevel || 0.98;
 processor.averaging = averaging || 0.95;
 processor.clipLag = clipLag || 750;
 
 // this will have no effect, since we don't copy the input to the output,
 // but works around a current Chrome bug.
processor.connect(audioContext.destination);
 
processor.checkClipping =function()
{
if (!this.clipping) return false;
if ((this.lastClip + this.clipLag) < window.performance.now())  this.clipping = false;
return this.clipping;
};
 
processor.shutdown =function(){ this.disconnect(); this.onaudioprocess = null;  };
return processor;
}
 
function volumeAudioProcess( event ) {
 var buf = event.inputBuffer.getChannelData(0);
 var bufLength = buf.length;
// var sum = 0;
 var xmax= 0;
 var x;
    
 for (var i=0; i<bufLength; i++) {
 x = buf[i];
  if (Math.abs(x)>=this.clipLevel) { this.clipping = true; this.lastClip = window.performance.now();  }
  if (Math.abs(x)>=Math.abs(xmax)) { xmax=Math.abs(x); } 
 //sum += x * x;
 }
Регулятор входных отсчетов подойдет для испытания экспериментальных детекторов с особыми постоянными времени и необычным количеством отсчетов в буфере .
Детектор может быть и на пиковое значение а также на СКЗ только синусоидального сигнала (наш случай) на основе пикового для модуля отсчетов *0,707 .

С защитной задержкой меньше 1 с плохо детектирует на следующих точках или возможен моддинг постоянной времени и количества отсчетов .
У на K=Umout/Uminp= (1.41*UrmsOut)/(1.41*Urms_input), множители сокращаются , рассматриваем только модули коэффициента передачи для синусоидального воздействия.

Добавлено через 1 час 40 минут
Способ приделать переключатели ослабления (усиления, расставить знаки и множители, выбрать букву , A-ослабление,K-коэффициент передачи , как удобнее подписывать ):
HTML5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
   
<table>
 
<tr>
<td>
         <label for="FminIn"  >Fmin: </label>
         <input type="text"  name="FminIn"  id="FminIn" value="50"    >  Hz     
         <input type="button" value="Input Fmin" onclick="OnChangeFmin()" >  
</td>
<td>    
        <label for="Delay" >Delay:</label>
         <input type="text"  name="DelayIn" id="DelayIn" value="100"    > ms 
         <input type="button" value="Input Delay" onclick="OnChangeDelay()" >       
</td>
<td>
<label for="SelAosc">Aosc</label>
<select size="1"  name="SelAosc" id="SelAosc" onchange="SelectAosc()"  >
    <option disabled>Select Aosc</option>
    <option selected value="1"> 0 dB</option>
    <option  value="0.316">10 dB </option>
    <option value="0.1">20 dB</option>
    <option value="0.0316">30 dB</option>
    <option value="0.010">40 dB</option>
   </select>
</td>
<td>  <output id="LabelAosc"  >0</output>  </td>
</tr>  
<tr>
<td>        
         <label for="FmaxIn" >Fmax:</label>
         <input type="text"  name="FmaxIn"  id="FmaxIn" value="10000"    >  Hz
         <input type="button" value="Input Fmax" onclick="OnChangeFmax()" >   <br> 
</td> 
<td>
         <label for="Step" >FStep: </label>
         <input type="text"   name="StepIn"  id="StepIn" value="50"    >  Hz
         <input type="button" value="Input step" onclick="OnChangeStep()" >  <br>   
</td> 
</tr>
<tr>
<td> Fmin: <output id="minfreq"  >0</output> Hz  </td>
<td> Delay: <output id="delay1"  >0</output> ms </td>
 
<td>
<label for="SelKinp">Kinp</label>
<select size="1"  name="SelKinp" id="SelKinp" onchange="SelectKinp()"   >
    <option disabled>Select Kin </option>
       <option value="10">  +20 dB</option>
    <option value="3.16">  +10 dB</option>
    <option selected value="1"> 0 dB</option>
    <option  value="0.316">-10 dB </option>
    <option value="0.1">-20 dB</option>
    <option value="0.0316">-30 dB</option>
    <option value="0.010">-40 dB</option>
   </select>
</td>
<td>  <output id="LabelKinp"  >0</output></td>
 </tr>
 <tr> 
<td> Fmax:  <output id="maxfreq" >0</output> Hz </td>
<td> FStep: <output id="step1"  >0</output> Hz</td>
 
 
 
</tr>
</table>


Javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
 var Kin=1;
 var Aout=1;
 
function SelectAosc()
{
 Aout=parseFloat(document.getElementById("SelAosc").options[document.getElementById("SelAosc").selectedIndex].value );
document.getElementById("LabelAosc").innerHTML = Aout;
gainNodeOut.gain.value =0.01*LevelOut *Aout;
}
function SelectKinp()
{
Kin=parseFloat(document.getElementById("SelKinp").options[document.getElementById("SelKinp").selectedIndex].value );
document.getElementById("LabelKinp").innerHTML = Kin;
gainNodeIn.gain.value =0.01*LevelIn*Kin ;
}
 
function  SetLevelIn()
{
LevelIn=document.getElementById("LevelInput").value;
document.getElementById("LevelIn1").innerHTML =LevelIn;
try {
gainNodeIn.gain.value =0.01*LevelIn*Kin ;
} catch (e) { }
 
 
}
 
 
function  SetLevelOut()
{
LevelOut=document.getElementById("LevelOutput").value;
document.getElementById("LevelOut1").innerHTML =LevelOut;
try {
 gainNodeOut.gain.value =0.01*LevelOut *Aout;
} catch (e) { }
 
}
 
 
// и далее  добавить в формулы *=Kin, *=Aout
Добавлено через 1 час 17 минут
Как доработать скрипт-процессор для обнуления отсчетов перед защитным интервалом задержки для повышения правильности реакции на сигналы соответствующих точек по оси частот и защиты от помех , добавив точки обработки ? Предусмотрено ли это в BaseAudioContext.createScriptProcessor()?

Javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
function createAudioMeter(audioContext, clipLevel, averaging, clipLag   )   ?
{
 var processor = audioContext.createScriptProcessor(512); //512,fix
 processor.onaudioprocess = volumeAudioProcess;
 processor.clipping = false;
 processor.lastClip = 0;
 processor.volume = 0;
 processor.clipLevel = clipLevel || 0.98;
 processor.averaging = averaging || 0.95;
 processor.clipLag = clipLag || 750;
 
  processor.resetvolume= 0 ;   ? 
 
 // this will have no effect, since we don't copy the input to the output,
 // but works around a current Chrome bug.
processor.connect(audioContext.destination);
 
...
 
 
function volumeAudioProcess( event ) {
 var buf = event.inputBuffer.getChannelData(0);
 var bufLength = buf.length;
// var sum = 0;
 var xmax= 0;
 var x;
    
 for (var i=0; i<bufLength; i++) {
 x = buf[i];
  if (Math.abs(x)>=this.clipLevel) { this.clipping = true; this.lastClip = window.performance.now();  }
  if (Math.abs(x)>=Math.abs(xmax)) { xmax=Math.abs(x); } 
 //sum += x * x;
 }
 
 
     //var rms =  Math.sqrt(sum / bufLength);
     //  this.volume =  Math.max(rms, this.volume*this.averaging);
//   xmax*=0.707 ;  //fix for peak 
   if(this.resetvolume==1)  { xmax= 0;  }
   this.volume = xmax; //get peak value or rms value for Um*sin(w*t)
}
Добавлено через 11 минут
кажется, так
Javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
function createAudioMeter(audioContext, clipLevel, averaging, clipLag ,   ResetVolume) 
{
 var processor = audioContext.createScriptProcessor(512); //512,fix
 processor.onaudioprocess = volumeAudioProcess;
 processor.clipping = false;
 processor.lastClip = 0;
 processor.volume = 0;
 processor.clipLevel = clipLevel || 0.98;
 processor.averaging = averaging || 0.95;
 processor.clipLag = clipLag || 750;
  processor.ResetVolume =ResetVolume;
 // this will have no effect, since we don't copy the input to the output,
 // but works around a current Chrome bug.
processor.connect(audioContext.destination);
 
processor.checkClipping =function()
{
if (!this.clipping) return false;
if ((this.lastClip + this.clipLag) < window.performance.now())  this.clipping = false;
return this.clipping;
};
 
processor.shutdown =function(){ this.disconnect(); this.onaudioprocess = null;  };
return processor;
}
 
function volumeAudioProcess( event ) {
 var buf = event.inputBuffer.getChannelData(0);
 var bufLength = buf.length;
// var sum = 0;
 var xmax= 0;
 var x;
    
 for (var i=0; i<bufLength; i++) {
 x = buf[i];
  if (Math.abs(x)>=this.clipLevel) { this.clipping = true; this.lastClip = window.performance.now();  }
  if (Math.abs(x)>=Math.abs(xmax)) { xmax=Math.abs(x); } 
 //sum += x * x;
 }
 
 
     //var rms =  Math.sqrt(sum / bufLength);
     //  this.volume =  Math.max(rms, this.volume*this.averaging);
//   xmax*=0.707 ;  //fix for peak 
  if(this.ResetVolume==1){ xmax=0;  }
   this.volume = xmax; //get peak value or rms value for Um*sin(w*t)
}
 
 
 
...
 
 
function GetLevel(){    meter.ResetVolume=0;       guard_delay();      return (meter.volume ) ; }
...
function UpdateFreqValue()
{    meter.ResetVolume=1; 
document.getElementById("curfreq1").innerHTML =currfreq.toFixed(2);  
//oscillator.frequency.value=currfreq; 
 oscillator.frequency.setValueAtTime(currfreq, audioContext.currentTime); 
// oscillator.frequency.linearRampToValueAtTime(currfreq, audioContext.currentTime);
}
0
3 / 3 / 0
Регистрация: 29.06.2018
Сообщений: 1,137
31.12.2020, 13:45  [ТС] 14
Регулировка с помощью Gain node из-за парсера детектора и шумов квантования при переключении уровней генератора кажется нелинейной . Требуется оптимизация парсера.

Добавлено через 19 минут
простая программа для масштабной сетки

Javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
var canvasContext = null;
function InitDrawing()
{
 canvasContext = document.getElementById( "meter" ).getContext("2d");
 canvasContext.clearRect(0,0,WIDTH, HEIGHT);
}
 
 
function ClearCanvas()
{
canvasContext.clearRect(0,0,WIDTH, HEIGHT);
}
var dxPos=1;
function SetPoint(numstep,  Y ,  num)
{
var xPos=numstep*dxPos;
var yPos ;
if (dBmode==0) { yPos=HEIGHT*(1-Y); }  
if (dBmode==1)  { yPos=HEIGHT*(1-0.7)-  Y *(HEIGHT/120);  }
       canvasContext.beginPath();
       canvasContext.moveTo( xPos, HEIGHT);
       canvasContext.lineTo( xPos, yPos);  //fix
        // canvasContext.moveTo( xPos, yPos);
         //canvasContext.lineTo( xPos+1, yPos);  //fix
    if (meter.checkClipping()){  canvasContext.strokeStyle ="red"; } // тёмно-синий цвет
    else  { canvasContext.strokeStyle ="green"; }
 canvasContext.lineWidth =dxPos;
  canvasContext.stroke();
}
 
 
 
function drawlevel(Y)
{
      var yPos1=HEIGHT*(1-Y);
       ClearCanvas();
       drawGrid();
       canvasContext.beginPath();
       canvasContext.moveTo( 100, HEIGHT);
       canvasContext.lineTo(100, yPos1);  //fix
       if (meter.checkClipping()){  canvasContext.strokeStyle ="red"; }   
       else  { canvasContext.strokeStyle ="green"; }
       canvasContext.lineWidth =20;
       canvasContext.stroke();
 
}
 
   
function drawGrid()
{
 
//var canvas = document.getElementById("myCanvas");
//var canvasContext = canvas.getContext("2d");
//ClearCanvas()
var istep =50;
var jstep =0.1;
var text; 
var xdata;
for (var i=0; i< WIDTH ; i+=istep)
{
       xdata=freqmin+ (i/dxPos)*fstep;  
       text=String(   xdata.toFixed(2));
       canvasContext.font = "9px Arial"
       canvasContext.fillText(text, i,HEIGHT  );
       canvasContext.beginPath();
       canvasContext.moveTo(i, HEIGHT);
       canvasContext.lineTo(i, 0);   
       canvasContext.strokeStyle ="blue";  
       canvasContext.lineWidth =1;
       canvasContext.stroke();
}
var yCoord;
for (var j=0; j<1  ; j+=jstep)
{
      yCoord=HEIGHT*(1-j);
       text=String(j.toFixed(2));
       canvasContext.font = "9px Arial"
       canvasContext.fillText(text, 0,yCoord  );
       canvasContext.beginPath();
       canvasContext.moveTo(0, yCoord);
       canvasContext.lineTo(WIDTH,  yCoord);   
       canvasContext.strokeStyle ="blue";  
       canvasContext.lineWidth =1;
       canvasContext.stroke();
}
 
}

levelmeter.js

Javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
function createAudioMeter(audioContext, clipLevel, averaging, clipLag ,   ResetVolume) 
{
 var processor = audioContext.createScriptProcessor(512); //512,fix
 processor.onaudioprocess = volumeAudioProcess;
 processor.clipping = false;
 processor.lastClip = 0;
 processor.volume = 0;
 processor.clipLevel = clipLevel || 0.98;
 processor.averaging = averaging || 0.95;
 processor.clipLag = clipLag || 750;
  processor.ResetVolume =ResetVolume;
 // this will have no effect, since we don't copy the input to the output,
 // but works around a current Chrome bug.
processor.connect(audioContext.destination);
 
processor.checkClipping =function()
{
if (!this.clipping) return false;
if ((this.lastClip + this.clipLag) < window.performance.now())  this.clipping = false;
return this.clipping;
};
 
processor.shutdown =function(){ this.disconnect(); this.onaudioprocess = null;  };
return processor;
}
 
function volumeAudioProcess( event ) {
 var buf = event.inputBuffer.getChannelData(0);
 var bufLength = buf.length;
// var sum = 0;
 var xmax= 0;
 var x;
    
 for (var i=0; i<bufLength; i++) {
 x = buf[i];
  if (Math.abs(x)>=this.clipLevel) { this.clipping = true; this.lastClip = window.performance.now();  }
  if (Math.abs(x)>=Math.abs(xmax)) { xmax=Math.abs(x); } 
 //sum += x * x;
 }
 
 // if(this.ResetVolume==1){ xmax=0;  }
 
     //var rms =  Math.sqrt(sum / bufLength);
     //  this.volume =  Math.max(rms, this.volume*this.averaging);
//   xmax*=0.707 ;  //fix for peak 
 
   this.volume = xmax; //get peak value or rms value for Um*sin(w*t)
}
перенес в отдельный модуль drawaxes.js

Линейность по переключателям (линейный вход на линейный выход ) в линейном режиме при индикации уровня при 1кГц, Input Level=16% , Output level= 10%, Aosc= 0dB(1.0), Kinp=+ 20 dB(10.0) ,

Добавлено через 58 секунд
Там еще особенность про регулировку усиления была (про не превышение 0,1...0,2 ).

Добавлено через 45 минут
Javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
function CreateOsc( )
{
//audioCtxGen = new (window.AudioContext || window.webkitAudioContext)();
 oscillator = audioContext.createOscillator();
 oscillator.type = 'sine';
 gainNodeOut = audioContext.createGain ? audioContext.createGain() : audioContext.createGainNode();
 oscillator.connect( gainNodeOut );
//oscillator.connect(audioContext.destination);
  gainNodeOut .connect(audioContext.destination);
  gainNodeOut .gain.value =0.01*LevelOut*Kout*0.1 ;
 oscillator.frequency.setValueAtTime(currfreq, audioContext.currentTime); // value in hertz
 oscillator.start();
}
 
 
function CreateInputLevelMeter(stream)
{
//  gainNodeIn = audioContext.createGain ? audioContext.createGain() : audioContext.createGainNode();
//  gainNodeIn.gain.value=0;
    mediaStreamSource = audioContext.createMediaStreamSource(stream);
    meter = createAudioMeter(audioContext);
    mediaStreamSource.connect(meter); //without  Gain
//    mediaStreamSource.connect(gainNodeIn); //connect input stream to  the input node
//    gainNodeIn.connect(meter);
//    gainNodeIn.gain.value =0.01*LevelIn*Kin *10;
 
}
Свыше -40 дБ может давать шум 1,5...6 дБ , но не с материнки , а из-за способа обработки семплов и соотношения усилений , но так уровень ограничения виднее только при ограничении, еще один множитель зависит от общего регулятор а громкости .

Добавлено через 3 минуты
HTML5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<td>
<label for="SelKosc">Kosc</label>
<select size="1"  name="SelKosc" id="SelKosc" onchange="SelectKosc()"  >
    <option disabled>Select Kosc</option>
    <option selected value="1"> 0 dB</option>
    <option  value="0.7071">-3.0 dB </option>
    <option  value="0.50">-6.0 dB </option>
    <option  value="0.316">-10 dB </option>
    <option  value="0.1778">-15 dB </option>
    <option value="0.1">-20 dB</option>
    <option value="0.0316">-30 dB</option>
    <option value="0.010">-40 dB</option>
    <option value="0.00316">-50 dB</option>
    <option value="0.001">-60 dB</option> 
   </select>
</td>
 
...
 
<td>
<label for="SelKinp">Kinp</label>
<select size="1"  name="SelKinp" id="SelKinp" onchange="SelectKinp()"   >
    <option disabled>Select Kin </option>
       <option value="1000">  +60 dB</option>
       <option value="316">  +50 dB</option>
       <option value="100">  +40 dB</option>
       <option value="31.6">  +30 dB</option>
       <option value="10">  +20 dB</option>
    <option value="3.16">  +10 dB</option>
    <option selected value="1"> 0 dB</option>
    <option  value="0.7071">-3.0 dB </option>
    <option  value="0.50">-6.0 dB </option>
    <option  value="0.316">-10 dB </option>
    <option  value="0.1778">-15 dB </option>
    <option value="0.1">-20 dB</option>
    <option value="0.0316">-30 dB</option>
    <option value="0.010">-40 dB</option>
    <option value="0.00316">-50 dB</option>
      <option value="0.001">-60 dB</option>
   </select>
</td>
Временные контролы для дебага.

Добавлено через 4 часа 30 минут
Встроил масштабирование, приделал переключатели, изменил цвет. Можно ввести автовыбор количества меток частоты.
drawaxes.js
Javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
var canvasContext = null;
function InitDrawing()
{
 canvasContext = document.getElementById( "meter" ).getContext("2d");
 canvasContext.clearRect(0,0,WIDTH, HEIGHT);
}
 
function ClearCanvas(){canvasContext.clearRect(0,0,WIDTH, HEIGHT);}
 
function ScaleY(  Ydata , pictB, pictT    )
{
 var pictH = (pictB -pictT );
 var ymin=0  ;
 var ymax=1 ;
if (dBmode==0){ ymin=0  ; ymax=1;  }
if (dBmode==1){ ymin=KminLog*1.0  ; ymax=KmaxLog*1.0  }
var  ytmp=(ymax-Ydata)/(ymax-ymin) ;  
 return ( pictB  - pictH *(1-ytmp) );      //ytmp=0...1
}
 
function ScaleX(  Xdata , pictL, pictR    )
{
 var pictW = (pictR -pictL);
 var  xmin=freqmin;
 var  xmax=freqmax;
var  xtmp=(Xdata-xmin)/(xmax-xmin) ;  
 return ( pictL+pictW*xtmp );      //xtmp=0...1
}
 
 
 
 
function SetPoint(currfreq,  ydata ,  num)
{
var pictBottom=HEIGHT*0.9 ;
var pictTop= HEIGHT*0.1;
var dxPos=1;
var xPos =ScaleX( currfreq , 20,  WIDTH );  
var yPos =ScaleY( ydata, pictBottom, pictTop);
 
 
       canvasContext.beginPath();
        //canvasContext.moveTo( xPos, pictBottom);
        //canvasContext.lineTo( xPos, yPos);  //fix
         canvasContext.moveTo( xPos, yPos);
          canvasContext.lineTo( xPos+dxPos, yPos);  //fix
    if (meter.checkClipping()){  canvasContext.strokeStyle ="red"; }  
    else  { canvasContext.strokeStyle ="green"; }
    canvasContext.lineWidth =2;
    canvasContext.stroke();
}
 
 
 
function drawlevel(xdata , ydata)
{
 
var pictBottom=HEIGHT*0.9 ;
var pictTop= HEIGHT*0.1;
      ClearCanvas();
      drawGrid();  
      var yPos1=   ScaleY(ydata , pictBottom, pictTop  ) ;  //Ydata=0...1  
      var xPos1=ScaleX( xdata  , 20,  WIDTH ); 
       canvasContext.beginPath();
       canvasContext.moveTo( xPos1, pictBottom);
       canvasContext.lineTo(xPos1, yPos1);  //fix
       if (meter.checkClipping()){  canvasContext.strokeStyle ="red"; }   
       else  { canvasContext.strokeStyle ="green"; }
       canvasContext.lineWidth =10;
       canvasContext.stroke();
}
 
 
 
   
function drawGrid ()
{
var pictBottom=HEIGHT*0.9 ;
var pictTop= HEIGHT*0.1;
 
 
//var canvas = document.getElementById("myCanvas");
//var canvasContext = canvas.getContext("2d");
//ClearCanvas()
var istep =10;
var jstep =0.1;
var text; 
var xdata;
var  xCoord,yCoord;
var y1min;
var y1max;
 
if (dBmode==0){ y1min=0  ; y1max=1 ; jstep=0.1;  }
if (dBmode==1){ y1min=KminLog*1.0  ; y1max=KmaxLog*1.0;  jstep=10; }
 
 
//draw vertical lines
for ( xdata=freqmin; xdata<=freqmax ; xdata+=fstep*20  )  //fix
{
        
        text=String(xdata.toFixed(2));
        xCoord=ScaleX( xdata , 20,  WIDTH ); 
       canvasContext.font = "9px Arial"
       canvasContext.fillText(text,  xCoord, HEIGHT  );
       canvasContext.beginPath();
       canvasContext.moveTo(xCoord, pictTop);
       canvasContext.lineTo(xCoord, pictBottom+10);   
       canvasContext.strokeStyle ="#d3d3d3";  
       canvasContext.lineWidth =1;
       canvasContext.stroke();
}
 
 
//draw horizontal lines 
for (var j=y1min;  j<=y1max ; j +=jstep)
{
       yCoord= ScaleY(  j, pictBottom, pictTop  );
       text=String(j.toFixed(1));
            
       canvasContext.font = "9px Arial"
  
       canvasContext.fillText(text, 0, yCoord  );
       canvasContext.beginPath();
       canvasContext.moveTo(0, yCoord);
       canvasContext.lineTo(WIDTH,  yCoord);   
       canvasContext.strokeStyle ="#d3d3d3";  
       canvasContext.lineWidth =1;
       canvasContext.stroke();
}
 
}
main.js

Javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
 
 
var freqmin=50.0;
var freqmax=10000.0;
var currfreq= 50.0;
var fstep =50.0;
var delay=100;
var LevelIn=50;
var LevelOut=50;
 
 
var  gainNodeOut =null;
var  gainNodeIn=null;
var audioContext = null;
var meter = null;
var oscillator =null;
 
var WIDTH=1000;
var HEIGHT=300;
var timeoutID=null;
 
var stepnum=0;
var dBmode=0; 
var streammode=0;
 var Kin=1;
 var Kout=1;
 
var Kmax=1;
var Kmin=0;
 
var KmaxLog=0;
var KminLog=-120;
 
 
 
function SelectKmax()
{
Kmax=parseFloat(document.getElementById("SelKmax").options[document.getElementById("SelKmax").selectedIndex].value );
KmaxLog=20*Math.log10(Kmax);
KmaxLog=KmaxLog.toFixed(0);
 document.getElementById("LabelKmax").innerHTML = KmaxLog;
 
}
function SelectKmin()
{
Kmin=parseFloat(document.getElementById("SelKmin").options[document.getElementById("SelKmin").selectedIndex].value );
KminLog=20*Math.log10(Kmin);
KminLog=KminLog.toFixed(0);
 document.getElementById("LabelKmin").innerHTML = KminLog;
}
 
 
function SelectKosc()
{
 Kout=parseFloat(document.getElementById("SelKosc").options[document.getElementById("SelKosc").selectedIndex].value );
 document.getElementById("LabelKosc").innerHTML = Kout;
 gainNodeOut.gain.value =0.01*LevelOut *Kout*0.1 ;
}
function SelectKinp()
{
Kin=parseFloat(document.getElementById("SelKinp").options[document.getElementById("SelKinp").selectedIndex].value );
document.getElementById("LabelKinp").innerHTML = Kin;
//gainNodeIn.gain.value =0.01*LevelIn*Kin*10  ;
}
 
function  SetLevelIn()
{
LevelIn=document.getElementById("LevelInput").value;
document.getElementById("LevelIn1").innerHTML =LevelIn;
try {
gainNodeIn.gain.value =0.01*LevelIn*Kin *10;
} catch (e) { }
}
 
 
function  SetLevelOut()
{
LevelOut=document.getElementById("LevelOutput").value;
document.getElementById("LevelOut1").innerHTML =LevelOut;
try {
 gainNodeOut.gain.value =0.01*LevelOut *Kout*0.1;
} catch (e) { }
}
 
 function UpdateLabels()
{
 document.getElementById("minfreq").innerHTML =freqmin;
 document.getElementById("maxfreq").innerHTML =freqmax;
 document.getElementById("delay1").innerHTML =delay;
 document.getElementById("step1").innerHTML =fstep;
}
window.onload = function Init() {UpdateLabels(); }
  
function OnChangeFmin(){freqmin=parseFloat(document.getElementById("FminIn").value);UpdateLabels();}
function OnChangeFmax(){ freqmax =parseFloat(document.getElementById("FmaxIn").value); UpdateLabels(); }
function OnChangeDelay(){ delay =parseFloat(document.getElementById("DelayIn").value) ; UpdateLabels(); }
function OnChangeStep(){ fstep =parseFloat(document.getElementById("StepIn").value) ;UpdateLabels(); }
 
function CreateOsc( )
{
//audioCtxGen = new (window.AudioContext || window.webkitAudioContext)();
 oscillator = audioContext.createOscillator();
 oscillator.type = 'sine';
 gainNodeOut = audioContext.createGain ? audioContext.createGain() : audioContext.createGainNode();
 oscillator.connect( gainNodeOut );
//oscillator.connect(audioContext.destination);
  gainNodeOut .connect(audioContext.destination);
  gainNodeOut .gain.value =0.01*LevelOut*Kout*0.1 ;
 oscillator.frequency.setValueAtTime(currfreq, audioContext.currentTime); // value in hertz
 oscillator.start();
}
 
 
function CreateInputLevelMeter(stream)
{
//  gainNodeIn = audioContext.createGain ? audioContext.createGain() : audioContext.createGainNode();
//  gainNodeIn.gain.value=0;
    mediaStreamSource = audioContext.createMediaStreamSource(stream);
    meter = createAudioMeter(audioContext);
    mediaStreamSource.connect(meter); //without  Gain
//    mediaStreamSource.connect(gainNodeIn); //connect input stream to  the input node
//    gainNodeIn.connect(meter);
//    gainNodeIn.gain.value =0.01*LevelIn*Kin *10;
 
}
 
 var Coeffs= new Float64Array(2048);
 Coeffs.fill(1);
  
 
 function GetLevel(){    meter.ResetVolume=0;       guard_delay();      return (meter.volume*  0.01*LevelIn*Kin *10 ) ; }
 //function GetLevel(){ meter.ResetVolume=0;       guard_delay();      return (meter.volume  ) ;} 
 
 
function GetValue(Ldata , nstep1, modedB, mode1  )
{
     
var datadBIn=20*Math.log10(Ldata );
var dataIn=Ldata ;
if (mode1==1) {  datadBIn+= Coeffs [nstep1] ; dataIn*= Coeffs[ nstep1] ; } 
      document.getElementById("Vinp1").innerHTML =dataIn.toExponential(4); 
      document.getElementById("Vinp2").innerHTML =datadBIn.toFixed(1); 
 if (modedB==1)   { return datadBIn; } else { return dataIn ; }
    return dataIn ;  
}
 
function CreateNormCoefsArray(stepnum,  mode, indata )
{  
var coeftmp=1;
var i=stepnum;
if (mode==0){ coeftmp=1/(indata+(1e-12));   } //obtain coeff.   for     Level*Coeffs=1return; 
if (mode==1){ coeftmp=(-indata);  } //obtain coeff.   for     Level+Coeffs =return;
Coeffs[i]=coeftmp;
}
 
 function dsleep(milliseconds) 
{
 var start = new Date().getTime();
 for (var i = 0; i < 1e7; i++) { if ((new Date().getTime() - start) > milliseconds) {   break; } }
}
 
 
 
 function guard_delay(){ dsleep(delay); }
 
function UpdateFreqValue()
{    
 meter.ResetVolume=1; 
document.getElementById("curfreq1").innerHTML =currfreq.toFixed(2);  
//oscillator.frequency.value=currfreq; 
 oscillator.frequency.setValueAtTime(currfreq, audioContext.currentTime); 
// oscillator.frequency.linearRampToValueAtTime(currfreq, audioContext.currentTime);
}
 
 
 function NormLoop(  ) 
{
 var tmpdata;
 if (currfreq<=freqmax)
{
   document.getElementById("nstep").innerHTML =stepnum ;
   meter.ResetVolume=1;
   UpdateFreqValue(); 
    tmpdata=GetLevel();   
   CreateNormCoefsArray(stepnum,  dBmode  , GetValue(tmpdata,  stepnum, dBmode, 0 ) );
   if  ( document.getElementById("NormCoefs").checked )  { tmpdata =GetValue(tmpdata, stepnum , dBmode , 1 ) ; }
  else {  tmpdata =GetValue(tmpdata, stepnum , dBmode , 0 ) ; }
 
    SetPoint( currfreq,  tmpdata);
    currfreq +=fstep;   
    stepnum++;
  }
  else{ StopOnClick();  }
}
 
 
 function drawLoop(  ) 
{
 var tmpdata;
 if (currfreq<=freqmax)
{
 
    document.getElementById("nstep").innerHTML =stepnum ;
    meter.ResetVolume=1; 
    UpdateFreqValue(); 
     tmpdata=GetLevel();  
 
   if  ( document.getElementById("NormCoefs").checked )  { tmpdata =GetValue(tmpdata, stepnum , dBmode , 1 ) ;
} else  {  tmpdata =GetValue(tmpdata, stepnum , dBmode , 0 ) ; }
     SetPoint( currfreq,  tmpdata);
    currfreq  += fstep;   
    stepnum++;
}
else{  StopOnClick(); }
}
 
 
 function drawLoop1(  ) 
{
    var tmpdata;
  
      document.getElementById("nstep").innerHTML =0 ;
       meter.ResetVolume=1;  
       UpdateFreqValue();    
       tmpdata=GetLevel();
       tmpdata =GetValue(tmpdata, stepnum , dBmode , 0 ) ;
       drawlevel(currfreq, tmpdata);
}
 
 
function GetNumberofPoints(){ return 1+Math.ceil ((freqmax-freqmin)/fstep); }
function gotStream(stream) 
{
    CreateInputLevelMeter(stream); 
    CreateOsc( ); 
    Coeffs.length=GetNumberofPoints();
if (streammode==0){   timeoutID=setInterval( drawLoop , 50);   }
if (streammode==1){     if (dBmode==1) { Coeffs.fill(0) ; } else { Coeffs.fill(1) ; }    timeoutID=setInterval( NormLoop , 50 );  } 
if (streammode==2){     timeoutID=setInterval(  drawLoop1  , 50 );  } 
 
 } 
 
function StopOnClick()
{
 clearInterval( timeoutID);
 timeoutID=null  ;
 oscillator.stop();
 oscillator.disconnect();
 currfreq=freqmin;
 gainNodeOut =null;
 gainNodeIn=null;
 meter =null;
 mediaStreamSource = null;
 audioContext =null;
 
stepnum=0;
if(streammode==2) canvasContext.clearRect(0,0,WIDTH, HEIGHT);
}
 
 
function ClearOnClick(){ StopOnClick(); ClearCanvas();}
function ClearCoefsOnClick(){ Coeffs.length=GetNumberofPoints() ; if (dBmode==1) { Coeffs.fill(0) ; } else { Coeffs.fill(1) ; } }
 
 
function RunOnClick() 
{
(document.getElementById("LogMode").checked==true)  ? dBmode=1 : dBmode=0; 
if (dBmode==1)  { document.getElementById("currmode").innerHTML ="Log. Mode " ;}
if (dBmode==0)  { document.getElementById("currmode").innerHTML ="Lin.  Mode " ; }
 currfreq=freqmin;
 streammode=0;
 UpdateLabels();
  InitDrawing();
  drawGrid();
 CreateStream();
}
 
function NormOnClick() 
{
(document.getElementById("LogMode").checked==true)  ? dBmode=1 : dBmode=0; 
if (dBmode==1)  { document.getElementById("currmode").innerHTML ="Log. Mode " ;}
if (dBmode==0)  { document.getElementById("currmode").innerHTML ="Lin.  Mode " ; }
currfreq=freqmin;
streammode=1;
UpdateLabels();
 InitDrawing();
 drawGrid();
CreateStream();
}
 
function LevelOnClick(){
 
(document.getElementById("LogMode").checked==true)  ? dBmode=1 : dBmode=0; 
if (dBmode==1)  { document.getElementById("currmode").innerHTML ="Log. Mode " ;}
if (dBmode==0)  { document.getElementById("currmode").innerHTML ="Lin.  Mode " ; }
 
 currfreq=parseFloat(prompt('Input frequency, Hz ?', 1000.0) );
 streammode=2;
 UpdateLabels();
 InitDrawing();
 CreateStream();
 
 
}
levelmeter.js

Javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
function createAudioMeter(audioContext, clipLevel, averaging, clipLag ,   ResetVolume) 
{
 var processor = audioContext.createScriptProcessor(512); //512,fix
 processor.onaudioprocess = volumeAudioProcess;
 processor.clipping = false;
 processor.lastClip = 0;
 processor.volume = 0;
 processor.clipLevel = clipLevel || 0.98;
 processor.averaging = averaging || 0.95;
 processor.clipLag = clipLag || 750;
  processor.ResetVolume =ResetVolume;
 // this will have no effect, since we don't copy the input to the output,
 // but works around a current Chrome bug.
processor.connect(audioContext.destination);
 
processor.checkClipping =function()
{
if (!this.clipping) return false;
if ((this.lastClip + this.clipLag) < window.performance.now())  this.clipping = false;
return this.clipping;
};
 
processor.shutdown =function(){ this.disconnect(); this.onaudioprocess = null;  };
return processor;
}
 
function volumeAudioProcess( event ) {
 var buf = event.inputBuffer.getChannelData(0);
 var bufLength = buf.length;
// var sum = 0;
 var xmax= 0;
 var x;
    
 for (var i=0; i<bufLength; i++) {
 x = buf[i];
  if (Math.abs(x)>=this.clipLevel) { this.clipping = true; this.lastClip = window.performance.now();  }
  if (Math.abs(x)>=Math.abs(xmax)) { xmax=Math.abs(x); } 
 //sum += x * x;
 }
 
 if(this.ResetVolume==1){ xmax=0;  }
 
     //var rms =  Math.sqrt(sum / bufLength);
     //  this.volume =  Math.max(rms, this.volume*this.averaging);
//   xmax*=0.707 ;  //fix for peak 
 
   this.volume = xmax; //get peak value or rms value for Um*sin(w*t)
}
var mediaStreamSource = null;
 
function didntGetStream() { alert('Stream generation failed.');  }
 
function CreateStream()
{
    window.AudioContext = window.AudioContext || window.webkitAudioContext; 
    audioContext = new AudioContext();
    try {
        navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
        navigator.getUserMedia(
        {
            "audio": {
                "mandatory": {
                    "googEchoCancellation": "false",
                    "googAutoGainControl": "false",
                    "googNoiseSuppression": "false",
                    "googHighpassFilter": "false"
                },
                "optional": []
            },
        }, gotStream, didntGetStream);
  } catch (e) { alert('getUserMedia threw exception :' + e);  }
}
 
 
 
 /*
function createSource(buffer) {
  var source = context.createBufferSource();
  var gainNode = context.createGainNode();
  source.buffer = buffer;
  source.connect(gainNode);
  gainNode.connect(context.destination);
  return {
    source: source,
    gainNode: gainNode
  };
}  
 
*/
0
Вложения
Тип файла: zip afr_html_scale.zip (6.0 Кб, 0 просмотров)
3 / 3 / 0
Регистрация: 29.06.2018
Сообщений: 1,137
31.12.2020, 17:08  [ТС] 15
index.html
HTML5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
<!doctype html>
<html>
    <head>
                  <title>AFR and Volume Meter Sample</title>
         
        <style>
        </style>
        <!-- Include the volume meter component -->
         <script src="levelmeter.js"></script> 
        <!-- Include the main app logic -->
                                    <script src="drawaxes.js"></script>
        <script src="main.js"></script>
 
    </head>
    <body>
 
<p>This sample shows how to implement an AFR meter 
 using volume meter in Web Audio, using a ScriptProcessor. </p>
        
<canvas id="meter" width="1000" height="300"></canvas>
    
<table>
<tr>
<td>Current frequency: <output id="curfreq1"  >0</output> Hz  </td>
<td>Current Num : <output id="nstep"  >0</output>    </td>
</tr>
<tr>
 <td>Current Input Level : <output id="Vinp1"  >0</output>   </td>
 <td>Current Input Level , dB: <output id="Vinp2"  >0</output></td>
</tr>
 
</table>
 
<table>
<tr>
<td>
  Input  level : 
<input type="range" id="LevelInput"    min="0" max="100" step="1"  oninput="SetLevelIn()" value="50"   > 
<output id="LevelIn1"  >50 </output>%
</td>
<td> 
   Output  level : 
<input type="range" id="LevelOutput"    min="0" max="100" step="1"  oninput="SetLevelOut()" value="50"   > 
<output id="LevelOut1"  >50  </output>%
</td>
</tr>
 
  </table>         
        
<table>
 
<tr>
<td>
         <label for="FminIn"  >Fmin: </label>
         <input type="text"  name="FminIn"  id="FminIn" value="50"    >  Hz     
         <input type="button" value="Input Fmin" onclick="OnChangeFmin()" >  
</td>
<td>    
        <label for="Delay" >Delay:</label>
         <input type="text"  name="DelayIn" id="DelayIn" value="100"    > ms 
         <input type="button" value="Input Delay" onclick="OnChangeDelay()" >       
</td>
<td>
<label for="SelKosc">Kosc</label>
<select size="1"  name="SelKosc" id="SelKosc" onchange="SelectKosc()"  >
    <option disabled>Select Kosc</option>
    <option selected value="1"> 0 dB</option>
    <option  value="0.7071">-3.0 dB </option>
    <option  value="0.50">-6.0 dB </option>
    <option  value="0.316">-10 dB </option>
    <option  value="0.1778">-15 dB </option>
    <option value="0.1">-20 dB</option>
    <option value="0.0316">-30 dB</option>
    <option value="0.010">-40 dB</option>
    <option value="0.00316">-50 dB</option>
    <option value="0.001">-60 dB</option> 
   </select>
</td>
<td>  <output id="LabelKosc"  >0</output>  </td>
 
<td>
<label for="SelKmax">Kmax, dB</label>
<select size="1"  name="SelKmax" id="SelKmax" onchange="SelectKmax()"   >
    <option value="3.16">  +10 dB</option>
    <option selected value="1"> 0 dB</option>
    <option  value="0.316">-10 dB </option>
    <option value="0.1"> -20 dB</option>
    <option value="0.0316"> -30 dB</option>
    <option value="0.010" > -40 dB</option>
    <option value="0.00316">-50 dB</option>
      <option value="0.001">-60 dB</option>
   </select>
</td>
<td>  <output id="LabelKmax"  >0</output>dB</td>
 
 
 
</tr>  
<tr>
<td>        
         <label for="FmaxIn" >Fmax:</label>
         <input type="text"  name="FmaxIn"  id="FmaxIn" value="10000"    >  Hz
         <input type="button" value="Input Fmax" onclick="OnChangeFmax()" >   <br> 
</td> 
<td>
         <label for="Step" >FStep: </label>
         <input type="text"   name="StepIn"  id="StepIn" value="50"    >  Hz
         <input type="button" value="Input step" onclick="OnChangeStep()" >  <br>   
</td> 
</tr>
<tr>
<td> Fmin: <output id="minfreq"  >0</output> Hz  </td>
<td> Delay: <output id="delay1"  >0</output> ms </td>
 
<td>
<label for="SelKinp">Kinp</label>
<select size="1"  name="SelKinp" id="SelKinp" onchange="SelectKinp()"   >
    <option disabled>Select Kin </option>
       <option value="1000">  +60 dB</option>
       <option value="316">  +50 dB</option>
       <option value="100">  +40 dB</option>
       <option value="31.6">  +30 dB</option>
       <option value="10">  +20 dB</option>
    <option value="3.16">  +10 dB</option>
    <option selected value="1"> 0 dB</option>
    <option  value="0.7071">-3.0 dB </option>
    <option  value="0.50">-6.0 dB </option>
    <option  value="0.316">-10 dB </option>
    <option  value="0.1778">-15 dB </option>
    <option value="0.1">-20 dB</option>
    <option value="0.0316">-30 dB</option>
    <option value="0.010">-40 dB</option>
    <option value="0.00316">-50 dB</option>
      <option value="0.001">-60 dB</option>
   </select>
</td>
<td>  <output id="LabelKinp"  >0</output></td>
<td>
<label for="SelKmin">Kmin, dB</label>
<select size="1"  name="SelKmin" id="SelKmin" onchange="SelectKmin()"   >
 
 
    <option value="0.316">-10 dB </option>
    <option value="0.1">-20 dB</option>
    <option value="0.0316">-30 dB</option>
    <option  value="0.010"> -40 dB</option>
    <option value="0.00316">-50 dB</option>
      <option selected value="0.001">-60 dB</option>
     <option value="0.000316">-70 dB</option>
     <option value="0.0001">-80 dB</option>
     <option value="0.0000316">-90 dB</option>
     <option value="0.00001">-100 dB</option>
   </select>
</td>
<td>  <output id="LabelKmin"  >-60</output>dB</td>
 
 
 
 </tr>
 <tr> 
<td> Fmax:  <output id="maxfreq" >0</output> Hz </td>
<td> FStep: <output id="step1"  >0</output> Hz</td>
 
 
 
</tr>
</table>           
 
 
 
 
<p> 
  
   <input type="radio" id="LinearMode" name="SelectMode" value="LinearMode" checked >
    <label for="LinearMode">Linear Mode </label>
    <input type="radio" id="LogMode" name="SelectMode" value="LogMode">
    <label for="LogMode">Log. Mode</label> 
    <input type="checkbox" id="NormCoefs" name="NormCoefs"  value="check" checked >
    <label for="NormCoef">Use coefficients </label>
<br>
    <output id="currmode"  ></output> </p>
 
 
 <input type="button"  value="Level" onclick="LevelOnClick()" > 
 <input type="button"  value="Normalize" onclick="NormOnClick()" > 
 <input type="button" value="ClearCoeffs" onclick="ClearCoefsOnClick()" >
 <input type="button"  value="Run" onclick="RunOnClick()" > 
 <input type="button" value="Stop" onclick="StopOnClick()" >
 <input type="button" value="Clear" onclick="ClearOnClick()" >
 
</p>
 
 
         
 </body>
</html>
Добавлено через 2 часа 59 минут
Для генератора можно оставить
Javascript
1
gainNodeOut.gain.value =0.01*LevelOut *Kout ;
, движок 10...100%, множитель генератора не больше 1.0 (0 дБ, дает полный столбик на регуляторе громкости от потока).
Канал левый.
А вот с линейного входа положение ползунка драйверов 48..56 % устраняет невидимый клиппинг до детектора , но умножать отсчеты на 1,5...2 ...10 для пикового детектора в парсере результата . Подумать про выбор правильного канала (левый,кажется, соединять оба).
Javascript
1
2
3
4
...
gainNodeIn.gain.value =0.01*LevelIn*Kin ;
...
 function GetLevel(){    meter.ResetVolume=0;       guard_delay();      return (meter.volume*  0.01*LevelIn*Kin ) ; }

Javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 for (var i=0; i<bufLength; i++) {
 x = buf[i];
  if (Math.abs(x)>=this.clipLevel) { this.clipping = true; this.lastClip = window.performance.now();  }
  if (Math.abs(x)>=Math.abs(xmax)) { xmax=Math.abs(x); } 
 //sum += x * x;
 }
 
 if(this.ResetVolume==1){ xmax=0;  }
 
     //var rms =  Math.sqrt(sum / bufLength);
     //  this.volume =  Math.max(rms, this.volume*this.averaging);
//   xmax*=0.707 ;  //fix for peak 
   //  this.volume = xmax ;
   this.volume = xmax*2 ; //get peak value or rms value for Um*sin(w*t)
}
Звук с входа на выход отключать. В линуксе в Хроме идет, в фаерфоксе немного глючит генератор.
В андроиде не открывается.

Добавлено через 18 минут
Javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
function CreateStream()
{
  //  window.AudioContext = window.AudioContext || window.webkitAudioContext;   
//может, добавить вариантов ?
 window.AudioContext=(window.AudioContext ||
  window.webkitAudioContext ||
  window.mozAudioContext ||
  window.oAudioContext ||
  window.msAudioContext);
 
 
    audioContext = new AudioContext();
    try {
        navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
        navigator.getUserMedia(
        {
            "audio": {
                "mandatory": {
                    "googEchoCancellation": "false",
                    "googAutoGainControl": "false",
                    "googNoiseSuppression": "false",
                    "googHighpassFilter": "false"
                },
                "optional": []
            },
        }, gotStream, didntGetStream);
  } catch (e) { alert('getUserMedia threw exception :' + e);  }
}
0
Вложения
Тип файла: zip drawaxes.zip (6.2 Кб, 0 просмотров)
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
31.12.2020, 17:08

Заказываю контрольные, курсовые, дипломные и любые другие студенческие работы здесь.

Генератор частот
Здравствуйте, уважаемые радиотехники помогите пожалуйста разобраться. Преподаватель дал задание...

Генератор частот
Здравствуйте! Подскажите пожалуйста код, позволяющий воспроизводить любую заданную частоту в Гц,...

Генератор произвольных частот на МК
какой алгоритм для генерации произвольной частоты на микроконтроллере? Подскажите, как добиться...

генератор частот с заданным шагом
всю голову издумал, как на меге сделать генератор частоты 1000Гц-2000Гц с шагом 1, 5 или 10Гц?

Хочу сделать генератор и дешифратор звуковых сигналов
Есть идея, но не знаю, реально ли это реализовать на ATMEGA8 (это почти всё, что я имею, ещё есть...

Генератор частот. Осталось немного доработать
Написал программу, которая генирирует звук заданной частоты на протяжение 8 секунд. Хочу, что бы он...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
15
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2021, vBulletin Solutions, Inc.