Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixstreambug #32

Merged
merged 3 commits into from
Nov 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 34 additions & 17 deletions example/radiomqtt/radiomqtt.gd
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ var audioresamplerate : int = 48000
var audioresamplesize : int = 960
var opussamplerate : int = 48000
var opusframesize : int = 960
var prefixbyteslength : int = 0
var opuscomplexity : int = 5
var opusoptimizeforvoice : bool = true

var prefixbytes = PackedByteArray([0,0,0,0,1])
var mqttpacketencodebase64 : bool = false

var recordedsamples = [ ]
Expand All @@ -44,6 +47,11 @@ func _ready():
print("ProjectSettings.get_setting_with_override(\"audio/driver/mix_rate\")=", ProjectSettings.get_setting_with_override("audio/driver/mix_rate"))
$VBoxPlayback/HBoxStream/MixRate.value = AudioServer.get_mix_rate()

if $VBoxFrameLength/HBoxOpusFrame/FrameDuration.selected == -1:
$VBoxFrameLength/HBoxOpusFrame/FrameDuration.select(3)
if $VBoxFrameLength/HBoxOpusBitRate/SampleRate.selected == -1:
$VBoxFrameLength/HBoxOpusBitRate/SampleRate.select(4)

# works better if we don't use ProjectSettings("audio/driver/mix_rate") anywhere, so what is it for?
if false and ProjectSettings.get_setting_with_override("audio/driver/mix_rate") != 0:
$VBoxFrameLength/HBoxAudioFrame/MicSampleRate.value = ProjectSettings.get_setting_with_override("audio/driver/mix_rate")
Expand All @@ -52,13 +60,6 @@ func _ready():
$VBoxFrameLength/HBoxAudioFrame/MicSampleRate.value = AudioServer.get_mix_rate()
$VBoxPlayback/HBoxStream/OutSampleRate.value = AudioServer.get_mix_rate()

if $VBoxFrameLength/HBoxOpusFrame/FrameDuration.selected == -1:
$VBoxFrameLength/HBoxOpusFrame/FrameDuration.select(3)
if $VBoxFrameLength/HBoxOpusBitRate/SampleRate.selected == -1:
$VBoxFrameLength/HBoxOpusBitRate/SampleRate.select(4)

if $VBoxFrameLength/HBoxOpusBitRate/BitRate.selected == -1:
$VBoxFrameLength/HBoxOpusBitRate/BitRate.select(2)
if not ClassDB.can_instantiate("AudioEffectOpusChunked"):
$TwovoipWarning.visible = true
$VBoxFrameLength/HBoxAudioFrame/ResampleRate.value = $VBoxFrameLength/HBoxAudioFrame/MicSampleRate.value
Expand Down Expand Up @@ -127,40 +128,44 @@ func updatesamplerates():
audioresamplesize = int(audioresamplerate*frametimems/1000.0)
opussamplerate = int($VBoxFrameLength/HBoxOpusBitRate/SampleRate.text)*1000
opusframesize = int(opussamplerate*frametimems/1000.0)
opuscomplexity = int($VBoxFrameLength/HBoxOpusExtra/ComplexitySpinBox.value)
opusoptimizeforvoice = $VBoxFrameLength/HBoxOpusExtra/OptimizeForVoice.button_pressed

print("aaa audiosamplesize ", audiosamplesize, " audiosamplerate ", audiosamplerate)

var noopuscompression = false
if opussamplerate == audioresamplerate:
$VBoxFrameLength/HBoxOpusBitRate/Compressed.disabled = false
if not $VBoxFrameLength/HBoxOpusBitRate/Compressed.button_pressed:
$VBoxFrameLength/HBoxOpusExtra/Compressed.disabled = false
if not $VBoxFrameLength/HBoxOpusExtra/Compressed.button_pressed:
noopuscompression = true
else:
$VBoxFrameLength/HBoxOpusBitRate/Compressed.disabled = true
$VBoxFrameLength/HBoxOpusExtra/Compressed.disabled = true
noopuscompression = true
opusbitrate = int($VBoxFrameLength/HBoxOpusBitRate/BitRate.text)
opusbitrate = int($VBoxFrameLength/HBoxOpusBitRate/BitRate.value)

if audioopuschunkedeffect != null:
audioopuschunkedeffect.audiosamplerate = audiosamplerate
audioopuschunkedeffect.audiosamplesize = audiosamplesize
audioopuschunkedeffect.opussamplerate = audioresamplerate
audioopuschunkedeffect.opusframesize = audioresamplesize
audioopuschunkedeffect.opuscomplexity = opuscomplexity
audioopuschunkedeffect.opusoptimizeforvoice = opusoptimizeforvoice

