# ----------------------------------------------------------------------------
#
#  Copyright (C) 2008-2022 Fons Adriaensen <fons@linuxaudio.org>
#    
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 3 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program.  If not, see <http:#www.gnu.org/licenses/>.
#
# ----------------------------------------------------------------------------


from time import sleep 
from zita_jacktools.jackclient import JackClient
from zita_jacktools import jacksignal_ext


class JackSignal(JackClient):

    """
    Jack client using numpy arrays for input and output.

    The main use of a JackSignal object is to generate
    arbitrary test signals and capture the results.
    There can be up to 100 inputs and outputs.
    In contrast to all other classes in zita_jacktools,
    a JackSignal is created without any inputs or outputs,
    these must be created explicitly while in the PASSIVE
    state.
    """

    TRIGGER = 9

    def __init__(self, client_name, server_name = None) :
        """
        Create a new JackSignal instance.

        The result should be checked using get_state(), if this is
        negative the only option is to delete the object.
        The optional 'server_name' allows to select between running
        Jack servers.
        """
        self._jsignal, base = jacksignal_ext.makecaps (self, client_name, server_name)
        super().__init__(base)
        

    def passive(self):
        """
        Set the state to PASSIVE.

        This is also the initial state after creation, unless it failed.
        In this state the object is a jack client, but the process callback
        does not access any ports. Use this state to create or delete ports.
        Connected output ports in PASSIVE state may output garbage.  
        """
        return jacksignal_ext.set_state (self._jsignal, JackSignal.PASSIVE)


    def silence(self) :
        """
        Set the state to SILENCE.

        In this state the object outputs silence and discards any
        input. Use this state to connect or disconnect ports and
        define signal buffers.
        """
        return jacksignal_ext.set_state (self._jsignal, JackSignal.SILENCE)


    def process(self):
        """
        Set the state to PROCESS.

        In this state the object outputs and records signals until
        the end of the all buffers is reached, taking looping into
        account if specified. At that point the object returns to
        the SILENCE state.
        """
        return jacksignal_ext.set_state (self._jsignal, JackSignal.PROCESS)


    def trigger(self, ind):
        """
        Select the trigger input and set the state to TRIGGER.

        In this state the object is waiting for an external start
        signal. The state will be set to PROCESS by the first sample
        on the trigger input that exceeds +0.5f. Playback and capture
        will start at the position of that sample.
        """
        return jacksignal_ext.set_state (self._jsignal, JackSignal.PROCESS)


    def get_position(self) :
        """
        Return state, frame count.

        The frame count is the number of frames processed
        since entering the process state.
        """
        return jacksignal_ext.get_posit (self._jsignal)


    def wait(self, dt = 0.1):
        """
        Wait while the state is TRIGGER or PROCESS,
        checking every 'dt' seconds.
        """
        actset =  (JackSignal.TRIGGER, JackClient.PROCESS)
        while self.get_state () in actset: sleep (dt); 

            
    def set_output_data(self, ind, data, **kwa):
        """
        Define an output signal.

        Set the playback buffer for the output port with index
        'ind' to 'data'. The 'data' must be a single dimension
        float32 array or array view. To reset, use 'None' as the
        'data' argument. The JackSignal object keeps a reference
        to the array buffer until it is reset or replaced.
        The keyword arguments can be:
        nskip (0): wait 'nskip' samples before starting playback.
        nloop (1): output the 'data' array 'nloop' times.
        """
        try: nloop = kwa ['nloop']
        except: nloop = 1
        try: nskip = kwa ['nskip']
        except: nskip = 0
        return jacksignal_ext.set_output_data (self._jsignal, ind, data, nloop, nskip)


    def set_input_data(self, ind, data, **kwa):
        """
        Define an input signal.

        Set the capture buffer for the input port with index
        'ind' to 'data'. The 'data' must be a single dimension
        float32 array or array view. To reset, use 'None' as the
        'data' argument. The JackSignal object keeps a reference
        to the array buffer until it is reset or replaced.
        The keyword arguments can be:
        nskip (0): wait 'nskip' samples before starting capture.
        nloop (1): write the 'data' array 'nloop' times.
        """
        try: nloop = kwa ['nloop']
        except: nloop = 1
        try: nskip = kwa ['nskip']
        except: nskip = 0
        return jacksignal_ext.set_input_data (self._jsignal, ind, data, nloop, nskip)

    
    def set_output_gain(self, ind, gain):
        """
        Set output gain.
        This can be used to calibrate a physical output.
        The default gain is 1.0.
        """
        return jacksignal_ext.set_output_gain (self._jsignal, ind, gain)

    
    def set_input_gain(self, ind, gain):
        """
        Set input gain.
        This can be used to calibrate a physical input.
        The default gain is 1.0.
        """
        return jacksignal_ext.set_input_gain (self._jsignal, ind, gain)


