/*** audio.c - MPEG Layer 1/2 audio handling in a MPEG stream ***/

/* (c) 1999 Jesper Svennevid */

/* Real AHI device support, bug-fixes and assembler optimizations: */
/* (c) 1999 Frank Wille <frank@phoenix.owl.de> */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <exec/memory.h>
#include <devices/ahi.h>
#include <proto/exec.h>
#include <clib/powerpc_protos.h>
#ifdef GLOBAL
#undef GLOBAL
#endif

#include "config.h"
#include "mpeg2dec.h"
#include "audio.h"
#include "global.h"
#include "mpegaudio/common.h"
#include "mpegaudio/decoder.h"


#define MAX_AUDIOBUFFER_SIZE  0x2000 /* size of one audio-buffer */
#define MAX_AUDIOFRAMES_DECODE  16 /* number of audio-frames to decode before flushing */


struct AudioBuffer
{
  /* contains as a max */ 
  unsigned char *databuffer;
  struct AudioBuffer *next;
};

struct AudioBuffer *ab_current = NULL;  /* current audio-buffer reading from */
struct AudioBuffer *ab_feed = NULL; /* current audio-buffer feeding into */
struct AudioBuffer *ab_free = NULL; /* free data-buffers */

/* writing offset into current buffer */
unsigned long feedOffset = 0;

/* reading offset into current buffer */
unsigned long readOffset = 0;

/* how much data present in the buffer */
unsigned long audioBufferSize = 0;

/* AHI sound */
struct AHIBlock {
  struct AHIBlock *next;
  struct AHIRequest *ahiReq;
  short *buffer;
  unsigned long filled;
};
#define MAXAHIBLKS 4
#define BUFSIZE 0x4000

static struct Device *ahiDevice = NULL;
static struct MsgPort *ahiPort = NULL;
static struct AHIBlock ahiblocks[MAXAHIBLKS];
static struct AHIBlock *currentAHI = &ahiblocks[0];
static struct AHIRequest *linkReq = NULL;

#ifdef __PPC__  /* PPC inline assembler */
void append_pcm(short *buf,short *pcm,int num,int sblimit,int stereo) =
    "\tmullw\tr8,r5,r6\n"
    "\tsubi\tr3,r3,4\n"
    "\tcmpwi\tr7,1\n"
    "\tbne+\t$+48\n"
    "\tandi.\tr0,r8,1\n"      /* mono copy */
    "\tsrawi\tr8,r8,1\n"
    "\tsubi\tr4,r4,4\n"
    "\tmtctr\tr8\n"
    "#barrier\n"
    "\tlwzu\tr0,4(r4)\n"
    "\tstwu\tr0,4(r3)\n"
    "\tbdnz\t$-8\n"
    "\tbeq\t$+52\n"
    "\tlhz\tr0,4(r4)\n"       /* copy last half word */
    "\tsth\tr0,4(r3)\n"
    "\tb\t$+40\n"
    "#barrier\n"
    "\tsubi\tr4,r4,2\n"       /* stereo copy */
    "\tslwi\tr11,r8,1\n"
    "\tadd\tr9,r4,r11\n"
    "\tmtctr\tr8\n"
    "#barrier\n"
    "\tlhzu\tr11,2(r9)\n"
    "\tlhzu\tr12,2(r4)\n"
    "\trlwimi\tr11,r12,16,0,15\n"
    "\tstwu\tr11,4(r3)\n"
    "\tbdnz\t$-16\n"
    "#barrier";
#endif

extern int Decode_Audio();



