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.filter import firdes
from optparse import OptionParser
class top_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('/')
def index():
HTML = 'HEAR:'
for item in (20000, 25000, 32000):
HTML += ' <a href="/valueofslider?slide_val={}">{}</a>'.format(item, item)
return HTML
@app.route('/valueofslider')
def slide():
slide_val = request.args.get('slide_val', 32000) # default value 32000
main(slide_val)
return slide_val
def main(slide_val):
samp_rate = int(slide_val) + 100
print('[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.filter import firdes
from optparse import OptionParser
class top_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
class MyThread(Thread):
def __init__(self, samp_rate):
Thread.__init__(self)
self.running = True
self.samp_rate = samp_rate
def run(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('/')
def index():
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')
def off():
'''use `slide_val=0` to turn it off'''
main(0)
return 'off'
@app.route('/valueofslider')
def slide():
slide_val = request.args.get('slide_val', 32000)
main(slide_val)
return slide_val
def main(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 = None
if 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.filter import firdes
from optparse import OptionParser
class top_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('/')
def index():
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')
def off():
'''use `slide_val=0` to turn it off'''
main(0)
return 'off'
@app.route('/valueofslider')
def slide():
slide_val = request.args.get('slide_val', 32000)
main(slide_val)
return slide_val
def main(slide_val):
global tb
print('[DEBUG] main:', slide_val)
samp_rate = int(slide_val)
# stop old sound
if 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.filter import firdes
from optparse import OptionParser
class TopBlock22(gr.top_block): # PEP8: CamelCaseName for classes
def __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('/')
def index():
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')
def test():
HTML = 'HEAR:'
for item in (0, 10000, 20000, 25000, 32000):
HTML += ' <a href="/set/{}">{}</a>'.format(item, item)
return HTML
@app.route('/off')
def off():
"""Turn off sound."""
sound(0)
#return jsonify({'val': 0})
return 'off'
@app.route('/set/<int:value>')
def set_value(value):
"""Set value. Use 0 to turn it off."""
sound(value)
#return jsonify({'val': value})
return str(value)
@app.route('/get')
def get_value():
"""Get current value."""
if tb:
value = tb.sample_rate
else:
value = 0
#return jsonify({'val': value})
return str(value)
@app.route('/valueofslider')
def slide():
sample_rate = request.args.get('slide_val', '32000')
sample_rate = int(sample_rate)
sound(sample_rate)
#return jsonify({'val': sample_rate})
return str(sample_rate)
def sound(sample_rate):
global tb
print('[sound] sample_rate:', sample_rate)
sample_rate = int(sample_rate)
# stop old sound
if 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.filter import firdes
from optparse import OptionParser
class TopBlock22(gr.top_block): # PEP8: CamelCaseName for classes
def __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))
def change(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('/')
def index():
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')
def test():
HTML = 'HEAR:'
for item in (0, 10000, 20000, 25000, 32000):
HTML += ' <a href="/set/{}">{}</a>'.format(item, item)
return HTML
@app.route('/off')
def off():
"""Turn off sound."""
sound(0)
#return jsonify({'val': 0})
return 'off'
@app.route('/set/<int:value>')
def set_value(value):
"""Set value. Use 0 to turn it off."""
sound(value)
#return jsonify({'val': value})
return str(value)
@app.route('/get')
def get_value():
"""Get value. Returns 0 when turned off."""
if tb:
value = tb.sample_rate
else:
value = 0
#return jsonify({'val': value})
return str(value)
@app.route('/valueofslider')
def slide():
sample_rate = request.args.get('slide_val', '32000')
sample_rate = int(sample_rate)
sound(sample_rate)
#return jsonify({'val': sample_rate})
return str(sample_rate)
def sound_old(sample_rate):
"""version which doesn't use `change()`"""
global tb
print('[sound] sample_rate:', sample_rate)
sample_rate = int(sample_rate)
# stop old sound
if 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()
def sound(sample_rate):
"""version which uses `change()`"""
global tb
print('[sound] sample_rate:', sample_rate)
sample_rate = int(sample_rate)
# change or stop old sound
if tb: # if tb is not None
if sample_rate > 0:
tb.change(sample_rate)
else:
tb.stop()
tb.wait()
tb = None
# create new sound (if not zero)
if not 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"