Wbserver Roundslider Widget Doesn't Start/update Audio Tone Program
Solution 1:
This code works for me.
Main problem was that function runs so fast that nobody can hear sound - so I added time.sleep(0.5)
I put all code in one file so it is easier to copy and paste to file to run it.
Main page http://localhost:5000/
displays few links with different values to test it.
from gnuradio import analog
from gnuradio import audio
from gnuradio import blocks
from gnuradio import eng_notation
from gnuradio import gr
from gnuradio.eng_option import eng_option
from gnuradio.filterimport firdes
from optparse import OptionParser
classtop_block_22(gr.top_block):
def__init__(self, samp_rate=32000): # default value for samp_rate
gr.top_block.__init__(self, "Top Block 22")
################################################### Variables##################################################
self.samp_rate = samp_rate # without "= 32000"print('[DEBUG] top_block_22:', self.samp_rate)
################################################### Blocks##################################################
self.blocks_add_xx = blocks.add_vff(1)
self.audio_sink = audio.sink(32000, '', True)
self.analog_sig_source_x_1 = analog.sig_source_f(samp_rate, analog.GR_COS_WAVE, 440, 0.4, 0)
self.analog_sig_source_x_0 = analog.sig_source_f(samp_rate, analog.GR_COS_WAVE, 350, 0.4, 0)
self.analog_noise_source_x_0 = analog.noise_source_f(analog.GR_GAUSSIAN, 0.005, -42)
################################################### Connections##################################################
self.connect((self.analog_noise_source_x_0, 0), (self.blocks_add_xx, 2))
self.connect((self.analog_sig_source_x_0, 0), (self.blocks_add_xx, 0))
self.connect((self.analog_sig_source_x_1, 0), (self.blocks_add_xx, 1))
self.connect((self.blocks_add_xx, 0), (self.audio_sink, 0))
# -----------------------------------------------------------------------------from flask import Flask, request
#from top_block_22 import top_block_22 import time
app = Flask(__name__)
@app.route('/')defindex():
HTML = 'HEAR:'for item in (20000, 25000, 32000):
HTML += ' <a href="/valueofslider?slide_val={}">{}</a>'.format(item, item)
return HTML
@app.route('/valueofslider')defslide():
slide_val = request.args.get('slide_val', 32000) # default value 32000
main(slide_val)
return slide_val
defmain(slide_val):
samp_rate = int(slide_val) + 100print('[DEBUG] main:', samp_rate)
tb = top_block_22(samp_rate) # run with value from variable
tb.start()
time.sleep(0.5)
tb.stop()
tb.wait()
if __name__ == '__main__':
app.run(debug=True)
EDIT: This version displays slider and it uses Thread
to play sound all time (after selecting first value on slider).
When slider sends new value then it stops old thread and creates new one with new samp_rate
.
But maybe it can be done without Thread
but using only tb.start
, tb.stop
, etc. It seems top_block_22
already uses thread to work.
It also uses http://localhost:5000/off
to send samp_rate=0
which stop sound.
from gnuradio import analog
from gnuradio import audio
from gnuradio import blocks
from gnuradio import eng_notation
from gnuradio import gr
from gnuradio.eng_option import eng_option
from gnuradio.filterimport firdes
from optparse import OptionParser
classtop_block_22(gr.top_block):
def__init__(self, samp_rate):
gr.top_block.__init__(self, "Top Block 22")
################################################### Variables##################################################
self.samp_rate = samp_rate
print('[DEBUG] top_block_22:', self.samp_rate)
################################################### Blocks##################################################
self.blocks_add_xx = blocks.add_vff(1)
self.audio_sink = audio.sink(32000, '', True)
self.analog_sig_source_x_1 = analog.sig_source_f(samp_rate, analog.GR_COS_WAVE, 440, 0.4, 0)
self.analog_sig_source_x_0 = analog.sig_source_f(samp_rate, analog.GR_COS_WAVE, 350, 0.4, 0)
self.analog_noise_source_x_0 = analog.noise_source_f(analog.GR_GAUSSIAN, 0.005, -42)
################################################### Connections##################################################
self.connect((self.analog_noise_source_x_0, 0), (self.blocks_add_xx, 2))
self.connect((self.analog_sig_source_x_0, 0), (self.blocks_add_xx, 0))
self.connect((self.analog_sig_source_x_1, 0), (self.blocks_add_xx, 1))
self.connect((self.blocks_add_xx, 0), (self.audio_sink, 0))
# -----------------------------------------------------------------------------from threading import Thread
classMyThread(Thread):
def__init__(self, samp_rate):
Thread.__init__(self)
self.running = True
self.samp_rate = samp_rate
defrun(self):
tb = top_block_22(self.samp_rate)
tb.start()
while self.running:
time.sleep(0.5) # need it to head sound
tb.stop()
tb.wait()
# -----------------------------------------------------------------------------from flask import Flask, request
#from top_block_22 import top_block_22 import time
app = Flask(__name__)
my_thread = None@app.route('/')defindex():
return'''<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>jQuery roundSlider - JS Bin</title>
<link rel="icon" href="data:,">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/roundSlider/1.3.2/roundslider.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/roundSlider/1.3.2/roundslider.min.js"></script>
</head>
<body>
<!-- Only html needed -->
<form class="form-inline" method="POST" action="{{ url_for('slide') }}">
<div id="slider"></div>
<script>
var val;
$("#slider").roundSlider({
radius: 215,
min: 0,
max: 100000,
change: function () {
var obj1 = $("#slider").data("roundSlider");
val = obj1.getValue();
value: 10
$.getJSON('/valueofslider', {
slide_val: val
});
}
});
</script>
</body>
</html>'''@app.route('/off')defoff():
'''use `slide_val=0` to turn it off'''
main(0)
return'off'@app.route('/valueofslider')defslide():
slide_val = request.args.get('slide_val', 32000)
main(slide_val)
return slide_val
defmain(slide_val):
global my_thread
print('[DEBUG] main:', slide_val)
samp_rate = int(slide_val)
if my_thread: # if my_thread is not None
my_thread.running = False
my_thread.join()
my_thread = Noneif samp_rate > 0:
my_thread = MyThread(samp_rate)
my_thread.start()
if __name__ == '__main__':
app.run(debug=True)
EDIT: Version without Thread
.
As before it display slider and it play sound all time after selecting first value. It use global value tb
to keep access to object and stop it when it get new samp_rate
, and run new object.
It also uses http://localhost:5000/off
to send samp_rate=0
which stop sound.
from gnuradio import analog
from gnuradio import audio
from gnuradio import blocks
from gnuradio import eng_notation
from gnuradio import gr
from gnuradio.eng_option import eng_option
from gnuradio.filterimport firdes
from optparse import OptionParser
classtop_block_22(gr.top_block):
def__init__(self, samp_rate):
gr.top_block.__init__(self, "Top Block 22")
################################################### Variables##################################################
self.samp_rate = samp_rate
print('[DEBUG] top_block_22:', self.samp_rate)
################################################### Blocks##################################################
self.blocks_add_xx = blocks.add_vff(1)
self.audio_sink = audio.sink(32000, '', True)
self.analog_sig_source_x_1 = analog.sig_source_f(samp_rate, analog.GR_COS_WAVE, 440, 0.4, 0)
self.analog_sig_source_x_0 = analog.sig_source_f(samp_rate, analog.GR_COS_WAVE, 350, 0.4, 0)
self.analog_noise_source_x_0 = analog.noise_source_f(analog.GR_GAUSSIAN, 0.005, -42)
################################################### Connections##################################################
self.connect((self.analog_noise_source_x_0, 0), (self.blocks_add_xx, 2))
self.connect((self.analog_sig_source_x_0, 0), (self.blocks_add_xx, 0))
self.connect((self.analog_sig_source_x_1, 0), (self.blocks_add_xx, 1))
self.connect((self.blocks_add_xx, 0), (self.audio_sink, 0))
# -----------------------------------------------------------------------------from flask import Flask, request
#from top_block_22 import top_block_22
app = Flask(__name__)
tb = None# global variable to keep it between requests@app.route('/')defindex():
return'''<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>jQuery roundSlider - JS Bin</title>
<link rel="icon" href="data:,">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/roundSlider/1.3.2/roundslider.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/roundSlider/1.3.2/roundslider.min.js"></script>
</head>
<body>
<!-- Only html needed -->
<form class="form-inline" method="POST" action="{{ url_for('slide') }}">
<div id="slider"></div>
<script>
var val;
$("#slider").roundSlider({
radius: 215,
min: 0,
max: 100000,
change: function () {
var obj1 = $("#slider").data("roundSlider");
val = obj1.getValue();
value: 10
$.getJSON('/valueofslider', {
slide_val: val
});
}
});
</script>
</body>
</html>'''@app.route('/off')defoff():
'''use `slide_val=0` to turn it off'''
main(0)
return'off'@app.route('/valueofslider')defslide():
slide_val = request.args.get('slide_val', 32000)
main(slide_val)
return slide_val
defmain(slide_val):
global tb
print('[DEBUG] main:', slide_val)
samp_rate = int(slide_val)
# stop old soundif tb: # if tb is not None
tb.stop()
tb.wait()
tb = None# create new sound (if not zero)if samp_rate > 0:
tb = top_block_22(samp_rate)
tb.start()
if __name__ == '__main__':
app.run(debug=True)
EDIT: last version
Few changes in JavaScript:
- it sets default value at start - 32000. There was mistake in JS.
- it sends request at start so it starts sound after loading page. There is no need to select value on slider.
Changes in Python:
- as suggest PEP 8 -- Style Guide for Python Code I set
UpperCaseName
for class -TopBlock22
- renamed
main
tosound
which better describe what function is doing http://localhost/set/<value>
sets sample rate so it can be used instead ofhttp://localhost/valueofslider?slider_val=<value>
.http://localhost/get
gets current sample ratehttp://localhost/off
sets value to 0 so it turns off sound. I used it to fast turn off sound when it was too annoying.print_function
from__future__
to useprint(text)
like in Python 3- functions return value as text but there are lines with
jsonify()
so they can retun it as JSON. Maybe it can be useful later.
In FAQ
I found How can I reconfigure a flow graph? How do I use lock(), unlock()? so maybe it can change sample rate without creating new object and maybe it gives better sound. I head click when it change sample rate.
Code:
from __future__ import print_function
from gnuradio import analog
from gnuradio import audio
from gnuradio import blocks
from gnuradio import eng_notation
from gnuradio import gr
from gnuradio.eng_option import eng_option
from gnuradio.filterimport firdes
from optparse import OptionParser
classTopBlock22(gr.top_block): # PEP8: CamelCaseName for classesdef__init__(self, sample_rate=32000):
gr.top_block.__init__(self, "Top Block 22")
################################################### Variables##################################################
self.sample_rate = sample_rate
print('[TopBlock22] sample_rate:', self.sample_rate)
################################################### Blocks##################################################
self.blocks_add_xx = blocks.add_vff(1)
self.audio_sink = audio.sink(32000, '', True)
self.analog_sig_source_x_1 = analog.sig_source_f(sample_rate, analog.GR_COS_WAVE, 440, 0.4, 0)
self.analog_sig_source_x_0 = analog.sig_source_f(sample_rate, analog.GR_COS_WAVE, 350, 0.4, 0)
self.analog_noise_source_x_0 = analog.noise_source_f(analog.GR_GAUSSIAN, 0.005, -42)
################################################### Connections##################################################
self.connect((self.analog_noise_source_x_0, 0), (self.blocks_add_xx, 2))
self.connect((self.analog_sig_source_x_0, 0), (self.blocks_add_xx, 0))
self.connect((self.analog_sig_source_x_1, 0), (self.blocks_add_xx, 1))
self.connect((self.blocks_add_xx, 0), (self.audio_sink, 0))
# -----------------------------------------------------------------------------from flask import Flask, request, jsonify
#from top_block_22 import TopBlock22 import time
app = Flask(__name__)
tb = None# global variable to keep it between requests@app.route('/')defindex():
return'''<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>GNURadio Slider Example</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/roundSlider/1.3.2/roundslider.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/roundSlider/1.3.2/roundslider.min.js"></script>
</head>
<body>
<div id="slider"></div>
<script>
// keep slider's value
var val;
// create slider
$("#slider").roundSlider({
radius: 215,
min: 0,
max: 100000,
value: 32000, // default value at start
change: function () {
var obj = $("#slider").data("roundSlider");
val = obj.getValue();
$.getJSON('/valueofslider', {
slide_val: val
});
}
});
// play sound at start
$.getJSON('/valueofslider', {slide_val: val});
</script>
</body>
</html>'''@app.route('/test')deftest():
HTML = 'HEAR:'for item in (0, 10000, 20000, 25000, 32000):
HTML += ' <a href="/set/{}">{}</a>'.format(item, item)
return HTML
@app.route('/off')defoff():
"""Turn off sound."""
sound(0)
#return jsonify({'val': 0})return'off'@app.route('/set/<int:value>')defset_value(value):
"""Set value. Use 0 to turn it off."""
sound(value)
#return jsonify({'val': value})returnstr(value)
@app.route('/get')defget_value():
"""Get current value."""if tb:
value = tb.sample_rate
else:
value = 0#return jsonify({'val': value})returnstr(value)
@app.route('/valueofslider')defslide():
sample_rate = request.args.get('slide_val', '32000')
sample_rate = int(sample_rate)
sound(sample_rate)
#return jsonify({'val': sample_rate})returnstr(sample_rate)
defsound(sample_rate):
global tb
print('[sound] sample_rate:', sample_rate)
sample_rate = int(sample_rate)
# stop old soundif tb: # if tb is not None
tb.stop()
tb.wait()
tb = None# create new sound (if not zero)if sample_rate > 0:
tb = TopBlock22(sample_rate)
tb.start()
if __name__ == '__main__':
app.run(debug=True)
EDIT: this time last version.
It uses lock/unlock
and disconnect/connect
to change sound without creating new object. Based on example from FAQ (How can I reconfigure a flow graph? How do I use lock(), unlock()?) but disconnect
needed two endpoints like connect
.
from __future__ import print_function
from gnuradio import analog
from gnuradio import audio
from gnuradio import blocks
from gnuradio import eng_notation
from gnuradio import gr
from gnuradio.eng_option import eng_option
from gnuradio.filterimport firdes
from optparse import OptionParser
classTopBlock22(gr.top_block): # PEP8: CamelCaseName for classesdef__init__(self, sample_rate=32000):
gr.top_block.__init__(self, "Top Block 22")
################################################### Variables##################################################
self.sample_rate = sample_rate
print('[TopBlock22] __init__: sample_rate:', self.sample_rate)
################################################### Blocks##################################################
self.blocks_add_xx = blocks.add_vff(1)
self.audio_sink = audio.sink(32000, '', True)
self.analog_sig_source_x_1 = analog.sig_source_f(sample_rate, analog.GR_COS_WAVE, 440, 0.4, 0)
self.analog_sig_source_x_0 = analog.sig_source_f(sample_rate, analog.GR_COS_WAVE, 350, 0.4, 0)
self.analog_noise_source_x_0 = analog.noise_source_f(analog.GR_GAUSSIAN, 0.005, -42)
################################################### Connections##################################################
self.connect((self.analog_noise_source_x_0, 0), (self.blocks_add_xx, 2))
self.connect((self.analog_sig_source_x_0, 0), (self.blocks_add_xx, 0))
self.connect((self.analog_sig_source_x_1, 0), (self.blocks_add_xx, 1))
self.connect((self.blocks_add_xx, 0), (self.audio_sink, 0))
defchange(self, sample_rate):
self.sample_rate = sample_rate
print('[TopBlock22] change: sample_rate:', self.sample_rate)
# lock
self.lock()
# disconect - needs two endpoints (not like in FAQ)
self.disconnect((self.analog_sig_source_x_0, 0), (self.blocks_add_xx, 0))
self.disconnect((self.analog_sig_source_x_1, 0), (self.blocks_add_xx, 1))
# create new
self.analog_sig_source_x_1 = analog.sig_source_f(sample_rate, analog.GR_COS_WAVE, 440, 0.4, 0)
self.analog_sig_source_x_0 = analog.sig_source_f(sample_rate, analog.GR_COS_WAVE, 350, 0.4, 0)
# connect again
self.connect((self.analog_sig_source_x_0, 0), (self.blocks_add_xx, 0))
self.connect((self.analog_sig_source_x_1, 0), (self.blocks_add_xx, 1))
# unlock
self.unlock()
# -----------------------------------------------------------------------------from flask import Flask, request, jsonify
app = Flask(__name__)
tb = None# global variable to keep it between requests@app.route('/')defindex():
return'''<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>GNURadio Slider Example</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/roundSlider/1.3.2/roundslider.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/roundSlider/1.3.2/roundslider.min.js"></script>
</head>
<body>
<div id="slider"></div>
<script>
// keep slider's value
var val;
// create slider
$("#slider").roundSlider({
radius: 215,
min: 0,
max: 100000,
value: 32000, // default value at start
change: function () {
var obj = $("#slider").data("roundSlider");
val = obj.getValue();
$.getJSON('/valueofslider', {
slide_val: val
});
}
});
// play sound at start
$.getJSON('/valueofslider', {slide_val: val});
</script>
</body>
</html>'''@app.route('/test')deftest():
HTML = 'HEAR:'for item in (0, 10000, 20000, 25000, 32000):
HTML += ' <a href="/set/{}">{}</a>'.format(item, item)
return HTML
@app.route('/off')defoff():
"""Turn off sound."""
sound(0)
#return jsonify({'val': 0})return'off'@app.route('/set/<int:value>')defset_value(value):
"""Set value. Use 0 to turn it off."""
sound(value)
#return jsonify({'val': value})returnstr(value)
@app.route('/get')defget_value():
"""Get value. Returns 0 when turned off."""if tb:
value = tb.sample_rate
else:
value = 0#return jsonify({'val': value})returnstr(value)
@app.route('/valueofslider')defslide():
sample_rate = request.args.get('slide_val', '32000')
sample_rate = int(sample_rate)
sound(sample_rate)
#return jsonify({'val': sample_rate})returnstr(sample_rate)
defsound_old(sample_rate):
"""version which doesn't use `change()`"""global tb
print('[sound] sample_rate:', sample_rate)
sample_rate = int(sample_rate)
# stop old soundif tb: # if tb is not None
tb.stop()
tb.wait()
tb = None# create new sound (if not zero)if sample_rate > 0:
tb = TopBlock22(sample_rate)
tb.start()
defsound(sample_rate):
"""version which uses `change()`"""global tb
print('[sound] sample_rate:', sample_rate)
sample_rate = int(sample_rate)
# change or stop old soundif tb: # if tb is not Noneif sample_rate > 0:
tb.change(sample_rate)
else:
tb.stop()
tb.wait()
tb = None# create new sound (if not zero)ifnot tb:
if sample_rate > 0:
tb = TopBlock22(sample_rate)
tb.start()
if __name__ == '__main__':
app.run(debug=True)
EDIT: I found that sample rate
can be change with
self.analog_sig_source_x_1.set_sampling_freq(sample_rate)
so it doesn't have to create new sig_source_f
class TopBlock22(gr.top_block):
# ... rest ...
def change(self, sample_rate):
self.sample_rate = sample_rate
print('[TopBlock22] change: sample_rate:', self.sample_rate)
self.analog_sig_source_x_1.set_sampling_freq(sample_rate)
self.analog_sig_source_x_0.set_sampling_freq(sample_rate)
I think that now I don't hear clicks when it changes sample rate
but now I fill it has small delay before it change it after mouse click.
To see other functions in this object you can use dir(self.analog_sig_source_x_1)
print('\n'.join(dir(self.analog_sig_source_x_1)))
...
set_amplitude
set_block_alias
set_frequency
set_max_noutput_items
set_max_output_buffer
set_min_noutput_items
set_min_output_buffer
set_offset
set_processor_affinity
set_sampling_freq
set_thread_priority
set_waveform
...
Source: Choosing, defining and configuring blocks, documentation for C/C++ version
Post a Comment for "Wbserver Roundslider Widget Doesn't Start/update Audio Tone Program"