//**************************************************************************
//*                     This file is part of the                           *
//*                      Mpxplay - audio player.                           *
//*                  The source code of Mpxplay is                         *
//*        (C) copyright 1998-2006 by PDSoft (Attila Padar)                *
//*                    http://mpxplay.cjb.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

#include <string.h>
#include "newfunc\newfunc.h"
#include "mpxinbuf.h"
#include "au_mixer\au_mixer.h"
#include "au_cards\au_cards.h"
#include "control\control.h"
#include "control\cntfuncs.h"
#include "decoders\decoders.h"
#include "display\display.h"
#include "playlist\playlist.h"

static void mpxplay_init_all(void);
static void mpxplay_init_1(void);
static void mpxplay_init_2(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 frame *,struct mainvars *);
static unsigned int set_playstartpos(struct frame *,struct mainvars *);
static void start_or_stop_playing(struct mainvars *);
static void main_part2(struct mainvars *);
static void check_seeking(struct mainvars *);
static void crossfade_auto_control(struct frame *,struct frame*,struct mainvars *);
static void crossfade_manual_set(struct mainvars *,unsigned int newpart);
static void crossfade_initvar(struct mainvars *,struct crossfade_info *);

extern mainvars mvps;
extern unsigned int refdisp,crossfadepart,outmode,displaymode,desktopmode;
extern unsigned int intsoundconfig,intsoundcontrol,shutdownatx_enabled;
extern unsigned int playcontrol,playreplay,playcountsong,playlistsave;
extern          int playstartpercent,playstartframe;
extern        char *playstarttime,*playcounttime;
extern unsigned int playcountframe,playcountpercent;
extern unsigned long int08counter,mpxplay_signal_events;
extern int MIXER_var_mutelen;

void main(int argc,char *argv[])
{
 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(argc,argv);

 mpxplay_init_1();
 if(outmode&OUTMODE_TYPE_MASK){
  mpxplay_init_2();
  mpxplay_init_run();
 }

 AU_stop(mvps.aui);
 mpxplay_restore_startdir();
 mpxplay_control_configfile_parts_saveini();  // save variables; save startup;
 mpxplay_control_configfile_close();          // close mpxplay.ini
 mpxplay_close_program(0);
}

static void mpxplay_init_1(void)
{
 struct mainvars *mvp=&mvps;
 mpxplay_control_checkvar(mvp);  // check (and correct the bad) variables
 mpxplay_save_startdir(mvp);
 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)
 mpxplay_mpxinbuf_init(mvp);
 mpxplay_infile_init(mvp);               // static struct build and infile-preinit (cdw_init(), mpx_init())
 mpxplay_playlist_init(mvp);     // alloc playlist & id3 memory
 mpxplay_display_init(mvp);
 refdisp=RDT_INIT_FULL|RDT_OPTIONS|RDT_HEADER;
 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_INIT_EDIT|RDT_EDITOR;
 refresh_desktop(mvp);
 get_allfileinfos(mvp);          // header (filetype),id3,filesize
 playlist_editlist_id3filter(mvp);// -if
 playlist_id3list_save(mvp);     // -is
 write_id3tags(mvp);             // -iw
}

static void mpxplay_init_2(void)
{
 mpxplay_control_startup_getstartpos(&mvps); // get startup position
 playlist_init_playsong(&mvps);   // startup, -pss
 AU_setmixer_all(mvps.aui);       // -scv,-sctr,-scbs
 MIXER_allfuncinit_init();
 mpxplay_videoout_init(mvps.voi);
 check_dosshellstart();           // -xs
 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
 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);
 }
 intsoundcontrol=intsoundconfig;  // -xr needs
 mpxplay_timer_addfunc(&check_seeking,&mvps,MPXPLAY_TIMERTYPE_REPEAT,1);
}

static void mpxplay_empty(void)
{
}

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

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

