//**************************************************************************
//*                     This file is part of the                           *
//*                      Mpxplay - audio player.                           *
//*                  The source code of Mpxplay is                         *
//*        (C) copyright 1998-2015 by PDSoft (Attila Padar)                *
//*                http://mpxplay.sourceforge.net                          *
//*                  email: mpxplay@freemail.hu                            *
//**************************************************************************
//*  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.                  *
//*  Please contact with the author (with me) if you want to use           *
//*  or modify this source.                                                *
//**************************************************************************
//function: Mpxplay main

//#define MPXPLAY_USE_DEBUGF 1
#define MPXPLAY_DEBUG_OUTPUT NULL // stdout
#define MPXPLAY_DEBUG_SEEK NULL // stdout
#define MPXPLAY_DEBUGOUT_SLEEP stdout

#include <string.h>
#include <stdlib.h>
#include "mpxplay.h"
#include "mpxinbuf.h"
#include "au_mixer\au_mixer.h"
#include "control\control.h"
#include "control\cntfuncs.h"
#include "decoders\decoders.h"
#include "diskdriv\diskdriv.h"
#include "display\display.h"
#ifdef MPXPLAY_GUI_QT
#include "disp_qt\disp_qt.h"
#endif

static void mpxplay_maininit_1(void);
static void mpxplay_maininit_2(void);
static void mpxplay_maininit_3(void);
static void mpxplay_init_run(void);

static void mpxplay_main_cycle(void);
static void mpxplay_tsr_cycle_1(void);
static void mpxplay_tsr_cycle_2(void);
static void main_part1(struct mainvars *);
static void open_new_infile(struct mainvars *);
static unsigned int mpxplay_set_playstartpos(struct mpxpframe_s *,struct mainvars *);
static void start_or_stop_playing(struct mainvars *);
static void mpxplay_newsong_check(struct mainvars *mvp);
static void mpxplay_pause_process(struct mainvars *mvp);
static void main_part2(struct mainvars *);
static void check_seeking(struct mainvars *);
static void crossfade_auto_control(struct mpxpframe_s *,struct mpxpframe_s*,struct mainvars *);
static void crossfade_manual_set(struct mainvars *,unsigned int newpart);
static void crossfade_initvar(struct mainvars *,struct crossfade_info *);

extern struct mainvars mvps;
extern mpxp_uint32_t mpxplay_programcontrol,mpxplay_inside_programcontrol;
extern unsigned int refdisp,crossfadepart,outmode,displaymode,desktopmode;
extern unsigned int intsoundconfig,intsoundcontrol,prebuffertype;
extern unsigned int playcontrol,playreplay,playrand,playcountsong,playlistsave;
extern          int playstartpercent,playstartframe,playstartmsec;
extern        char *playstarttime,*playcounttime,*playendtime;
extern unsigned int playcountframe,playcountpercent;
extern unsigned long mpxplay_signal_events;
extern int MIXER_var_mutelen;
static volatile unsigned int mtc1_running;

void main(int argc,char *argv[])
{
 //pds_mswin_previousinstance_close();
 newfunc_init(argv[0]);                      // is windows?; is LFN?; init textdisplay, init error handlers
 mpxplay_control_initvar(argc,argv,&mvps);   // set variables to default
 mpxplay_control_configfile_parts_loadini(); // open and load configfile;read keyboard codes,global variables
 mpxplay_control_getcommandlineopts();

 mpxplay_maininit_1();
#ifdef MPXPLAY_GUI_CONSOLE
 mpxplay_maininit_2();
#endif
 if(outmode&OUTMODE_TYPE_MASK){
  mpxplay_maininit_3();
  mpxplay_init_run();
  mpxplay_close_program(MPXERROR_OK);
 }else
  mpxplay_close_program(MPXERROR_UNDEFINED);
}

static void mpxplay_maininit_1(void)
{
 struct mainvars *mvp=&mvps;
 mpxplay_save_startdir(mvp);
 mpxplay_control_checkvar(mvp);  // check (and correct the bad) variables
#ifdef MPXPLAY_GUI_CONSOLE
 AU_init(mvp->aui);              // audio init (and test)
 mpxplay_control_configfile_parts_init(); // init (hw) parts of mpxplay.ini (mouse,joy,serial-port,LCD,startup)
#endif
 mpxplay_mpxinbuf_init(mvp);
 mpxplay_infile_init(mvp);       // static struct build and infile-preinit (mpx_init())
 mpxplay_playlist_init(mvp);     // alloc playlist & id3 memory
 mpxplay_display_init(mvp);
#ifdef MPXPLAY_GUI_CONSOLE
 refdisp|=RDT_INIT_FULL|RDT_OPTIONS|RDT_HEADER|RDT_ID3INFO|RDT_EDITOR;
 refresh_desktop(mvp);
#endif
}

static void mpxplay_maininit_2(void)
{
 struct mainvars *mvp=&mvps;
#ifndef MPXPLAY_GUI_CONSOLE
 AU_init(mvp->aui);                // audio init (and test)
 mpxplay_control_configfile_parts_init(); // init (hw) parts of mpxplay.ini (mouse,joy,serial-port,LCD,startup)
#endif
 if(outmode&OUTMODE_TYPE_MASK){
  AU_setmixer_all(mvps.aui);       // -scv,-sctr,-scbs
  MIXER_allfuncinit_init(&mvps,mvps.aui,mvps.fr_primary);
 }
 mpxplay_control_startup_loadlastlist(); // !!! moved here from configfile_parts_init
 refdisp|=RDT_INIT_EDIT|RDT_TABINFOS;
#ifdef MPXPLAY_GUI_CONSOLE
 refdisp|=RDT_EDITOR;
#endif
 refresh_desktop(mvp);
 playlist_get_allfilenames(mvp); // playlist, directory or drive scan
 playlist_id3list_load(mvp,NULL);// -ig
 playlist_init_playside(mvp);    // playside,editorside
 refdisp|=RDT_EDITOR;
 refresh_desktop(mvp);
 playlist_chkentry_get_allfileinfos(mvp); // header (filetype),id3,filesize
 playlist_editlist_id3filter(mvp);// -if
 playlist_id3list_save(mvp);     // -is
 playlist_write_id3tags(mvp);    // -iw
 if(outmode&OUTMODE_TYPE_MASK){
  mpxplay_control_startup_getstartpos(&mvps); // get startup position
  playlist_init_playsong(&mvps);   // startup, -pss
  mpxplay_mpxinbuf_prealloc(&mvps);// -bp
  AU_ini_interrupts(mvps.aui);     // dma monitor, interrupt decoder
  mpxplay_timer_reset_counters();  // to redirect int08 callings if no int08
  intsoundcontrol=intsoundconfig;  // -xr needs
  mpxplay_timer_addfunc(&check_seeking,&mvps,MPXPLAY_TIMERTYPE_REPEAT,1);
  if(funcbit_test(intsoundconfig,INTSOUND_TSR))
   mpxplay_timer_addfunc(&mpxplay_newsong_check,&mvps,MPXPLAY_TIMERTYPE_REPEAT,1);
  refdisp|=RDT_EDITOR;
 }
#ifdef MPXPLAY_GUI_QT
 mpxplay_dispqt_video_waitfor_init();
#endif
}

static void mpxplay_maininit_3(void)
{
#ifdef MPXPLAY_GUI_CONSOLE
 mpxplay_videoout_init(mvps.voi);
 check_dosshellstart();           // -xs
#endif
#ifdef __DOS__
 if(funcbit_test(intsoundconfig,INTSOUND_TSR)){  // -xr
  mpxplay_timer_addfunc(&mpxplay_tsr_cycle_1,NULL,MPXPLAY_TIMERTYPE_INT08|MPXPLAY_TIMERTYPE_REPEAT|MPXPLAY_TIMERFLAG_OWNSTACK|MPXPLAY_TIMERFLAG_INDOS,0);
  mpxplay_timer_addfunc(&mpxplay_tsr_cycle_2,NULL,MPXPLAY_TIMERTYPE_INT08|MPXPLAY_TIMERTYPE_REPEAT|MPXPLAY_TIMERFLAG_OWNSTACK|MPXPLAY_TIMERFLAG_INDOS,0);
 }
#endif
}

#ifdef MPXPLAY_GUI_CONSOLE
static void mpxplay_empty(void)
{
 pds_threads_sleep(10);
}
#endif // MPXPLAY_GUI_CONSOLE

#ifdef MPXPLAY_WIN32

