//=========================================================
//  MusE
//  Linux Music Editor
//  $Id: audioprefetch.cpp,v 1.14 2004/07/14 15:27:26 wschweer Exp $
//
//  (C) Copyright 2001 Werner Schweer (ws@seh.de)
//=========================================================

#include <poll.h>
#include <stdio.h>
#include <unistd.h>
#include <values.h>

#include "audioprefetch.h"
#include "globals.h"
#include "track.h"
#include "song.h"
#include "audio.h"
#include "sync.h"

enum { PREFETCH_TICK, PREFETCH_SEEK
      };

//---------------------------------------------------------
//   PrefetchMsg
//---------------------------------------------------------

struct PrefetchMsg : public ThreadMsg {
      int pos;
      };

AudioPrefetch* audioPrefetch;

//---------------------------------------------------------
//   AudioPrefetch
//---------------------------------------------------------

AudioPrefetch::AudioPrefetch(int prio, const char* name)
   : Thread(prio,name)
      {
      seekPos  = ~0;
      writePos = ~0;
      seekDone = true;
      }

//---------------------------------------------------------
//   readMsg
//---------------------------------------------------------

static void readMsgP(void* p, void*)
      {
      AudioPrefetch* at = (AudioPrefetch*)p;
      at->readMsg1(sizeof(PrefetchMsg));
      }

//---------------------------------------------------------
//   start
//---------------------------------------------------------

void AudioPrefetch::start()
      {
      clearPollFd();
      addPollFd(toThreadFdr, POLLIN, ::readMsgP, this, 0);
      Thread::start();
      }

//---------------------------------------------------------
//   ~AudioPrefetch
//---------------------------------------------------------

AudioPrefetch::~AudioPrefetch()
      {
      }

//---------------------------------------------------------
//   processMsg
//---------------------------------------------------------

void AudioPrefetch::processMsg1(const void* m)
      {
      const PrefetchMsg* msg = (PrefetchMsg*)m;
      switch(msg->id) {
            case PREFETCH_TICK:
                  if (audio->isRecording())
                        audio->writeTick();
                  prefetch();
                  seekPos = ~0;     // invalidate cached last seek position
                  break;
            case PREFETCH_SEEK:
                  // process seek in background
                  seek(msg->pos);
                  break;
            default:
                  printf("AudioPrefetch::processMsg1: unknown message\n");
            }
      }

//---------------------------------------------------------
//   msgTick
//---------------------------------------------------------

void AudioPrefetch::msgTick()
      {
      PrefetchMsg msg;
      msg.id  = PREFETCH_TICK;
      while (sendMsg1(&msg, sizeof(msg))) {
            printf("AudioPrefetch::msgTick(): send failed!\n");
            }
      }

//---------------------------------------------------------
//   msgSeek
//    called from audio RT context
//---------------------------------------------------------

void AudioPrefetch::msgSeek(unsigned samplePos)
      {
      if (samplePos == seekPos) {
            seekDone = true;
            return;
            }
      seekDone = false;
      PrefetchMsg msg;
      msg.id  = PREFETCH_SEEK;
      msg.pos = samplePos;
      while (sendMsg1(&msg, sizeof(msg))) {
            printf("AudioPrefetch::msgSeek::sleep(1)\n");
            sleep(1);
            }
      }

//---------------------------------------------------------
//   prefetch
//---------------------------------------------------------

void AudioPrefetch::prefetch()
      {
      if (writePos == ~0U) {
            printf("prefetch(): invalid write position\n");
            return;
            }
      if (song->loop() && !audio->bounce() && !extSyncFlag.value()) {
            const Pos& loop = song->rPos();
            unsigned n = loop.frame() - writePos;
            if (n < segmentSize) {
                  unsigned lpos = song->lPos().frame();
                  // adjust loop start so we get exact loop len
                  if (n > lpos)
                        n = 0;
// printf("prefetch seek %d\n", writePos);
                  writePos = lpos - n;
                  }
            }
      WaveTrackList* tl = song->waves();
      for (iWaveTrack it = tl->begin(); it != tl->end(); ++it) {
            WaveTrack* track = *it;
            int ch           = track->channels();
            float* bp[ch];
// printf("prefetch %d\n", writePos);
            if (track->prefetchFifo()->getWriteBuffer(ch, segmentSize, bp, writePos)) {
                  printf("Prefetch: NO BUFFER\n");
                  continue;
                  }
            track->fetchData(writePos, segmentSize, bp);
            }
      writePos += segmentSize;
      }

//---------------------------------------------------------
//   seek
//---------------------------------------------------------

void AudioPrefetch::seek(unsigned seekTo)
      {
// printf("seek %d\n", seekTo);
      writePos = seekTo;
      WaveTrackList* tl = song->waves();
      for (iWaveTrack it = tl->begin(); it != tl->end(); ++it) {
            WaveTrack* track = *it;
            track->clearPrefetchFifo();
            }
      for (int i = 0; i < (FIFO_BUFFER-1); ++i)
            prefetch();
      seekPos  = seekTo;
      seekDone = true;
      }

