The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.
/*
 * Control interface to front ends.
 * written/copyrights 1997 by Brian Foutz (and Michael Hipp) 
 */

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>

#include <limits.h>

#include <sys/socket.h>

#include "control_tk3play.h"

#include "mpg123.h"
#include "buffer.h"

int mode = MODE_STOPPED;
int init = 0;
int framecnt = 0;
AudioInfo sai, oldsai;

extern struct audio_info_struct ai;
extern FILE *filept;
extern int tabsel_123[2][3][16];
extern int buffer_pid;
int rewindspeed;

#ifndef PATH_MAX
#define PATH_MAX 1024
#endif

int tk3play_sendmsg(int type, float data)
{
  int n;
  n = printf("%d %f\n",type,data);
  fflush(stdout);
  return n;
}

int still_playing_old(void)
{
  int channels, outrate, bufferused;

  channels = sai.stereo ? 2 : 1;
  outrate = sai.frequency*channels*2;
  bufferused = xfermem_get_usedspace(buffermem);

  if (mode == MODE_PLAYING_OLD_FINISHED_DECODING_NEW) {

    return (bufferused/(float)outrate > sai.length*8/(float)sai.bitrate);
  }

  return ((bufferused)/(float)outrate > rd->tell(rd)*8/(float)sai.bitrate);
}

int buffer_used(void) 
{
  if (param.usebuffer)
    return xfermem_get_usedspace(buffermem);
  else
   return 0;
}

float calc_time(void)
{
  int channels, outrate, bufferused, old_channels, old_outrate;
  int new_buf_bytes, old_buf_bytes;
  float old_buf_sec, new_buf_sec, old_total_sec;
  float stream_sec, buffer_sec, time;

  if (!param.usebuffer)
    return rd->tell(rd)*8/(float)sai.bitrate;

  channels = sai.stereo ? 2 : 1;
  old_channels = oldsai.stereo ? 2 : 1;
  outrate = sai.frequency*channels*2;
  old_outrate = oldsai.frequency*old_channels*2;
  bufferused = xfermem_get_usedspace(buffermem);

  buffer_sec = bufferused/(float)outrate;
  time = 0.0;

  if (mode == MODE_PLAYING_AND_DECODING) {

    stream_sec = rd->tell(rd)*8/(float)sai.bitrate;
    time = stream_sec - buffer_sec;
  }

  if (mode == MODE_PLAYING_NOT_DECODING) {

    time = sai.length*8/(float)sai.bitrate - buffer_sec;
  }

  if (mode == MODE_PLAYING_OLD_DECODING_NEW) {

    stream_sec = rd->tell(rd)*8/(float)sai.bitrate;
    new_buf_bytes = stream_sec * outrate;
    old_buf_bytes = bufferused - new_buf_bytes;
    old_buf_sec = old_buf_bytes / (float)old_outrate;
    old_total_sec = oldsai.length*8/(float)oldsai.bitrate;
    time = old_total_sec - old_buf_sec;
  }

  if (mode == MODE_PLAYING_OLD_FINISHED_DECODING_NEW) {

    new_buf_sec = sai.length*8/(float)sai.bitrate;
    new_buf_bytes = new_buf_sec * outrate;
    old_buf_bytes = bufferused - new_buf_bytes;
    old_buf_sec = old_buf_bytes / (float)old_outrate;
    old_total_sec = oldsai.length*8/(float)oldsai.bitrate;
    time = old_total_sec - old_buf_sec;
  }

  return time;
}