static void mpxplay_init_run(void)
{
 funcbit_smp_disable(intsoundcontrol,INTSOUND_DECODER);
 funcbit_smp_int32_put(mvps.partselect,1);
#ifdef MPXPLAY_GUI_CONSOLE
 if( (funcbit_test(intsoundconfig,INTSOUND_TSR) && newfunc_newhandler08_maincycles_init(&mvps, NULL, &mpxplay_tsr_cycle_1, &mpxplay_tsr_cycle_2))
  || (funcbit_test(intsoundconfig,INTSOUND_DECODER) && newfunc_newhandler08_maincycles_init(&mvps, NULL, &mpxplay_main_cycle, NULL))
#else
  if( (funcbit_test(intsoundconfig,INTSOUND_TSR) && newfunc_newhandler08_maincycles_init(&mvps, &mpxplay_maininit_2, &mpxplay_tsr_cycle_1, &mpxplay_tsr_cycle_2))
   || (funcbit_test(intsoundconfig,INTSOUND_DECODER) && newfunc_newhandler08_maincycles_init(&mvps, &mpxplay_maininit_2, &mpxplay_main_cycle, NULL))
#endif
 ){
  funcbit_smp_copy(intsoundcontrol,intsoundconfig,INTSOUND_DECODER);
#ifdef MPXPLAY_GUI_QT
  mpxplay_dispqt_main_start(0,NULL);
#else
  do{
   mpxplay_empty();
  }while(mvps.partselect!=0);
#endif
 }else{
  funcbit_smp_copy(intsoundcontrol,intsoundconfig,INTSOUND_DECODER);
  do{
   mpxplay_main_cycle();
  }while(mvps.partselect!=0);
 }
}

#else

static void mpxplay_init_run(void)
{
 if(funcbit_test(intsoundcontrol,INTSOUND_TSR)){
  do{
   mpxplay_empty();
  }while(mvps.partselect!=0);
 }else{
  do{
   mpxplay_main_cycle();
  }while(mvps.partselect!=0);
 }
}
#endif

static void mpxplay_main_cycle(void)
{
 struct mainvars *mvp=&mvps;
 switch(mvp->partselect){
  case 1:funcbit_smp_int32_put(mtc1_running,1);main_part1(mvp);funcbit_smp_int32_put(mtc1_running,0);break;
  case 2:main_part2(mvp);break;
 }
}

static void mpxplay_tsr_cycle_1(void)
{
 if(intsoundcontrol&INTSOUND_DECODER){
#ifdef __DOS__
  mtc1_running=1;
#endif
  mpxplay_main_cycle();
#ifdef __DOS__
  mtc1_running=0;
#endif
 }
}

static void mpxplay_tsr_cycle_2(void)
{
 if((intsoundcontrol&INTSOUND_DECODER) && !mtc1_running)
  mpxplay_timer_execute_maincycle_funcs();
}

//------------------------------------------------------------------------

static void main_part1(struct mainvars *mvp)
{
 if(!playcountsong--){
  mvp->partselect=0;
 }else{
  do{
   if(refdisp)
    refresh_desktop(mvp);
   if(mvp->adone && (mvp->adone!=ADONE_REOPEN)){
    unsigned int retcode;
    mpxplay_starttimes_clear();
    if((mvp->psip->editloadtype&PLL_JUKEBOX) && ((mvp->psip!=mvp->psie) || (mvp->direction==0) || (mvp->adone==ADONE_EOF) || crossfadepart))
     retcode=playlist_jukebox_skip(mvp);
    else
     retcode=playlist_skip(mvp);
    if(!retcode){
     struct playlist_side_info *psi=mvp->psip;
     if((playcontrol&PLAYC_EXITENDLIST) && (!(psi->editsidetype&PLT_DIRECTORY) || (psi->lastentry>=psi->firstsong))){
      mpxplay_keyboard_execute_exit(mvp);
      return;
     }
     mpxplay_stop_and_clear(mvp,1);
     break;
    }
   }
   if((mvp->aktfilenum->infobits&PEIF_INDEXED) && ((outmode&OUTMODE_TYPE_FILE) || (mvp->aui->card_handler->infobits&SNDCARD_FLAGS_DISKWRITER)==SNDCARD_FLAGS_DISKWRITER))
    funcbit_enable(mvp->aui->card_controlbits,AUINFOS_CARDCNTRLBIT_AUTOTAGLFN);
   else
    funcbit_disable(mvp->aui->card_controlbits,AUINFOS_CARDCNTRLBIT_AUTOTAGLFN);

   open_new_infile(mvp);
   if(mvp->adone){
    char sout[64];
    snprintf(sout,sizeof(sout),"Searching next file (%2d/%d)",mvp->aktfilenum-mvp->psip->firstsong+1,mvp->psip->lastentry-mvp->psip->firstsong+1);
    display_message(0,0,sout);
    display_message(1,0,"");
   }
  }while(mvp->adone && (mvp->direction!=0) && !funcbit_test(playcontrol,PLAYC_ABORTNEXT));

  start_or_stop_playing(mvp);
  mpxplay_control_keyboard_songnum_reset();
  mvp->direction=mvp->seek_relative=mvp->sndempty=mvp->adone=mvp->fdone=0;
  mvp->newfilenum=NULL;
  funcbit_disable(playcontrol,PLAYC_CONTINUOUS_SEEK);
  mpxplay_timer_reset_counters();
  funcbit_enable(mpxplay_signal_events,MPXPLAY_SIGNALTYPE_NEWFILE);
  clear_message();
  refdisp|=RDT_BROWSER|RDT_EDITOR|RDT_OPTIONS|RDT_HEADER|RDT_ID3INFO;
  mvp->partselect=2;
 }
}

static void open_new_infile(struct mainvars *mvp)
{
 struct mpxpframe_s *frp_primary = mvp->fr_primary, *frp;
 struct mpxplay_audio_decoder_info_s *adi;
 struct playlist_entry_info *pei = mvp->aktfilenum;
 unsigned int infobits_save, intsoundctrl_save = intsoundcontrol;

 frp = (crossfadepart || funcbit_test(intsoundconfig,INTSOUND_DECODER))? frp_primary->fro : frp_primary;

 mvp->adone=ADONE_RESTART;
 if((frp->buffertype&PREBUFTYPE_LOADNEXT_OK) || (playlist_open_infile(mvp->psip,pei,frp) == MPXPLAY_ERROR_OK)){

  playlist_pei0_set(mvp,pei,0);

  adi=frp->infile_infos->audio_decoder_infos;

  if(funcbit_test(frp->filetype, HFT_FILE_EXT)){ // QtMedia / DShow
#ifdef MPXPLAY_GUI_QT
   mpxplay_infile_duration_set(frp, ((frp->infile_infos->timemsec)? frp->infile_infos->timemsec : pei->timemsec));
   if(playcontrol&PLAYC_RUNNING){
    if(!(playcontrol&PLAYC_PAUSENEXT)) // !!! because QT video always starts in pause mode
     funcbit_enable(playcontrol, PLAYC_STARTNEXT);
   }else if(playcontrol&PLAYC_STARTNEXT)
    funcbit_enable(playcontrol, PLAYC_STARTFLAG);
   AU_stop(mvp->aui);
   AU_setrate(mvp->aui,adi);
   mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT, "FILE EXT bt:%8.8X ft:%d ms:%d", frp->buffertype, frp->filetype, playstartmsec);
#endif
  }else{ // built-in decoder(s)

   mpxplay_diskdrive_file_config(frp->filehand_datas, MPXPLAY_DISKDRIV_CFGFUNCNUM_SET_CALLBACKPSIFN, mpxplay_playlist_modify_entry_alltab_by_mdds_and_filename, (mvp->psip->editsidetype&PLT_DIRECTORY)? mvp->psip->mdds : NULL);

   if((playcontrol&PLAYC_RUNNING) && !(playcontrol&PLAYC_PAUSENEXT) && (!(mvp->aui->card_infobits&AUINFOS_CARDINFOBIT_PLAYING) || (playcontrol&PLAYC_STARTFLAG))) // switching from EXT
    funcbit_enable(playcontrol, PLAYC_STARTNEXT);
   else if(playcontrol&PLAYC_STARTNEXT)
    funcbit_enable(playcontrol, PLAYC_STARTFLAG);

   infobits_save=adi->infobits;

   if(crossfadepart && !(adi->infobits&ADI_FLAG_BITSTREAMOUT)){
    struct mpxplay_audioout_info_s *aui = mvp->aui;
    aui->pei = aui->mvp->pei0;
    aui->chan_song = adi->outchannels;
    aui->bits_song = adi->bits;
    aui->freq_song = adi->freq;
   }else{
    AU_setrate(mvp->aui,adi);
   }

   if((infobits_save&ADI_FLAG_BITSTREAMOUT)!=(adi->infobits&ADI_FLAG_BITSTREAMOUT)){
    frp->infile_infos->allframes=frp->allframes=0;
    miis_to_frp(frp->infile_infos,frp); // re-call after AU_setrate
   }
  }

  if(!(playcontrol&PLAYC_RUNNING))
   crossfadepart=CROSS_CLEAR; // don't modify it to crossfade_manual_set(CROSS_CLEAR)

  if(!funcbit_test(frp->filetype, HFT_FILE_EXT))
   if(mpxplay_decoders_alloc(frp,1)!=MPXPLAY_ERROR_INFILE_OK)
    goto err_out_oni;

  if(!mpxplay_set_playstartpos(frp,mvp))
   goto err_out_oni;

  PDS_THREADS_MUTEX_LOCK(&frp->mutexhnd_frame, -1);

  if(!funcbit_test(frp->filetype, HFT_FILE_EXT) || !funcbit_test(frp_primary->filetype, HFT_FILE_EXT)) {
   funcbit_smp_disable(intsoundcontrol,(INTSOUND_DECODER|INTSOUND_TSR));
#ifdef MPXPLAY_GUI_QT
   mpxplay_dispqt_avmixer_set_avformat(frp->filetype & HFT_FILE_EXT);
#endif
  }

  if(!MIXER_configure(&mvp->mmi,mvp->aui,frp))
   goto err_out_oni;

  funcbit_smp_disable(playcontrol,PLAYC_BEGINOFSONG);

  mpxplay_mpxinbuf_set_intsound(frp,intsoundconfig);

  mvp->fr_primary = frp;

  if(crossfadepart==CROSS_CLEAR){
   MIXER_allfuncinit_reinit(frp->mpi);
   if(mvp->seek_relative>=0 && !funcbit_test(intsoundconfig,INTSOUND_DECODER))
    funcbit_smp_enable(playcontrol,PLAYC_BEGINOFSONG);
   //if((playcontrol&PLAYC_RUNNING) && (frp->infile_infos->seektype!=MPX_SEEKTYPE_BOF))
   if(playcontrol&PLAYC_RUNNING)
    MIXER_allfuncinit_restart(frp->mpi); // TODO: doesn't fit for gapless playing
   clear_volnum();
  }

  if((crossfadepart==CROSS_LOAD) && !funcbit_test(frp->filetype, HFT_FILE_EXT))
   crossfade_manual_set(mvp,CROSS_FADE);
  else
   crossfade_manual_set(mvp,CROSS_CLEAR);

  mvp->foundfile=1;
  mvp->adone=0;
  funcbit_smp_int32_put(mvp->idone, ((frp->filetype & HFT_FILE_EXT)? MPXPLAY_ERROR_INFILE_EOF : MPXPLAY_ERROR_INFILE_OK));

  PDS_THREADS_MUTEX_UNLOCK(&frp->mutexhnd_frame);
  funcbit_smp_copy(intsoundcontrol,intsoundctrl_save,(INTSOUND_DECODER|INTSOUND_TSR));

#ifdef MPXPLAY_LINK_VIDEO
  mpxplay_infile_video_config_open(mvp->voi,frp0->infile_infos->video_decoder_infos);
  if((playcontrol&PLAYC_AUTOGFX) && displaymode && frp0->infile_infos->video_decoder_infos->video_res_x)
   mpxplay_display_switch_to_graphmode(mvp);
#endif

  if(playrand){
   if(mvp->direction<0)                 //
    playlist_randlist_popq();           // pop prev/curr-file
   playlist_randlist_pushq(mvp->psip,mvp->aktfilenum); // push curr
  }
 }

 return;

err_out_oni:
 if(!frp->pcmdec_buffer)
  crossfadepart=CROSS_CLEAR;
 funcbit_smp_copy(intsoundcontrol,intsoundctrl_save,(INTSOUND_DECODER|INTSOUND_TSR));
 PDS_THREADS_MUTEX_UNLOCK(&frp->mutexhnd_frame);
 return;
}

