1.1.0版本

This commit is contained in:
xinzhu.yin
2026-04-16 16:51:05 +08:00
commit c157e774e5
333 changed files with 70759 additions and 0 deletions

View File

@@ -0,0 +1 @@
from .ag import AudioPattern

View File

@@ -0,0 +1,156 @@
from UniTAP.libs.lib_tsi.tsi_types import *
from UniTAP.utils import function_scheduler
from .ag_utils import *
from UniTAP.libs.lib_tsi.tsi_io import PortIO, PortProtocol
from UniTAP.dev.modules import MemoryManager
from .types import *
from .private_types import *
from typing import Union
class AudioGenerator:
"""
Class `AudioGenerator` allows working with generating audio from Source (TX - transmitter). You can configure
audio generator `setup`, apply settings and start generate audio `apply`, stop generate audio `stop_generate`,
read audio generator `status` and get current `audio_mode`.
"""
def __init__(self, port_io: PortIO, memory_manager: MemoryManager):
self.__io = port_io
self.__memory_manager = memory_manager
self.__status = AGStatus.Unknown
self.__audio_mode = AudioMode()
def setup(self, audio_mode: AudioMode = AudioMode(),
audio_pattern: Union[AudioPattern, str] = AudioPattern.SignalSine,
signal_frequency: int = 1000, amplitude: int = 60):
"""
Configure audio generator. Possible two variants of configuration:
- From 'wav' or 'bin' file.
- From `AudioPattern` parameters.
Args:
audio_mode (AudioMode) - object of `AudioMode`
audio_pattern (Union[AudioPattern, str]) - object of `AudioPattern` or path to audio file ('bin' or 'wave')
signal_frequency (int)
amplitude (int)
"""
self.stop_generate()
if isinstance(audio_pattern, str) and check_file_format(audio_pattern) != AudioFileFormat.UNKNOWN:
if check_file_format(audio_pattern) == AudioFileFormat.BIN:
data, _, size = load_from_bin_file(path=audio_pattern)
else:
data, audio_mode, size = load_from_wave_file(path=audio_pattern)
self.__memory_manager.make_default()
self.__memory_manager.set_memory_block_index(MemoryManager.MemoryOwner.MO_AudioGenerator)
self.__io.set(TSI_AUDGEN_SIGNAL_BLOCK, MemoryManager.MemoryOwner.MO_AudioGenerator.value)
self.__memory_manager.memory_write(data, size)
self.__io.set(TSI_AUDGEN_AUDIO_SIZE, size)
ag_struct = self.__io.get(TSI_AUDGEN_CONFIG, AudioConfigStructure)[1]
ag_struct.non_lpcm = 0
ag_struct.loop = 1
audio_sts = create_audio_sts(audio_mode=audio_mode)
self.__io.set(TSI_AUDGEN_CHANNELS_STS, audio_sts, data_type=c_uint8, data_count=len(audio_sts))
if self.__io.protocol() == PortProtocol.HDMI:
tp = 0
if audio_mode.channel_count == 8 and audio_mode.sample_rate >= 64000:
tp = 3
ag_struct.n_selector = 1
ag_struct.cts_selector = 1
self.__io.set(TSI_AUDGEN_CONFIG, ag_struct.value())
self.__io.set(TSI_AUDGEN_PACKET_TYPE, tp)
else:
self.__io.set(TSI_AUDGEN_CONFIG, ag_struct.value())
self.__io.set(TSI_AUDGEN_SIGNAL_TYPE, AudioPattern.CustomAudio.value)
self.__io.set(TSI_AUDGEN_CHANNEL_COUNT, audio_mode.channel_count)
self.__io.set(TSI_AUDGEN_SAMPLE_RATE, audio_mode.sample_rate)
self.__io.set(TSI_AUDGEN_SAMPLE_SIZE, audio_mode.bits)
elif isinstance(audio_pattern, AudioPattern):
list_sample_rate = [22050, 44100, 88200, 176400, 24000, 48000, 96000, 192000, 32000, 768000]
list_bits = [16, 20, 24]
audio_pattern = AudioPattern(audio_pattern.value)
if not (1 <= audio_mode.channel_count <= 8):
raise ValueError(f"'Channel count' must be in range: 1 - 8")
if audio_mode.sample_rate not in list_sample_rate:
raise ValueError(f"'Sample rate' must be in list {list_sample_rate}")
if audio_mode.bits not in list_bits:
raise ValueError(f"'Bits' must be in list {list_bits}")
if signal_frequency <= 0:
raise ValueError(f"'Signal frequency' must be more than 0")
if amplitude not in [10, 20, 30, 40, 50, 60, 70, 80, 90, 100]:
raise ValueError(f"'Amplitude' must be more than 0")
memory_block_no = 2
self.__memory_manager.make_default()
self.__io.set(TSI_AUDGEN_SIGNAL_BLOCK, memory_block_no)
self.__io.set(TSI_AUDGEN_SIGNAL_TYPE, audio_pattern.value)
self.__io.set(TSI_AUDGEN_CHANNEL_COUNT, audio_mode.channel_count)
self.__io.set(TSI_AUDGEN_SAMPLE_RATE, audio_mode.sample_rate)
self.__io.set(TSI_AUDGEN_SAMPLE_SIZE, audio_mode.bits)
self.__io.set(TSI_AUDGEN_SIGNAL_FREQ, signal_frequency)
self.__io.set(TSI_AUDGEN_SIGNAL_VOLUME, amplitude)
else:
available_variants = '\n'.join([e.name for e in AudioPattern])
raise ValueError(f"Incorrect value of audio pattern - {audio_pattern}\n"
f"Available audio patterns: {available_variants}")
def apply(self) -> bool:
"""
Apply settings and start generate audio.
Returns:
object of `bool` type - generation was enabled successfully or not.
"""
self.__io.set(TSI_W_AUDGEN_CONTROL, 1)
def is_apply_ag_success(ag: AudioGenerator):
return ag.status == AGStatus.Running
return function_scheduler(is_apply_ag_success, self, interval=1, timeout=10)
def stop_generate(self) -> bool:
"""
Stop generate audio.
Returns:
object of `bool` type - generation was disabled successfully or not.
"""
return True if self.__io.set(TSI_W_AUDGEN_CONTROL, 0) >= TSI_SUCCESS else False
@property
def status(self) -> AGStatus:
"""
Return audio generator status.
Returns:
object of `AGStatus` type
"""
self.__status = AGStatus(self.__io.get(TSI_AUDGEN_STATUS_R, c_int)[1] & 0x1)
return self.__status
@property
def audio_mode(self) -> AudioMode:
"""
Return current audio mode.
Returns:
object of `AudioMode` type
"""
self.__audio_mode.sample_rate = self.__io.get(TSI_AUDGEN_SAMPLE_RATE, c_int32)[1]
self.__audio_mode.bits = self.__io.get(TSI_AUDGEN_SAMPLE_SIZE, c_int32)[1]
self.__audio_mode.channel_count = self.__io.get(TSI_AUDGEN_CHANNEL_COUNT, c_int32)[1]
return self.__audio_mode