static unsigned int mtc1_running;

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

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){
    if(!(playcontrol&PLAYC_HIGHSCAN)){
     playstartframe=playstartpercent=0;
     playstarttime=NULL;
    }
    if((mvp->psip->editsidetype&PLT_DOOMQUEUE) && (mvp->psip!=mvp->psie || mvp->direction==0))
     playlist_jukebox_skip(mvp);
    else{
     if(!playlist_skip(mvp)){
      struct playlist_side_info *psi=mvp->psip;
      if((playcontrol&PLAYC_EXITENDLIST) && (!(psi->editsidetype&PLT_DIRECTORY) || (psi->lastentry>=psi->firstsong))){
       mvp->partselect=0;
       return;
      }else
       mpxplay_stop_and_clear(mvp,1);
     }
    }
   }
   open_new_infile(NULL,mvp);
   if(mvp->adone){
    char sout[64];
    sprintf(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);
  refdisp|=RDT_BROWSER|RDT_EDITOR|RDT_OPTIONS|RDT_HEADER;
  mvp->partselect=2;
 }
}

static void open_new_infile(struct frame *frp,struct mainvars *mvp)
{
 struct frame *frp0=mvp->frp0;
 struct mpxplay_audio_decoder_info_s *adi=frp0->infile_infos->audio_decoder_infos;
 unsigned int last_freq=adi->freq,infobits_save;

 if(!frp){
  frp=frp0;
  if(crossfadepart || funcbit_test(intsoundconfig,INTSOUND_DECODER))
   frp++;
 }

 mvp->adone=ADONE_RESTART;
 if(playlist_open_infile(frp,mvp)){
  adi=frp->infile_infos->audio_decoder_infos;

  infobits_save=adi->infobits;

  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) || (adi->freq!=last_freq))
   crossfadepart=CROSS_CLEAR; // don't modify it to crossfade_manual_set(CROSS_CLEAR)

  if(mpxplay_decoders_alloc(frp,1)!=MPXPLAY_ERROR_INFILE_OK)
   goto err_out_oni;

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

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

  funcbit_disable(intsoundcontrol,(INTSOUND_DECODER|INTSOUND_TSR));

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

  funcbit_disable(playcontrol,PLAYC_BEGINOFSONG);
  if(crossfadepart==CROSS_CLEAR){
   if(mvp->seek_relative>=0 && !funcbit_test(intsoundconfig,INTSOUND_DECODER))
    funcbit_enable(playcontrol,PLAYC_BEGINOFSONG);
   MIXER_allfuncinit_reinit();
   MIXER_allfuncinit_restart();
  }
  mpxplay_mpxinbuf_set_intsound(frp,intsoundconfig);

  if(frp!=frp0)
   pds_memxch((char *)frp0,(char *)frp,sizeof(struct frame)-sizeof(frp0->fro));

  if(crossfadepart==CROSS_LOAD)
   crossfade_manual_set(mvp,CROSS_FADE);
  else
   crossfade_manual_set(mvp,CROSS_CLEAR);

  clear_volnum();

  mvp->adone=0;
  mvp->idone=MPXPLAY_ERROR_INFILE_OK;
  mvp->foundfile=1;

  funcbit_copy(intsoundcontrol,intsoundconfig,(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

  playlist_randlist_pushq(mvp->psip,mvp->aktfilenum);
 }
 return;

err_out_oni:
 if(!frp->pcmdec_buffer)
  crossfadepart=CROSS_CLEAR;
 funcbit_copy(intsoundcontrol,intsoundconfig,(INTSOUND_DECODER|INTSOUND_TSR));
 return;
}

long mpxplay_calculate_timesec_to_framenum(struct frame *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 frame *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;

 return index_end;
}