long mpxplay_calculate_timesec_to_framenum(struct mpxpframe_s *frp,char *pst)
{
 long timesech=pds_strtime_to_hexhtime(pst);
 timesech=PDS_HEXHTIME_TO_HSECONDS(timesech);
 return (long)((float)frp->allframes*(float)timesech*10.0/(float)frp->infile_infos->timemsec);
}

long mpxplay_calculate_index_start_end(struct mpxpframe_s *frp,struct mainvars *mvp,struct playlist_entry_info *pei)
{
 long index_end;
 frp->index_start=frp->index_end=0;

 if(pei->pstime){
  frp->index_start=(long)((float)frp->allframes*(float)pei->pstime/(float)frp->infile_infos->timemsec);
  if(frp->index_start>=frp->allframes)
   if(frp->allframes>mvp->seek_frames)
    frp->index_start=frp->allframes-mvp->seek_frames;
   else
    frp->index_start=0;
 }

 if(pei->petime){
  frp->index_end=(long)((float)frp->allframes*(float)pei->petime/(float)frp->infile_infos->timemsec);
  if(frp->index_end>frp->allframes)
   frp->index_end=frp->allframes;
 }

 frp->timesec=(pei->petime && (pei->petime<frp->infile_infos->timemsec))? pei->petime:frp->infile_infos->timemsec;
 if(frp->timesec>=pei->pstime)
  frp->timesec-=pei->pstime;
 frp->timesec=(frp->timesec+500)/1000;

 index_end=(frp->index_end)? frp->index_end:frp->allframes;
 frp->index_len=(index_end>frp->index_start)? (index_end-frp->index_start):1;

 mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT, "C%d ts:%d st:%d et:%d its:%d fn:%d af:%d il:%d ms:%d", (long)(mvp->aktfilenum-mvp->psip->firstsong+1),
		 frp->timesec, pei->pstime, pei->petime,
		 frp->infile_infos->timemsec, frp->frameNum, frp->allframes, frp->index_len, playstartmsec);

 return index_end;
}

static long mpxplay_calculate_playstartpos(struct mpxpframe_s *frp,struct mainvars *mvp)
{
 long newframenum=mvp->seek_relative,index_end;

 index_end=mpxplay_calculate_index_start_end(frp,mvp,mvp->pei0);

 if(playstartpercent)
  playstartframe=frp->index_len*playstartpercent/100;
 else if((playcontrol&(PLAYC_HIGHSCAN|PLAYC_HSSTARTPOSHALF))==(PLAYC_HIGHSCAN|PLAYC_HSSTARTPOSHALF))
  playstartframe=frp->index_len>>1;
 else if(playstarttime)
   playstartframe=mpxplay_calculate_timesec_to_framenum(frp,playstarttime);
 if(playstartmsec && !(playcontrol&PLAYC_HIGHSCAN))
   playstartframe=(long)((float)frp->allframes*(float)playstartmsec/(float)frp->infile_infos->timemsec);
 if(playstartframe)
  newframenum=frp->index_start+playstartframe;

 if(playcountpercent)
  playcountframe=frp->index_len*playcountpercent/100;
 if(playcounttime)
  playcountframe=mpxplay_calculate_timesec_to_framenum(frp,playcounttime);

 if(!playcountframe && playendtime){ // playendtime has lower priority
  unsigned int pef=mpxplay_calculate_timesec_to_framenum(frp,playendtime);
  if(pef>playstartframe)
   playcountframe=pef-playstartframe;
 }

 if(newframenum<0){
  newframenum=-newframenum;
  if(index_end>(frp->index_start+newframenum))
   newframenum=index_end-newframenum;
  else
   newframenum=frp->index_start;
 }else{
  index_end=(frp->index_end)? frp->index_end:(frp->index_start+frp->index_len);
  if((playcontrol&PLAYC_HIGHSCAN) && ((newframenum+playcountframe)>index_end)){ // in HS mode if song is less than 1:10
   newframenum=frp->index_start;                // seek to bof
   if(frp->index_len>playcountframe)
    newframenum+=frp->index_len>>1;             // seek to half
  }
  if(!newframenum || (frp->index_end && (newframenum>frp->index_end)))
   newframenum=frp->index_start;
 }

 return newframenum;
}