$HBoxBigButtons/VBoxPTT/Denoise.disabled = not (audioopuschunkedeffect.denoiser_available() and audioresamplerate == 48000)
audioopuschunkedeffect.opusbitrate = opusbitrate
else:
$VBoxFrameLength/HBoxOpusBitRate/Compressed.disabled = true
$VBoxFrameLength/HBoxOpusExtra/Compressed.disabled = true
$HBoxBigButtons/VBoxPTT/Denoise.disabled = true

mqttpacketencodebase64 = $HBoxMosquitto/base64.button_pressed
$VBoxFrameLength/HBoxAudioFrame/LabFrameLength.text = "%d samples" % audiosamplesize
$VBoxFrameLength/HBoxAudioFrame/LabResampleFrameLength.text = "%d samples" % audioresamplesize
recordedheader = { "opusframesize":audioresamplesize,
"opussamplerate":audioresamplerate,
"prefixbyteslength":prefixbyteslength,
"prefixbyteslength":len(prefixbytes),
"mqttpacketencoding":"base64" if mqttpacketencodebase64 else "binary" }
if len(recordedsamples) != 0 and len(recordedsamples[0]) != audiosamplesize:
recordedsamples = resamplerecordedsamples(recordedsamples, audiosamplesize)
var prefixbytes = PackedByteArray()
recordedopuspacketsMemSize = 0
recordedopuspackets = null
recordedresampledpackets = null
Expand Down Expand Up @@ -279,7 +284,6 @@ func _process(_delta):
endtalking()
$HBoxMicTalk/MicWorking.button_pressed = $AudioStreamMicrophone.playing

var prefixbytes = PackedByteArray()
if audioeffectcapture == null:
assert (audioopuschunkedeffect != null)
while audioopuschunkedeffect.chunk_available():
Expand Down Expand Up @@ -417,7 +421,7 @@ func _on_frame_duration_item_selected(_index):

func _on_sample_rate_item_selected(index):
$VBoxFrameLength/HBoxAudioFrame/ResampleRate.value = int($VBoxFrameLength/HBoxOpusBitRate/SampleRate.text)*1000
$VBoxFrameLength/HBoxOpusBitRate/Compressed.button_pressed = true
$VBoxFrameLength/HBoxOpusExtra/Compressed.button_pressed = true
updatesamplerates()

func _on_resample_state_item_selected(_index):
Expand Down Expand Up @@ -448,3 +452,16 @@ func _on_sample_rate_value_changed(value):

func _on_spin_box_value_changed(value):
$AudioStreamMicrophone.pitch_scale = value

func _on_volume_db_spin_box_value_changed(value):
$AudioStreamMicrophone.volume_db = value

func _on_complexity_spin_box_value_changed(value):
updatesamplerates()

func _on_audio_optimized_check_button_toggled(toggled_on):
updatesamplerates()


func _on_bit_rate_value_changed(value):
updatesamplerates()
59 changes: 41 additions & 18 deletions example/radiomqtt/radiomqtt.tscn
Original file line number Diff line number Diff line change
Expand Up @@ -306,17 +306,27 @@ popup/item_4/id = 4
popup/item_5/text = "60ms"
popup/item_5/id = 5

[node name="Label" type="Label" parent="VBoxFrameLength/HBoxOpusFrame"]
[node name="LabelPitchRate" type="Label" parent="VBoxFrameLength/HBoxOpusFrame"]
layout_mode = 2
text = "Mic Pitch rate:"

[node name="SpinBox" type="SpinBox" parent="VBoxFrameLength/HBoxOpusFrame"]
[node name="PitchRateSpinBox" type="SpinBox" parent="VBoxFrameLength/HBoxOpusFrame"]
layout_mode = 2
min_value = 0.2
max_value = 5.0
step = 0.0
value = 1.0

[node name="LabelVolumeDB" type="Label" parent="VBoxFrameLength/HBoxOpusFrame"]
layout_mode = 2
text = "VolumeDB"

[node name="VolumeDBSpinBox" type="SpinBox" parent="VBoxFrameLength/HBoxOpusFrame"]
layout_mode = 2
min_value = -10.0
max_value = 30.0
step = 0.0

[node name="HBoxAudioFrame" type="HBoxContainer" parent="VBoxFrameLength"]
layout_mode = 2

Expand Down Expand Up @@ -384,23 +394,33 @@ popup/item_4/id = 4
layout_mode = 2
text = "Bit Rate"