static unsigned int set_playstartpos(struct frame *frp,struct mainvars *mvp)
{
 long newframenum=mvp->seek_relative,index_end;

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

 //fprintf(stdout,"is:%5d ie:%5d pcf:%3d bsf:%d\n",frp->index_start,index_end,frp->index_len,frp->infile_infos->audio_stream->bs_framesize);

 if(playstartpercent)
  playstartframe=frp->index_len*playstartpercent/100;
 if(playstarttime)
  playstartframe=mpxplay_calculate_timesec_to_framenum(frp,playstarttime);
 if(playstartframe){
  newframenum=frp->index_start+playstartframe;
  //if(newframenum>=frp->index_end) // ??? doesn't work
  // return 0;                      //
 }

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

 if(newframenum<0){
  newframenum=-newframenum;
  if(index_end>(frp->index_start+newframenum))
   newframenum=index_end-newframenum;
  else
   newframenum=frp->index_start;
 }else{
  if(!newframenum || (frp->index_end && (newframenum>frp->index_end)))
   newframenum=frp->index_start;
 }

 newframenum=mpxplay_infile_fseek(frp,newframenum);
 if(newframenum<0) // possible in demo mode if song is less than 1 min
  newframenum=mpxplay_infile_fseek(frp,frp->index_start);
 if(newframenum<0) // major seeking problem
  return 0;

 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)
{
 if(playcontrol&PLAYC_ABORTNEXT){
  funcbit_enable(playcontrol,PLAYC_PAUSENEXT);
  funcbit_disable(playcontrol,PLAYC_ABORTNEXT);
 }
 if(playcontrol&PLAYC_AUTOPAUSE)
  funcbit_enable(playcontrol,PLAYC_PAUSENEXT);
 if(playcontrol&PLAYC_PAUSENEXT){
  funcbit_disable(playcontrol,(PLAYC_PAUSENEXT|PLAYC_STARTNEXT));
  AU_stop(mvp->aui);
 }
 if(!(playcontrol&PLAYC_RUNNING)){
  funcbit_enable(mvp->aui->card_controlbits,AUINFOS_CARDCNTRLBIT_DMACLEAR);
  MIXER_allfuncinit_restart();
  crossfade_reset(mvp);
 }
 if(playcontrol&PLAYC_STARTNEXT){
  funcbit_disable(playcontrol,PLAYC_STARTNEXT);
  AU_prestart(mvp->aui);
 }
}

void mpxplay_stop_and_clear(struct mainvars *mvp,unsigned int endwaitflag)
{
 struct playlist_side_info *psip=mvp->psip;
 if(endwaitflag)
  AU_wait_and_stop(mvp->aui);
 else
  AU_stop(mvp->aui);
 mvp->aktfilenum=psip->firstsong-1;
 playlist_randlist_clearall(psip);
 //if(psip==mvp->psie && psip->firstsong<=psip->lastentry)
 // playlist_editorhighline_set(psip,psip->firstsong);
 if(!mvp->foundfile)
  playlist_editorhighline_set(psip,psip->firstsong);
 crossfade_reset(mvp);
 playlist_close_infile(mvp->frp0,mvp);
 mpxplay_infile_close(mvp->frp0+1);
 funcbit_disable(playcontrol,(PLAYC_STARTNEXT|PLAYC_PAUSENEXT|PLAYC_HIGHSCAN|PLAYC_CONTINUOUS_SEEK|PLAYC_ABORTNEXT));
 playstartframe=playstartpercent=playcountframe=playcountpercent=0;
 playstarttime=playcounttime=NULL;
 mvp->step=mvp->direction=mvp->foundfile=0;
 mvp->newfilenum=mvp->newsong=NULL;
 mvp->idone=MPXPLAY_ERROR_INFILE_EOF;
 refdisp|=RDT_BROWSER|RDT_EDITOR|RDT_OPTIONS|RDT_HEADER;
}

