mirror of
https://github.com/DJ2LS/FreeDATA
synced 2024-05-14 08:04:33 +00:00
adding resampler to MM tests. still occasional fails on virtual card Pyaudio tests
This commit is contained in:
parent
03dccc4348
commit
2bf5c44cbb
|
@ -15,7 +15,7 @@ import numpy as np
|
|||
|
||||
#--------------------------------------------GET PARAMETER INPUTS
|
||||
parser = argparse.ArgumentParser(description='Simons TEST TNC')
|
||||
parser.add_argument('--bursts', dest="N_BURSTS", default=0, type=int)
|
||||
parser.add_argument('--bursts', dest="N_BURSTS", default=1, type=int)
|
||||
parser.add_argument('--framesperburst', dest="N_FRAMES_PER_BURST", default=1, type=int)
|
||||
parser.add_argument('--audiodev', dest="AUDIO_INPUT_DEVICE", default=-1, type=int, help="audio device number to use")
|
||||
parser.add_argument('--debug', dest="DEBUGGING_MODE", action="store_true")
|
||||
|
@ -37,10 +37,11 @@ DEBUGGING_MODE = args.DEBUGGING_MODE
|
|||
TIMEOUT = args.TIMEOUT
|
||||
|
||||
# AUDIO PARAMETERS
|
||||
AUDIO_FRAMES_PER_BUFFER = 2048
|
||||
AUDIO_FRAMES_PER_BUFFER = 2400*2
|
||||
MODEM_SAMPLE_RATE = codec2.api.FREEDV_FS_8000
|
||||
AUDIO_SAMPLE_RATE_RX = 8000
|
||||
assert (AUDIO_SAMPLE_RATE_RX % MODEM_SAMPLE_RATE) == 0
|
||||
AUDIO_SAMPLE_RATE_RX = 48000
|
||||
# make sure our resampler will work
|
||||
assert (AUDIO_SAMPLE_RATE_RX / MODEM_SAMPLE_RATE) == codec2.api.FDMDV_OS_48
|
||||
|
||||
# SET COUNTERS
|
||||
rx_total_frames_datac0 = 0
|
||||
|
@ -78,6 +79,8 @@ datac3_bytes_out = create_string_buffer(datac3_bytes_per_frame * 2)
|
|||
codec2.api.freedv_set_frames_per_burst(datac3_freedv,N_FRAMES_PER_BURST)
|
||||
datac3_buffer = codec2.audio_buffer(2*AUDIO_FRAMES_PER_BUFFER)
|
||||
|
||||
resampler = codec2.resampler()
|
||||
|
||||
# check if we want to use an audio device then do an pyaudio init
|
||||
if AUDIO_INPUT_DEVICE != -1:
|
||||
p = pyaudio.PyAudio()
|
||||
|
@ -106,6 +109,7 @@ if AUDIO_INPUT_DEVICE != -1:
|
|||
timeout = time.time() + TIMEOUT
|
||||
print(time.time(),TIMEOUT, timeout)
|
||||
receive = True
|
||||
nread_exceptions = 0
|
||||
|
||||
# initial nin values
|
||||
datac0_nin = codec2.api.freedv_nin(datac0_freedv)
|
||||
|
@ -129,13 +133,23 @@ def print_stats():
|
|||
|
||||
while receive and time.time() < timeout:
|
||||
if AUDIO_INPUT_DEVICE != -1:
|
||||
data_in = stream_rx.read(AUDIO_FRAMES_PER_BUFFER, exception_on_overflow = False)
|
||||
else:
|
||||
data_in = sys.stdin.buffer.read(AUDIO_FRAMES_PER_BUFFER*2)
|
||||
|
||||
x = np.frombuffer(data_in, dtype=np.int16)
|
||||
if len(x) == 0:
|
||||
try:
|
||||
data_in48k = stream_rx.read(AUDIO_FRAMES_PER_BUFFER, exception_on_overflow = True)
|
||||
except OSError as err:
|
||||
print(err, file=sys.stderr)
|
||||
if str(err).find("Input overflowed"):
|
||||
nread_exceptions += 1
|
||||
if str(err).find("Stream closed"):
|
||||
receive = False
|
||||
else:
|
||||
data_in48k = sys.stdin.buffer.read(AUDIO_FRAMES_PER_BUFFER*2)
|
||||
|
||||
# insert samples in buffer
|
||||
x = np.frombuffer(data_in48k, dtype=np.int16)
|
||||
if len(x) != AUDIO_FRAMES_PER_BUFFER:
|
||||
receive = False
|
||||
x = resampler.resample48_to_8(x)
|
||||
|
||||
datac0_buffer.push(x)
|
||||
datac1_buffer.push(x)
|
||||
datac3_buffer.push(x)
|
||||
|
@ -186,6 +200,9 @@ while receive and time.time() < timeout:
|
|||
if rx_bursts_datac0 == N_BURSTS and rx_bursts_datac1 == N_BURSTS and rx_bursts_datac3 == N_BURSTS:
|
||||
receive = False
|
||||
|
||||
if nread_exceptions:
|
||||
print("nread_exceptions %d - receive audio lost! Consider increasing Pyaudio frames_per_buffer..." % \
|
||||
nread_exceptions, file=sys.stderr)
|
||||
# INFO IF WE REACHED TIMEOUT
|
||||
if time.time() > timeout:
|
||||
print(f"TIMEOUT REACHED", file=sys.stderr)
|
||||
|
|
|
@ -13,7 +13,7 @@ import argparse
|
|||
import sys
|
||||
sys.path.insert(0,'..')
|
||||
import codec2
|
||||
|
||||
import numpy as np
|
||||
|
||||
# GET PARAMETER INPUTS
|
||||
parser = argparse.ArgumentParser(description='FreeDATA TEST')
|
||||
|
@ -38,9 +38,9 @@ AUDIO_OUTPUT_DEVICE = args.AUDIO_OUTPUT_DEVICE
|
|||
|
||||
|
||||
# AUDIO PARAMETERS
|
||||
AUDIO_FRAMES_PER_BUFFER = 2048
|
||||
AUDIO_FRAMES_PER_BUFFER = 2400
|
||||
MODEM_SAMPLE_RATE = codec2.api.FREEDV_FS_8000
|
||||
AUDIO_SAMPLE_RATE_TX = 8000
|
||||
AUDIO_SAMPLE_RATE_TX = 48000
|
||||
assert (AUDIO_SAMPLE_RATE_TX % MODEM_SAMPLE_RATE) == 0
|
||||
|
||||
if AUDIO_OUTPUT_DEVICE != -1:
|
||||
|
@ -65,6 +65,7 @@ if AUDIO_OUTPUT_DEVICE != -1:
|
|||
output_device_index=AUDIO_OUTPUT_DEVICE,
|
||||
)
|
||||
|
||||
resampler = codec2.resampler()
|
||||
modes = [codec2.api.FREEDV_MODE_DATAC0, codec2.api.FREEDV_MODE_DATAC1, codec2.api.FREEDV_MODE_DATAC3]
|
||||
for m in modes:
|
||||
|
||||
|
@ -120,20 +121,19 @@ for m in modes:
|
|||
mod_out_silence = create_string_buffer(samples_delay*2)
|
||||
txbuffer += bytes(mod_out_silence)
|
||||
|
||||
# resample up to 48k (resampler works on np.int16)
|
||||
x = np.frombuffer(txbuffer, dtype=np.int16)
|
||||
txbuffer_48k = resampler.resample8_to_48(x)
|
||||
|
||||
# check if we want to use an audio device or stdout
|
||||
if AUDIO_OUTPUT_DEVICE != -1:
|
||||
|
||||
# sample rate conversion from 8000Hz to 48000Hz
|
||||
#audio = audioop.ratecv(txbuffer,2,1,MODEM_SAMPLE_RATE, AUDIO_SAMPLE_RATE_TX, None)
|
||||
stream_tx.write(txbuffer)
|
||||
|
||||
stream_tx.write(txbuffer_48k.tobytes())
|
||||
else:
|
||||
|
||||
# this test needs a lot of time, so we are having a look at times...
|
||||
starttime = time.time()
|
||||
|
||||
# print data to terminal for piping the output to other programs
|
||||
sys.stdout.buffer.write(txbuffer)
|
||||
sys.stdout.buffer.write(txbuffer_48k)
|
||||
sys.stdout.flush()
|
||||
|
||||
# and at least print the needed time to see which time we needed
|
||||
|
|
|
@ -120,14 +120,16 @@ while receive and time.time() < timeout:
|
|||
data_in48k = stream_rx.read(AUDIO_FRAMES_PER_BUFFER, exception_on_overflow = True)
|
||||
except OSError as err:
|
||||
print(err, file=sys.stderr)
|
||||
if str(err).find("Input overflowed"):
|
||||
nread_exceptions += 1
|
||||
if str(err).find("Stream closed"):
|
||||
receive = False
|
||||
else:
|
||||
data_in48k = sys.stdin.buffer.read(AUDIO_FRAMES_PER_BUFFER*2)
|
||||
|
||||
# insert samples in buffer
|
||||
x = np.frombuffer(data_in48k, dtype=np.int16)
|
||||
x.tofile(frx)
|
||||
|
||||
if len(x) != AUDIO_FRAMES_PER_BUFFER:
|
||||
receive = False
|
||||
x = resampler.resample48_to_8(x)
|
||||
|
|
|
@ -72,7 +72,6 @@ if AUDIO_OUTPUT_DEVICE != -1:
|
|||
)
|
||||
|
||||
|
||||
AUDIO_FRAMES_PER_BUFFER_8K = int(AUDIO_FRAMES_PER_BUFFER/codec2.api.FDMDV_OS_48)
|
||||
resampler = codec2.resampler()
|
||||
|
||||
# data binary string
|
||||
|
|
|
@ -25,7 +25,7 @@ trap myInterruptHandler SIGINT
|
|||
# make sure all child processes are killed when we exit
|
||||
trap 'jobs -p | xargs -r kill' EXIT
|
||||
|
||||
python3 test_multimode_rx.py --timeout 60 --framesperburst 2 --bursts 2 --audiodev -2 &
|
||||
python3 test_multimode_rx.py --timeout 60 --framesperburst 2 --bursts 2 --audiodev -2 --debug &
|
||||
rx_pid=$!
|
||||
sleep 1
|
||||
python3 test_multimode_tx.py --framesperburst 2 --bursts 2 --audiodev -2 --delay 500
|
||||
|
|
|
@ -20,25 +20,6 @@ class FREEDV_MODE(Enum):
|
|||
def FREEDV_GET_MODE(mode):
|
||||
return FREEDV_MODE[mode].value
|
||||
|
||||
class audio_buffer:
|
||||
# a buffer of int16 samples, using a fixed length numpy array self.buffer for storage
|
||||
# self.nbuffer is the current number of samples in the buffer
|
||||
def __init__(self, size):
|
||||
print("create audio_buffer: ", size)
|
||||
self.size = size
|
||||
self.buffer = np.zeros(size, dtype=np.int16)
|
||||
self.nbuffer = 0
|
||||
def push(self,samples):
|
||||
# add samples at the end of the buffer
|
||||
assert self.nbuffer+len(samples) <= self.size
|
||||
self.buffer[self.nbuffer:self.nbuffer+len(samples)] = samples
|
||||
self.nbuffer += len(samples)
|
||||
def pop(self,size):
|
||||
# remove samples from the start of the buffer
|
||||
self.nbuffer -= size;
|
||||
self.buffer[:self.nbuffer] = self.buffer[size:size+self.nbuffer]
|
||||
assert self.nbuffer >= 0
|
||||
|
||||
# LOAD FREEDV
|
||||
libname = "libcodec2.so"
|
||||
api = ctypes.CDLL(libname)
|
||||
|
@ -120,6 +101,26 @@ api.rx_sync_flags_to_text = [
|
|||
"EBS-",
|
||||
"EBST"]
|
||||
|
||||
# audio buffer ---------------------------------------------------------
|
||||
|
||||
class audio_buffer:
|
||||
# a buffer of int16 samples, using a fixed length numpy array self.buffer for storage
|
||||
# self.nbuffer is the current number of samples in the buffer
|
||||
def __init__(self, size):
|
||||
print("create audio_buffer: ", size)
|
||||
self.size = size
|
||||
self.buffer = np.zeros(size, dtype=np.int16)
|
||||
self.nbuffer = 0
|
||||
def push(self,samples):
|
||||
# add samples at the end of the buffer
|
||||
assert self.nbuffer+len(samples) <= self.size
|
||||
self.buffer[self.nbuffer:self.nbuffer+len(samples)] = samples
|
||||
self.nbuffer += len(samples)
|
||||
def pop(self,size):
|
||||
# remove samples from the start of the buffer
|
||||
self.nbuffer -= size;
|
||||
self.buffer[:self.nbuffer] = self.buffer[size:size+self.nbuffer]
|
||||
assert self.nbuffer >= 0
|
||||
|
||||
# resampler ---------------------------------------------------------
|
||||
|
||||
|
@ -130,8 +131,7 @@ api.fdmdv_8_to_48_short.argtype = [c_void_p, c_void_p, c_int]
|
|||
api.fdmdv_48_to_8_short.argtype = [c_void_p, c_void_p, c_int]
|
||||
|
||||
class resampler:
|
||||
# a buffer of int16 samples, using a fixed length numpy array self.buffer for storage
|
||||
# self.nbuffer is the current number of samples in the buffer
|
||||
# resample an array of variable length, we just store the filter memories here
|
||||
MEM8 = api.FDMDV_OS_TAPS_48_8K
|
||||
MEM48 = api.FDMDV_OS_TAPS_48K
|
||||
|
||||
|
@ -142,7 +142,7 @@ class resampler:
|
|||
|
||||
def resample48_to_8(self,in48):
|
||||
assert in48.dtype == np.int16
|
||||
# length of input vector must be an interger multiple of api.FDMDV_OS_48
|
||||
# length of input vector must be an integer multiple of api.FDMDV_OS_48
|
||||
assert(len(in48) % api.FDMDV_OS_48 == 0)
|
||||
|
||||
# concat filter memory and input samples
|
||||
|
@ -150,11 +150,14 @@ class resampler:
|
|||
in48_mem[:self.MEM48] = self.filter_mem48
|
||||
in48_mem[self.MEM48:] = in48
|
||||
|
||||
# In C: pin48=&in48[MEM48]
|
||||
pin48,flag = in48_mem.__array_interface__['data']
|
||||
pin48 += 2*self.MEM48
|
||||
n8 = int(len(in48) / api.FDMDV_OS_48)
|
||||
out8 = np.zeros(n8, dtype=np.int16)
|
||||
api.fdmdv_48_to_8_short(out8.ctypes, pin48, n8);
|
||||
|
||||
# store memory for next time
|
||||
self.filter_mem48 = in48_mem[:self.MEM48]
|
||||
|
||||
return out8
|
||||
|
@ -167,10 +170,13 @@ class resampler:
|
|||
in8_mem[:self.MEM8] = self.filter_mem8
|
||||
in8_mem[self.MEM8:] = in8
|
||||
|
||||
# In C: pin8=&in8[MEM8]
|
||||
pin8,flag = in8_mem.__array_interface__['data']
|
||||
pin8 += 2*self.MEM8
|
||||
out48 = np.zeros(api.FDMDV_OS_48*len(in8), dtype=np.int16)
|
||||
api.fdmdv_8_to_48_short(out48.ctypes, pin8, len(in8));
|
||||
|
||||
# store memory for next time
|
||||
self.filter_mem8 = in8_mem[:self.MEM8]
|
||||
|
||||
return out48
|
||||
|
|
Loading…
Reference in a new issue