static unsigned int mpxplay_set_playstartpos(struct mpxpframe_s *frp,struct mainvars *mvp)
{
 long prereadbytes, newframenum;

 prereadbytes = (funcbit_test(frp->filetype, HFT_FILE_EXT))? 0 : mpxplay_diskdrive_file_config(frp->filehand_datas,MPXPLAY_DISKDRIV_CFGFUNCNUM_GET_PREREADBUFBYTES,NULL,NULL);
 newframenum = mpxplay_calculate_playstartpos(frp,mvp); // needed at streams too

 if(prereadbytes > 0){
  char sout[256];
  int len = pds_strcpy(sout,"Preloading next file:\n");
  pds_strncpy(&sout[len], mvp->pei0->filename, sizeof(sout) - len);
  sout[sizeof(sout) - 1] = 0;
  display_timed_message(sout);
 }

 if(!(frp->filetype&HFT_STREAM) && !(frp->buffertype&PREBUFTYPE_NON_SEEKABLE)){
  long newframepos = newframenum, seek_retry = 2;
  do{
   funcbit_enable(frp->infile_infos->seektype,MPX_SEEKTYPE_BACKWARD);
   newframenum = mpxplay_infile_fseek(frp,newframepos);
   if((newframenum>=0) || (mvp->seek_relative>=0))
    break;
   newframepos-=100;
  }while((--seek_retry) && (newframepos>=frp->index_start));

  if(newframenum<0){ // possible in demo mode too if song is less than 1 min
   funcbit_enable(frp->infile_infos->seektype,MPX_SEEKTYPE_BACKWARD);
   newframenum=mpxplay_infile_fseek(frp,frp->index_start);
  }
  if(newframenum<0){ // major seeking problem
   if(prereadbytes>0)
    clear_message();
   return 0;
  }
 }else{
  newframenum = 0;
 }

 if(prereadbytes > 0){
  unsigned int retry = prereadbytes / frp->prebufferblocksize + 32;
  while((frp->prebufferbytes_forward < prereadbytes) && !mpxplay_mpxinbuf_buffer_protected_check(frp) && --retry){ pds_threads_sleep(0);}
  display_clear_timed_message();
 }

 if(newframenum)
  frp->infile_infos->seektype=MPX_SEEKTYPE_NORM;
 else
  frp->infile_infos->seektype=MPX_SEEKTYPE_BOF;
 if(!(playcontrol&PLAYC_RUNNING))
  funcbit_enable(frp->infile_infos->seektype,MPX_SEEKTYPE_PAUSE);
#ifdef MPXPLAY_LINK_VIDEO
 if(frp->infile_infos->video_decoder_infos->video_res_x)
  if((displaymode&DISP_GRAPHICAL) || ((playcontrol&PLAYC_AUTOGFX) && displaymode))
   funcbit_enable(frp->infile_infos->seektype,MPX_SEEKTYPE_VIDEO);
#endif
 frp->frameNum=newframenum;

 if(!(desktopmode&DTM_EDIT_MAGNETFOLLOWSKIP))
  playlist_editorhighline_set(mvp->psip,mvp->aktfilenum);

 return 1;
}

static void start_or_stop_playing(struct mainvars *mvp)
{
 struct mpxpframe_s *frp = mvp->fr_primary;
 if(playcontrol&PLAYC_ABORTNEXT){
  funcbit_enable(playcontrol,PLAYC_PAUSENEXT);
  funcbit_disable(playcontrol,PLAYC_ABORTNEXT);
 }
 if(playcontrol&PLAYC_PAUSEALL)
  funcbit_enable(playcontrol,PLAYC_PAUSENEXT);
 if(playcontrol&PLAYC_PAUSENEXT){
  funcbit_disable(playcontrol,(PLAYC_PAUSENEXT|PLAYC_STARTNEXT));
#ifdef MPXPLAY_GUI_QT
  mpxplay_dispqt_video_pause();
  if(!funcbit_test(frp->filetype, HFT_FILE_EXT))
#endif
   AU_stop(mvp->aui);
 }
 if(!(playcontrol&PLAYC_RUNNING)){
  funcbit_enable(mvp->aui->card_controlbits,AUINFOS_CARDCNTRLBIT_DMACLEAR);
#ifdef MPXPLAY_GUI_QT
  if(!funcbit_test(frp->filetype, HFT_FILE_EXT))
#endif
   MIXER_allfuncinit_restart(frp->mpi);
  crossfade_reset(mvp);
 }
 if(playcontrol&PLAYC_STARTNEXT){
  funcbit_disable(playcontrol,PLAYC_STARTNEXT);
#ifdef MPXPLAY_GUI_QT
  if(funcbit_test(frp->filetype, HFT_FILE_EXT)){
   mpxplay_dispqt_video_waitfor_init();
   mpxplay_dispqt_video_play();
   funcbit_enable(playcontrol, PLAYC_RUNNING);
   mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT, "START EXT");
   mpxplay_infile_framenum_refresh(frp);
  }else{
   if(funcbit_test(frp->fro->filetype, HFT_FILE_EXT))
    mpxplay_dispqt_video_close((void *)frp->fro->infile_infos);
   if(!(intsoundcontrol & INTSOUND_DECODER))
    mpxplay_dispqt_video_close((void *)frp->infile_infos);
   mpxplay_dispqt_video_play();
#endif
   AU_prestart(mvp->aui);
#ifdef MPXPLAY_GUI_QT
  }
#endif
  mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT, "START --- playc:%d", (playcontrol & PLAYC_RUNNING));
 }
 funcbit_disable(playcontrol,PLAYC_STARTFLAG);
}

void mpxplay_playcontrol_pause(struct mainvars *mvp, unsigned int stopflag, unsigned int endwaitflag)
{
 if(playcontrol&PLAYC_RUNNING){
#ifdef MPXPLAY_GUI_QT
  struct mpxpframe_s *frp = mvp->fr_primary;
  if(funcbit_test(frp->filetype, HFT_FILE_EXT))
   funcbit_disable(playcontrol, PLAYC_RUNNING);
  else{
   if(stopflag)
	mpxplay_dispqt_video_signal_stop();
   else
    mpxplay_dispqt_video_pause();
#endif
   if(endwaitflag)
    AU_wait_and_stop(mvp->aui);
   else
    AU_stop(mvp->aui);
#ifdef MPXPLAY_GUI_QT
  }
#endif
 }
}

void mpxplay_stop_and_clear(struct mainvars *mvp, unsigned int endwaitflag)
{
 struct playlist_side_info *psip=mvp->psip;
 struct mpxpframe_s *frp_primary = mvp->fr_primary, *frp_secondary = frp_primary->fro;
 int wait_count = 5000 / 10;
 funcbit_enable(mpxplay_signal_events, MPXPLAY_SIGNALTYPE_DISKDRIVTERM); // TODO: currently this is required for tcp open
 funcbit_smp_enable(playcontrol, PLAYC_ABORTNEXT);
 mpxplay_diskdrive_file_terminate(frp_primary->filehand_datas);
 mpxplay_diskdrive_file_terminate(frp_secondary->filehand_datas);
 mpxplay_playcontrol_pause(mvp, 1, endwaitflag);
 while(funcbit_test(mpxplay_signal_events, MPXPLAY_SIGNALTYPE_DISKDRIVRUN) && --wait_count) {pds_threads_sleep(MPXPLAY_THREADS_SHORTTASKSLEEP);}  // first terminate file reading
 if(funcbit_test(mpxplay_signal_events, MPXPLAY_SIGNALTYPE_DISKDRIVRUN))
  return;
 if(PDS_THREADS_MUTEX_LOCK(&(frp_secondary->mutexhnd_frame), -1) != 0)  // then terminate decoding
  return;
 if(PDS_THREADS_MUTEX_LOCK(&(frp_primary->mutexhnd_frame), -1) != 0)
 {
  PDS_THREADS_MUTEX_UNLOCK(&(frp_secondary->mutexhnd_frame));
  return;
 }
 if(mvp->foundfile && (mvp->aktfilenum >= psip->firstsong) && (mvp->aktfilenum <= psip->lastentry)) // 1. stop
  funcbit_smp_pointer_put(mvp->newfilenum, mvp->aktfilenum);
 else{ // 2. stop
  funcbit_smp_pointer_put(mvp->aktfilenum, (psip->firstsong - 1));
  playlist_randlist_clearall(psip);
  playlist_editorhighline_set(psip,psip->firstsong);
  playlist_chkentry_enable_entries(psip);
  funcbit_smp_pointer_put(mvp->newfilenum, NULL);
 }
 crossfade_reset(mvp);
 playlist_close_infile(mvp,frp_primary);
 mpxplay_timer_addfunc(mpxplay_infile_close, frp_secondary, (MPXPLAY_TIMERTYPE_WAKEUP|MPXPLAY_TIMERFLAG_INDOS), 0);
 funcbit_disable(playcontrol,(PLAYC_STARTNEXT|PLAYC_PAUSENEXT|PLAYC_HIGHSCAN|PLAYC_CONTINUOUS_SEEK|PLAYC_STARTFLAG));
 mpxplay_starttimes_clear();
 funcbit_smp_int32_put(mvp->step, 0);
 funcbit_smp_int32_put(mvp->direction, 0);
 funcbit_smp_int32_put(mvp->foundfile, 0);
 funcbit_smp_pointer_put(mvp->newsong, NULL);
 funcbit_smp_int32_put(mvp->idone, MPXPLAY_ERROR_INFILE_EOF);
 funcbit_disable(mpxplay_signal_events, MPXPLAY_SIGNALTYPE_DISKDRIVTERM);
 refdisp|=RDT_BROWSER|RDT_EDITOR|RDT_OPTIONS|RDT_HEADER|RDT_ID3INFO;
 PDS_THREADS_MUTEX_UNLOCK(&(frp_primary->mutexhnd_frame));
 PDS_THREADS_MUTEX_UNLOCK(&(frp_secondary->mutexhnd_frame));
}

