<commit_before>/*
 * Copyright (C) 2011 <NAME>' Lauer <<EMAIL>>
 * Copyright (C) 2011 Denis 'GNUtoo' Carikli <<EMAIL>>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.

 * This library 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
 * Lesser General Public License for more details.

 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 *
 */
extern int snd_pcm_format_set_silence (Alsa.PcmFormat format,void * data, uint samples );


/**
 * @class RingBuffer
 *
 * Provides a ring buffer for alsa frames
 **/
errordomain RingError {
    Overflow,
    Underflow
}

class RingBuffer : GLib.Object
{
    private uint8[] ring;
    private int ring_head;
    private int ring_tail;
    private int ring_size;
    
    public RingBuffer( int size )
    {
        this.ring = new uint8[size];
        this.ring_head = 0;
        this.ring_tail = 0;
        this.ring_size = size;
    }

    public void write( uint8[] x, int count ) throws RingError
    {
        int new_ring_head = (ring_head + count) % ring_size;
        int free = (ring_size + ring_tail - ring_head) % ring_size;
        if ( free == 0 )
            free = ring_size;
        stderr.printf( "RingBuffer.write: ring_head=%d, ring_tail=%d, count=%d, free=%d\n", ring_head, ring_tail, count, free );
        if ( count > free )
        {
            throw new RingError.Overflow( @"Buffer is full (free: $free / wanted to write: $count)" );
        }		 

        stderr.printf( "RingBuffer.write: new ring_head would be %d\n", new_ring_head );
        /* check wraparound */
        if ( new_ring_head == 0 || new_ring_head > ring_head )
        {
            /* check next line for +-1 errors I made! */
            stdout.printf( "RingBuffer.write: does not overlap - chunk = %d\n", count );
            Memory.copy( &ring[ring_head], x, count );
        }
        else
        {
            stdout.printf( "RingBuffer.write: does overlap - first chunk = %d\n", ring_size - ring_head );
            /* check next 2 lines for +-1 errors I made! */
            Memory.copy( &ring[ring_head], x, ring_size - ring_head );
            stdout.printf( "RingBuffer.write: second chunk = %d\n", ring_size - ring_head );
            Memory.copy( &ring[0], &x[ring_size - ring_head], count - (ring_size - ring_head) );
        }
        ring_head = new_ring_head;
    }

    /* we pass the pointer to buffer as a parameter, so you can use existing buffers */
    /* wouldn't want the function to malloc a new buffer each time */
    public void read( uint8[] x, int count ) throws RingError
    {
        int avail = (ring_size + ring_head - ring_tail) % ring_size;

        stderr.printf( "RingBuffer.read: ring_head=%d, ring_tail=%d, count=%d, avail=%d\n", ring_head, ring_tail, count, avail );
        if ( avail < count )
        {
            throw new RingError.Underflow( @"Buffer has only $avail bytes available ($count requested)" );
        }

        int new_ring_tail = (ring_tail + count) % ring_size;
        stderr.printf( "RingBuffer.read: new_ring_tail would be %d\n", new_ring_tail );

        if ( new_ring_tail == 0 || new_ring_tail > ring_tail )
        {
            stderr.printf( "RingBuffer.read: does not wrap - chunk = %d\n", count );
            Memory.copy( x, &ring[ring_tail], count );
        }
        else
        {
            stderr.printf( "RingBuffer.read: does overwrap - first chunk = %d\n", ring_size - ring_tail );
            Memory.copy( x, &ring[ring_tail], ring_size - ring_tail );
            stderr.printf( "RingBuffer.read: second chunk = %d\n", count - (ring_size - ring_tail) );
            Memory.copy( &x[ring_size - ring_tail], ring, count - (ring_size - ring_tail) );
        }
        ring_tail = new_ring_tail;
    }

    public void reset()
    {
        ring_tail = ring_head = 0;
    }
}



/**
 * @class CmtHandler
 *
 * Handles Audio via libcmtspeechdata
 **/
public class CmtHandler : FsoFramework.AbstractObject
{
    private CmtSpeech.Connection connection;
    private IOChannel channel;
    private FsoAudio.PcmDevice pcmout = null;
    private FsoAudio.PcmDevice pcmin = null;
    private bool status;
    private const int FCOUNT = 160;
    private const int FRAMESIZE = 2;
    private const int BUFSIZE = 3 * FCOUNT * FRAMESIZE;

