From 8a9da5cf454540c59e1c37b89a02a4f2c00865a2 Mon Sep 17 00:00:00 2001 From: GeoNomad Date: Sun, 5 Apr 2015 07:16:59 -0700 Subject: [PATCH 01/15] Pause recording when signal below squelch level If recording and the squelch level is set, the recording will not include the noise between transmissions. Good for monitoring infrequently used channels such as ham repeaters and fire department communications. --- .gitignore | 1 + extension/Thumbs.db | Bin 0 -> 4096 bytes extension/audio.js | 5 ++++- extension/auxwindows.js | 2 +- 4 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 extension/Thumbs.db diff --git a/.gitignore b/.gitignore index f113a2d..db1c9c0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *.pem *.crx *.zip +*.bak nacl-src/pnacl diff --git a/extension/Thumbs.db b/extension/Thumbs.db new file mode 100644 index 0000000000000000000000000000000000000000..9debdfdfa759fb7a7b1eb5872ee955da934be609 GIT binary patch literal 4096 zcmeH|2T+tr7Jz?-5g4L?fXs@DEJ_pykR%L4jv_M*X~|&-3dl(iXBlo zr~BXJ|9=N?&{fdo)JNSW%_ zBT-a@tDg7RkTyG3`KZpG-5NVrj?5z!k1;jRa{Q!k!Rs+;Ww8*YRP)n2D(WJux^_lF zz!h}e$eA|=NmcN`M(dI1!QM_oA?YuYu{zjSWuvO)cI`>0BoXDJYiGsR$%mC+Hwvo+ zh_^4qb*=P`t03{Isyef&%3oeqi@?+lz@;n(N~B!0BlKw9ezN;KZ^K4iOF*1&fsUkU>l*Y%BPPs`{pZDbTRcf1b3K!TA8&eF{;wtDt4_}GB&!wpQt@mf1pHT*;W8Tl-7RsT)$;I?aR>rvwh zB^xp|+#rmp3PzA6At)mF%*t?HP0%jkO>O|?#g6E+PksM)PN5Pcz!gR^O0cn#sQ4g#;ff z>~o`X;>)aQ??l5XYUWa3V%utAwQNAOU7dZAr>(x(tn@uDB_aK}Np;S_A-OgW)hU5d zIEj(wKUISYbmuV|lwv3ptt4V8(^KRN%~5|;EQ<%n-)3FILF6zSiU5vj?~D%Ny_>q7 zlE-e|_`x>%rXU$TcO}BySWR$QlUTapU-8x#ua5}H$lpp~AI-j`&~6BunxO=)a_xzH z?*}e2HQXPz1rLgCTp|%2!_YgM3Npf1GMRE{!tW*~brZ@z7_r{w@b2T!T@w$*!+dM^ z+Qo!agtGRhS;AkK+iOPHv&}XCA*H)?TU}3pYjMi^c1f?lzG&YKG^X+N313R2T~2d- zQfbEG5gcr8r#_CY^n=EbflR8lR++V}>7wv!u0fA#Xnwf-{vXK(8M@1ar8RieJ|iPP ccePa`G8w!hkb#bgcB!Ug2;KfB|LGn03lATtvj6}9 literal 0 HcmV?d00001 diff --git a/extension/audio.js b/extension/audio.js index bf6b3db..a1fc556 100644 --- a/extension/audio.js +++ b/extension/audio.js @@ -39,6 +39,7 @@ function Player() { function play(leftSamples, rightSamples, level, squelch) { var buffer = ac.createBuffer(2, leftSamples.length, OUT_RATE); if (level >= squelch) { +// console.log(level); buffer.getChannelData(0).set(leftSamples); buffer.getChannelData(1).set(rightSamples); } @@ -50,7 +51,9 @@ function Player() { ac.currentTime + TIME_BUFFER); source.start(lastPlayedAt); if (wavSaver != null) { - wavSaver.writeSamples(leftSamples, rightSamples); + if ( level >= squelch ) { + wavSaver.writeSamples(leftSamples, rightSamples); + } } } diff --git a/extension/auxwindows.js b/extension/auxwindows.js index 4cd16e3..3e1faae 100644 --- a/extension/auxwindows.js +++ b/extension/auxwindows.js @@ -138,7 +138,7 @@ var AuxWindows = (function() { 'width': 700, 'height': 600 }, - 'state': 'maximized', + 'state': 'normal', 'resizable': true }); } From 03cebeadc660475a3fc0debde6bd6ba45ed4a5a6 Mon Sep 17 00:00:00 2001 From: GeoNomad Date: Sun, 5 Apr 2015 07:28:34 -0700 Subject: [PATCH 02/15] Update .gitignore and remove commented out code --- .gitignore | 1 + extension/audio.js | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index db1c9c0..05a68e3 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ *.crx *.zip *.bak +*.db nacl-src/pnacl diff --git a/extension/audio.js b/extension/audio.js index a1fc556..f01d212 100644 --- a/extension/audio.js +++ b/extension/audio.js @@ -39,7 +39,6 @@ function Player() { function play(leftSamples, rightSamples, level, squelch) { var buffer = ac.createBuffer(2, leftSamples.length, OUT_RATE); if (level >= squelch) { -// console.log(level); buffer.getChannelData(0).set(leftSamples); buffer.getChannelData(1).set(rightSamples); } From 65a4075c480db29708d802cf86f1f7f558a74d5a Mon Sep 17 00:00:00 2001 From: Peter Date: Sun, 5 Apr 2015 07:29:54 -0700 Subject: [PATCH 03/15] Delete Thumbs.db --- extension/Thumbs.db | Bin 4096 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 extension/Thumbs.db diff --git a/extension/Thumbs.db b/extension/Thumbs.db deleted file mode 100644 index 9debdfdfa759fb7a7b1eb5872ee955da934be609..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4096 zcmeH|2T+tr7Jz?-5g4L?fXs@DEJ_pykR%L4jv_M*X~|&-3dl(iXBlo zr~BXJ|9=N?&{fdo)JNSW%_ zBT-a@tDg7RkTyG3`KZpG-5NVrj?5z!k1;jRa{Q!k!Rs+;Ww8*YRP)n2D(WJux^_lF zz!h}e$eA|=NmcN`M(dI1!QM_oA?YuYu{zjSWuvO)cI`>0BoXDJYiGsR$%mC+Hwvo+ zh_^4qb*=P`t03{Isyef&%3oeqi@?+lz@;n(N~B!0BlKw9ezN;KZ^K4iOF*1&fsUkU>l*Y%BPPs`{pZDbTRcf1b3K!TA8&eF{;wtDt4_}GB&!wpQt@mf1pHT*;W8Tl-7RsT)$;I?aR>rvwh zB^xp|+#rmp3PzA6At)mF%*t?HP0%jkO>O|?#g6E+PksM)PN5Pcz!gR^O0cn#sQ4g#;ff z>~o`X;>)aQ??l5XYUWa3V%utAwQNAOU7dZAr>(x(tn@uDB_aK}Np;S_A-OgW)hU5d zIEj(wKUISYbmuV|lwv3ptt4V8(^KRN%~5|;EQ<%n-)3FILF6zSiU5vj?~D%Ny_>q7 zlE-e|_`x>%rXU$TcO}BySWR$QlUTapU-8x#ua5}H$lpp~AI-j`&~6BunxO=)a_xzH z?*}e2HQXPz1rLgCTp|%2!_YgM3Npf1GMRE{!tW*~brZ@z7_r{w@b2T!T@w$*!+dM^ z+Qo!agtGRhS;AkK+iOPHv&}XCA*H)?TU}3pYjMi^c1f?lzG&YKG^X+N313R2T~2d- zQfbEG5gcr8r#_CY^n=EbflR8lR++V}>7wv!u0fA#Xnwf-{vXK(8M@1ar8RieJ|iPP ccePa`G8w!hkb#bgcB!Ug2;KfB|LGn03lATtvj6}9 From d0021933113594e577c687a28301994960e4893f Mon Sep 17 00:00:00 2001 From: GeoNomad Date: Sun, 5 Apr 2015 12:09:44 -0700 Subject: [PATCH 04/15] Added MUTE function (key m/M) Pressing m or M reduces volume to 0. --- extension/interface.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/extension/interface.js b/extension/interface.js index a0e075a..0ed746b 100644 --- a/extension/interface.js +++ b/extension/interface.js @@ -262,6 +262,12 @@ function Interface(fmRadio) { function changeVolumeUp() { setVolume(fmRadio.getVolume() + 0.1); } + /** + * Mute volume (change to 0). + */ + function muteVolume() { + setVolume(0.0); + } /** * Changes volume with the mouse wheel. @@ -809,8 +815,14 @@ function Interface(fmRadio) { case 98: // b switchBand(); break; + case 70: // F case 102: // f - showFrequencyEditor(); +// showFrequencyEditor(); not defined +// frequencyDisplay.click(); but this causes crash + break; + case 77: // M + case 109: // m + muteVolume(); break; case 80: // P case 112: // p From aff7421629d54fbaeab6bb6ccb7a33827270ae03 Mon Sep 17 00:00:00 2001 From: GeoNomad Date: Mon, 6 Apr 2015 07:19:00 -0700 Subject: [PATCH 05/15] Added Signal Bar Signal Bar shows signal strength. Changed squelch to operate on damped signal level. Quieter when set close to trigger level. Doesn't drop out on weak signals. --- extension/audio.js | 8 ++++++-- extension/help.html | 1 + extension/interface.css | 21 +++++++++++++++++++++ extension/interface.html | 1 + extension/interface.js | 2 ++ 5 files changed, 31 insertions(+), 2 deletions(-) diff --git a/extension/audio.js b/extension/audio.js index f01d212..c45de5b 100644 --- a/extension/audio.js +++ b/extension/audio.js @@ -24,6 +24,8 @@ function Player() { var frameno = 0; var wavSaver = null; + + var dampedLevel = 0; var ac = new (window.AudioContext || window.webkitAudioContext)(); var gainNode = ac.createGain ? ac.createGain() : ac.createGainNode(); @@ -38,7 +40,9 @@ function Player() { */ function play(leftSamples, rightSamples, level, squelch) { var buffer = ac.createBuffer(2, leftSamples.length, OUT_RATE); - if (level >= squelch) { + dampedLevel = 0.75 * dampedLevel + .25*level*100; + signalBar.value = Math.floor(dampedLevel); + if ((dampedLevel/100) >= squelch) { buffer.getChannelData(0).set(leftSamples); buffer.getChannelData(1).set(rightSamples); } @@ -50,7 +54,7 @@ function Player() { ac.currentTime + TIME_BUFFER); source.start(lastPlayedAt); if (wavSaver != null) { - if ( level >= squelch ) { + if ((dampedLevel/100) >= squelch) { wavSaver.writeSamples(leftSamples, rightSamples); } } diff --git a/extension/help.html b/extension/help.html index 0252bd4..3f434ae 100644 --- a/extension/help.html +++ b/extension/help.html @@ -124,6 +124,7 @@

Keyboard shortcuts

Space BarTurn radio on/off Up ArrowVolume Up Down ArrowVolume Down +mMute Volume Left ArrowDecrease Frequency Right ArrowIncrease Frequency <Scan for stations by decreasing frequency diff --git a/extension/interface.css b/extension/interface.css index e5bfc64..dab9154 100644 --- a/extension/interface.css +++ b/extension/interface.css @@ -367,6 +367,27 @@ option { width: 30px; } +.signalDisplay { + bottom: 10px; + left: 10px; + width: 125px; +} + +progress { + left: 0px; + width: 100px; + height: 15px; + border: 1px solid #afd; + background: #343; +// visibility: hidden; +} +progress::-webkit-progress-bar { + background: #343; +} +progress[value]::-webkit-progress-value { + background-image: -webkit-linear-gradient(left, red 0%, orange 70%, yellow 90%, white 100%); +} + .freqMinusButton, .freqPlusButton { position: absolute; top: 0; diff --git a/extension/interface.html b/extension/interface.html index d5414b8..3b0c604 100644 --- a/extension/interface.html +++ b/extension/interface.html @@ -87,6 +87,7 @@ +
diff --git a/extension/interface.js b/extension/interface.js index 0ed746b..913f270 100644 --- a/extension/interface.js +++ b/extension/interface.js @@ -105,6 +105,8 @@ function Interface(fmRadio) { setVisible(recordButton, !fmRadio.isRecording()); setVisible(stopButton, fmRadio.isRecording()); + +// setVisible(signalDisplay, isFreeTuning()); selectCurrentPreset(); } From 34b044bd8639f3512df39918f69d1ce8892d370b Mon Sep 17 00:00:00 2001 From: GeoNomad Date: Mon, 6 Apr 2015 07:27:31 -0700 Subject: [PATCH 06/15] Updated image for readme Shows signal strength bar graph. --- image-src/interface.png | Bin 19828 -> 18363 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/image-src/interface.png b/image-src/interface.png index 3910f65f0eca912ff54e1910eb7ad8174bbce49b..94bebdf8d83c00401c95c465aadb3fad23802515 100644 GIT binary patch literal 18363 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 literal 19828 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 U5Zybg0wMg&(ZSii4(Ct)Kb@u=@c;k- From 146b4bd58aa8253e0372b3108117d6ab1512ef65 Mon Sep 17 00:00:00 2001 From: GeoNomad Date: Mon, 6 Apr 2015 07:30:42 -0700 Subject: [PATCH 07/15] Updated image again fighting cache, I think --- README.md | 2 +- image-src/{interface.png => interface2.png} | Bin 2 files changed, 1 insertion(+), 1 deletion(-) rename image-src/{interface.png => interface2.png} (100%) diff --git a/README.md b/README.md index 1f80136..8f8f433 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ An application to listen to broadcast FM and AM radio from your Chrome browser or your ChromeBook computer using a $15 USB digital TV tuner. -![Radio Receiver screenshot](image-src/interface.png) +![Radio Receiver screenshot](image-src/interface2.png) ## What is this diff --git a/image-src/interface.png b/image-src/interface2.png similarity index 100% rename from image-src/interface.png rename to image-src/interface2.png From 51ffed02b2fb2e20fb80f8108dd260d78ba25a1b Mon Sep 17 00:00:00 2001 From: GeoNomad Date: Mon, 6 Apr 2015 15:54:28 -0700 Subject: [PATCH 08/15] PgDn shortcut to next preset Prelude to a preset scanning function. --- extension/appconfig.js | 2 +- extension/help.html | 1 + extension/interface.js | 20 ++++++++++++++++++++ extension/settings.js | 2 +- 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/extension/appconfig.js b/extension/appconfig.js index c0eab5f..37891e5 100644 --- a/extension/appconfig.js +++ b/extension/appconfig.js @@ -34,7 +34,7 @@ function AppConfig() { /** Upconverter. */ upconverter: { enable: false, - frequency: 125000000 + frequency: 99999020 // Peter's up converter }, /** Whether free tuning is enabled. */ freeTuning: false diff --git a/extension/help.html b/extension/help.html index 3f434ae..10459f1 100644 --- a/extension/help.html +++ b/extension/help.html @@ -127,6 +127,7 @@

Keyboard shortcuts

mMute Volume Left ArrowDecrease Frequency Right ArrowIncrease Frequency +PgDnNext Preset <Scan for stations by decreasing frequency >Scan for stations by increasing frequency fEdit frequency diff --git a/extension/interface.js b/extension/interface.js index 913f270..ecaa014 100644 --- a/extension/interface.js +++ b/extension/interface.js @@ -692,6 +692,23 @@ function Interface(fmRadio) { saveSettings(); update(); } + +function nextPreset(){ +// presetsBox.value = "18110000"; +// var x = presetsBox.index(); + if ( presetsBox[presetsBox.length-1].selected ) { + presetsBox[1].selected = "1"; + selectPreset(); + } else { + for (i = 0; i < presetsBox.length; i++){ + if ( presetsBox[i].selected ){ + presetsBox[i+1].selected = "1"; + selectPreset(); + break; + } + } + } + } /** * Closes the window. @@ -785,6 +802,9 @@ function Interface(fmRadio) { } if (e.type == 'keydown') { switch (e.keyCode) { + case 34: // PgDn + nextPreset(); + break; case 37: frequencyMinus(); break; diff --git a/extension/settings.js b/extension/settings.js index 5d92470..1f3e537 100644 --- a/extension/settings.js +++ b/extension/settings.js @@ -27,7 +27,7 @@ autoGain.checked = settings && settings['autoGain']; gain.value = (settings && settings['gain']) || 0; gain.disabled = autoGain.checked; useUpconverter.checked = settings && settings['useUpconverter']; -upconverterFreq.value = (settings && settings['upconverterFreq']) || 125000000; +upconverterFreq.value = (settings && settings['upconverterFreq']) || 99999020; //Peter's upconverter upconverterFreqInput.className = useUpconverter.checked ? '' : 'invisible'; upconverterFreq.disabled = !useUpconverter.checked; enableFreeTuning.checked = settings && settings['enableFreeTuning']; From 1bbb8f1bebe79420e813c601c18f70fc78e41e20 Mon Sep 17 00:00:00 2001 From: GeoNomad Date: Mon, 6 Apr 2015 15:59:14 -0700 Subject: [PATCH 09/15] PgDn shortcut to next preset Prelude to preset scanner. --- extension/interface.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/extension/interface.js b/extension/interface.js index ecaa014..ada1e55 100644 --- a/extension/interface.js +++ b/extension/interface.js @@ -693,9 +693,12 @@ function Interface(fmRadio) { update(); } + /** + * Selects the next preset in the list. + */ + function nextPreset(){ -// presetsBox.value = "18110000"; -// var x = presetsBox.index(); + if ( presetsBox.length < 3 ) { return; } if ( presetsBox[presetsBox.length-1].selected ) { presetsBox[1].selected = "1"; selectPreset(); From 80d1a08e403bb03d32de2d926927f59255292128 Mon Sep 17 00:00:00 2001 From: GeoNomad Date: Mon, 6 Apr 2015 20:03:44 -0700 Subject: [PATCH 10/15] Click signal bar graph to set squelch Clicking on the signal strength bar graph now sets the squelch level. The signal strength can be displayed numerically by hovering the mouse over the signal strength bar graph. --- extension/audio.js | 1 + extension/help.html | 4 +++- extension/interface.js | 13 +++++++++++-- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/extension/audio.js b/extension/audio.js index c45de5b..a0153f5 100644 --- a/extension/audio.js +++ b/extension/audio.js @@ -42,6 +42,7 @@ function Player() { var buffer = ac.createBuffer(2, leftSamples.length, OUT_RATE); dampedLevel = 0.75 * dampedLevel + .25*level*100; signalBar.value = Math.floor(dampedLevel); + signalBar.title = signalBar.value; if ((dampedLevel/100) >= squelch) { buffer.getChannelData(0).set(leftSamples); buffer.getChannelData(1).set(rightSamples); diff --git a/extension/help.html b/extension/help.html index 10459f1..6ee05a2 100644 --- a/extension/help.html +++ b/extension/help.html @@ -69,6 +69,8 @@

Using presets

To switch to a preset, click the preset selection box (7) and choose your new preset. The radio will switch to it immediately.

To delete a preset, switch to it and press the “Remove” button (8).

To change a preset's name, switch to it and click the “Save” button (6). Type the new name and press “Save”.

+

To save a set of presets for loading later, go to “Settings” (12) and +click on the Manage your Presets link.

Changing volume and stereo

To change the volume, click on the “loudspeaker” icon (9) and move the slider left and right to decrease or increase the volume. You can also use your mouse wheel on the icon to change volume directly.

@@ -101,7 +103,7 @@

Using Free Tuning

If you have configured an upconverter in the settings window you can enable and disable it here by clicking the “Upconverter” field (5).

-

The “Squelch” setting (6) lets you monitor a frequency without having to hear static when nobody is transmitting. The squelch level is the minimum strength level the signal must reach before you can hear it; if it's set to 0, you'll hear everything.

+

The “Squelch” setting (6) lets you monitor a frequency without having to hear static when nobody is transmitting. The squelch level is the minimum strength level the signal must reach before you can hear it; if it's set to 0, you'll hear everything. Click on the signal strength display to set the squelch value slightly above the noise level.

Settings

You can change a lot of settings by clicking on the “wrench” icon (12). There you can change several settings:

diff --git a/extension/interface.js b/extension/interface.js index ada1e55..905f7ab 100644 --- a/extension/interface.js +++ b/extension/interface.js @@ -693,6 +693,14 @@ function Interface(fmRadio) { update(); } + /** + * Change squelch by clicking on signal strength display. + */ + +function clickChangeSquelch(value){ + changeSquelch(value.offsetX); + } + /** * Selects the next preset in the list. */ @@ -700,12 +708,12 @@ function Interface(fmRadio) { function nextPreset(){ if ( presetsBox.length < 3 ) { return; } if ( presetsBox[presetsBox.length-1].selected ) { - presetsBox[1].selected = "1"; + presetsBox[1].selected = true; selectPreset(); } else { for (i = 0; i < presetsBox.length; i++){ if ( presetsBox[i].selected ){ - presetsBox[i+1].selected = "1"; + presetsBox[i+1].selected = true; selectPreset(); break; } @@ -942,6 +950,7 @@ function nextPreset(){ upconverterDisplay.addEventListener('click', toggleUpconverter); attachDisplayInputEvents(squelchDisplay, squelchInput, changeSquelch); squelchDisplay.addEventListener('mousewheel', changeSquelchWheel); + signalDisplay.addEventListener('click', clickChangeSquelch); freqMinusButton.addEventListener('click', frequencyMinus); freqPlusButton.addEventListener('click', frequencyPlus); scanDownButton.addEventListener('click', scanDown); From 810e4497830930363ef1891d4f7914f7e0e00bb7 Mon Sep 17 00:00:00 2001 From: GeoNomad Date: Tue, 7 Apr 2015 10:01:48 -0700 Subject: [PATCH 11/15] Save squelch setting with presets 1. Added squelch to the parameters saved with each preset station. 2. Changed default checkbox when importing presets to show "Delete these presets" with boxes prechecked. It was not intuitive before that it would not delete the current presets unless the box was manually checked. This use assumes that each file is a different set of presets most of the time. Not an overlay. 3. Display the squelch setting saved in the preset manager. --- extension/auxwindows.js | 1 + extension/presetmanager.js | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/extension/auxwindows.js b/extension/auxwindows.js index 3e1faae..a368ae2 100644 --- a/extension/auxwindows.js +++ b/extension/auxwindows.js @@ -36,6 +36,7 @@ var AuxWindows = (function() { win.contentWindow['opener'] = window; var modeData = copyObject(band.getMode()); modeData['step'] = band.getStep(); + modeData['squelch'] = squelch; var stationData = { 'frequency': frequency, 'display': band.toDisplayName(frequency, true), diff --git a/extension/presetmanager.js b/extension/presetmanager.js index cd89edf..6cb11e9 100644 --- a/extension/presetmanager.js +++ b/extension/presetmanager.js @@ -80,7 +80,7 @@ function showPresets(table, presets, delPresets, precheck) { var isDouble = preset && delPreset; var line; if (delPreset) { - line = makePresetLine(value, delPreset, i, true, isDouble, precheck); + line = makePresetLine(value, delPreset, i, true, isDouble, true); table.firstElementChild.appendChild(line); } if (preset) { @@ -173,6 +173,9 @@ function makeModeCell(preset) { if (mode['upconvert']) { text += '; upconvert=on'; } + if (mode['squelch']) { + text += '; squelch='+mode['squelch']; + } } td.appendChild(document.createTextNode(text)); return td; From b5f81fbb555ba579b15167999c0089be4174aa9f Mon Sep 17 00:00:00 2001 From: GeoNomad Date: Tue, 7 Apr 2015 11:48:42 -0700 Subject: [PATCH 12/15] PgDn for previous preset. Fixed missing f shortcut 1. PgDn now selects the previous preset in the list. 2. f keyboard shortcut now works. (was in help but not working) NOTE: using PgUp or PgDn too quickly can cause multiple decoder instances. Looking for a good way to block this... For now, users should be careful. --- extension/help.html | 1 + extension/interface.js | 28 ++++++++++++++++++++++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/extension/help.html b/extension/help.html index 6ee05a2..17a5bfa 100644 --- a/extension/help.html +++ b/extension/help.html @@ -129,6 +129,7 @@

Keyboard shortcuts

mMute Volume Left ArrowDecrease Frequency Right ArrowIncrease Frequency +PgUpPrevious Preset PgDnNext Preset <Scan for stations by decreasing frequency >Scan for stations by increasing frequency diff --git a/extension/interface.js b/extension/interface.js index 905f7ab..b57625d 100644 --- a/extension/interface.js +++ b/extension/interface.js @@ -701,12 +701,34 @@ function clickChangeSquelch(value){ changeSquelch(value.offsetX); } + /** + * Selects the previous preset in the list. + */ + +function prevPreset(){ + if ( presetsBox.length < 3 ) { return; } + signalBar.value = 0; + if ( presetsBox[0].selected || presetsBox[1].selected ) { + presetsBox[presetsBox.length-1].selected = true; + selectPreset(); + } else { + for (i = 0; i < presetsBox.length; i++){ + if ( presetsBox[i].selected ){ + presetsBox[i-1].selected = true; + selectPreset(); + break; + } + } + } + } + /** * Selects the next preset in the list. */ function nextPreset(){ if ( presetsBox.length < 3 ) { return; } + signalBar.value = 0; if ( presetsBox[presetsBox.length-1].selected ) { presetsBox[1].selected = true; selectPreset(); @@ -813,6 +835,9 @@ function nextPreset(){ } if (e.type == 'keydown') { switch (e.keyCode) { + case 33: // PgUp + prevPreset(); + break; case 34: // PgDn nextPreset(); break; @@ -850,8 +875,7 @@ function nextPreset(){ break; case 70: // F case 102: // f -// showFrequencyEditor(); not defined -// frequencyDisplay.click(); but this causes crash + frequencyDisplay.click(); // but this causes crash break; case 77: // M case 109: // m From 4041c1fc4460968c8d4959ff7bdb5bc049c71e49 Mon Sep 17 00:00:00 2001 From: GeoNomad Date: Tue, 7 Apr 2015 16:11:24 -0700 Subject: [PATCH 13/15] Shift-PgDn scans preset list A rudimentary test of preset scanning. Shift-PgDn will start a scan of the preset list. If the signal exceeds the squelch level, scanning will pause. Just a kludge for testing. But it seems to work, surprisingly. --- extension/audio.js | 15 ++++++++++++++- extension/help.html | 9 +++++++-- extension/interface.js | 15 +++++++++------ 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/extension/audio.js b/extension/audio.js index a0153f5..0e2b684 100644 --- a/extension/audio.js +++ b/extension/audio.js @@ -57,7 +57,20 @@ function Player() { if (wavSaver != null) { if ((dampedLevel/100) >= squelch) { wavSaver.writeSamples(leftSamples, rightSamples); - } + } + } + if (typeof scanPresets == 'undefined') { // proof of concept for preset scanning + scanPresets = false; // not likely to be the way it will be implemented + scanCount = 0; // a test to find the possible problems... + } + if ( scanPresets ) { + scanCount++; + if ( scanCount > 10 ){ + scanCount = 0; + if ((dampedLevel/100) < squelch) { + interface.nextPreset(false); + } + } } } diff --git a/extension/help.html b/extension/help.html index 17a5bfa..9bcbbea 100644 --- a/extension/help.html +++ b/extension/help.html @@ -69,8 +69,11 @@

Using presets

To switch to a preset, click the preset selection box (7) and choose your new preset. The radio will switch to it immediately.

To delete a preset, switch to it and press the “Remove” button (8).

To change a preset's name, switch to it and click the “Save” button (6). Type the new name and press “Save”.

-

To save a set of presets for loading later, go to “Settings” (12) and +

To export a set of presets for loading later, go to “Settings” (12) and click on the Manage your Presets link.

+

Use the PgUp and PgDn keys to select the previous or next preset station in the list.

+

Hold down the shift key and press PgDn to begin automatic scanning of preset stations. +The scan will pause if the squelch level is exceeded. Each preset can have its own squelch value assigned.

Changing volume and stereo

To change the volume, click on the “loudspeaker” icon (9) and move the slider left and right to decrease or increase the volume. You can also use your mouse wheel on the icon to change volume directly.

@@ -103,7 +106,8 @@

Using Free Tuning

If you have configured an upconverter in the settings window you can enable and disable it here by clicking the “Upconverter” field (5).

-

The “Squelch” setting (6) lets you monitor a frequency without having to hear static when nobody is transmitting. The squelch level is the minimum strength level the signal must reach before you can hear it; if it's set to 0, you'll hear everything. Click on the signal strength display to set the squelch value slightly above the noise level.

+

The “Squelch” setting (6) lets you monitor a frequency without having to hear static when nobody is transmitting. The squelch level is the minimum strength level the signal must reach before you can hear it; if it's set to 0, you'll hear everything. +Click on the signal strength display to set the squelch value slightly above the noise level.

Settings

You can change a lot of settings by clicking on the “wrench” icon (12). There you can change several settings:

@@ -131,6 +135,7 @@

Keyboard shortcuts

Right ArrowIncrease Frequency PgUpPrevious Preset PgDnNext Preset +Shift-PgDnScan Preset List <Scan for stations by decreasing frequency >Scan for stations by increasing frequency fEdit frequency diff --git a/extension/interface.js b/extension/interface.js index b57625d..be28c67 100644 --- a/extension/interface.js +++ b/extension/interface.js @@ -705,7 +705,7 @@ function clickChangeSquelch(value){ * Selects the previous preset in the list. */ -function prevPreset(){ +function prevPreset(shiftKey){ if ( presetsBox.length < 3 ) { return; } signalBar.value = 0; if ( presetsBox[0].selected || presetsBox[1].selected ) { @@ -725,9 +725,11 @@ function prevPreset(){ /** * Selects the next preset in the list. */ - -function nextPreset(){ +function nextPreset(shiftKey){ if ( presetsBox.length < 3 ) { return; } + if ( shiftKey ) { + scanPresets = ! scanPresets; + } signalBar.value = 0; if ( presetsBox[presetsBox.length-1].selected ) { presetsBox[1].selected = true; @@ -836,10 +838,10 @@ function nextPreset(){ if (e.type == 'keydown') { switch (e.keyCode) { case 33: // PgUp - prevPreset(); + prevPreset(e.shiftKey); break; case 34: // PgDn - nextPreset(); + nextPreset(e.shiftKey); break; case 37: frequencyMinus(); @@ -875,7 +877,7 @@ function nextPreset(){ break; case 70: // F case 102: // f - frequencyDisplay.click(); // but this causes crash + frequencyDisplay.click(); break; case 77: // M case 109: // m @@ -998,6 +1000,7 @@ function nextPreset(){ return { attach: attach, + nextPreset: nextPreset, update: update }; } From ec6e18b7f8b9cafcf9e3f48544873b85ffb49f7e Mon Sep 17 00:00:00 2001 From: GeoNomad Date: Sat, 11 Apr 2015 07:41:42 -0700 Subject: [PATCH 14/15] Monitor mode - Open squelch temporarily Click on "Squelch:" to temporarily open squelch and monitor frequency. Preset scanning stops. Click again to resume preset squelch setting and continue scanning presets. --- extension/audio.js | 14 ++++-- extension/interface.html | 2 +- extension/interface.js | 95 ++++++++++++++++++++++-------------- extension/radiocontroller.js | 8 ++- 4 files changed, 75 insertions(+), 44 deletions(-) diff --git a/extension/audio.js b/extension/audio.js index 0e2b684..dcb668b 100644 --- a/extension/audio.js +++ b/extension/audio.js @@ -63,13 +63,17 @@ function Player() { scanPresets = false; // not likely to be the way it will be implemented scanCount = 0; // a test to find the possible problems... } + if (radio.requestingBlocks > 0) { + return; + } if ( scanPresets ) { scanCount++; - if ( scanCount > 10 ){ + if ((dampedLevel/100) > squelch) { + scanCount = -5; // resume scanning after x+n no signal events + } + if ( scanCount > 5 && (level < squelch)) { // scan every n events + interface.nextPreset(); // tweak this parameter if too fast for dongle/decoder scanCount = 0; - if ((dampedLevel/100) < squelch) { - interface.nextPreset(false); - } } } } @@ -113,7 +117,7 @@ function Player() { function setVolume(volume) { gainNode.gain.value = volume; } - + return { play: play, setVolume: setVolume, diff --git a/extension/interface.html b/extension/interface.html index 3b0c604..09b8d96 100644 --- a/extension/interface.html +++ b/extension/interface.html @@ -82,7 +82,7 @@
-
Squelch:
+
Squelch:
100
diff --git a/extension/interface.js b/extension/interface.js index be28c67..752e1fb 100644 --- a/extension/interface.js +++ b/extension/interface.js @@ -33,7 +33,7 @@ function Interface(fmRadio) { * The current band configuration; */ var currentBand = Bands['WW']['FM']; - + var currentSquelch = 0; /** * Updates the UI. */ @@ -694,56 +694,76 @@ function Interface(fmRadio) { } /** - * Change squelch by clicking on signal strength display. + * Monitor - open squelch by clicking on "Squelch:" to monitor frequency + * Click again on "MONITOR" to resume stored squelch setting */ - -function clickChangeSquelch(value){ - changeSquelch(value.offsetX); + + function squelchClick() { + var mode = appConfig.state.mode.get(); + if ( Squelch.textContent == 'Squelch:') { + currentSquelch = mode.params.squelch; + fmRadio.setSquelch(0); + squelchDisplay.hidden = true; + Squelch.textContent = 'MONITOR'; + } else { + fmRadio.setSquelch(currentSquelch); + Squelch.textContent = 'Squelch:'; + squelchDisplay.hidden = false; + } } + /** - * Selects the previous preset in the list. + * Change squelch by clicking on signal strength display. */ -function prevPreset(shiftKey){ - if ( presetsBox.length < 3 ) { return; } - signalBar.value = 0; - if ( presetsBox[0].selected || presetsBox[1].selected ) { - presetsBox[presetsBox.length-1].selected = true; - selectPreset(); - } else { - for (i = 0; i < presetsBox.length; i++){ - if ( presetsBox[i].selected ){ - presetsBox[i-1].selected = true; - selectPreset(); - break; + function clickChangeSquelch(value){ + changeSquelch(value.offsetX); + } + + /** + * Selects the previous preset in the list. + */ + + function prevPreset(shiftKey){ + if ( presetsBox.length < 3 ) { return; } + signalBar.value = 0; + if ( presetsBox[0].selected || presetsBox[1].selected ) { + presetsBox[presetsBox.length-1].selected = true; + selectPreset(); + } else { + for (i = 0; i < presetsBox.length; i++){ + if ( presetsBox[i].selected ){ + presetsBox[i-1].selected = true; + selectPreset(); + break; + } } } - } - } + } /** * Selects the next preset in the list. */ -function nextPreset(shiftKey){ - if ( presetsBox.length < 3 ) { return; } - if ( shiftKey ) { - scanPresets = ! scanPresets; - } - signalBar.value = 0; - if ( presetsBox[presetsBox.length-1].selected ) { - presetsBox[1].selected = true; - selectPreset(); - } else { - for (i = 0; i < presetsBox.length; i++){ - if ( presetsBox[i].selected ){ - presetsBox[i+1].selected = true; - selectPreset(); - break; + function nextPreset(shiftKey){ + if ( presetsBox.length < 3 ) { return; } + if ( shiftKey ) { + scanPresets = ! scanPresets; + } + signalBar.value = 0; + if ( presetsBox[presetsBox.length-1].selected ) { + presetsBox[1].selected = true; + selectPreset(); + } else { + for (i = 0; i < presetsBox.length; i++){ + if ( presetsBox[i].selected ){ + presetsBox[i+1].selected = true; + selectPreset(); + break; + } } } - } - } + } /** * Closes the window. @@ -989,6 +1009,7 @@ function nextPreset(shiftKey){ window.addEventListener('message', getMessage); window.addEventListener('keydown', handleShortcut); window.addEventListener('keypress', handleShortcut); + Squelch.addEventListener('mousedown',squelchClick); fmRadio.setInterface(this); fmRadio.setOnError(showErrorWindow); loadSettings(function() { diff --git a/extension/radiocontroller.js b/extension/radiocontroller.js index c147ef6..b12881f 100644 --- a/extension/radiocontroller.js +++ b/extension/radiocontroller.js @@ -155,6 +155,10 @@ function RadioController() { function getSquelch() { return squelch; } + + function getRB() { + return requestingBlocks; + } /** * Searches a given frequency band for a station, starting at the @@ -686,6 +690,8 @@ function RadioController() { stopRecording: stopRecording, isRecording: isRecording, setInterface: setInterface, - setOnError: setOnError + setOnError: setOnError, + requestingBlocks: getRB + }; } From 3f7c9328f0fdf08efc81b0df219b6c3c95a4ddb3 Mon Sep 17 00:00:00 2001 From: GeoNomad Date: Tue, 14 Apr 2015 06:31:28 -0700 Subject: [PATCH 15/15] Updated README to show changes in this fork --- README.md | 21 +++++++++++++++++++++ extension/interface.js | 2 -- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8f8f433..1469e28 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,24 @@ +# Modifications in this fork: + +* Automatically pauses recording if signal is below the squelch threshold. + +* m key mutes audio instantly (recording is not affected). Adjust volute to resume. + +* Signal level is displayed with a bar. Hover mouse to read numerical value. + +* Clicking signal bar sets squelch level. + +* PgDn/PgUp moves through the list of saved preset stations. + +* Shift-PgDn begins (or ends) scanning through preset stations. Scanning stops when signal is above preset squelch level and resumes when signal drops below squelch level for a while. + +* Export/Import preset list includes the squelch setting for each station. + +* Clicking on the "Squelch" heading enters or leaves MONITOR mode, where squelch is temporarily set to zero to monitor frequency. + +* f/F key moves to the frequency display for editing (this is in the original help file, but didn't work). + + # Radio Receiver An application to listen to broadcast FM and AM radio from your Chrome browser or your ChromeBook computer using a $15 USB digital TV tuner. diff --git a/extension/interface.js b/extension/interface.js index 752e1fb..e42c6e8 100644 --- a/extension/interface.js +++ b/extension/interface.js @@ -105,8 +105,6 @@ function Interface(fmRadio) { setVisible(recordButton, !fmRadio.isRecording()); setVisible(stopButton, fmRadio.isRecording()); - -// setVisible(signalDisplay, isFreeTuning()); selectCurrentPreset(); }