void mpxplay_play_start_or_pause(struct mainvars *mvp)
{
 if(playcontrol&PLAYC_RUNNING){
  mpxplay_playcontrol_pause(mvp, 0, 0);
 }else{
  struct mpxpframe_s *frp = mvp->fr_primary;
#ifdef MPXPLAY_GUI_QT
  if(funcbit_test(frp->filetype, HFT_FILE_EXT)){
   mpxplay_dispqt_video_play();
   funcbit_enable(playcontrol, PLAYC_RUNNING);
  }else
#endif
   if(funcbit_test(frp->filetype, HFT_FILE_BUILTIN)){
#ifdef MPXPLAY_GUI_QT
    mpxplay_dispqt_video_play();
#endif
    AU_prestart(mvp->aui);
   }else{
    funcbit_enable(playcontrol, (PLAYC_STARTNEXT|PLAYC_STARTFLAG));
    mvp->adone = ADONE_RESTART;
   }
  clear_message();
 }
}

//-------------------------------------------------------------------------
static void main_part2(struct mainvars *mvp)
{
 struct mpxpframe_s *frp0 = mvp->fr_primary, *frp1 = frp0->fro;
 unsigned long i, secfile_load = 0;

 if(funcbit_test(frp0->filetype, HFT_FILE_EXT))
  goto skip_builtin_file_read;

 if(playcontrol&PLAYC_RUNNING){
  secfile_load = 0;
  if(!(frp0->infile_infos->seektype&MPX_SEEKTYPES_CLEARBUF)){
   mvp->fdone=mpxplay_mpxinbuf_buffer_fillness_check(mvp,frp0);  // read file 0.
   i=1;
   if(mvp->fdone && !(frp0->filetype&HFT_STREAM) && (frp0->filepos<frp0->filesize) && (mpxplay_diskdrive_file_config(frp0->filehand_datas,MPXPLAY_DISKFILE_CFGFUNCNUM_SET_READWAIT,&i,NULL)>0)){
    if(!frp0->readwait_endtime){
     frp0->readwait_endtime=pds_gettimem()+10000; // !!! in msec
     mvp->fdone=0;
    }else if(pds_gettimem()<frp0->readwait_endtime)
     mvp->fdone=0;
    //else
    // display_timed_message("File read error!");
   }else
    frp0->readwait_endtime=0;
   if((prebuffertype&PREBUFTYPE_PRELOADNEXT) && !(playreplay&REPLAY_SONG) && mvp->fdone && !(frp1->buffertype&PREBUFTYPE_LOADNEXT_MASK) && (crossfadepart!=CROSS_FADE)){ // -bpn
    if(frp1->prebufferbegin){
     struct playlist_entry_info *ehl_save=mvp->psip->editorhighline,*newfi=mvp->newfilenum,*pei=newfi;
     if((pei || (pei=playlist_get_newfilenum(mvp)))      // is nextfile?
       && (!(mvp->aktfilenum->infobits&PEIF_INDEXED) || !(pei->infobits&PEIF_INDEXED) || (pds_strcmp(mvp->pei0->filename,pei->filename)!=0)) // isn't the same file with index(es)?
       && (GET_HFT(pei->entrytype)!=HFT_FILE_EXT)        // not QT video
       && (playlist_open_infile(mvp->psip,mvp->newfilenum,frp1) == MPXPLAY_ERROR_OK) // can open?
       && mpxplay_mpxinbuf_alloc(mvp,frp1)               // can init buffer?
       && (mpxplay_infile_fseek(frp1,0)==0) ){           // can seek to bof?
      funcbit_enable(frp1->buffertype,PREBUFTYPE_LOADNEXT_OK);
      refdisp|=RDT_EDITOR;
     }else{
      funcbit_enable(frp1->buffertype,PREBUFTYPE_LOADNEXT_FAILED);
      mvp->newfilenum=newfi;
     }
     playlist_editorhighline_set(mvp->psip,ehl_save);
    }else{
     display_timed_message("No buffer (free memory) to preload next file!");
     funcbit_enable(frp1->buffertype,PREBUFTYPE_LOADNEXT_FAILED);
    }
   }
   if(frp1->buffertype&PREBUFTYPE_LOADNEXT_OK)
    secfile_load = 1;
  }

  if(crossfadepart==CROSS_FADE)
   secfile_load = 1;

  if(secfile_load && (frp1->filetype!=HFT_FILE_EXT))
   mpxplay_mpxinbuf_buffer_protected_check(frp1);   // read file 1. (next)

  if(!funcbit_test(intsoundcontrol,INTSOUND_DECODER)) // if not interrupt decoding
   mvp->idone=mpxplay_infile_decode(mvp->aui);        // then mpxplay_infile_decode called from here

  if(mvp->idone!=MPXPLAY_ERROR_INFILE_OK){
   if((frp0->filetype&HFT_STREAM) && (mvp->idone==MPXPLAY_ERROR_INFILE_SYNCLOST)) {
    mvp->adone=ADONE_REOPEN;
    funcbit_enable(playcontrol,PLAYC_STARTNEXT); // FIXME: ???
   }else if((mvp->idone==MPXPLAY_ERROR_INFILE_EOF) || (((mvp->idone==MPXPLAY_ERROR_INFILE_NODATA) || (mvp->idone==MPXPLAY_ERROR_INFILE_SYNCLOST)) && mvp->fdone && !(frp0->infile_infos->seektype&MPX_SEEKTYPES_CLEARBUF))){ // if end of file 0. and not seeking
    crossfade_part_step(mvp);
    mvp->adone=ADONE_EOF;      // next song
   }else
    mvp->idone=MPXPLAY_ERROR_INFILE_OK;
  }
  if(mvp->cfi->usecrossfade && (mvp->idone==MPXPLAY_ERROR_INFILE_OK))
   crossfade_auto_control(frp0,frp1,mvp);
  if(playcountframe && (frp0->framecounter>=playcountframe))
   mvp->adone=ADONE_EOF;
 }else{ // paused
  mvp->fdone=mpxplay_mpxinbuf_buffer_fillness_check(mvp,frp0);  // read file 0.
 }

skip_builtin_file_read:
 if(frp0->index_end && (frp0->frameNum>=frp0->index_end) && !mvp->newsong && (mvp->step==0)){
   mvp->adone=ADONE_EOF;
   mpxplay_debugf(MPXPLAY_DEBUG_OUTPUT, "MIE is:%d ie:%d fn:%d", frp0->index_start, frp0->index_end, frp0->frameNum);
 }

//------ display and control funcs -------------------------------------
 if(!funcbit_test(intsoundcontrol,INTSOUND_TSR)) // if not TSR mode
  mpxplay_timer_execute_maincycle_funcs();
//----------------------------------------------------------------------

 if(!funcbit_test(intsoundconfig,INTSOUND_TSR))
  mpxplay_newsong_check(mvp);
 mpxplay_pause_process(mvp);
 funcbit_disable(mpxplay_signal_events,MPXPLAY_SIGNALMASK_OTHER); // ??? TSR mode
}