    /* alsa parameters */
    private Alsa.PcmFormat format = Alsa.PcmFormat.S16_LE;
    private Alsa.PcmAccess access = Alsa.PcmAccess.RW_INTERLEAVED;

    /* silence buffer */
    private uint8[] silence_buffer = null;

    /* playback Thread */
    private unowned Thread<void *> playbackThread = null;
    private int runPlaybackThread = 0;
    private Mutex playbackMutex = new Mutex();
    private RingBuffer fromModem;

    /* record Thread */
    private unowned Thread<void *> recordThread = null;
    private bool runRecordThread = false;
    private Mutex recordMutex = new Mutex();
    private RingBuffer toModem;
    private int timing; // feed UL to the modem (in ms)

    //
    // Constructor
    //
    public CmtHandler()
    {
        status = false;

        assert( logger.debug( "Initializing cmtspeech" ) );
        CmtSpeech.init();

        assert( logger.debug( "Setting up traces" ) );
        CmtSpeech.trace_toggle( CmtSpeech.TraceType.STATE_CHANGE, true );
        CmtSpeech.trace_toggle( CmtSpeech.TraceType.IO, true );
        CmtSpeech.trace_toggle( CmtSpeech.TraceType.DEBUG, true );

        assert( logger.debug( "Instanciating connection" ) );
        connection = new CmtSpeech.Connection();
        if ( connection == null )
        {
            logger.error( "Can't instanciate connection" );
            return;
        }

        var fd = connection.descriptor();

        if ( fd == -1 )
        {
            logger.error( "Cmtspeech file descriptor invalid" );
        }

        timing = 50;
        fromModem = new RingBuffer( BUFSIZE );
        toModem = new RingBuffer( BUFSIZE );

        assert( logger.debug( "Hooking up fd with main loop" ) );
        channel = new IOChannel.unix_new( fd );
        channel.add_watch( IOCondition.IN | IOCondition.HUP, onInputFromChannel );

        logger.info( "Created" );
    }

    //
    // Private API
    //

    private void play_silence( int count )
    {
        Alsa.PcmSignedFrames ret;
        int retries = 3;

        while ( count > 0 && retries > 0 )
        {
            try
            {
                ret = pcmout.writei( silence_buffer, FCOUNT );
                if ( ret == -Posix.EPIPE )
                {
                    pcmout.recover( -Posix.EPIPE, 0 );
                    retries--;
                }
                else
                {
                    count--;
                }
            }
            catch ( FsoAudio.SoundError e )
            {
                logger.error( @"Error: $(e.message)" );
                return;
            }
        }
    }

    private bool feed_to_modem()
    {
        CmtSpeech.FrameBuffer ulbuf = null;

        assert( logger.debug( "feeding from ringbuffer to modem" ) );
        if ( connection.protocol_state() == CmtSpeech.State.ACTIVE_DLUL )
        {
            var ok = connection.ul_buffer_acquire( out ulbuf );
            if ( ok == 0 )
            {
                assert( logger.debug( "protocol state is ACTIVE_DLUL, uploading as well..." ) );
                try
                {
                    recordMutex.lock();
                    toModem.read( (uint8[])ulbuf.payload, ulbuf.pcount );
                    recordMutex.unlock();
                    ulbuf.frame_flags = CmtSpeech.FrameFlags.VALID;
                }
                catch ( RingError e )
                {
                    recordMutex.unlock();
                    Memory.copy( ulbuf.payload, silence_buffer, ulbuf.pcount > FCOUNT * FRAMESIZE ? FCOUNT * FRAMESIZE : ulbuf.pcount );
                }
                connection.ul_buffer_release( ulbuf );
            }
        }
        return false;
    }

