/*---------------------------------------------------------------------------*/
/* audio_io_channel.h                                                        */
/* copyright (c) innovaphone 2017                                            */
/*---------------------------------------------------------------------------*/

#define MAX_EXEC_INTERVAL_8KHZ          160
#define MAX_SAMPLE_RATE                 16000
#define MAX_CODEC_SAMPLE_RATE           16000
#define JB_MAX_DESCRIPTOR_TIME          320
#define JB_RECV_SIZE                    2048 /* 200 ms for PCMA */
#define SEND_MAX_FRAME_SAMPLES          160

class AudioIoChannel : public IAudioIoChannel, public UJitterBuffer {
    class IAudioIo * audioIo;
    class UMediaIoChannel *user;
    enum AudioCoder coder;
    bool sc;
    unsigned execInterval8khz;
    unsigned execJitter8khz;

    class IJitterBuffer *jitter_buffer;

    IFileWrite* rtpStream;
    bool recordRtpStream;

    bool channel_opened;
    bool channel_attached;
    unsigned int record_sample_rate_id;
    unsigned int playback_sample_rate_id;
    unsigned int payload_ms;
    unsigned int payload_sample_rate_id;
    unsigned int timestamp_rate_multiplier;

    unsigned int recv_stuffing_limit;
    unsigned int recv_lost_burst;
    unsigned int recv_lost_packets;
    unsigned int jitter_buffer_descriptor_time;
    short *pull_buffer;
    short recv_pull_buffer[(MAX_EXEC_INTERVAL_8KHZ + JB_MAX_DESCRIPTOR_TIME) * (MAX_SAMPLE_RATE / 8000)];
    unsigned int recv_pull_end;
    unsigned int recv_pull_pos;
    short decode_resid_buffer[120 * 16]; /* 120ms at 16000 for Opus */
    int decode_resid_pos;
    int decode_resid_size;
    short *feedback_pull_buffer;
    short send_pull_buffer[(MAX_EXEC_INTERVAL_8KHZ + JB_MAX_DESCRIPTOR_TIME) * (MAX_SAMPLE_RATE / 8000)];
    unsigned int send_pull_end;
    unsigned int send_pull_pos;
    void decode_frame(byte payload, word length, byte *data, short *buffer, short *feedback_buffer);
    t_g711plc g711plc;
    word simulate_packet_loss_counter;
    t_resample_up decoder_resampler;
    t_resample_up feedback_resampler;
    union {
        t_g722_decoder g722;
        ulong64 g729ab[(5 * 1024) / sizeof(ulong64) /* 3600 bytes needed */];
        ulong64 opus[(19 * 1024) / sizeof(ulong64) /* 17760 bytes needed */];
    } decoder;

    short send_linear_buffer[SEND_MAX_FRAME_SAMPLES * (MAX_SAMPLE_RATE/8000)];
    unsigned int send_linear_pos;
    dword send_timestamp;
    word send_sequence;
    union {
        short s[JB_RECV_SIZE / sizeof(short)];
        byte b[JB_RECV_SIZE];
    } send_data;
    unsigned int send_data_pos;
    unsigned int send_sample_pos;
    t_resample_down encoder_resampler;
    union {
        t_g722_encoder g722;
        ulong64 g729ab[(11 * 1024) / sizeof(ulong64) /* 10372 bytes needed */];
        ulong64 opus[(28 * 1024) / sizeof(ulong64) /* 26714 bytes needed */];
    } encoder;

    void update_dsp();
    void decode_frame(enum AudioCoder payload, unsigned length, const byte *data, short *buffer, short *feedback_buffer);
    void JitterBufferPulled(IJitterBuffer * const jitterBuffer, const void * buf, size_t len);

    void Open() override;
    void RtpRecv(void * buf, size_t len, dword timestamp, short sequenceNumberDiff, bool marker) override;
    void SctpRecv(void * buf, size_t len) override {};
    void EventRecv(enum MediaEndpointEvent event, void* ctx) override {};
    void Close() override;

public:
    AudioIoChannel(class IAudioIo * audioIo, class UMediaIoChannel * const user);
    ~AudioIoChannel();

    void Initialize(enum AudioCoder coder, bool sc, unsigned mediaPacketizationMs, unsigned execInterval8khz,
        unsigned execJitter8khz, unsigned pullSampleRate, unsigned putSampleRate, bool recordRtpStream);
    bool Pull();
    const short *PulledSamples();
    const short *FeedbackSamples();
    void PutSamples(const short *buffer);
};