static void mpxplay_newsong_check(struct mainvars *mvp)
{
 struct mpxpframe_s *frp0 = mvp->fr_primary;
 if(mvp->newsong)
  playlist_jukebox_add_entry(mvp,mvp->psie);
 if(mvp->newsong || (mvp->step!=0)){
  struct crossfade_info *cfi=mvp->cfi;
  if((crossfadepart==CROSS_OUT) || (crossfadepart==CROSS_LOAD) || (!(frp0->filetype & HFT_FILE_ALL) && !mvp->newsong)){
   playlist_get_newfilenum(mvp);
   playlist_randlist_resetsignflag(mvp->newfilenum);
   refdisp|=RDT_EDITOR;
#ifdef MPXPLAY_GUI_QT
   refdisp|=RDT_BROWSER;
#endif
   cfi->crosswait=10;
  }else{
   if(cfi->usecrossfade && (playcontrol&PLAYC_RUNNING) && !cfi->crosswait){
    crossfade_reset(mvp);
    cfi->crossfadebegin=frp0->frameNum;
    crossfade_manual_set(mvp,CROSS_OUT);
   }else{
    if((mvp->step<0) || mvp->newsong || !mvp->newfilenum){
     if(mvp->aktfilenum>=mvp->psip->firstsong){
      playlist_randlist_resetsignflag(mvp->newfilenum);
      mvp->newfilenum=NULL;
     }
     if(playlist_get_newfilenum(mvp))
      mvp->adone=ADONE_RESTART;
    }else{
     mvp->step=0;
     mvp->newsong=NULL;
     mvp->adone=ADONE_RESTART;
    }
   }
  }
 }
 if(mvp->adone){
  if(mvp->adone==ADONE_REOPEN){
   mpxplay_starttimes_clear();
   if(!(playcontrol&PLAYC_HIGHSCAN))
    playstartframe = frp0->frameNum - frp0->index_start;
   goto jump_to_next;
  }
  if(playcountsong){
   struct playlist_entry_info *pei;
   if((mvp->adone==ADONE_EOF) && (playreplay&REPLAY_SONG) && !mvp->newfilenum){  // repeat song
    mpxplay_starttimes_clear();
    mvp->adone=mvp->fdone=0;
    mvp->seek_absolute=mpxplay_calculate_playstartpos(mvp->fr_primary,mvp)+1;
    mvp->seek_relative=0;
    check_seeking(mvp);
    if(mvp->adone)
     goto jump_to_next; // failed seeking
    frp0->framecounter=0;
    mvp->idone=MPXPLAY_ERROR_INFILE_OK;
    playcountsong--;
    goto mainpart2_end;
   }
   if((crossfadepart!=CROSS_OUT && crossfadepart!=CROSS_LOAD) || !(playcontrol&PLAYC_RUNNING)){
    pei=mvp->newfilenum;
    if(!pei){
     mvp->direction=0;
     pei=playlist_get_newfilenum(mvp);
    }
    if((pei>=mvp->psip->firstsong) && (pei<=mvp->psip->lastentry)
     && (mvp->pei0->infobits&PEIF_INDEXED) && (pei->infobits&PEIF_INDEXED)
     && (pds_strcmp(mvp->pei0->filename,pei->filename)==0)
    ){ // gapless indexed playing (don't skip to next song, continue the current one)
     unsigned long curr_petime=mvp->pei0->petime;
     if((!(desktopmode&DTM_EDIT_MAGNETFOLLOWSKIP) || (mvp->aktfilenum==mvp->psie->editorhighline)) && !(mvp->psip->editloadtype&PLL_JUKEBOX))
      playlist_editorhighline_set(mvp->psip,pei);
     if((mvp->psip->editloadtype&PLL_JUKEBOX) && ((mvp->psip!=mvp->psie) || (mvp->direction==0) || (mvp->adone==ADONE_EOF))){
      playlist_editlist_delete_entry_manual(mvp->psip,mvp->aktfilenum);
      pei=mvp->newfilenum;
     }
     mvp->aktfilenum=pei;
     mvp->newfilenum=NULL;
     playlist_pei0_set(mvp,pei,0);
     if((mvp->adone==ADONE_EOF) && !(playcontrol&PLAYC_HIGHSCAN) && curr_petime && (curr_petime==pei->pstime)){
      mpxplay_calculate_index_start_end(frp0,mvp,pei);
      mvp->seek_absolute=0;
      mvp->seek_relative=0;
     }else{
      mpxplay_starttimes_clear();
      mvp->adone=mvp->fdone=0;
      mvp->seek_absolute=mpxplay_calculate_playstartpos(frp0,mvp)+1;
      mvp->seek_relative=0;
      //mpxplay_debugf(MPXPLAY_DEBUG_SEEK, "CSEEK1 sa:%d is:%d ie:%d", mvp->seek_absolute, mvp->fr_primary->index_start, mvp->fr_primary->index_end);
      check_seeking(mvp);
      //mpxplay_debugf(MPXPLAY_DEBUG_SEEK, "CSEEK2 sa:%d ad:%d", mvp->seek_absolute, mvp->adone);
      if(mvp->adone)
       goto jump_to_next; // failed seeking
      mvp->idone=MPXPLAY_ERROR_INFILE_OK;
     }
     if(playrand){
      if(mvp->direction<0)                 //
       playlist_randlist_popq();           // pop prev/curr-file
      playlist_randlist_pushq(mvp->psip,mvp->aktfilenum); // push curr
     }
     mvp->adone=0;
     mvp->direction=0;
     mvp->foundfile=1;
     frp0->framecounter=0;
     crossfade_initvar(mvp,mvp->cfi);
     playcountsong--;
     if(!(frp0->filetype & HFT_FILE_EXT) && ((outmode&OUTMODE_TYPE_FILE) || (mvp->aui->card_handler->infobits&SNDCARD_SETRATE)))
      AU_setrate(mvp->aui,frp0->infile_infos->audio_decoder_infos);
     start_or_stop_playing(mvp);
     refdisp|=RDT_BROWSER|RDT_EDITOR|RDT_OPTIONS|RDT_HEADER|RDT_ID3INFO;
     funcbit_enable(mpxplay_signal_events,MPXPLAY_SIGNALTYPE_NEWFILE);
     goto mainpart2_end;
    }
   }
  } // else finish it and jump to next
jump_to_next:
  mvp->partselect=1;
 }
mainpart2_end:
 return;
}

static void mpxplay_pause_process(struct mainvars *mvp)
{
 if(!mvp->adone && (mvp->partselect==2)){
#if !defined(__DOS__)
  struct mpxpframe_s *frp = mvp->fr_primary;
#endif
  if((intsoundcontrol&INTSOUND_DECODER) && mpxplay_check_buffers_full(mvp)
#if defined(__DOS__)
   && !(intsoundcontrol&INTSOUND_TSR) && !funcbit_test(mpxplay_signal_events,MPXPLAY_SIGNALMASK_OTHER)
  ){
   pds_cpu_hlt(); // int08 wakes up
  }
#else
  ){
   if((intsoundcontrol&INTSOUND_TSR) || !funcbit_test(mpxplay_signal_events,MPXPLAY_SIGNALMASK_OTHER))
    pds_threads_sleep(1000/INT08_CYCLES_NEW);
   else if(!funcbit_test(mpxplay_signal_events,MPXPLAY_SIGNALTYPE_DISKACCESS))
    pds_threads_sleep(5);
   else if(frp->filetype&HFT_STREAM)
    pds_threads_sleep(0);
   mpxplay_debugf(MPXPLAY_DEBUGOUT_SLEEP,"mpxplay_pause_process END1 frp:%8.8X pbr:%7d pbf:%7d fill:%d full:%d disk:%d rt:%d",
   (unsigned int)frp, frp->prebufferbytes_rewind, frp->prebufferbytes_forward,
   ((frp->buffertype&PREBUFTYPE_FILLED)? 1 : 0), mpxplay_check_buffers_full(mvp), ((mpxplay_signal_events & MPXPLAY_SIGNALTYPE_DISKACCESS)? 1 : 0),
   ((mpxplay_signal_events & MPXPLAY_SIGNALTYPE_REALTIME)? 1 : 0));
  }else if((frp->filetype&HFT_STREAM) || (!(intsoundcontrol&INTSOUND_DECODER) && (mvp->aui->card_handler->infobits&SNDCARD_INT08_ALLOWED))){
   pds_threads_sleep(0);
  }else{
   mpxplay_debugf(MPXPLAY_DEBUGOUT_SLEEP,"mpxplay_pause_process END2 frp:%8.8X pbr:%7d pbf:%7d fill:%d full:%d disk:%d rt:%d",
   (unsigned int)frp, frp->prebufferbytes_rewind, frp->prebufferbytes_forward,
   ((frp->buffertype&PREBUFTYPE_FILLED)? 1 : 0), mpxplay_check_buffers_full(mvp), ((mpxplay_signal_events & MPXPLAY_SIGNALTYPE_DISKACCESS)? 1 : 0),
   ((mpxplay_signal_events & MPXPLAY_SIGNALTYPE_REALTIME)? 1 : 0));
  }
#endif
  if(!(playcontrol&PLAYC_RUNNING))
   AU_pause_process(mvp->aui);
 }
}