    private void * playbackThreadFunc()
    {
        while ( runPlaybackThread > 0 )
        {
            var buf = new uint8[ FCOUNT * FRAMESIZE ];
            Alsa.PcmSignedFrames frames;

            try<commit_msg>fsoaudiod: cmtspeech: check with is_active if we should feed audo to the modem<commit_after>/*
 * Copyright (C) 2011 Michael 'Mickey' Lauer <<EMAIL>>
 * Copyright (C) 2011 Denis 'GNUtoo' Carikli <<EMAIL>>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.

 * This library 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
 * Lesser General Public License for more details.

 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 *
 */
extern int snd_pcm_format_set_silence (Alsa.PcmFormat format,void * data, uint samples );


/**
 * @class RingBuffer
 *
 * Provides a ring buffer for alsa frames
 **/
errordomain RingError {
    Overflow,
    Underflow
}

class RingBuffer : GLib.Object
{
    private uint8[] ring;
    private int ring_head;
    private int ring_tail;
    private int ring_size;
    
    public RingBuffer( int size )
    {
        this.ring = new uint8[size];
        this.ring_head = 0;
        this.ring_tail = 0;
        this.ring_size = size;
    }

    public void write( uint8[] x, int count ) throws RingError
    {
        int new_ring_head = (ring_head + count) % ring_size;
        int free = (ring_size + ring_tail - ring_head) % ring_size;
        if ( free == 0 )
            free = ring_size;
        stderr.printf( "RingBuffer.write: ring_head=%d, ring_tail=%d, count=%d, free=%d\n", ring_head, ring_tail, count, free );
        if ( count > free )
        {
            throw new RingError.Overflow( @"Buffer is full (free: $free / wanted to write: $count)" );
        }		 

        stderr.printf( "RingBuffer.write: new ring_head would be %d\n", new_ring_head );
        /* check wraparound */
        if ( new_ring_head == 0 || new_ring_head > ring_head )
        {
            /* check next line for +-1 errors I made! */
            stdout.printf( "RingBuffer.write: does not overlap - chunk = %d\n", count );
            Memory.copy( &ring[ring_head], x, count );
        }
        else
        {
            stdout.printf( "RingBuffer.write: does overlap - first chunk = %d\n", ring_size - ring_head );
            /* check next 2 lines for +-1 errors I made! */
            Memory.copy( &ring[ring_head], x, ring_size - ring_head );
            stdout.printf( "RingBuffer.write: second chunk = %d\n", ring_size - ring_head );
            Memory.copy( &ring[0], &x[ring_size - ring_head], count - (ring_size - ring_head) );
        }
        ring_head = new_ring_head;
    }

    /* we pass the pointer to buffer as a parameter, so you can use existing buffers */
    /* wouldn't want the function to malloc a new buffer each time */
    public void read( uint8[] x, int count ) throws RingError
    {
        int avail = (ring_size + ring_head - ring_tail) % ring_size;

        stderr.printf( "RingBuffer.read: ring_head=%d, ring_tail=%d, count=%d, avail=%d\n", ring_head, ring_tail, count, avail );
        if ( avail < count )
        {
            throw new RingError.Underflow( @"Buffer has only $avail bytes available ($count requested)" );
        }

        int new_ring_tail = (ring_tail + count) % ring_size;
        stderr.printf( "RingBuffer.read: new_ring_tail would be %d\n", new_ring_tail );

        if ( new_ring_tail == 0 || new_ring_tail > ring_tail )
        {
            stderr.printf( "RingBuffer.read: does not wrap - chunk = %d\n", count );
            Memory.copy( x, &ring[ring_tail], count );
        }
        else
        {
            stderr.printf( "RingBuffer.read: does overwrap - first chunk = %d\n", ring_size - ring_tail );
            Memory.copy( x, &ring[ring_tail], ring_size - ring_tail );
            stderr.printf( "RingBuffer.read: second chunk = %d\n", count - (ring_size - ring_tail) );
            Memory.copy( &x[ring_size - ring_tail], ring, count - (ring_size - ring_tail) );
        }
        ring_tail = new_ring_tail;
    }

    public void reset()
    {
        ring_tail = ring_head = 0;
    }
}



/**
 * @class CmtHandler
 *
 * Handles Audio via libcmtspeechdata
 **/
public class CmtHandler : FsoFramework.AbstractObject
{
    private CmtSpeech.Connection connection;
    private IOChannel channel;
    private FsoAudio.PcmDevice pcmout = null;
    private FsoAudio.PcmDevice pcmin = null;
    private bool status;
    private const int FCOUNT = 160;
    private const int FRAMESIZE = 2;
    private const int BUFSIZE = 3 * FCOUNT * FRAMESIZE;

    /* alsa parameters */
    private Alsa.PcmFormat format = Alsa.PcmFormat.S16_LE;
    private Alsa.PcmAccess access = Alsa.PcmAccess.RW_INTERLEAVED;

    /* silence buffer */
    private uint8[] silence_buffer = null;

    /* playback Thread */
    private unowned Thread<void *> playbackThread = null;
    private int runPlaybackThread = 0;
    private Mutex playbackMutex = new Mutex();
    private RingBuffer fromModem;

    /* record Thread */
    private unowned Thread<void *> recordThread = null;
    private bool runRecordThread = false;
    private Mutex recordMutex = new Mutex();
    private RingBuffer toModem;
    private int timing; // feed UL to the modem (in ms)

    //
    // Constructor
    //
    public CmtHandler()
    {
        status = false;

        assert( logger.debug( "Initializing cmtspeech" ) );
        CmtSpeech.init();

        assert( logger.debug( "Setting up traces" ) );
        CmtSpeech.trace_toggle( CmtSpeech.TraceType.STATE_CHANGE, true );
        CmtSpeech.trace_toggle( CmtSpeech.TraceType.IO, true );
        CmtSpeech.trace_toggle( CmtSpeech.TraceType.DEBUG, true );

        assert( logger.debug( "Instanciating connection" ) );
        connection = new CmtSpeech.Connection();
        if ( connection == null )
        {
            logger.error( "Can't instanciate connection" );
            return;
        }

        var fd = connection.descriptor();

        if ( fd == -1 )
        {
            logger.error( "Cmtspeech file descriptor invalid" );
        }

        timing = 50;
        fromModem = new RingBuffer( BUFSIZE );
        toModem = new RingBuffer( BUFSIZE );

        assert( logger.debug( "Hooking up fd with main loop" ) );
        channel = new IOChannel.unix_new( fd );
        channel.add_watch( IOCondition.IN | IOCondition.HUP, onInputFromChannel );

        logger.info( "Created" );
    }

    //
    // Private API
    //

    private void play_silence( int count )
    {
        Alsa.PcmSignedFrames ret;
        int retries = 3;

        while ( count > 0 && retries > 0 )
        {
            try
            {
                ret = pcmout.writei( silence_buffer, FCOUNT );
                if ( ret == -Posix.EPIPE )
                {
                    pcmout.recover( -Posix.EPIPE, 0 );
                    retries--;
                }
                else
                {
                    count--;
                }
            }
            catch ( FsoAudio.SoundError e )
            {
                logger.error( @"Error: $(e.message)" );
                return;
            }
        }
    }

    private bool feed_to_modem()
    {
        CmtSpeech.FrameBuffer ulbuf = null;

        assert( logger.debug( "feeding from ringbuffer to modem" ) );
        if ( connection.is_active() )
        {
            var ok = connection.ul_buffer_acquire( out ulbuf );
            if ( ok == 0 )
            {
                assert( logger.debug( "protocol state is ACTIVE_DLUL, uploading as well..." ) );
                try
                {
                    recordMutex.lock();
                    toModem.read( (uint8[])ulbuf.payload, ulbuf.pcount );
                    recordMutex.unlock();
                    ulbuf.frame_flags = CmtSpeech.FrameFlags.VALID;
                }
                catch ( RingError e )
                {
                    recordMutex.unlock();
                    Memory.copy( ulbuf.payload, silence_buffer, ulbuf.pcount > FCOUNT * FRAMESIZE ? FCOUNT * FRAMESIZE : ulbuf.pcount );
                }
                connection.ul_buffer_release( ulbuf );
            }
        }
        return false;
    }

    private void * playbackThreadFunc()
    {
        while ( runPlaybackThread > 0 )
        {
            var buf = new uint8[ FCOUNT * FRAMESIZE ];
            Alsa.PcmSignedFrames frames;

            try
{'fraction_non_alphanumeric': 0.10166858457997699, 'fraction_numerical': 0.006329113924050633, 'mean_word_length': 1.951936141304348, 'pattern_counts': {'":': 0, '<': 18, '<?xml version=': 0, '>': 30, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4823526', 'n_tokens_mistral': 5293, 'n_tokens_neox': 5008, 'n_words': 1477}