redesign of buffer management

This commit is contained in:
dj2ls 2021-12-14 23:07:01 +01:00
parent 2642ae414b
commit b27563fe77
7 changed files with 158 additions and 54 deletions

View file

@ -74,5 +74,12 @@ add_test(NAME 001_highsnr_virtual2_P_P
./test_virtual2.sh")
set_tests_properties(001_highsnr_virtual2_P_P PROPERTIES PASS_REGULAR_EXPRESSION "RECEIVED BURSTS: 5 RECEIVED FRAMES: 10 RX_ERRORS: 0")
# lets Python do a multimode test
add_test(NAME 001_highsnr_virtual3_P_P_MM
COMMAND sh -c "export LD_LIBRARY_PATH=${CODEC2_BUILD_DIR}/src;
PATH=$PATH:${CODEC2_BUILD_DIR}/src;
cd ${CMAKE_CURRENT_SOURCE_DIR}/test/001_highsnr_stdio_audio;
./test_virtual_mm.sh")
set_tests_properties(001_highsnr_virtual3_P_P_MM PROPERTIES PASS_REGULAR_EXPRESSION "RECEIVED BURSTS: 5 RECEIVED FRAMES: 10 RX_ERRORS: 0")
endif()

View file

@ -15,7 +15,7 @@ import codec2
#--------------------------------------------GET PARAMETER INPUTS
parser = argparse.ArgumentParser(description='Simons TEST TNC')
parser.add_argument('--bursts', dest="N_BURSTS", default=0, type=int)
parser.add_argument('--framesperburst', dest="N_FRAMES_PER_BURST", default=0, 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")
parser.add_argument('--list', dest="LIST", action="store_true", help="list audio devices by number and exit")
@ -32,14 +32,17 @@ if args.LIST:
N_BURSTS = args.N_BURSTS
N_FRAMES_PER_BURST = args.N_FRAMES_PER_BURST
AUDIO_INPUT_DEVICE = args.AUDIO_INPUT_DEVICE
DEBUGGING_MODE = args.DEBUGGING_MODE
TIMEOUT = args.TIMEOUT
# AUDIO PARAMETERS
AUDIO_FRAMES_PER_BUFFER = 2048
AUDIO_FRAMES_PER_BUFFER = 4096
MODEM_SAMPLE_RATE = codec2.api.FREEDV_FS_8000
AUDIO_SAMPLE_RATE_RX = 48000
assert (AUDIO_SAMPLE_RATE_RX % MODEM_SAMPLE_RATE) == 0
BUF_SIZE = 160
# SET COUNTERS
rx_total_frames_datac0 = 0
rx_frames_datac0 = 0
@ -79,7 +82,19 @@ datac3_buffer = bytes()
# check if we want to use an audio device then do an pyaudio init
if AUDIO_INPUT_DEVICE != -1:
p = pyaudio.PyAudio()
# --------------------------------------------OPEN AUDIO CHANNEL RX
# auto search for loopback devices
if AUDIO_INPUT_DEVICE == -2:
loopback_list = []
for dev in range(0,p.get_device_count()):
if 'Loopback: PCM' in p.get_device_info_by_index(dev)["name"]:
loopback_list.append(dev)
if len(loopback_list) >= 2:
AUDIO_INPUT_DEVICE = loopback_list[0] #0 = RX 1 = TX
print(f"loopback_list rx: {loopback_list}", file=sys.stderr)
else:
quit()
print(f"AUDIO INPUT DEVICE: {AUDIO_INPUT_DEVICE} DEVICE: {p.get_device_info_by_index(AUDIO_INPUT_DEVICE)['name']} AUDIO SAMPLE RATE: {AUDIO_SAMPLE_RATE_RX}", file=sys.stderr)
stream_rx = p.open(format=pyaudio.paInt16,
channels=1,
rate=AUDIO_SAMPLE_RATE_RX,
@ -92,29 +107,30 @@ if AUDIO_INPUT_DEVICE != -1:
timeout = time.time() + TIMEOUT
receive = True
BUF_SIZE = 160
while receive and time.time() < timeout:
if AUDIO_INPUT_DEVICE != -1:
buf_size_converted = int(BUF_SIZE*(AUDIO_SAMPLE_RATE_RX/MODEM_SAMPLE_RATE))
data_in = stream_rx.read(buf_size_converted, exception_on_overflow=False)
data_in = audioop.ratecv(data_in,2,1,AUDIO_SAMPLE_RATE_RX, MODEM_SAMPLE_RATE, None)
data_in = data_in[0].rstrip(b'\x00')
data_in = stream_rx.read(AUDIO_FRAMES_PER_BUFFER, exception_on_overflow = False)
data_in = audioop.ratecv(data_in,2,1,AUDIO_SAMPLE_RATE_RX, MODEM_SAMPLE_RATE, None)
datac0_buffer += data_in[0]
datac1_buffer += data_in[0]
datac3_buffer += data_in[0]
else:
data_in = sys.stdin.buffer.read(BUF_SIZE)
datac0_buffer += data_in
datac1_buffer += data_in
datac3_buffer += data_in
datac0_buffer += data_in
datac1_buffer += data_in
datac3_buffer += data_in
datac0_nin = codec2.api.freedv_nin(datac0_freedv) * 2
datac0_nin = codec2.api.freedv_nin(datac0_freedv) * 2
if len(datac0_buffer) >= (datac0_nin):
datac0_audio = datac0_buffer[:datac0_nin]
nbytes = codec2.api.freedv_rawdatarx(datac0_freedv, datac0_bytes_out, datac0_buffer[:datac0_nin]) # demodulate audio
datac0_buffer = datac0_buffer[datac0_nin:]
nbytes = codec2.api.freedv_rawdatarx(datac0_freedv, datac0_bytes_out, datac0_audio) # demodulate audio
if nbytes == datac0_bytes_per_frame:
rx_total_frames_datac0 = rx_total_frames_datac0 + 1
rx_frames_datac0 = rx_frames_datac0 + 1
@ -126,9 +142,8 @@ while receive and time.time() < timeout:
datac1_nin = codec2.api.freedv_nin(datac1_freedv) * 2
if len(datac1_buffer) >= (datac1_nin):
datac1_audio = datac1_buffer[:datac1_nin]
nbytes = codec2.api.freedv_rawdatarx(datac1_freedv, datac1_bytes_out, datac1_buffer[:datac1_nin]) # demodulate audio
datac1_buffer = datac1_buffer[datac1_nin:]
nbytes = codec2.api.freedv_rawdatarx(datac1_freedv, datac1_bytes_out, datac1_audio) # demodulate audio
if nbytes == datac1_bytes_per_frame:
rx_total_frames_datac1 = rx_total_frames_datac1 + 1
rx_frames_datac1 = rx_frames_datac1 + 1
@ -139,9 +154,8 @@ while receive and time.time() < timeout:
datac3_nin = codec2.api.freedv_nin(datac3_freedv) * 2
if len(datac3_buffer) >= (datac3_nin):
datac3_audio = datac3_buffer[:datac3_nin]
nbytes = codec2.api.freedv_rawdatarx(datac3_freedv, datac3_bytes_out, datac3_buffer[:datac3_nin]) # demodulate audio
datac3_buffer = datac3_buffer[datac3_nin:]
nbytes = codec2.api.freedv_rawdatarx(datac3_freedv, datac3_bytes_out, datac3_audio) # demodulate audio
if nbytes == datac3_bytes_per_frame:
rx_total_frames_datac3 = rx_total_frames_datac3 + 1
rx_frames_datac3 = rx_frames_datac3 + 1
@ -151,6 +165,23 @@ while receive and time.time() < timeout:
rx_bursts_datac3 = rx_bursts_datac3 + 1
if DEBUGGING_MODE:
sync0 = codec2.api.freedv_get_rx_status(datac0_freedv)
sync1 = codec2.api.freedv_get_rx_status(datac1_freedv)
sync3 = codec2.api.freedv_get_rx_status(datac3_freedv)
datac0_rxstatus = codec2.api.freedv_get_rx_status(datac0_freedv)
datac0_state = codec2.api.rx_sync_flags_to_text[datac0_rxstatus]
datac1_rxstatus = codec2.api.freedv_get_rx_status(datac1_freedv)
datac1_state = codec2.api.rx_sync_flags_to_text[datac1_rxstatus]
datac3_rxstatus = codec2.api.freedv_get_rx_status(datac3_freedv)
datac3_state = codec2.api.rx_sync_flags_to_text[datac3_rxstatus]
print(f"NIN0: {datac0_nin} STATE0: {datac3_state} | NIN1: {datac1_nin} STATE1: {datac1_state} | NIN3: {datac3_nin} STATE3: {datac3_state}", file=sys.stderr)
if rx_bursts_datac0 == N_BURSTS and rx_bursts_datac1 == N_BURSTS and rx_bursts_datac3 == N_BURSTS:
receive = False
@ -159,3 +190,4 @@ if time.time() > timeout:
print(f"TIMEOUT REACHED", file=sys.stderr)
print(f"DATAC0: {rx_bursts_datac0}/{rx_total_frames_datac0} DATAC1: {rx_bursts_datac1}/{rx_total_frames_datac1} DATAC3: {rx_bursts_datac3}/{rx_total_frames_datac3}", file=sys.stderr)

View file

@ -46,8 +46,19 @@ assert (AUDIO_SAMPLE_RATE_TX % MODEM_SAMPLE_RATE) == 0
if AUDIO_OUTPUT_DEVICE != -1:
# pyaudio init
p = pyaudio.PyAudio()
# auto search for loopback devices
if AUDIO_OUTPUT_DEVICE == -2:
loopback_list = []
for dev in range(0,p.get_device_count()):
if 'Loopback: PCM' in p.get_device_info_by_index(dev)["name"]:
loopback_list.append(dev)
if len(loopback_list) >= 2:
AUDIO_OUTPUT_DEVICE = loopback_list[1] #0 = RX 1 = TX
print(f"loopback_list tx: {loopback_list}", file=sys.stderr)
else:
quit()
# pyaudio init
stream_tx = p.open(format=pyaudio.paInt16,
channels=1,
rate=AUDIO_SAMPLE_RATE_TX,
@ -116,7 +127,7 @@ for m in modes:
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)
audio = audioop.ratecv(txbuffer,2,1,MODEM_SAMPLE_RATE, AUDIO_SAMPLE_RATE_TX, None)
stream_tx.write(audio[0])
else:
@ -134,7 +145,7 @@ for m in modes:
# and at last check if we had an openend pyaudio instance and close it
if AUDIO_OUTPUT_DEVICE != 0:
if AUDIO_OUTPUT_DEVICE != -1:
stream_tx.close()
p.terminate()

View file

@ -27,6 +27,7 @@ parser.add_argument('--framesperburst', dest="N_FRAMES_PER_BURST", default=0, ty
parser.add_argument('--mode', dest="FREEDV_MODE", type=str, choices=['datac0', 'datac1', 'datac3'])
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")
parser.add_argument('--timeout', dest="TIMEOUT", default=10, type=int, help="Timeout (seconds) before test ends")
parser.add_argument('--list', dest="LIST", action="store_true", help="list audio devices by number and exit")
args = parser.parse_args()
@ -42,10 +43,10 @@ N_FRAMES_PER_BURST = args.N_FRAMES_PER_BURST
AUDIO_INPUT_DEVICE = args.AUDIO_INPUT_DEVICE
MODE = codec2.FREEDV_MODE[args.FREEDV_MODE].value
DEBUGGING_MODE = args.DEBUGGING_MODE
TIMEOUT = args.TIMEOUT
# AUDIO PARAMETERS
AUDIO_FRAMES_PER_BUFFER = 2048
AUDIO_FRAMES_PER_BUFFER = 2048 # seems to be best if >=1024
MODEM_SAMPLE_RATE = codec2.api.FREEDV_FS_8000
AUDIO_SAMPLE_RATE_RX = 48000
assert (AUDIO_SAMPLE_RATE_RX % MODEM_SAMPLE_RATE) == 0
@ -53,6 +54,19 @@ assert (AUDIO_SAMPLE_RATE_RX % MODEM_SAMPLE_RATE) == 0
# check if we want to use an audio device then do an pyaudio init
if AUDIO_INPUT_DEVICE != -1:
p = pyaudio.PyAudio()
# auto search for loopback devices
if AUDIO_INPUT_DEVICE == -2:
loopback_list = []
for dev in range(0,p.get_device_count()):
if 'Loopback: PCM' in p.get_device_info_by_index(dev)["name"]:
loopback_list.append(dev)
if len(loopback_list) >= 2:
AUDIO_INPUT_DEVICE = loopback_list[0] #0 = RX 1 = TX
print(f"loopback_list rx: {loopback_list}", file=sys.stderr)
else:
quit()
print(f"AUDIO INPUT DEVICE: {AUDIO_INPUT_DEVICE} DEVICE: {p.get_device_info_by_index(AUDIO_INPUT_DEVICE)['name']} AUDIO SAMPLE RATE: {AUDIO_SAMPLE_RATE_RX}", file=sys.stderr)
stream_rx = p.open(format=pyaudio.paInt16,
channels=1,
rate=AUDIO_SAMPLE_RATE_RX,
@ -83,37 +97,38 @@ rx_total_frames = 0
rx_frames = 0
rx_bursts = 0
rx_errors = 0
timeout = time.time() + 10
timeout = time.time() + TIMEOUT
receive = True
audio_buffer = bytes()
nbytes = 0
BUF_SIZE = 160
while receive and time.time() < timeout:
data_in = b''
if AUDIO_INPUT_DEVICE != -1:
nin = codec2.api.freedv_nin(freedv)
nin_converted = int(nin*(AUDIO_SAMPLE_RATE_RX/MODEM_SAMPLE_RATE))
if DEBUGGING_MODE == True:
print("nin: %5d nin_converted: %5d " % (nin,nin_converted), end='', file=sys.stderr)
data_in = stream_rx.read(nin_converted, exception_on_overflow = False)
data_in = stream_rx.read(AUDIO_FRAMES_PER_BUFFER, exception_on_overflow = False)
data_in = audioop.ratecv(data_in,2,1,AUDIO_SAMPLE_RATE_RX, MODEM_SAMPLE_RATE, None)
data_in = data_in[0].rstrip(b'\x00')
audio_buffer += data_in[0]
else:
nin = codec2.api.freedv_nin(freedv) * 2
if DEBUGGING_MODE == True:
print("nin: %5d " % (nin), end='', file=sys.stderr)
data_in = sys.stdin.buffer.read(nin)
nbytes = codec2.api.freedv_rawdatarx(freedv, bytes_out, data_in) # demodulate audio
total_n_bytes = total_n_bytes + nbytes
audio_buffer += sys.stdin.buffer.read(BUF_SIZE)
nin = codec2.api.freedv_nin(freedv) * 2
rx_status = codec2.api.freedv_get_rx_status(freedv)
if DEBUGGING_MODE == True:
rx_status_string = codec2.api.rx_sync_flags_to_text[rx_status]
print(f"rx_status: {rx_status_string}", file=sys.stderr)
rx_status = codec2.api.freedv_get_rx_status(freedv)
if DEBUGGING_MODE:
state = codec2.api.rx_sync_flags_to_text[rx_status]
print(f"NIN: {nin} STATE: {state} BUFFER: {len(audio_buffer)}", file=sys.stderr)
#decode as soon as we filled the audio buffer
if len(audio_buffer) >= (nin):
nbytes = codec2.api.freedv_rawdatarx(freedv, bytes_out, audio_buffer[:nin]) # demodulate audio
audio_buffer = audio_buffer[nin:]
total_n_bytes = total_n_bytes + nbytes
if nbytes == bytes_per_frame:
rx_total_frames = rx_total_frames + 1
rx_frames = rx_frames + 1
@ -124,7 +139,7 @@ while receive and time.time() < timeout:
if rx_status & codec2.api.FREEDV_RX_BIT_ERRORS:
rx_errors = rx_errors + 1
if rx_bursts == N_BURSTS:
receive = False

View file

@ -37,7 +37,6 @@ N_FRAMES_PER_BURST = args.N_FRAMES_PER_BURST
DELAY_BETWEEN_BURSTS = args.DELAY_BETWEEN_BURSTS/1000
AUDIO_OUTPUT_DEVICE = args.AUDIO_OUTPUT_DEVICE
MODE = codec2.FREEDV_MODE[args.FREEDV_MODE].value
@ -49,8 +48,19 @@ assert (AUDIO_SAMPLE_RATE_TX % MODEM_SAMPLE_RATE) == 0
# check if we want to use an audio device then do an pyaudio init
if AUDIO_OUTPUT_DEVICE != -1:
# pyaudio init
p = pyaudio.PyAudio()
# auto search for loopback devices
if AUDIO_OUTPUT_DEVICE == -2:
loopback_list = []
for dev in range(0,p.get_device_count()):
if 'Loopback: PCM' in p.get_device_info_by_index(dev)["name"]:
loopback_list.append(dev)
if len(loopback_list) >= 2:
AUDIO_OUTPUT_DEVICE = loopback_list[1] #0 = RX 1 = TX
print(f"loopback_list tx: {loopback_list}", file=sys.stderr)
else:
quit()
# pyaudio init
stream_tx = p.open(format=pyaudio.paInt16,
channels=1,
rate=AUDIO_SAMPLE_RATE_TX,
@ -115,7 +125,7 @@ for i in range(1,N_BURSTS+1):
txbuffer += bytes(mod_out)
print(f"BURST: {i}/{N_BURSTS} FRAME: {n}/{N_FRAMES_PER_BURST}", file=sys.stderr)
print(f"TX BURST: {i}/{N_BURSTS} FRAME: {n}/{N_FRAMES_PER_BURST}", file=sys.stderr)
# append postamble to txbuffer
codec2.api.freedv_rawdatapostambletx(freedv, mod_out_postamble)

View file

@ -18,8 +18,8 @@ RX_LOG=$(mktemp)
# make sure all child processes are killed when we exit
trap 'jobs -p | xargs -r kill' EXIT
python3 test_rx.py --mode datac0 --frames 2 --bursts 5 --audiodev 4 --debug &
python3 test_rx.py --mode datac0 --frames 2 --bursts 5 --audiodev -2 --debug &
rx_pid=$!
sleep 1
python3 test_tx.py --mode datac0 --frames 2 --bursts 5 --delay 500 --audiodev 5
python3 test_tx.py --mode datac0 --frames 2 --bursts 5 --delay 500 --audiodev -2
wait ${rx_pid}

View file

@ -0,0 +1,29 @@
#!/bin/bash -x
# Run a test using the virtual sound cards
function check_alsa_loopback {
lsmod | grep snd_aloop >> /dev/null
if [ $? -eq 1 ]; then
echo "ALSA loopback device not present. Please install with:"
echo
echo " sudo modprobe snd-aloop index=1,2 enable=1,1 pcm_substreams=1,1 id=CHAT1,CHAT2"
exit 1
fi
}
check_alsa_loopback
RX_LOG=$(mktemp)
# make sure all child processes are killed when we exit
trap 'jobs -p | xargs -r kill' EXIT
python3 test_multimode_rx.py --timeout 30 --framesperburst 2 --bursts 1 --audiodev -2 --debug &
rx_pid=$!
sleep 1
python3 test_multimode_tx.py --framesperburst 2 --bursts 1 --audiodev -2
#tail -f ${RX_LOG} | sed '/RECEIVED BURSTS/ q'
wait ${rx_pid}