//-------------------------------------------------------------------------
//extern unsigned long *int24errorcount_r;
static void main_part2(struct mainvars *mvp)
{
 struct frame *frp0=mvp->frp0,*frp1=frp0+1;
 if(playcontrol&PLAYC_RUNNING){
  if(!(frp0->infile_infos->seektype&MPX_SEEKTYPES_CLEARBUF))
   mvp->fdone=mpxplay_mpxinbuf_buffer_check(frp0);  // read file 0.
  if(crossfadepart==CROSS_FADE)          // if crossfade
   mpxplay_mpxinbuf_buffer_check(frp1);   // read file 1.

  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((mvp->idone==MPXPLAY_ERROR_INFILE_EOF) || ((mvp->idone==MPXPLAY_ERROR_INFILE_NODATA) && mvp->fdone)){ // if end of file 0.
    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;
 }
 if(frp0->index_end && (frp0->frameNum>=frp0->index_end))
  mvp->adone=ADONE_EOF;

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

 if(mvp->newsong){
  struct playlist_side_info *psi=mvp->psie;
  if(psi->psio->editsidetype&PLT_DOOMQUEUE)   // jukebox left side handling
   playlist_jukebox_add_entry(mvp,psi);
 }
 if(mvp->newsong || (mvp->step!=0)){
  struct crossfade_info *cfi=mvp->cfi;
  if(crossfadepart==CROSS_OUT || crossfadepart==CROSS_LOAD){
   playlist_get_newfilenum(mvp);
   playlist_randlist_resetsignflag(mvp->newfilenum);
   refdisp|=RDT_EDITOR;
   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->newfilenum || mvp->newsong){
     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_EOF) && !mvp->newfilenum && playcountsong){
   struct playlist_entry_info *pei;
   if(playreplay&REPLAY_SONG){  // repeat song
    mvp->adone=mvp->fdone=0;
    mvp->seek_absolute=frp0->index_start+1;
    check_seeking(mvp);
    mvp->idone=MPXPLAY_ERROR_INFILE_OK;
    playcountsong--;
    goto mainpart2_end;
   }
   pei=mvp->aktfilenum+1;
   if((outmode&OUTMODE_TYPE_AUDIO) && !(playcontrol&PLAYC_HIGHSCAN)
    && (pei>=mvp->psip->firstsong) && (pei<=mvp->psip->lastentry)
    && mvp->pei0->petime && (mvp->pei0->petime==pei->pstime)
    && (pds_strcmp(mvp->pei0->filename,pei->filename)==0)
   ){ // gapless indexed playing (don't skip to next song, continue the current one)
    if((!(desktopmode&DTM_EDIT_MAGNETFOLLOWSKIP) || (mvp->aktfilenum==mvp->psie->editorhighline)) && !(mvp->psip->editsidetype&PLT_DOOMQUEUE))
     playlist_editorhighline_set(mvp->psip,pei);
    mvp->aktfilenum=pei;
    playlist_pei0_set(mvp,pei);
    mpxplay_calculate_index_start_end(mvp->frp0,mvp,pei);
    playlist_randlist_pushq(mvp->psip,pei);
    mvp->adone=0;
    refdisp|=RDT_BROWSER|RDT_EDITOR|RDT_HEADER;
    playcountsong--;
    if(mvp->psip->editsidetype&PLT_DOOMQUEUE)
     playlist_editlist_delfile_one(mvp->psip,pei-1,EDITLIST_MODE_ALL);
    funcbit_enable(mpxplay_signal_events,MPXPLAY_SIGNALTYPE_NEWFILE);
    goto mainpart2_end;
   }
  } // else finish it and jump to next
  if(!(playcontrol&PLAYC_HIGHSCAN)){
   playstartpercent=playstartframe=playcountframe=playcountpercent=0;
   playstarttime=playcounttime=NULL;
  }
  mvp->partselect=1;
 }else{
  if((intsoundcontrol&INTSOUND_DECODER) && !(intsoundcontrol&INTSOUND_TSR)
    && !funcbit_test(mpxplay_signal_events,MPXPLAY_SIGNALMASK_OTHER)
    && mpxplay_check_buffers_full(mvp)){
   pds_cpu_hlt(); // int08 wakes up
  }
  if(!(playcontrol&PLAYC_RUNNING))
   AU_pause_process(mvp->aui);
 }
mainpart2_end:
 funcbit_disable(mpxplay_signal_events,MPXPLAY_SIGNALMASK_OTHER);

/*{
 char sout[32];
 static unsigned int count;
 sprintf(sout,"err:%8.8X %d",*int24errorcount_r,count);
 display_message(1,0,sout);
}*/
}

