Improved loopback test suite to support multiple configuration.

To run the test suite:

./run_test.sh sample_format sample_rate channels duration
Where:
    sample_format can be one of S16_LE, S24_3LE, S32_LE
    sample_rate can be one of 44100, 48000, 96000
    channels can be one of 1, 2, 4
    duration is in the range 1 to 10 minutes

The test suite creates a raw file with the specified parameters, runs a loopback test where the file gets played and recorded using the loopback network interface and checks that the recorded file, after the initial silence, contains the expected samples sequence.
This test was developed to further investigate the issue #17.
This commit is contained in:
Andrea Bondavalli 2021-01-31 18:26:41 +01:00
parent b34efd2a45
commit 10b5749d2a
7 changed files with 220 additions and 53 deletions

View File

@ -22,8 +22,8 @@ if ! [ -x "$(command -v arecord)" ]; then
exit 1
fi
if ! [ -x "$(command -v speaker-test)" ]; then
echo 'Error: speaker-test is not installed.' >&2
if ! [ -x "$(command -v aplay)" ]; then
echo 'Error: aplay is not installed.' >&2
exit 1
fi
@ -37,6 +37,59 @@ if ! [ -r "3rdparty/ravenna-alsa-lkm/driver/MergingRavennaALSA.ko" ]; then
exit 1
fi
cd test
echo 'Compiling tools ...' >&2
make
echo 'Creating test file ...' >&2
if ! ./createtest $1 $2 $3 $4 ; then
echo 'Usage run_test.sh sample_format sample_rate channels duration' >&2
echo ' sample_format can be one of S16_LE, S24_3LE, S32_LE' >&2
echo ' sample_rate can be one of 44100, 48000, 96000' >&2
echo ' channels can be one of 1, 2, 4' >&2
echo ' duration is in the range 1 to 10 minutes' >&2
exit 1
else
echo 'test file created' >&2
fi
cd ..
SAMPLE_FORMAT=$1
SAMPLE_RATE=$2
CHANNELS=$3
DURATION=$4
SEC=$((DURATION*60))
if [ $SAMPLE_FORMAT == "S16_LE" ]; then
CODEC="L16"
elif [ $SAMPLE_FORMAT == "S24_3LE" ]; then
CODEC="L24"
elif [ $SAMPLE_FORMAT == "S32_LE" ] ; then
CODEC="AM824"
fi
if [ $SAMPLE_RATE == "44100" ]; then
PTIME="1.08843537415"
elif [ $SAMPLE_RATE == "48000" ]; then
PTIME="1"
elif [ $SAMPLE_RATE == "96000" ]; then
PTIME="0.5"
fi
MAP="[ "
for (( ch=0; ch<$CHANNELS; ch++ ))
do
MAP+=$ch
if (( ch != ($CHANNELS - 1) )); then
MAP+=","
fi
done
MAP+=" ]"
echo 'Creating configuration files ..' >&2
sed 's/48000/'"$SAMPLE_RATE"'/g;s/status.json/status_.json/g;' test/daemon.conf > test/daemon_.conf
sed 's/\/2/\/'"$CHANNELS"'/g;s/48000/'"$SAMPLE_RATE"'/g;s/L24/'"$CODEC"'/g;s/ptime:1/ptime:'"$PTIME"'/;s/\[ 0, 1 \]/'"$MAP"'/g' test/status.json > test/status_.json
trap cleanup EXIT
#configure system parms
@ -52,47 +105,53 @@ if [ -x /usr/bin/pulseaudio ]; then
systemctl --user stop pulseaudio.sservice > /dev/null 2>&1
fi
#uninstall kernel module
if sudo lsmod | grep MergingRavennaALSA > /dev/null 2>&1 ; then
sudo rmmod MergingRavennaALSA
fi
#install kernel module
sudo insmod 3rdparty/ravenna-alsa-lkm/driver/MergingRavennaALSA.ko
cleanup
if [ -f ./test/sink_test.wav ] ; then
rm -f sink_test.wav
if [ -f ./test/sink_test.raw ] ; then
rm -f ./test/sink_test.raw
fi
echo "Starting PTP master ..."
sudo /usr/sbin/ptp4l -i lo -l7 -E -S &
#echo "Starting AES67 daemon ..."
./daemon/aes67-daemon -c ./test/daemon.conf &
#open browser on configuration page
if [ -x "$(command -v xdg-open)" ]; then
xdg-open "http://127.0.0.1:8080/PTP"
fi
echo "Starting AES67 daemon ..."
./daemon/aes67-daemon -c ./test/daemon_.conf &
echo "Waiting for PTP slave to sync ..."
sleep 30
#starting recording on sink
echo "Starting to record 240 secs from sink ..."
arecord -M -d 240 -D plughw:RAVENNA -f S24_3LE -r 48000 -c 2 -t wav /tmp/sink_test.wav > /dev/null 2>&1 &
#starting record on sink
echo "Starting to record $SEC sec from sink ..."
arecord -M -d $SEC -D plughw:RAVENNA -f $SAMPLE_FORMAT -r $SAMPLE_RATE -c $CHANNELS -t raw /tmp/sink_test.raw &
sleep 10
#starting playback on source
echo "Starting to playback test on source ..."
aplay -M -D plughw:RAVENNA ./test/test.wav > /dev/null 2>&1 &
aplay -M -D plughw:RAVENNA -f $SAMPLE_FORMAT -r $SAMPLE_RATE -c $CHANNELS ./test/test.raw > /dev/null 2>&1 &
while killall -0 arecord 2>/dev/null ; do
sleep 1
done
killall aplay
if [ -f /tmp/sink_test.wav ] ; then
mv /tmp/sink_test.wav test/
echo "Recording to file \"test/sink_test.wav\" successfull"
while killall -0 aplay 2>/dev/null ; do
sleep 1
done
if [ -f /tmp/sink_test.raw ] ; then
mv /tmp/sink_test.raw test/
echo "Recording to file \"test/sink_test.raw\" successfull"
else
echo "Recording failed"
fi
echo "Terminating processes ..."
echo "Test result:"
cd test
./check $SAMPLE_FORMAT $CHANNELS
cd ..
echo "Terminating processes ..."

