Функция Flask socketio не отключается, когда аудиозаписи длиннее, чем около 30 секунд, но только в том случае, если их обслуживает gunicorn в службах приложений Azure.Кажется, что локальный dev имеет почти неограниченную емкость, и более короткие аудиозаписи работают нормально в среде Azure.
Кажется, что код действительно работает правильно, за исключением запуска voice_disconnect_request()
.Хотя код после disconnect()
выполняется (т.е. @socketio.on('disconnect', namespace='/voice_enroll') -> voice_test_disconnect()
), поэтому я не уверен, почему emit('my_response',{'data': 'Voice Enroll Complete!', 'count': session['receive_count']})
не работает.По какой-то причине, если это не сработает, $('form#connect').submit(function(event)
будет запущен снова, что автоматически создаст новое соединение и запустит рекордер.
Кнопки на сайте действительно отключатся / включатся правильно и сразу,поэтому кажется, что кнопка записи не должна быть активной, функция отключения должна быть полностью корректной, и она не должна снова вызывать $('form#connect').submit(function(event)
.В случаях, когда запрос на отключение отправляется надлежащим образом, все работает как положено.
Я пытался запустить voice_enroll()
в потоке (код показан ниже), так как это занимает несколько секунд, и я был обеспокоен, что некоторыетам некоторая задержка.
Я попытался использовать разные размеры буфера для audio_context.
Я изменил множество асинхронных режимов и настроек Gunicorn, но на самом деле я в растерянности из-за чего ещепытаться.Там нет сообщений об ошибках нигде.Кажется, что он просто сдался и начал все сначала.
Я пробовал это для настроек gunicorn
gunicorn --worker-class eventlet -w 1 --bind '0.0.0.0:8000' app:server --timeout 300
gunicorn --worker-class gevent -w 1 --bind '0.0.0.0:8000' app:server --timeout 300
gunicorn -w 1 --threads 12 --bind '0.0.0.0:8000' app:server --timeout 300
python
@socketio.on('my_event', namespace='/voice_enroll')
def voice_message(message):
session['receive_count'] = session.get('receive_count', 0) + 1
emit('my_response',
{'data': message['data'], 'count': session['receive_count']})
@socketio.on('disconnect_request', namespace='/voice_enroll')
def voice_disconnect_request():
session['receive_count'] = session.get('receive_count', 0) + 1
emit('my_response',
{'data': 'Voice Enroll Complete!', 'count': session['receive_count']})
disconnect()
@socketio.on('connect', namespace='/voice_enroll')
def voice_test_connect():
session['audio'] = []
emit('my_response', {'data': 'Connected', 'count': 0})
@socketio.on('sample_rate', namespace='/voice_enroll')
def voice_handle_my_sample_rate(sampleRate):
session['sample_rate'] = sampleRate
session['receive_count'] = session.get('receive_count', 0) + 1
emit('my_response', {'data': "sampleRate : %s" % sampleRate, 'count': session['receive_count'] })
@socketio.on('audio', namespace='/voice_enroll')
def voice_handle_my_custom_event(audio):
"""
"""
values = OrderedDict(sorted(audio.items(), key=lambda t: int(t[0]))).values()
session['audio'] += values
@socketio.on('disconnect', namespace='/voice_enroll')
def voice_test_disconnect():
# https://stackoverflow.com/a/18644461/466693
sample_rate = session['sample_rate']
my_audio = np.array(session['audio'], np.float32)
scaled = np.round(32767*np.sin(my_audio))
scipy.io.wavfile.write('out.wav', sample_rate, scaled.astype(np.int16))
audio = AudioSegment.from_file('out.wav', format='wav').set_frame_rate(MS_VOICE_FRAMERATE)
audio.export('temp.wav', format='wav')
thread = Thread(target=enroll_voice, args=('temp.wav', current_user.username))
thread.start()
session['audio'] = []
и
JS
var namespace = '/voice_enroll';
var socket = null;
var mediaStream = null;
// prepare button state
$('#disconnect input')[0].disabled = true;
// audio functions
function initializeRecorder(stream){
// https://stackoverflow.com/a/42360902/466693
mediaStream = stream;
// get sample rate
audio_context = new AudioContext;
sampleRate = audio_context.sampleRate;
console.log('<sample_rate>', sampleRate);
socket.emit('sample_rate', sampleRate);
var audioInput = audio_context.createMediaStreamSource(stream);
console.log("Created media stream.");
var bufferSize = 8192;
// record only 1 channel
var recorder = audio_context.createScriptProcessor(bufferSize, 1, 1);
// specify the processing function
recorder.onaudioprocess = recorderProcess;
// connect stream to our recorder
audioInput.connect(recorder);
// connect our recorder to the previous destination
recorder.connect(audio_context.destination);
}
function recorderProcess(e) {
var left = e.inputBuffer.getChannelData(0);
socket.emit('audio', left);
}
$('form#connect').submit(function(event) {
if(socket == null){
socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port + namespace);
socket.on('connect', function() {
socket.emit('my_event', {data: 'I\'m connected!'});
navigator.getUserMedia({audio: true}, initializeRecorder, function(a, b, c){
console.log(a, b, c);
});
});
socket.on('my_response', function(msg) {
$('#log').append('<br>' + $('<div/>').text('Received #' + msg.count + ': ' + msg.data).html());
});
}
else {
socket.disconnect();
socket.connect();
}
$('#connect input')[0].disabled = true;
$('#disconnect input')[0].disabled = false;
return false;
});
$('form#disconnect').submit(function(event) {
mediaStream.getAudioTracks()[0].stop();
audio_context.close();
socket.emit('disconnect_request');
$('#connect input')[0].disabled = false;
$('#disconnect input')[0].disabled = true;
return false;
});
Опция Flask socketio должна корректно завершиться, выдав сообщение о разъединении, чтобы предупредить конечных пользователей об успешном завершении процесса, как это происходит для локального разработчика и для коротких аудиосообщений.