[node name="BitRate" type="OptionButton" parent="VBoxFrameLength/HBoxOpusBitRate"]
[node name="BitRate" type="SpinBox" parent="VBoxFrameLength/HBoxOpusBitRate"]
layout_mode = 2
item_count = 5
popup/item_0/text = "24000"
popup/item_1/text = "12000"
popup/item_1/id = 1
popup/item_2/text = "10000"
popup/item_2/id = 2
popup/item_3/text = "6000"
popup/item_3/id = 3
popup/item_4/text = "3000"
popup/item_4/id = 4
min_value = 500.0
max_value = 200000.0
value = 12000.0

[node name="HBoxOpusExtra" type="HBoxContainer" parent="VBoxFrameLength"]
layout_mode = 2

[node name="Compressed" type="CheckButton" parent="VBoxFrameLength/HBoxOpusExtra"]
layout_mode = 2
button_pressed = true
text = "Opus Compressed"

[node name="LabelComplexity" type="Label" parent="VBoxFrameLength/HBoxOpusExtra"]
layout_mode = 2
text = "Opus Complexity"

[node name="ComplexitySpinBox" type="SpinBox" parent="VBoxFrameLength/HBoxOpusExtra"]
layout_mode = 2
max_value = 10.0
value = 5.0

[node name="Compressed" type="CheckButton" parent="VBoxFrameLength/HBoxOpusBitRate"]
[node name="OptimizeForVoice" type="CheckButton" parent="VBoxFrameLength/HBoxOpusExtra"]
layout_mode = 2
button_pressed = true
text = "Compressed"
text = "Voice optimized"

[node name="HSeparator4" type="HSeparator" parent="."]
custom_minimum_size = Vector2(0, 20)
Expand Down Expand Up @@ -735,12 +755,15 @@ text = "sil"
[connection signal="toggled" from="HBoxMicTalk/MicWorking" to="." method="_on_mic_working_toggled"]
[connection signal="value_changed" from="HBoxMicTalk/HSliderVox" to="HBoxMicTalk" method="_on_h_slider_vox_value_changed"]
[connection signal="item_selected" from="VBoxFrameLength/HBoxOpusFrame/FrameDuration" to="." method="_on_frame_duration_item_selected"]
[connection signal="value_changed" from="VBoxFrameLength/HBoxOpusFrame/SpinBox" to="." method="_on_spin_box_value_changed"]
[connection signal="value_changed" from="VBoxFrameLength/HBoxOpusFrame/PitchRateSpinBox" to="." method="_on_spin_box_value_changed"]
[connection signal="value_changed" from="VBoxFrameLength/HBoxOpusFrame/VolumeDBSpinBox" to="." method="_on_volume_db_spin_box_value_changed"]
[connection signal="value_changed" from="VBoxFrameLength/HBoxAudioFrame/MicSampleRate" to="." method="_on_sample_rate_value_changed"]
[connection signal="value_changed" from="VBoxFrameLength/HBoxAudioFrame/ResampleRate" to="." method="_on_resample_rate_value_changed"]
[connection signal="item_selected" from="VBoxFrameLength/HBoxOpusBitRate/SampleRate" to="." method="_on_sample_rate_item_selected"]
[connection signal="item_selected" from="VBoxFrameLength/HBoxOpusBitRate/BitRate" to="." method="_on_option_button_item_selected"]
[connection signal="toggled" from="VBoxFrameLength/HBoxOpusBitRate/Compressed" to="." method="_on_compressed_toggled"]
[connection signal="value_changed" from="VBoxFrameLength/HBoxOpusBitRate/BitRate" to="." method="_on_bit_rate_value_changed"]
[connection signal="toggled" from="VBoxFrameLength/HBoxOpusExtra/Compressed" to="." method="_on_compressed_toggled"]
[connection signal="value_changed" from="VBoxFrameLength/HBoxOpusExtra/ComplexitySpinBox" to="." method="_on_complexity_spin_box_value_changed"]
[connection signal="toggled" from="VBoxFrameLength/HBoxOpusExtra/OptimizeForVoice" to="." method="_on_audio_optimized_check_button_toggled"]
[connection signal="pressed" from="VBoxPlayback/HBoxPlaycount/Play" to="." method="_on_play_pressed"]
[connection signal="item_selected" from="VBoxPlayback/HBoxPlaycount/VBoxExpt/SavOptions" to="." method="_on_sav_options_item_selected"]
[connection signal="broker_connection_failed" from="MQTTnetwork/MQTT" to="MQTTnetwork" method="_on_mqtt_broker_connection_failed"]
Expand Down
29 changes: 22 additions & 7 deletions src/audio_effect_opus_chunked.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ void AudioEffectOpusChunked::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_opusframesize"), &AudioEffectOpusChunked::get_opusframesize);
ClassDB::bind_method(D_METHOD("set_opusbitrate", "opusbitrate"), &AudioEffectOpusChunked::set_opusbitrate);
ClassDB::bind_method(D_METHOD("get_opusbitrate"), &AudioEffectOpusChunked::get_opusbitrate);
ClassDB::bind_method(D_METHOD("set_opuscomplexity", "opuscomplexity"), &AudioEffectOpusChunked::set_opuscomplexity);
ClassDB::bind_method(D_METHOD("get_opuscomplexity"), &AudioEffectOpusChunked::get_opuscomplexity);
ClassDB::bind_method(D_METHOD("set_opusoptimizeforvoice", "opusoptimizeforvoice"), &AudioEffectOpusChunked::set_opusoptimizeforvoice);
ClassDB::bind_method(D_METHOD("get_opusoptimizeforvoice"), &AudioEffectOpusChunked::get_opusoptimizeforvoice);
ClassDB::bind_method(D_METHOD("set_audiosamplerate", "audiosamplerate"), &AudioEffectOpusChunked::set_audiosamplerate);
ClassDB::bind_method(D_METHOD("get_audiosamplerate"), &AudioEffectOpusChunked::get_audiosamplerate);
ClassDB::bind_method(D_METHOD("set_audiosamplesize", "audiosamplesize"), &AudioEffectOpusChunked::set_audiosamplesize);
Expand All @@ -50,15 +54,14 @@ void AudioEffectOpusChunked::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_audiosamplechunks"), &AudioEffectOpusChunked::get_audiosamplechunks);