8
test/Makefile Normal file
View File

@ -0,0 +1,8 @@
CXX=g++
CC=g++
all: check createtest
createtest: createtest.o
check: check.o
clean:
rm *.o
rm check createtest

View File

@ -4,10 +4,34 @@
using namespace std;
int main ()
int main(int argc, char* argv[])
{
if (argc < 3) {
cerr << "Usage: " << argv[0] << " sample_format channels" << endl;
exit(1);
}
int len(0);
string format(argv[1]);
if (format == "S16_LE")
len = 2;
else if (format == "S24_3LE")
len = 3;
else if (format == "S32_LE")
len = 4;
else {
cerr << "Unsupported format " << format << endl;
exit(1);
}
int channels = atoi(argv[2]);
if (channels != 1 && channels != 2 && channels != 4 && channels != 8) {
cerr << "Unsupported channels " << channels << endl;
exit(1);
}
fstream fl;
fl.open("./sink_test.wav", ios::in|ios::binary);
fl.open("./sink_test.raw", ios::in|ios::binary);
if (!fl.is_open()) {
cout << "cannot open file " << endl;
exit(1);
@ -15,36 +39,40 @@ int main ()
auto begin = fl.tellp();
fl.seekp(0, ios::end);
auto end = fl.tellp();
fl.seekp(68, ios::beg);
unsigned char ch = 0;
fl.seekp(0, ios::beg);
auto prev = fl.tellp();
while(fl.tellp() < end && ch == 0)
{
prev = fl.tellp();
ch = fl.get();
}
unsigned char curr = 0;
// skip inital silence
while((prev = fl.tellp()) != end && fl.get() == 0);
uint8_t curr = 0, byte = 0;
bool first = true;
fl.seekp(prev);
while(fl.tellp() < end)
{
while(fl.tellp() != end) {
int ch(channels);
while (ch--) {
if (!first) {
ch = fl.get();
if (ch != (uint8_t)curr) break;
byte = fl.get();
if (byte != (uint8_t)curr) goto end;
}
if (len > 1) {
byte = fl.get();
if (byte != (uint8_t)(curr+1)) goto end;
if (len > 2) {
byte = fl.get();
if (byte != (uint8_t)(curr+2)) goto end;
if (len > 3) {
byte = fl.get();
if (byte != (uint8_t)(curr+3)) goto end;
}
}
}
ch = fl.get();
if (ch != (uint8_t)(curr+1)) break;
ch = fl.get();
if (ch != (uint8_t)(curr+2)) break;
ch = fl.get();
if (ch != (uint8_t)curr) break;
ch = fl.get();
if (ch != (uint8_t)(curr+1)) break;
ch = fl.get();
if (ch != (uint8_t)(curr+2)) break;
curr += 3;
first = false;
}
curr += len;
}
end:
//cout << "expected " << (int)curr << " byte " << (int)byte << endl;
int rc = 0;
if (fl.tellp() != end)
{

70
test/createtest.cc Normal file
View File

@ -0,0 +1,70 @@
// basic file operations
#include <iostream>
#include <fstream>
using namespace std;
int main(int argc, char* argv[])
{
if (argc < 5) {
cerr << "Usage " << argv[0] << " sample_format sample_rate channels duration" << endl;
exit(1);
}
int len(0);
string format(argv[1]);
if (format == "S16_LE")
len = 2;
else if (format == "S24_3LE")
len = 3;
else if (format == "S32_LE")
len = 4;
else {
cerr << "Unsupported format " << format << endl;
exit(1);
}
int rate(atoi(argv[2]));
if (rate != 44100 && rate != 48000 && rate != 96000) {
cerr << "Unsupported rate " << rate << endl;
exit(1);
}
int channels = atoi(argv[3]);
if (channels != 1 && channels != 2 && channels != 4 && channels != 8) {
cerr << "Unsupported channels " << channels << endl;
exit(1);
}
int duration = atoi(argv[4]);
if (duration > 10 || duration < 1) {
cerr << "Unsupported duration " << duration << " minutes" << endl;
exit(1);
}
int secs(duration * 60);
unsigned char byte(0);
fstream myfile;
myfile.open("test.raw", ios::out|ios::binary);
while(secs--) {
int samples(rate);
while (samples--) {
int ch(channels);
while (ch--) {
myfile.put(byte);
if (len > 1) {
myfile.put(byte+1);
if (len > 2) {
myfile.put(byte+2);
if (len > 3)
myfile.put(byte+3);
}
}
}
byte+=len;
}
}
myfile.close();
return 0;
}

View File

@ -4,19 +4,21 @@
"http_base_dir": "./webui/build",
"log_severity": 3,
"playout_delay": 0,
"tic_frame_size_at_1fs": 192,
"tic_frame_size_at_1fs": 48,
"max_tic_frame_size": 1024,
"sample_rate": 48000,
"rtp_mcast_base": "239.1.0.1",
"rtp_port": 5004,
"ptp_domain": 0,
"ptp_dscp": 48,
"sap_mcast_addr": "224.2.127.254",
"sap_interval": 30,
"syslog_proto": "none",
"syslog_server": "255.255.255.254:1234",
"status_file": "./demo/status.json",
"mdns_enabled": false,
"status_file": "./test/status.json",
"interface_name": "lo",
"mac_addr": "01:00:5e:01:00:01",
"ip_addr": "127.0.0.1"
"mdns_enabled": false,
"mac_addr": "00:00:00:00:00:00",
"ip_addr": "127.0.0.1",
"node_id": "AES67 daemon 007f0100"
}

View File

@ -21,7 +21,7 @@
"use_sdp": true,
"source": "http://127.0.0.1:8080/api/source/sdp/0",
"sdp": "v=0\no=- 657664 657666 IN IP4 127.0.0.1\ns=AES67 daemon 000a0900 ALSA Source 0\nc=IN IP4 239.1.0.1/15\nt=0 0\na=clock-domain:PTPv2 0\nm=audio 5004 RTP/AVP 98\nc=IN IP4 239.1.0.1/15\na=rtpmap:98 L24/48000/2\na=sync-time:0\na=framecount:48\na=ptime:1\na=mediaclk:direct=0\na=ts-refclk:ptp=IEEE1588-2008:00-1D-C1-FF-FE-50-36-33:0\na=recvonly\n",
"delay": 576,
"delay": 960,
"ignore_refclk_gmid": true,
"map": [ 0, 1 ]
} ]

Binary file not shown.