unsigned int mpxplay_check_buffers_full(struct mainvars *mvp)
{
 struct mpxpframe_s *frp = mvp->fr_primary;
 if(!(playcontrol&PLAYC_RUNNING) || (frp->buffertype&PREBUFTYPE_BUFFFILL_NOCHK))
  return 1;
 if(((intsoundcontrol&INTSOUND_DECODER) || (mvp->aui->card_infobits&AUINFOS_CARDINFOBIT_DMAFULL)) && (frp->buffertype&PREBUFTYPE_FILLED))
  return 1;
#ifdef MPXPLAY_GUI_QT
 if(funcbit_test(frp->filetype, HFT_FILE_EXT))
  return 1;
#endif
 return 0;
}

//-------------------------------------------------------------------------
static void check_seeking(struct mainvars *mvp)
{
 struct mpxpframe_s *frp=mvp->fr_primary;
 unsigned int intsoundcntrl_save;

 if(((mvp->seek_relative!=0) || mvp->seek_absolute) && funcbit_test(frp->filetype, HFT_FILE_ALL) && ((MIXER_var_mutelen<3) || !(playcontrol&PLAYC_RUNNING) || mvp->seek_absolute || funcbit_test(frp->filetype, HFT_FILE_EXT))){
#if defined(MPXPLAY_GUI_QT)
  if(funcbit_test(frp->filetype, HFT_FILE_EXT)){
   frp->infile_infos->seektype=MPX_SEEKTYPE_NORM;
   mpxplay_infile_framenum_refresh(frp);
  }
#endif
  if(mvp->seek_absolute)
   mvp->seek_absolute--;
  else
   mvp->seek_absolute=frp->frameNum+mvp->seek_relative;
  if(mvp->seek_absolute<frp->index_start){
   mvp->step=-1;
   crossfade_reset(mvp);
   mvp->cfi->crosswait=15;
  }else{
   long framenum_get;
   if(frp->index_end && (mvp->seek_absolute>=frp->index_end) && (mvp->seek_relative>=0))
    framenum_get=MPXPLAY_ERROR_INFILE_EOF;
   else{
    if(mvp->seek_absolute<(frp->index_start+100) && mvp->seek_relative<=0)
     mvp->seek_absolute=frp->index_start;
    if(mvp->seek_relative!=0)
     funcbit_enable(frp->infile_infos->seektype,MPX_SEEKTYPE_RELATIVE);
    if(!funcbit_test(frp->filetype, HFT_FILE_EXT)){
     if(MIXER_var_mutelen)
      MIXER_var_mutelen=255;
     //if(MIXER_getfunction("MIX_MUTE"))
     // MIXER_setfunction(frp->mpi,"MIX_MUTE",MIXER_SETMODE_ABSOLUTE,255);
    }
    framenum_get = mpxplay_infile_fseek((funcbit_test(frp->buffertype, PREBUFTYPE_BUFFFILL_NOSKHLP))? frp : mpxplay_mpxinbuf_seekhelper_init(frp), mvp->seek_absolute);
    //mpxplay_debugf(MPXPLAY_DEBUG_SEEK, "FSEEK sa:%d fg:%d", mvp->seek_absolute, framenum_get);
   }

   if(framenum_get==MPXPLAY_ERROR_INFILE_EOF){
    if(!funcbit_test(frp->buffertype, PREBUFTYPE_BUFFFILL_NOSKHLP))
     mpxplay_mpxinbuf_seekhelper_close(frp);
    if(!funcbit_test(frp->filetype, HFT_FILE_EXT))
     MIXER_setfunction(frp->mpi,"MIX_MUTE",MIXER_SETMODE_RESET,0);
    if(mvp->seek_relative<0){
     mvp->step=-1;
     crossfade_reset(mvp);
     mvp->cfi->crosswait=15;
    }else{
     if(crossfadepart==CROSS_OUT)
      crossfade_manual_set(mvp,CROSS_LOAD);
     mvp->seek_relative=0;
     mvp->adone=ADONE_RESTART;
    }
   }else{
    if(!funcbit_test(frp->filetype, HFT_FILE_EXT)) {
     if(!(playcontrol&PLAYC_RUNNING)){
      funcbit_enable(mvp->aui->card_controlbits,AUINFOS_CARDCNTRLBIT_DMACLEAR);
      MIXER_allfuncinit_restart(frp->mpi);
     }
     if(mvp->seek_absolute)
      MIXER_setfunction(frp->mpi,"MIX_MUTE",MIXER_SETMODE_ABSOLUTE,4);
     else
      MIXER_setfunction(frp->mpi,"MIX_MUTE",MIXER_SETMODE_RESET,0);
    }

    MPXPLAY_INTSOUNDDECODER_DISALLOW;
    if(!funcbit_test(frp->buffertype, PREBUFTYPE_BUFFFILL_NOSKHLP))
     mpxplay_mpxinbuf_seekhelper_close(frp);
    frp->frameNum=framenum_get;
    //mpxplay_debugf(MPXPLAY_DEBUG_SEEK, "FSEEK2 sa:%d fg:%d", mvp->seek_absolute, framenum_get);
    if(mvp->seek_absolute){
     frp->infile_infos->seektype=MPX_SEEKTYPE_NORM;
     funcbit_enable(mvp->aui->card_controlbits,AUINFOS_CARDCNTRLBIT_DMADONTWAIT);
    }else{
     frp->infile_infos->seektype=MPX_SEEKTYPE_BOF;
     clear_volnum();
     if(!funcbit_test(frp->filetype, HFT_FILE_EXT))
      if(playcontrol&PLAYC_RUNNING)
       MIXER_allfuncinit_restart(frp->mpi);
    }
    if(!(playcontrol&PLAYC_RUNNING))
     funcbit_enable(frp->infile_infos->seektype,MPX_SEEKTYPE_PAUSE);
#ifdef MPXPLAY_LINK_VIDEO
    if((displaymode&DISP_GRAPHICAL) && frp->infile_infos->video_decoder_infos->video_res_x)
     funcbit_enable(frp->infile_infos->seektype,MPX_SEEKTYPE_VIDEO);
#endif

    if((crossfadepart==CROSS_OUT) && (mvp->seek_relative<0)){
     crossfade_reset(mvp);
     mvp->cfi->crosswait=5;
    }

    MPXPLAY_INTSOUNDDECODER_ALLOW;

    if(!(playcontrol&PLAYC_CONTINUOUS_SEEK))
     mvp->seek_relative=0;
   }
  }
  mvp->seek_absolute=mvp->sndempty=0;
 }
}

//**************************************************************************

static void crossfade_auto_control(struct mpxpframe_s *frp0,struct mpxpframe_s *frp1,struct mainvars *mvp)
{
 struct playlist_side_info *psip=mvp->psip;
 struct crossfade_info *cfi=mvp->cfi;
 if(!cfi->crosswait){
  switch(crossfadepart){
   case CROSS_CLEAR:
	  if( (frp0->frameNum>=cfi->crossfadebegin)
           && (!(psip->editloadtype&PLL_JUKEBOX) || (psip->lastentry>psip->firstentry))
           && !(playcontrol&(PLAYC_PAUSEALL|PLAYC_PAUSENEXT))){
           if((mvp->pei0->infobits&PEIF_INDEXED) && !(playreplay&REPLAY_SONG)){ // !!! don't do crossfade between continuous indexes
            struct playlist_entry_info *pei=mvp->newfilenum;
            if(!pei){
             mvp->direction=0;
             pei=playlist_get_newfilenum(mvp);
            }
            if( (pei>=mvp->psip->firstsong) && (pei<=mvp->psip->lastentry)
             && (pei->infobits&PEIF_INDEXED) && (mvp->pei0->petime==pei->pstime)
             && (pds_strcmp(mvp->pei0->filename,pei->filename)==0)
            ){
             //cfi->crosswait=15;
             break;
            }
           }
	   crossfade_manual_set(mvp,CROSS_OUT);
          }
	  break;
   case CROSS_OUT:
	  if(frp0->frameNum>=(cfi->crossfadebegin+cfi->crossfadepoint)){
	   if( (!(psip->editloadtype&PLL_JUKEBOX) || (psip->lastentry>psip->firstentry))
            && !(playcontrol&(PLAYC_PAUSEALL|PLAYC_PAUSENEXT))){
	     crossfade_manual_set(mvp,CROSS_LOAD);
	   }else{
	    if(frp0->frameNum>cfi->crossfadeend)
	     mvp->adone=ADONE_EOF;
           }
          }
	  break;
   case CROSS_FADE:
          if((frp1->frameNum>=cfi->crossfadeend) || (frp0->frameNum>=(frp0->index_start+cfi->crossfade_in_len)))
	   crossfade_manual_set(mvp,CROSS_IN);
	  break;
   case CROSS_IN:
	  if(!(cfi->crossfadetype&CFT_FADEIN) || (frp0->frameNum>=(frp0->index_start+cfi->crossfade_in_len)))
	   crossfade_manual_set(mvp,CROSS_CLEAR);
	  break;
  }
 }else{
  if(pds_threads_timer_tick_get()>=(cfi->crosscounter+REFRESH_DELAY_JOYMOUSE)){
   cfi->crosswait--;
   cfi->crosscounter=pds_threads_timer_tick_get();
  }
 }
}

