mirror of
https://github.com/DJ2LS/FreeDATA
synced 2024-05-14 08:04:33 +00:00
optimized audio processing
moved from queue to deque, decode only when not transmitting.
This commit is contained in:
parent
a552109265
commit
d0f6686899
71
tnc/modem.py
71
tnc/modem.py
|
@ -25,8 +25,7 @@ import queue
|
||||||
import codec2
|
import codec2
|
||||||
import audio
|
import audio
|
||||||
|
|
||||||
|
from collections import deque
|
||||||
|
|
||||||
|
|
||||||
MODEM_STATS_NR_MAX = 320
|
MODEM_STATS_NR_MAX = 320
|
||||||
MODEM_STATS_NC_MAX = 51
|
MODEM_STATS_NC_MAX = 51
|
||||||
|
@ -92,7 +91,7 @@ class RF():
|
||||||
self.modem_received_queue = MODEM_RECEIVED_QUEUE
|
self.modem_received_queue = MODEM_RECEIVED_QUEUE
|
||||||
|
|
||||||
# init FIFO queue to store modulation out in
|
# init FIFO queue to store modulation out in
|
||||||
self.modoutqueue = queue.Queue()
|
self.modoutqueue = deque()
|
||||||
|
|
||||||
# define fft_data buffer
|
# define fft_data buffer
|
||||||
self.fft_data = bytes()
|
self.fft_data = bytes()
|
||||||
|
@ -232,51 +231,46 @@ class RF():
|
||||||
"""
|
"""
|
||||||
|
|
||||||
x = np.frombuffer(data_in48k, dtype=np.int16)
|
x = np.frombuffer(data_in48k, dtype=np.int16)
|
||||||
time_sampler_start = time.time()
|
|
||||||
x = self.resampler.resample48_to_8(x)
|
x = self.resampler.resample48_to_8(x)
|
||||||
time_sampler_end = time.time()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
length_x = len(x)
|
||||||
|
|
||||||
time_buffer_start = time.time()
|
time_buffer_start = time.time()
|
||||||
|
|
||||||
|
# avoid decoding when transmitting to reduce CPU
|
||||||
|
if not static.TRANSMITTING:
|
||||||
# avoid buffer overflow by filling only if buffer not full
|
# avoid buffer overflow by filling only if buffer not full
|
||||||
if not self.datac0_buffer.nbuffer+len(x) > self.datac0_buffer.size:
|
if not self.datac0_buffer.nbuffer+length_x > self.datac0_buffer.size:
|
||||||
self.datac0_buffer.push(x)
|
self.datac0_buffer.push(x)
|
||||||
else:
|
else:
|
||||||
static.BUFFER_OVERFLOW_COUNTER[0] += 1
|
static.BUFFER_OVERFLOW_COUNTER[0] += 1
|
||||||
|
|
||||||
# avoid buffer overflow by filling only if buffer not full and selected datachannel mode
|
# avoid buffer overflow by filling only if buffer not full and selected datachannel mode
|
||||||
if not self.datac1_buffer.nbuffer+len(x) > self.datac1_buffer.size:
|
if not self.datac1_buffer.nbuffer+length_x > self.datac1_buffer.size:
|
||||||
if RECEIVE_DATAC1:
|
if RECEIVE_DATAC1:
|
||||||
self.datac1_buffer.push(x)
|
self.datac1_buffer.push(x)
|
||||||
else:
|
else:
|
||||||
static.BUFFER_OVERFLOW_COUNTER[1] += 1
|
static.BUFFER_OVERFLOW_COUNTER[1] += 1
|
||||||
|
|
||||||
# avoid buffer overflow by filling only if buffer not full and selected datachannel mode
|
# avoid buffer overflow by filling only if buffer not full and selected datachannel mode
|
||||||
if not self.datac3_buffer.nbuffer+len(x) > self.datac3_buffer.size:
|
if not self.datac3_buffer.nbuffer+length_x > self.datac3_buffer.size:
|
||||||
if RECEIVE_DATAC3:
|
if RECEIVE_DATAC3:
|
||||||
self.datac3_buffer.push(x)
|
self.datac3_buffer.push(x)
|
||||||
else:
|
else:
|
||||||
static.BUFFER_OVERFLOW_COUNTER[2] += 1
|
static.BUFFER_OVERFLOW_COUNTER[2] += 1
|
||||||
|
|
||||||
if self.modoutqueue.empty() or self.mod_out_locked:
|
if not self.modoutqueue or self.mod_out_locked:
|
||||||
data_out48k = bytes(self.AUDIO_FRAMES_PER_BUFFER_TX*2)
|
data_out48k = np.zeros(frame_count, dtype=np.int16)
|
||||||
self.fft_data = bytes(x)
|
self.fft_data = bytes(x)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
data_out48k = self.modoutqueue.get()
|
data_out48k = self.modoutqueue.popleft()
|
||||||
self.fft_data = bytes(data_out48k)
|
self.fft_data = bytes(data_out48k)
|
||||||
|
|
||||||
time_buffer_end = time.time()
|
|
||||||
|
|
||||||
|
|
||||||
#print(f"SAMPLER {time_sampler_end - time_sampler_start} BUFFER {time_buffer_end - time_buffer_start}")
|
|
||||||
return (data_out48k, audio.pyaudio.paContinue)
|
return (data_out48k, audio.pyaudio.paContinue)
|
||||||
|
|
||||||
# --------------------------------------------------------------------------------------------------------
|
# --------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
def transmit(self, mode, repeats, repeat_delay, frames):
|
def transmit(self, mode, repeats, repeat_delay, frames):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -326,11 +320,8 @@ class RF():
|
||||||
for i in range(1,repeats+1):
|
for i in range(1,repeats+1):
|
||||||
# write preamble to txbuffer
|
# write preamble to txbuffer
|
||||||
codec2.api.freedv_rawdatapreambletx(freedv, mod_out_preamble)
|
codec2.api.freedv_rawdatapreambletx(freedv, mod_out_preamble)
|
||||||
#time.sleep(0.05)
|
|
||||||
#threading.Event().wait(0.05)
|
|
||||||
txbuffer += bytes(mod_out_preamble)
|
txbuffer += bytes(mod_out_preamble)
|
||||||
|
|
||||||
|
|
||||||
# create modulaton for n frames in list
|
# create modulaton for n frames in list
|
||||||
for n in range(0,len(frames)):
|
for n in range(0,len(frames)):
|
||||||
|
|
||||||
|
@ -346,23 +337,16 @@ class RF():
|
||||||
|
|
||||||
data = (ctypes.c_ubyte * bytes_per_frame).from_buffer_copy(buffer)
|
data = (ctypes.c_ubyte * bytes_per_frame).from_buffer_copy(buffer)
|
||||||
codec2.api.freedv_rawdatatx(freedv,mod_out,data) # modulate DATA and save it into mod_out pointer
|
codec2.api.freedv_rawdatatx(freedv,mod_out,data) # modulate DATA and save it into mod_out pointer
|
||||||
#time.sleep(0.05)
|
|
||||||
#threading.Event().wait(0.05)
|
|
||||||
txbuffer += bytes(mod_out)
|
txbuffer += bytes(mod_out)
|
||||||
|
|
||||||
|
|
||||||
# append postamble to txbuffer
|
# append postamble to txbuffer
|
||||||
codec2.api.freedv_rawdatapostambletx(freedv, mod_out_postamble)
|
codec2.api.freedv_rawdatapostambletx(freedv, mod_out_postamble)
|
||||||
txbuffer += bytes(mod_out_postamble)
|
txbuffer += bytes(mod_out_postamble)
|
||||||
#time.sleep(0.05)
|
|
||||||
#threading.Event().wait(0.05)
|
|
||||||
# add delay to end of frames
|
# add delay to end of frames
|
||||||
samples_delay = int(self.MODEM_SAMPLE_RATE*(repeat_delay/1000))
|
samples_delay = int(self.MODEM_SAMPLE_RATE*(repeat_delay/1000))
|
||||||
mod_out_silence = create_string_buffer(samples_delay*2)
|
mod_out_silence = create_string_buffer(samples_delay*2)
|
||||||
txbuffer += bytes(mod_out_silence)
|
txbuffer += bytes(mod_out_silence)
|
||||||
#time.sleep(0.05)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# resample up to 48k (resampler works on np.int16)
|
# resample up to 48k (resampler works on np.int16)
|
||||||
x = np.frombuffer(txbuffer, dtype=np.int16)
|
x = np.frombuffer(txbuffer, dtype=np.int16)
|
||||||
|
@ -371,28 +355,21 @@ class RF():
|
||||||
# explicitly lock our usage of mod_out_queue
|
# explicitly lock our usage of mod_out_queue
|
||||||
self.mod_out_locked = True
|
self.mod_out_locked = True
|
||||||
|
|
||||||
# split modualted audio to chunks
|
chunk_length = self.AUDIO_FRAMES_PER_BUFFER_TX #4800
|
||||||
#https://newbedev.com/how-to-split-a-byte-string-into-separate-bytes-in-python
|
chunk = [txbuffer_48k[i:i+chunk_length] for i in range(0, len(txbuffer_48k), chunk_length)]
|
||||||
txbuffer_48k = bytes(txbuffer_48k)
|
|
||||||
|
|
||||||
chunk = [txbuffer_48k[i:i+self.AUDIO_FRAMES_PER_BUFFER_RX*2] for i in range(0, len(txbuffer_48k), self.AUDIO_FRAMES_PER_BUFFER_RX*2)]
|
|
||||||
# add modulated chunks to fifo buffer
|
|
||||||
for c in chunk:
|
for c in chunk:
|
||||||
# if data is shorter than the expcected audio frames per buffer we need to append 0
|
if len(c) < chunk_length:
|
||||||
# to prevent the callback from stucking/crashing
|
delta = chunk_length - len(c)
|
||||||
if len(c) < self.AUDIO_FRAMES_PER_BUFFER_RX*2:
|
delta_zeros = np.zeros(delta, dtype=np.int16)
|
||||||
delta = bytes(self.AUDIO_FRAMES_PER_BUFFER_RX*2 - len(c))
|
c = np.append(c, delta_zeros)
|
||||||
c += delta
|
structlog.get_logger("structlog").debug("[TNC] mod out shorter than audio buffer", delta=delta)
|
||||||
structlog.get_logger("structlog").debug("[TNC] mod out shorter than audio buffer", delta=len(delta))
|
self.modoutqueue.append(c)
|
||||||
self.modoutqueue.put(c)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Release our mod_out_lock so we can use the queue
|
# Release our mod_out_lock so we can use the queue
|
||||||
self.mod_out_locked = False
|
self.mod_out_locked = False
|
||||||
|
|
||||||
while not self.modoutqueue.empty():
|
while self.modoutqueue:
|
||||||
pass
|
time.sleep(0.01)
|
||||||
|
|
||||||
static.PTT_STATE = self.hamlib.set_ptt(False)
|
static.PTT_STATE = self.hamlib.set_ptt(False)
|
||||||
|
|
||||||
|
@ -580,10 +557,6 @@ class RF():
|
||||||
# delete fft_buffer
|
# delete fft_buffer
|
||||||
self.fft_data = bytes()
|
self.fft_data = bytes()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# https://gist.github.com/ZWMiller/53232427efc5088007cab6feee7c6e4c
|
# https://gist.github.com/ZWMiller/53232427efc5088007cab6feee7c6e4c
|
||||||
audio_data = np.fromstring(data_in, np.int16)
|
audio_data = np.fromstring(data_in, np.int16)
|
||||||
# Fast Fourier Transform, 10*log10(abs) is to scale it to dB
|
# Fast Fourier Transform, 10*log10(abs) is to scale it to dB
|
||||||
|
|
Loading…
Reference in a new issue