int tk3play_handlemsg(struct frame *fr,struct timeval *timeout)
{
  char filename[PATH_MAX];
  fd_set readfds;
  char *line;
  int n, scann, ok;
  int rtype;
  int rdata;
  static int oldmode;

  FD_ZERO(&readfds);
  FD_SET(0,&readfds);
  n = select(fileno(stdin)+1,&readfds,NULL,NULL,timeout);
  if (n == 0) return 0;
  scann = scanf("%d %d",&rtype,&rdata);
  while (scann == 2) {

  switch(rtype) {
  case MSG_CTRL:
    switch(rdata) {

    case PLAY_STOP:
      if (mode != MODE_STOPPED) {
	buffer_resync();
	if (mode == MODE_PLAYING_AND_DECODING ||
            mode == MODE_PLAYING_OLD_DECODING_NEW) {
	  rd->close(rd);
	}
	mode = MODE_STOPPED;
      }
      /* tk3play_sendmsg(MSG_RESPONSE,PLAY_STOP); */
      break;

    case PLAY_PAUSE:
      if (mode != MODE_STOPPED) {
	if (mode == MODE_PAUSED) {
	  buffer_start();
	  mode = oldmode;
	}
	else {
	  oldmode = mode;
	  mode = MODE_PAUSED;
	  buffer_stop();
	}
      }
      /* tk3play_sendmsg(MSG_RESPONSE,PLAY_PAUSE); */
      break;
    }
    break;

  case MSG_SONG:
    fcntl(0,F_SETFL,0);
    line = fgets(filename,PATH_MAX,stdin);
    line = fgets(filename,PATH_MAX,stdin);
    fcntl(0,F_SETFL,O_NONBLOCK);

    if (line == NULL) {
      fprintf(stderr,"Error reading filename!\n");
      exit(1);
    }
    *(filename+strlen(filename)-1)=0;

    if (mode == MODE_PLAYING_AND_DECODING ||
	mode == MODE_PLAYING_OLD_DECODING_NEW) {
      fprintf(stderr,"Still decoding old song, filename ignored\n");
    }

    if (mode == MODE_PLAYING_OLD_FINISHED_DECODING_NEW) {
      fprintf(stderr,"One song has been buffered, ignoring new filename\n");
    }

    if (mode == MODE_STOPPED) {
	mode = MODE_PLAYING_AND_DECODING;
	open_stream(filename,-1);
	init = 1;
	framecnt = 0;
	read_frame_init();
    }

    if (mode == MODE_PLAYING_NOT_DECODING) {
      mode = MODE_PLAYING_OLD_DECODING_NEW;
      open_stream(filename,-1);
      init = 1;
      framecnt = 0;
      read_frame_init();
    }

    /* tk3play_sendmsg(MSG_RESPONSE,MSG_SONG); */
    break;

  case MSG_JUMPTO:
    if (buffer_used() > 0)
       buffer_resync();

    ok = 1;
    if (rdata < framecnt) {
      rd->rewind(rd);
      read_frame_init();
      for (framecnt = 0; ok && framecnt < rdata; framecnt++) {
        ok = read_frame(fr);
	if (fr->lay == 3)
	  set_pointer(fr->sideInfoSize,512);
      }
    } else {
      for (;ok && framecnt < rdata; framecnt++) {
        ok = read_frame(fr);
	if (fr->lay == 3)
	  set_pointer(fr->sideInfoSize,512);
      }
    }
    mode = MODE_PLAYING_AND_DECODING;
    break;

  case MSG_HALF:
    param.halfspeed = rdata;
    break;

  case MSG_DOUBLE:
    if (rdata < 0) {
       rewindspeed = -rdata;
       param.doublespeed = 0;
    } else {
       param.doublespeed = rdata;
       rewindspeed = 0;
    }
    break;

  case MSG_SCALE:
    param.outscale = rdata;
    make_decode_tables(param.outscale);
    break;

  case MSG_QUIT:
#ifndef OS2
    if (param.usebuffer) {
      buffer_resync();
      xfermem_done_writer (buffermem);
      waitpid (buffer_pid, NULL, 0);
      xfermem_done (buffermem);
    }
    else {
#endif
      audio_flush(param.outmode, &ai);
      free (pcm_sample);
#ifndef OS2
    }
#endif

    if(param.outmode==DECODE_AUDIO)
      audio_close(&ai);

    tk3play_sendmsg(MSG_RESPONSE,MSG_QUIT);
    
    exit( 0 );
  }

  scann = scanf("%d %d",&rtype,&rdata);
  }

  if (scann != -1) {
    fprintf(stderr,"scann = %d\n",scann);
    fprintf(stderr,"Error scanning input control line!\n");
    exit(1);
  }

  return 1;
}

  
void control_tk3play(struct mpstr *mp,struct frame *fr) 
{
  struct timeval timeout;
  static int hp = 0;

  fcntl(0,F_SETFL,O_NONBLOCK);

  while(1) {
    if (mode == MODE_PLAYING_AND_DECODING) {
      timeout.tv_sec = 0;
      timeout.tv_usec = 0;
      if (tk3play_handlemsg(fr,&timeout))
	continue;

      if (!read_frame(fr)) {
        sai.length = rd->tell(rd);
        rd->close(rd);
	tk3play_sendmsg(MSG_FRAMES,framecnt);
        tk3play_sendmsg(MSG_NEXT,0);
        if (param.usebuffer) {
	  mode = MODE_PLAYING_NOT_DECODING;
        } else {
          mode = MODE_STOPPED;
        }
	continue;
      }

      framecnt++;
      if (framecnt < param.startFrame) {
        if (fr->lay == 3)
          set_pointer(fr->sideInfoSize,512);
        continue;
      }
      if (param.doublespeed && (framecnt % param.doublespeed)) {
	if (fr->lay == 3)
          set_pointer(fr->sideInfoSize,512);
      } else {
        play_frame(mp,init,fr);
        if (init) {
	  sai.bitrate = tabsel_123[fr->lsf][fr->lay-1][fr->bitrate_index] 
                        * 1000;
          sai.frequency = freqs[fr->sampling_frequency];
	  sai.stereo = fr->stereo;
	  sai.type = fr->lay;
	  sai.sample = 16;
	  printf("%d %d %d %d %d %d\n",MSG_INFO,sai.bitrate,sai.frequency,
	         sai.stereo,sai.type,sai.sample);
          fflush(stdout);
	  tk3play_sendmsg(MSG_FRAMES,framecnt);
	  tk3play_sendmsg(MSG_BUFFER,buffer_used());
	  tk3play_sendmsg(MSG_TIME,calc_time());
	  init = param.startFrame = 0;
        }
      }

      if (param.halfspeed) 
	if (hp--) {
          framecnt--;
	}
        else
	  hp = param.halfspeed - 1;

      if (rewindspeed && (framecnt>1)) {
        rd->back_frame(rd,fr,rewindspeed+1);
	framecnt -= rewindspeed+1;
      }

      if(!(framecnt & 0xf)) {
	tk3play_sendmsg(MSG_FRAMES,framecnt);
	tk3play_sendmsg(MSG_BUFFER,buffer_used());
	tk3play_sendmsg(MSG_TIME,calc_time());
      }

    }

    if (mode == MODE_PLAYING_OLD_DECODING_NEW) {
      timeout.tv_sec = 0;
      timeout.tv_usec = 0;
      if (tk3play_handlemsg(fr,&timeout))
	continue;

      if (!read_frame(fr)) {
	sai.length = rd->tell(rd);
        rd->close(rd);
	tk3play_sendmsg(MSG_FRAMES,framecnt);
	mode = MODE_PLAYING_OLD_FINISHED_DECODING_NEW;
	continue;
      }
      play_frame(mp,init,fr);
      framecnt++;

      if (init) {
	oldsai = sai;
	sai.bitrate = tabsel_123[fr->lsf][fr->lay-1][fr->bitrate_index] * 1000;
	sai.frequency = freqs[fr->sampling_frequency];
	sai.stereo = fr->stereo;
	sai.type = fr->lay;
	sai.sample = 16;
	tk3play_sendmsg(MSG_FRAMES,framecnt);
	tk3play_sendmsg(MSG_BUFFER,buffer_used());
	tk3play_sendmsg(MSG_TIME,calc_time());
	init = 0;
      }

      if (!still_playing_old()) {
	mode = MODE_PLAYING_AND_DECODING;
	tk3play_sendmsg(MSG_SONGDONE,0);
	tk3play_sendmsg(MSG_BUFFER,buffer_used());
	tk3play_sendmsg(MSG_TIME,calc_time());
	printf("%d %d %d %d %d %d\n",MSG_INFO,sai.bitrate,sai.frequency,
	       sai.stereo,sai.type,sai.sample);
        fflush(stdout);
      }

      if(!(framecnt & 0xf)) {
	tk3play_sendmsg(MSG_FRAMES,framecnt);
	tk3play_sendmsg(MSG_BUFFER,buffer_used());
	tk3play_sendmsg(MSG_TIME,calc_time());
      }
    }

    if (mode == MODE_PLAYING_NOT_DECODING) {
      timeout.tv_sec = 0;
      timeout.tv_usec = 200000;
      if (tk3play_handlemsg(fr,&timeout))
	continue;

      tk3play_sendmsg(MSG_BUFFER,buffer_used());
      tk3play_sendmsg(MSG_TIME,calc_time());
      if (xfermem_get_usedspace(buffermem) == 0) {
	mode = MODE_STOPPED;
	tk3play_sendmsg(MSG_SONGDONE,0);
      }
    }

    if (mode == MODE_PLAYING_OLD_FINISHED_DECODING_NEW) {
      timeout.tv_sec = 0;
      timeout.tv_usec = 200000;
      if (tk3play_handlemsg(fr,&timeout))
	continue;

      tk3play_sendmsg(MSG_BUFFER,buffer_used());
      tk3play_sendmsg(MSG_TIME,calc_time());
      if (!still_playing_old()) {
	mode = MODE_PLAYING_NOT_DECODING;
	tk3play_sendmsg(MSG_SONGDONE,0);
	printf("%d %d %d %d %d %d\n",MSG_INFO,sai.bitrate,sai.frequency,
	       sai.stereo,sai.type,sai.sample);
        fflush(stdout);
	tk3play_sendmsg(MSG_NEXT,0);
      }
    }

    if (mode == MODE_STOPPED || mode == MODE_PAUSED) {
      while (!tk3play_handlemsg(fr,NULL));
    }

  }     /* while (1) */
}