ADD_PROPERTY(PropertyInfo(Variant::INT, "opussamplerate", PROPERTY_HINT_RANGE, "20,192000,1"), "set_opussamplerate", "get_opussamplerate");
ADD_PROPERTY(PropertyInfo(Variant::INT, "opusframesize", PROPERTY_HINT_RANGE, "20,2880,2"), "set_opusframesize", "get_opusframesize");
ADD_PROPERTY(PropertyInfo(Variant::INT, "opusbitrate", PROPERTY_HINT_RANGE, "3000,24000,1000"), "set_opusbitrate", "get_opusbitrate");

ADD_PROPERTY(PropertyInfo(Variant::INT, "opusframesize", PROPERTY_HINT_RANGE, "20,2880,1"), "set_opusframesize", "get_opusframesize");
ADD_PROPERTY(PropertyInfo(Variant::INT, "opusbitrate", PROPERTY_HINT_RANGE, "500,200000,1"), "set_opusbitrate", "get_opusbitrate");
ADD_PROPERTY(PropertyInfo(Variant::INT, "opuscomplexity", PROPERTY_HINT_RANGE, "0,10,1"), "set_opuscomplexity", "get_opuscomplexity");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "opusoptimizeforvoice"), "set_opusoptimizeforvoice", "get_opusoptimizeforvoice");
ADD_PROPERTY(PropertyInfo(Variant::INT, "audiosamplerate", PROPERTY_HINT_RANGE, "20,192000,1"), "set_audiosamplerate", "get_audiosamplerate");
ADD_PROPERTY(PropertyInfo(Variant::INT, "audiosamplesize", PROPERTY_HINT_RANGE, "10,4000,1"), "set_audiosamplesize", "get_audiosamplesize");
ADD_PROPERTY(PropertyInfo(Variant::INT, "audiosamplechunks", PROPERTY_HINT_RANGE, "1,200,1"), "set_audiosamplechunks", "get_audiosamplechunks");

// ClassDB::bind_method(D_METHOD("Dcreateencoder"), &AudioEffectOpusChunked::createencoder);

ClassDB::bind_method(D_METHOD("chunk_available"), &AudioEffectOpusChunked::chunk_available);
ClassDB::bind_method(D_METHOD("resampled_current_chunk"), &AudioEffectOpusChunked::resampled_current_chunk);
ClassDB::bind_method(D_METHOD("denoiser_available"), &AudioEffectOpusChunked::denoiser_available);
Expand Down Expand Up @@ -188,7 +191,19 @@ void AudioEffectOpusChunked::createencoder() {
}
int opuserror2 = opus_encoder_ctl(opusencoder, OPUS_SET_BITRATE(opusbitrate));
if (opuserror2 != 0) {
godot::UtilityFunctions::printerr("opus_encoder_ctl error error ", opuserror2);
godot::UtilityFunctions::printerr("opus_encoder_ctl bitrate error error ", opuserror2);
chunknumber = -2;
return;
}
int opuserror3 = opus_encoder_ctl(opusencoder, OPUS_SET_COMPLEXITY(complexity));
if (opuserror3 != 0) {
godot::UtilityFunctions::printerr("opus_encoder_ctl complexity error error ", opuserror3);
chunknumber = -2;
return;
}
int opuserror4 = opus_encoder_ctl(opusencoder, OPUS_SET_SIGNAL(signal_type));
if (opuserror4 != 0) {
godot::UtilityFunctions::printerr("opus_encoder_ctl signal_type error error ", opuserror4);
chunknumber = -2;
return;
}
Expand Down Expand Up @@ -329,7 +344,7 @@ PackedByteArray AudioEffectOpusChunked::read_opus_packet(const PackedByteArray&
}