unsigned int mpxplay_check_buffers_full(struct mainvars *mvp)
{
 if(!(playcontrol&PLAYC_RUNNING) || ((mvp->aui->card_infobits&AUINFOS_CARDINFOBIT_DMAFULL) && (mvp->frp0->buffertype&PREBUFTYPE_FILLED)))
  return 1;
 return 0;
}

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

 if(((mvp->seek_relative!=0) || mvp->seek_absolute) && frp->infile_funcs && ((MIXER_var_mutelen<3) || !(playcontrol&PLAYC_RUNNING))){
  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(MIXER_var_mutelen)
     MIXER_var_mutelen=255;
    //if(MIXER_getfunction("MIX_MUTE"))
    // MIXER_setfunction("MIX_MUTE",MIXER_SETMODE_ABSOLUTE,255);
    framenum_get=mpxplay_infile_fseek(mpxplay_mpxinbuf_seekhelper_init(frp),mvp->seek_absolute);
   }

   if(framenum_get==MPXPLAY_ERROR_INFILE_EOF){
    mpxplay_mpxinbuf_seekhelper_close(frp);
    MIXER_setfunction("MIX_MUTE",MIXER_SETMODE_RESET,0);
    if(crossfadepart==CROSS_OUT)
     crossfade_manual_set(mvp,CROSS_LOAD);
    mvp->seek_relative=0;
    mvp->adone=ADONE_EOF;
   }else{
    if(!(playcontrol&PLAYC_RUNNING)){
     funcbit_enable(mvp->aui->card_controlbits,AUINFOS_CARDCNTRLBIT_DMACLEAR);
     MIXER_allfuncinit_restart();
    }
    if(mvp->seek_absolute)
     MIXER_setfunction("MIX_MUTE",MIXER_SETMODE_ABSOLUTE,4);
    else
     MIXER_setfunction("MIX_MUTE",MIXER_SETMODE_RESET,0);

    MPXPLAY_INTSOUNDDECODER_DISALLOW;
    mpxplay_mpxinbuf_seekhelper_close(frp);
    frp->frameNum=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(!(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);

    MPXPLAY_INTSOUNDDECODER_ALLOW;

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

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

static void crossfade_auto_control(struct frame *frp0,struct frame *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->editsidetype&PLT_DOOMQUEUE) || (psip->lastentry>psip->firstentry))
           && !(playcontrol&(PLAYC_AUTOPAUSE|PLAYC_PAUSENEXT))){
	    crossfade_manual_set(mvp,CROSS_OUT);
          }
	  break;
   case CROSS_OUT:
	  if(frp0->frameNum>=(cfi->crossfadebegin+cfi->crossfadepoint)){
	   if( (!(psip->editsidetype&PLT_DOOMQUEUE) || (psip->lastentry>psip->firstentry))
            && !(playcontrol&(PLAYC_AUTOPAUSE|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>=cfi->crossfade_in_len)
	   crossfade_manual_set(mvp,CROSS_IN);
	  break;
   case CROSS_IN:
	  if(!(cfi->crossfadetype&CFT_FADEIN) || (frp0->frameNum>=cfi->crossfade_in_len))
	   crossfade_manual_set(mvp,CROSS_CLEAR);
	  break;
  }
 }else{
  if(int08counter>=(cfi->crosscounter+REFRESH_DELAY_JOYMOUSE)){
   cfi->crosswait--;
   cfi->crosscounter=int08counter;
  }
 }
}

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  :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->frp0+1);
 }

 MPXPLAY_INTSOUNDDECODER_DISALLOW;

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

 MIXER_setfunction("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 frame *frp0=mvp->frp0;
 if(cfi->crossfadelimit)
  cfi->crossfadepoint=cfi->crossfade_out_len;
 cfi->crossfadebegin=frp0->allframes-cfi->crossfade_out_len;
}

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

//-------------------------------------------------------------------------
//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) (maybe MPXPLAY.INI or MP3 file is corrupt)!",EEI_INFILENAME|EEI_CRASH},//13
 {"Program crashed (EXCEPTION ERROR) (maybe MPXPLAY.INI or audio file is corrupt)!" ,EEI_INFILENAME|EEI_CRASH}//14
};

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

 funcbit_disable(intsoundcontrol,INTSOUND_DECODER);
 if(!exitcode)
  exitcode=((100*mvp->frp0->frameNum)/mvp->frp0->allframes)+16;
 mpxplay_display_close(mvp);
 AU_close(mvp->aui);
 MIXER_allfuncinit_close();
 mpxplay_control_configfile_parts_close();
 playlist_savelist_save_playlist(mvp,NULL,NULL,playlistsave);
 mpxplay_playlist_close();
 mpxplay_infile_deinit();
 mpxplay_mpxinbuf_close(mvp);

 if(exitcode<15){
  if(exitinfo[exitcode].extrainfo&EEI_INFILENAME)
   display_warning_message(mvp->pei0->filename);
  display_warning_message(exitinfo[exitcode].exiterrmsg);
  if(!(exitinfo[exitcode].extrainfo&EEI_CRASH))
   newfunc_exception_handlers_close();
 }
 newfunc_close();
 if(shutdownatx_enabled)
  pds_shutdown_atx();

 newfunc_error_handlers_close();
 exit(exitcode);
}
