This is a helper class providing a template for testing audio codec programs such as lame mp3 encoder, flac tools, ogg vorbis-tools, ... Signed-off-by: Julien Olivain <ju.o@free.fr> Signed-off-by: Thomas Petazzoni <thomas.petazzoni@bootlin.com>
87 lines
3.2 KiB
Python
87 lines
3.2 KiB
Python
import math
|
|
import os
|
|
|
|
import infra.basetest
|
|
|
|
|
|
class TestAudioCodecBase(infra.basetest.BRTest):
|
|
"""Common class to test an audio codec package.
|
|
|
|
This base class builds a Buildroot system image containing the
|
|
package enabled in its config, start the emulator, login to it. It
|
|
prepares an input test WAV file containing a tone. This WAV file
|
|
is encoded into a new output file in the native encoder format. It
|
|
is then decoded to a new output WAV file. This final output WAV
|
|
file is checked to contain the expected tone generated in the
|
|
initial input WAV file. There is no specific check about file
|
|
size, nor audio quality. The acceptance criteria is the
|
|
recognition of the tone. Note: the tone generation is made with
|
|
the Sox package, and the tone recognition is made with the Aubio
|
|
package.
|
|
|
|
Each test case that inherits from this class must have:
|
|
__test__ = True - to let nose2 know that it is a test case
|
|
config - defconfig fragment with the packages to run the test
|
|
encode_test() - the function that runs the encoder and produces an
|
|
encoded file.
|
|
decode_test() - the function that runs the decoder and produces a
|
|
decode file.
|
|
"""
|
|
__test__ = False
|
|
config = infra.basetest.BASIC_TOOLCHAIN_CONFIG + \
|
|
"""
|
|
BR2_PACKAGE_AUBIO=y
|
|
BR2_PACKAGE_SOX=y
|
|
BR2_TARGET_ROOTFS_CPIO=y
|
|
# BR2_TARGET_ROOTFS_TAR is not set
|
|
"""
|
|
input_file = "reference.wav"
|
|
decoded_file = "decoded.wav"
|
|
tone_freq = 440 # General Midi note A3
|
|
|
|
def login(self):
|
|
cpio_file = os.path.join(self.builddir, "images", "rootfs.cpio")
|
|
self.emulator.boot(arch="armv5",
|
|
kernel="builtin",
|
|
options=["-initrd", cpio_file])
|
|
self.emulator.login()
|
|
|
|
def test_run(self):
|
|
self.login()
|
|
self.prepare_data()
|
|
self.encode_test(self.input_file)
|
|
self.decode_test(self.decoded_file)
|
|
self.check_test()
|
|
|
|
def prepare_data(self):
|
|
# Generate a sinusoidal tone.
|
|
cmd = "sox -V -r 48000 -n -b 16 -c 1 "
|
|
cmd += self.input_file
|
|
cmd += f" synth 3 sin {self.tone_freq} vol -10dB"
|
|
self.assertRunOk(cmd)
|
|
|
|
def encode_test(self, input_filename):
|
|
msg = "method must be implemented in derived class"
|
|
raise NotImplementedError(msg)
|
|
|
|
def decode_test(self, output_filename):
|
|
msg = "method must be implemented in derived class"
|
|
raise NotImplementedError(msg)
|
|
|
|
def note_from_freq(self, freq):
|
|
"""Return a note number from the input frequency in Hertz."""
|
|
return round((12 * math.log(freq / 440) / math.log(2)) + 69)
|
|
|
|
def check_test(self):
|
|
expected_note = self.note_from_freq(self.tone_freq)
|
|
out, ret = self.emulator.run(f"aubionotes {self.decoded_file}", timeout=20)
|
|
self.assertEqual(ret, 0)
|
|
note_found = False
|
|
for line in out:
|
|
values = line.split()
|
|
if len(values) == 3:
|
|
note = round(float(values[0]))
|
|
if note == expected_note:
|
|
note_found = True
|
|
self.assertTrue(note_found, "The expected note was not found")
|