void AudioEffectOpusChunked::flush_opus_encoder(bool denoise) {
if (opusframesize == 0)
if ((opusframesize == 0) || (chunknumber == -1))
return;
// this just sends 5 empty chunks into the encoder. doesn't necessarily work
float* paudioresamples = (float*)singleresamplebuffer.ptrw();
Expand Down
9 changes: 8 additions & 1 deletion src/audio_effect_opus_chunked.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
#include "rnnoise_stub.h"
#endif


namespace godot {

class AudioEffectOpusChunked;
Expand Down Expand Up @@ -117,7 +118,9 @@ class AudioEffectOpusChunked : public AudioEffect {

int opussamplerate = 48000;
int opusframesize = 960;
int opusbitrate = 24000;
int opusbitrate = 12000;
int complexity = 5;
int signal_type = OPUS_SIGNAL_VOICE;

SpeexResamplerState* speexresampler = NULL;
PackedVector2Array audioresampledbuffer; // size opusframesize*ringbufferchunks
Expand Down Expand Up @@ -188,6 +191,10 @@ class AudioEffectOpusChunked : public AudioEffect {
int get_audiosamplesize() { return audiosamplesize; };
void set_audiosamplechunks(int laudiosamplechunks) { chunknumber = -1; ringbufferchunks = laudiosamplechunks; };
int get_audiosamplechunks() { return ringbufferchunks; };
void set_opuscomplexity(int lcomplexity) { chunknumber = -1; complexity = lcomplexity; };
int get_opuscomplexity() { return complexity; };
void set_opusoptimizeforvoice(bool loptimizeforvoice) { chunknumber = -1; signal_type = (loptimizeforvoice ? OPUS_SIGNAL_VOICE : OPUS_SIGNAL_MUSIC); };
bool get_opusoptimizeforvoice() { return (signal_type == OPUS_SIGNAL_VOICE); };

AudioEffectOpusChunked();
~AudioEffectOpusChunked();
Expand Down
7 changes: 6 additions & 1 deletion src/audio_stream_opus_chunked.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,17 +107,18 @@ void AudioStreamOpusChunked::createdecoder() {
return;
}
}

float Dtimeframeopus = opusframesize*1.0F/opussamplerate;
float Dtimeframeaudio = audiosamplesize*1.0F/audiosamplerate;
if (audiosamplesize != opusframesize) {
int speexerror = 0;
int resamplingquality = 10;
speexresampler = speex_resampler_init(channels, opussamplerate, audiosamplerate, resamplingquality, &speexerror);
audiopreresampledbuffer.resize(opusframesize);
godot::UtilityFunctions::prints("Decoder timeframeopus preresampler", Dtimeframeopus, "timeframeaudio", Dtimeframeaudio);
} else {
godot::UtilityFunctions::prints("Decoder timeframeopus no-resampling needed", Dtimeframeopus, "timeframeaudio", Dtimeframeaudio);
}
audiopreresampledbuffer.resize(opusframesize);
Daudioresampledbuffer.resize(audiosamplesize);

audiosamplebuffer.resize(audiosamplesize*audiosamplechunks);
Expand Down Expand Up @@ -216,6 +217,10 @@ PackedVector2Array* AudioStreamOpusChunked::Popus_packet_to_chunk(const PackedBy
else
return &audiopreresampledbuffer;
}
if (audiopreresampledbuffer.size() != opusframesize) {
godot::UtilityFunctions::printerr("Errorrrr mismatch resampledaudio size");
return &audiopreresampledbuffer;
}
int decodedsamples = opus_decode_float(opusdecoder, opusbytepacket.ptr() + begin, opusbytepacket.size() - begin,
(float*)audiopreresampledbuffer.ptrw(), opusframesize, decode_fec);
if (audiosamplesize == opusframesize) {
Expand Down