# Copyright 2024-2025 XMOS LIMITED.
# This Software is subject to the terms of the XMOS Public Licence: Version 1.
import os
from filelock import FileLock
import numpy as np
from audio_dsp.dsp.utils import Q_max
from audio_dsp.dsp.generic import Q_SIG

def float_to_qxx(arr_float, q = Q_SIG, dtype = np.int32):
  arr_int32 = np.clip((np.array(arr_float) * Q_max(q)), np.iinfo(dtype).min, np.iinfo(dtype).max).astype(dtype)
  return arr_int32


def qxx_to_float(arr_int, q = Q_SIG):
  arr_float = np.array(arr_int).astype(np.float64) / Q_max(q)
  return arr_float


def q_convert_flt(arr_float, old_q, new_q, dtype = np.int32):
    # change the Q format of a floating point array.
    # Since the max changes by (2**old - 1)/(2**new - 1), it is not quite
    # a multiple of 2 change.
    arr_int32 = np.clip((np.array(arr_float) * Q_max(old_q)), np.iinfo(dtype).min, np.iinfo(dtype).max).astype(dtype)
    arr_int32 <<= (new_q - old_q)
    out_flt = arr_int32 / float(Q_max(new_q))
    return out_flt


def xdist_safe_bin_write(sig_int, sig_path):
  # write a integer signal (sig_int) to sig_path in a multithread safe way
  # If sig_path already exists, it will only be overwritten if running without
  # xdist
  worker_id = os.environ.get("PYTEST_XDIST_WORKER")
  with FileLock(str(sig_path) + ".lock"):
    # only write the file if it doesn't exist, or we're running single threaded
    if not sig_path.is_file() or worker_id is None:
      sig_int.tofile(sig_path)


def assert_allclose(actual, desired, rtol=1e-07, atol=0):
  # numpy assert_all close, but excluding zero values (which break rtol)
  np.testing.assert_allclose(actual[desired!=0], desired[desired!=0], rtol=rtol, atol=atol)