static void crossfade_manual_set(struct mainvars *mvp,unsigned int newpart)
{
 struct playlist_side_info *psip=mvp->psip;
 struct crossfade_info cfi_new;
 unsigned int intsoundcntrl_save;

 pds_memcpy(&cfi_new,mvp->cfi,sizeof(struct crossfade_info));

 switch(newpart){
  case CROSS_OUT:
    mvp->cfi->crosswait=0;
    if(mvp->newfilenum)
     mvp->step=0;
    else{
     if((playreplay&REPLAY_SONG) && mvp->aktfilenum>=psip->firstsong && mvp->aktfilenum<=psip->lastentry)
      mvp->newfilenum=mvp->aktfilenum;
     else{
      playlist_get_newfilenum(mvp);
      playlist_randlist_resetsignflag(mvp->newfilenum);
     }
    }
    if(mvp->newfilenum){
     cfi_new.crossfadeend=cfi_new.crossfadebegin+cfi_new.crossfade_out_len;
    }else{
     mvp->cfi->crosswait=15;
     crossfade_initvar(mvp,mvp->cfi);
     return;
    }
    refdisp|=RDT_EDITOR;
    break;
  case CROSS_LOAD :mvp->adone=ADONE_EOF;break;
  case CROSS_FADE :crossfade_initvar(mvp,&cfi_new);break; // at open_new_infile
  case CROSS_CLEAR:crossfade_initvar(mvp,&cfi_new);refdisp|=RDT_EDITOR;
  case CROSS_IN   :mpxplay_infile_reset(mvp->fr_primary->fro);
                   mpxplay_timer_addfunc(mpxplay_infile_close, mvp->fr_primary->fro, (MPXPLAY_TIMERTYPE_WAKEUP|MPXPLAY_TIMERFLAG_INDOS), 0);
 }

 MPXPLAY_INTSOUNDDECODER_DISALLOW;

 crossfadepart=newpart;
 pds_smp_memcpy((char *)mvp->cfi,(char *)&cfi_new,sizeof(struct crossfade_info));

 if(!funcbit_test(mvp->fr_primary->filetype, HFT_FILE_EXT))
  MIXER_setfunction(mvp->fr_primary->mpi,"MIX_CROSSFADER",MIXER_SETMODE_ABSOLUTE,newpart);

 MPXPLAY_INTSOUNDDECODER_ALLOW;
 refdisp|=RDT_OPTIONS;
}

void crossfade_part_step(struct mainvars *mvp)
{
 switch(crossfadepart){
  case CROSS_OUT  :crossfade_manual_set(mvp,CROSS_LOAD);break;
  case CROSS_FADE :if(mvp->cfi->crossfadetype&CFT_FADEIN){
		    crossfade_manual_set(mvp,CROSS_IN);
		    break;
		   }
  case CROSS_IN   :crossfade_manual_set(mvp,CROSS_CLEAR);
 }
}

static void crossfade_initvar(struct mainvars *mvp,struct crossfade_info *cfi)
{
 struct mpxpframe_s *frp0=mvp->fr_primary;
 if(cfi->crossfadelimit)
  cfi->crossfadepoint=cfi->crossfade_out_len;
 cfi->crossfadebegin=((frp0->index_end)? frp0->index_end:frp0->allframes)-cfi->crossfade_out_len;
}

void crossfade_reset(struct mainvars *mvp)
{
 if(crossfadepart){
  if(crossfadepart!=CROSS_OUT) // ???
   mvp->newfilenum=NULL;       //
  mvp->direction=0;
  crossfade_manual_set(mvp,CROSS_CLEAR);
 }
}

void mpxplay_starttimes_clear(void)
{
 if(!(playcontrol&PLAYC_HIGHSCAN)){
  playstartframe=playstartpercent=playcountframe=playcountpercent=playstartmsec=0;
  playstarttime=playcounttime=playendtime=NULL;
 }
}

//-------------------------------------------------------------------------
#ifdef MPXPLAY_GUI_CONSOLE
//extra exit infos
#define EEI_INFILENAME 1
#define EEI_CRASH      2

static struct exitinfo_s{
 char *exiterrmsg;
 unsigned int extrainfo;
}exitinfo[]={
 {"",0},                                               //0
 {"Abnormal termination",           EEI_INFILENAME},   //1
 {"Floating point error",           EEI_INFILENAME},   //2
 {"Illegal operation",              EEI_INFILENAME},   //3
 {"Interrupted by CTRL-C",          EEI_INFILENAME},   //4
 {"Invalid access to storage",      EEI_INFILENAME},   //5
 {"Request for program termination",EEI_INFILENAME},   //6
 {"Interrupted by CTRL-BREAK",      EEI_INFILENAME},   //7
 {"Soundcard init failed!",               0},             //8
 {"Not enough extended memory (or extender's limit reached)!",0}, //9
 {"Not enough conventional memory!",      0},             //10
 {"No file(s) found (empty playlist)!",   0},             //11
 {"Can't open/write output file! (exists or disk full or sharing violation)",EEI_INFILENAME},//12
 {"Program crashed (DIVISION BY ZERO) (bad environment, mpxplay.ini or audio file)",EEI_INFILENAME|EEI_CRASH},//13
 {"Program crashed (EXCEPTION ERROR) (bad environment, mpxplay.ini or audio file)" ,EEI_INFILENAME|EEI_CRASH},//14
 {"",0} // 15
};
#endif

void mpxplay_close_program(unsigned int exitcode)
{
 struct mainvars *mvp=&mvps;

 if(mvp->aui)
  AU_stop(mvp->aui);
 newfunc_timer_threads_suspend();
 mvp->partselect = 0;
 mpxplay_restore_startdir();
 if((exitcode==MPXERROR_OK) && mvp->fr_primary && mvp->fr_primary->allframes)
  exitcode=((100*mvp->fr_primary->frameNum)/mvp->fr_primary->allframes)+16;
 mpxplay_display_close(mvp);
 if(mvp->aui)
  AU_close(mvp->aui);
 MIXER_allfuncinit_close(mvp);
 if((exitcode==MPXERROR_OK) || (exitcode>MPXERROR_UNDEFINED)){
  playlist_savelist_save_playlist(mvp,NULL,NULL,playlistsave);
  mpxplay_control_configfile_parts_saveini();  // save variables; save startup;
 }
 mpxplay_control_configfile_parts_close();    // hw close
 mpxplay_control_configfile_close();          // close mpxplay.ini
#ifdef MPXPLAY_GUI_CONSOLE
 if(exitcode<16){
  if((exitinfo[exitcode].extrainfo&EEI_INFILENAME) && (mvp->pei0) && (mvp->pei0->filename))
   display_warning_message(mvp->pei0->filename);
  display_warning_message(exitinfo[exitcode].exiterrmsg);
 }
#endif
 mpxplay_infile_deinit(mvp);
 mpxplay_mpxinbuf_close(mvp);
 mpxplay_playlist_close(mvp);

#ifdef MPXPLAY_GUI_CONSOLE
 if(exitcode<16){
  if(!(exitinfo[exitcode].extrainfo&EEI_CRASH))
   newfunc_exception_handlers_close();
 }
#endif
 newfunc_close();
 newfunc_error_handlers_close();

 if(!(mpxplay_inside_programcontrol&MPXPLAY_IPROGRAMC_SHUTDOWN_FORBIDD) && ((mpxplay_programcontrol&MPXPLAY_PROGRAMC_SHUTDOWNATX) || (mpxplay_inside_programcontrol&MPXPLAY_IPROGRAMC_SHUTDOWN_FORCE)))
  pds_shutdown_atx();

 exit(exitcode);
}