void audio_cleanup()
{
  int i;

  if (ahiDevice) {
    for (i=0; i<MAXAHIBLKS; i++) {
      if (ahiblocks[i].filled) {
        AbortIO((struct IORequest *)ahiblocks[i].ahiReq);
        WaitIO((struct IORequest *)ahiblocks[i].ahiReq);
      }
    }
    CloseDevice((struct IORequest *)ahiblocks[0].ahiReq);
    ahiDevice = NULL;
    for (i=MAXAHIBLKS-1; i>=0; i--) {
      if (ahiblocks[i].buffer)
        FreeVecPPC(ahiblocks[i].buffer);
      if (ahiblocks[i].ahiReq)
        DeleteIORequest((struct IORequest *)ahiblocks[i].ahiReq);
    }
    memset(ahiblocks,0,sizeof(struct AHIBlock)*MAXAHIBLKS);
    DeleteMsgPort(ahiPort);
    ahiPort = NULL;
  }
}


int audio_init()
{
  int i;

  memset(ahiblocks,0,sizeof(struct AHIBlock)*MAXAHIBLKS);

  if (ahiPort = CreateMsgPort()) {
    if (currentAHI->ahiReq = (struct AHIRequest *)CreateIORequest(
                              ahiPort,sizeof(struct AHIRequest))) {
      currentAHI->ahiReq->ahir_Version = 4;
      if (!OpenDevice(AHINAME,0,(struct IORequest *)currentAHI->ahiReq,0)) {
        ahiDevice = currentAHI->ahiReq->ahir_Std.io_Device;
      }
      else {
        DeleteIORequest(currentAHI->ahiReq);
        DeleteMsgPort(ahiPort);
        return (0);
      }
    }
    else {
      DeleteMsgPort(ahiPort);
      return (0);
    }
  }

  /* create other AHIRequests */
  for (i=1; i<MAXAHIBLKS; i++) {
    if (ahiblocks[i].ahiReq = (struct AHIRequest *)
        CreateIORequest(ahiPort,sizeof(struct AHIRequest))) {
      *ahiblocks[i].ahiReq = *currentAHI->ahiReq;
    }
    else {
      audio_cleanup();
      return (0);
    }
  }

  /* alloc buffers and init AHIBlks */
  for (i=0; i<MAXAHIBLKS; i++) {
    struct AHIRequest *a = ahiblocks[i].ahiReq;

    if (ahiblocks[i].buffer = AllocVecPPC(BUFSIZE*sizeof(short),
                                           MEMF_CLEAR|MEMF_PUBLIC,8)) {
      ahiblocks[i].next = &ahiblocks[(i+1)%MAXAHIBLKS];
      a->ahir_Std.io_Command = CMD_WRITE;
      a->ahir_Std.io_Flags   = 0;
      a->ahir_Std.io_Data    = ahiblocks[i].buffer;
      a->ahir_Std.io_Offset  = 0;
      a->ahir_Volume = 0x10000;
      a->ahir_Position = 0x8000;
    }
    else {
      audio_cleanup();
      return (0);
    }
  }

  /* success */
  atexit(audio_cleanup);
  return (-1);
}


void audio_feedbuffer(unsigned char data)
{
  if(ab_feed)
  {
    if(feedOffset>=MAX_AUDIOBUFFER_SIZE)
    {
      if(ab_free)
      {
        struct AudioBuffer *temp = ab_free;

        ab_free = temp->next;
        temp->next = NULL;
        ab_feed->next = temp;
        ab_feed = temp;
      }
      else
      {
        struct AudioBuffer *temp = malloc(sizeof(struct AudioBuffer));
        if(temp)
        {
          if( (temp->databuffer = malloc(MAX_AUDIOBUFFER_SIZE)) )
          {
            temp->next = NULL;
            ab_feed->next = temp;
            ab_feed = temp;
          }
          else
            exit(0);
        }
        else
          exit(0);
      }
      feedOffset = 0;
    }
  }
  else
  {
    if( (ab_feed = malloc(sizeof(struct AudioBuffer))) )
    {
      if( !(ab_feed->databuffer = malloc(MAX_AUDIOBUFFER_SIZE)) )
        exit(0);
    }
    else
      exit(0);
    ab_current = ab_feed;
  }

  /* feed mpeg-audio data to buffer */

  ab_feed->databuffer[feedOffset++] = data;
  audioBufferSize++;
}