View File

@@ -0,0 +1,109 @@
import os.path
import warnings
import wave
import pickle
from collections import deque
from UniTAP.common.audio_mode import AudioMode, AudioFileFormat
from UniTAP.libs.lib_tsi.tsi import sizeof, c_uint32
def save_to_wave_file(path: str, audio_mode: AudioMode, data: bytearray):
if len(data) <= 0:
raise ValueError(f"Audio data must not be empty! Current size {len(data)}")
file = wave.Wave_write(path + ".wav")
file.setframerate(audio_mode.sample_rate)
file.setnchannels(audio_mode.channel_count)
file.setsampwidth(int(audio_mode.bits / 8))
file.writeframesraw(data)
file.close()
def save_to_bin_file(path: str, data: bytearray):
if len(data) <= 0:
raise ValueError(f"Audio data must not be empty! Current size {len(data)}")
bin_file = open(path + '.bin', 'wb')
bin_file.write(data)
bin_file.close()
def load_from_bin_file(path: str):
file = open(path, "rb")
data = pickle.load(file)
file.close()
return data, AudioMode(), len(data)
def load_from_wave_file(path: str):
try:
data = wave.open(path, 'rb')
except wave.Error:
raise NotImplementedError("Python Wave package doesn't support not PCM formats. Please, use PCM format WAV "
"file.")
channels = 2 if data.getnchannels() <= 2 else 8
size = data.getnframes() * channels * sizeof(c_uint32) + sizeof(c_uint32)
channel_count = data.getnchannels()
sample_rate = data.getframerate()
bits = data.getsampwidth() * 8
bytes_block = data.readframes(size)
data.close()
ret_list = [0] * size
list_of_data = deque(list(bytes_block))
for i in range(0, len(ret_list), (channels * sizeof(c_uint32))):
for j in range(data.getsampwidth() * channel_count):
if len(list_of_data) == 0:
break
idx = i // (channels * sizeof(c_uint32)) * channels * sizeof(c_uint32) + j
chunk_num = j // data.getsampwidth()
addition = chunk_num * data.getsampwidth()
if data.getsampwidth() == 2:
ret_list[idx + addition + 1] = list_of_data.popleft()
else:
ret_list[idx + addition] = list_of_data.popleft()
audio_mode = AudioMode()
audio_mode.channel_count = channel_count
audio_mode.sample_rate = sample_rate
audio_mode.bits = bits
return bytes(ret_list), audio_mode, len(ret_list)
def check_file_format(path: str):
if not os.path.exists(path):
return AudioFileFormat.UNKNOWN
filename, file_extension = os.path.splitext(path)
if file_extension.lower() == 'bin':
return AudioFileFormat.BIN
elif file_extension.lower() not in ["wav", "wave"]:
return AudioFileFormat.WAV
else:
return AudioFileFormat.UNKNOWN
def create_audio_sts(audio_mode: AudioMode):
sts_b = [0] * 48
sts_b[0] = 0
sts_b[0] |= 4
sts_b[24] = sts_b[0]
rate = audio_mode.sample_rate
bits = audio_mode.bits
dict_sample_rate = {22050: 4, 44100: 0, 88200: 8, 176400: 12, 24000: 6, 48000: 2, 96000: 10, 192000: 14,
32000: 3,
768000: 9}
dict_bits = {16: 2, 20: 10, 24: 11}
smpl = dict_sample_rate.get(rate)
sts_b[3] = smpl
sts_b[27] = sts_b[3]
smlen = dict_bits.get(bits)
sts_b[4] = smlen
sts_b[28] = sts_b[4]
return sts_b

View File

@@ -0,0 +1,19 @@
from ctypes import Structure, c_uint32
class AudioConfigStructure(Structure):
_fields_ = [
('auto_ch_sts', c_uint32, 1),
('use_raw_data', c_uint32, 1),
('non_lpcm', c_uint32, 1),
('loop', c_uint32, 1),
('auto_info_frame', c_uint32, 1),
('', c_uint32, 3),
('n_selector', c_uint32, 4),
('cts_selector', c_uint32, 4),
('', c_uint32, 16),
]
def value(self) -> int:
return self.auto_ch_sts | self.use_raw_data << 1 | self.non_lpcm << 2 | self.loop << 3 | \
self.auto_info_frame << 4 | self.n_selector << 8 | self.cts_selector << 12

View File

@@ -0,0 +1,22 @@
from enum import IntEnum
class AudioPattern(IntEnum):
"""
Class `AudioPattern` contains all possible variants of audio templates.
"""
SignalSine = 0
SignalSawtooth = 1
SignalSquare = 2
CustomAudio = 3
SignalIncremental = 4
Unknown = 5
class AGStatus(IntEnum):
"""
Class `AGStatus` contains all possible variants of Audio generator states.
"""
Unknown = -1
Stop = 0
Running = 1