#ifndef __PPC__
unsigned char audio_pollbuffer()
{
  if(ab_current)
  {
    if((ab_current==ab_feed)&&(readOffset>=feedOffset))
      return 0x00;
    if(readOffset>=MAX_AUDIOBUFFER_SIZE)
    {
      struct AudioBuffer *temp = ab_current->next;

      if(!temp) return 0x00;

      ab_current->next = ab_free;
      ab_free = ab_current;
      ab_current = temp;      
      readOffset = 0;
    }
    if(audioBufferSize)
    {
      audioBufferSize--;
      return ab_current->databuffer[readOffset++];
    }
  }
  return 0x00;
}
#endif


unsigned long audioOpened = 0;

typedef short PCM[2][3][SBLIMIT];
  PCM FAR *pcm_sample;
  PCM FAR *pcm_sample_full;
typedef unsigned int SAM[2][3][SBLIMIT];
  SAM FAR *sample;
  SAM FAR *sample_full;
typedef double FRA[2][3][SBLIMIT];
  FRA FAR *fraction;
  FRA FAR *fraction_full;
typedef double VE[2][HAN_SIZE];
  VE FAR *w;
  VE FAR *w_full;

    layer             info;

    int               error_protection, crc_error_count, total_error_count;
    unsigned int      old_crc, new_crc;
    unsigned int      bit_alloc[2][SBLIMIT], scfsi[2][SBLIMIT],
                      scale_index[2][3][SBLIMIT];
    unsigned long     bitsPerSlot, samplesPerFrame, frameNum = 0;
    unsigned long     frameBits, gotBits = 0;
    char              t[50];
    int topSb = 0;
    int notEnoughData = 0;


int Decode_Audio()
{
  int i,j,k,sync,stereo,clip;
  static unsigned long sample_frames;
  static Bit_stream_struc bs;
  static frame_params fr_ps;
  static int done;
  static unsigned long bufferCounter;
  extern FILE *pipe_file;

  if (!Enable_Sound)
    return 0;

  if (audioBufferSize < 1024)
    return 0; /* hmm, we should always have data to poll */

  if(!audioOpened) {
    pcm_sample = (PCM FAR *) mem_alloc((long) sizeof(PCM), "PCM Samp");
    sample = (SAM FAR *) mem_alloc((long) sizeof(SAM), "Sample");
    fraction = (FRA FAR *) mem_alloc((long) sizeof(FRA), "fraction");
    w = (VE FAR *) mem_alloc((long) sizeof(VE), "w");

    pipe_file = stdout;   /* @@@ */
    done = FALSE;
    bufferCounter = 0;

    fr_ps.header = &info;
    fr_ps.tab_num = -1;                /* no table loaded */
    fr_ps.alloc = NULL;
    for (i=0;i<HAN_SIZE;i++) for (j=0;j<2;j++) (*w)[j][i] = 0.0;

    open_bit_stream_r(&bs, "", BUFFER_SIZE);

    sample_frames = 0;
    audioOpened = 1L;
  }

#if 0
    if(!notEnoughData)
    {
#endif
      sync = seek_sync(&bs, SYNC_WORD, SYNC_WORD_LNGTH);
      frameBits = sstell(&bs) - gotBits;
      gotBits += frameBits;

      if (!sync) return 0;

      decode_info(&bs, &fr_ps);
      hdr_to_frps(&fr_ps);
#if 0
    }
    else
      notEnoughData = NULL;
#endif

    stereo = fr_ps.stereo;
    error_protection = info.error_protection;
    crc_error_count = 0;
    total_error_count = 0;

#if 0
    if(audioBufferSize<(((144*(bitrate[info.lay-1][info.bitrate_index]*1000)/((long)(s_freq[info.sampling_frequency]*1000)))+(info.padding?1:0))*2))
    {
      printf("out of data\n");
      notEnoughData = 1L;
      return 0;
    }
#endif

    if (error_protection) buffer_CRC(&bs, &old_crc);

    switch (info.lay) {

       case 1:
          bitsPerSlot = 32;
          samplesPerFrame = 384;
          I_decode_bitalloc(&bs,bit_alloc,&fr_ps);
          I_decode_scale(&bs, bit_alloc, scale_index, &fr_ps);

          clip = 0;
          for (i=0;i<SCALE_BLOCK;i++) {
             I_buffer_sample(&bs,(*sample),bit_alloc,&fr_ps);
             I_dequantize_sample(*sample,*fraction,bit_alloc,&fr_ps);
             I_denormalize_sample((*fraction),scale_index,&fr_ps);
             if(topSb>0)        /* clear channels to 0 */
                for(j=topSb; j<fr_ps.sblimit; ++j)
                   for(k=0; k<stereo; ++k)
                      (*fraction)[k][0][j] = 0;

             for (j=0;j<stereo;j++)
                clip += SubBandSynthesis (&((*fraction)[j][0][0]), j,
                                          &((*pcm_sample)[j][0][0]));
             out_fifo(*pcm_sample, 1, &fr_ps, done, pipe_file, &sample_frames);
          }
          return 1;

       case 2:
          bitsPerSlot = 8;
          samplesPerFrame = 1152;
          II_decode_bitalloc(&bs, bit_alloc, &fr_ps);
          II_decode_scale(&bs, scfsi, bit_alloc, scale_index, &fr_ps);

          clip = 0;
          for (i=0;i<SCALE_BLOCK;i++) {
             II_buffer_sample(&bs,(*sample),bit_alloc,&fr_ps);
             II_dequantize_sample((*sample),bit_alloc,(*fraction),&fr_ps);
             II_denormalize_sample((*fraction),scale_index,&fr_ps,i>>2);

             if(topSb>0)        /* debug : clear channels to 0 */
                for(j=topSb; j<fr_ps.sblimit; ++j)
                   for(k=0; k<stereo; ++k)
                      (*fraction)[k][0][j] =
                      (*fraction)[k][1][j] =
                      (*fraction)[k][2][j] = 0;

             for (j=0;j<3;j++) for (k=0;k<stereo;k++) {
                clip += SubBandSynthesis (&((*fraction)[k][j][0]), k,
                                          &((*pcm_sample)[k][j][0]));
             }
             out_fifo(*pcm_sample, 3, &fr_ps, done, pipe_file, &sample_frames);
          }
          return 1;
    }
}


void out_fifo(short FAR pcm_sample[2][3][SBLIMIT],
              int num,frame_params *fr_ps,int done,FILE *f,
              unsigned long *psampFrames)

{
  int i,j,l;
  int stereo = fr_ps->stereo;
  short *p;

  if (currentAHI->filled + num*SBLIMIT*stereo >= BUFSIZE) {
    /* flush buffer, go to next AHIBlock */
    struct AHIRequest *a = currentAHI->ahiReq;

    a->ahir_Std.io_Length = currentAHI->filled * sizeof(short);
    a->ahir_Type = stereo>1 ? AHIST_S16S : AHIST_M16S;
    a->ahir_Frequency = s_freq[info.sampling_frequency]*1000;
    a->ahir_Link = linkReq;
    SendIO((struct IORequest *)a);

    if (linkReq)  /* wait for last audio block to finish */
      WaitIO((struct IORequest *)linkReq);

    linkReq = a;
    currentAHI = currentAHI->next;
    currentAHI->filled = 0;
  }

  p = currentAHI->buffer + currentAHI->filled;
  currentAHI->filled += num*SBLIMIT*stereo;
  *psampFrames += num*SBLIMIT;

#ifdef __PPC__
  append_pcm(p,(short *)pcm_sample,num,SBLIMIT,stereo);
#else
  for (i=0; i<num; i++)
    for (j=0; j<SBLIMIT; j++)
      for (l=0;l<stereo; l++)
        *p++ = pcm_sample[l][i][j];
#endif
}
