コード例 #1
0
ファイル: uade123.c プロジェクト: 9a3eedi/Droidsound
int main(int argc, char *argv[])
{
  int i;
  char configname[PATH_MAX] = "";
  char playername[PATH_MAX] = "";
  char scorename[PATH_MAX] = "";
  int playernamegiven = 0;
  char tmpstr[PATH_MAX + 256];
  long subsong = -1;
  FILE *listfile = NULL;
  int have_modules = 0;
  int ret;
  char *endptr;
  int uadeconf_loaded, songconf_loaded;
  char songconfname[PATH_MAX] = "";
  char uadeconfname[PATH_MAX];
  struct uade_config uc_cmdline;
  char songoptions[256] = "";
  int have_song_options = 0;
  int plistdir;
  int scanmode = 0;

  struct uade_state state = {};
  char *basedir;

  enum {
    OPT_FIRST = 0x1FFF,
    OPT_BASEDIR,
    OPT_REPEAT,
    OPT_SCAN,
    OPT_SCOPE,
    OPT_SET,
    OPT_STDERR,
    OPT_VERSION
  };

  struct option long_options[] = {
    {"ao-option",        1, NULL, UC_AO_OPTION},
    {"basedir",          1, NULL, OPT_BASEDIR},
    {"buffer-time",      1, NULL, UC_BUFFER_TIME},
    {"cygwin",           0, NULL, UC_CYGWIN_DRIVE_WORKAROUND},
    {"debug",            0, NULL, 'd'},
    {"detect-format-by-content", 0, NULL, UC_CONTENT_DETECTION},
    {"disable-timeouts", 0, NULL, UC_DISABLE_TIMEOUTS},
    {"enable-timeouts",  0, NULL, UC_ENABLE_TIMEOUTS},
    {"ep-option",        1, NULL, 'x'},
    {"filter",           2, NULL, UC_FILTER_TYPE},
    {"force-led",        1, NULL, UC_FORCE_LED},
    {"frequency",        1, NULL, UC_FREQUENCY},
    {"gain",             1, NULL, 'G'},
    {"get-info",         0, NULL, 'g'},
    {"headphones",       0, NULL, UC_HEADPHONES},
    {"headphones2",      0, NULL, UC_HEADPHONES2},
    {"help",             0, NULL, 'h'},
    {"ignore",           0, NULL, 'i'},
    {"interpolator",     1, NULL, UC_RESAMPLER},
    {"jump",             1, NULL, 'j'},
    {"keys",             1, NULL, 'k'},
    {"list",             1, NULL, '@'},
    {"magic",            0, NULL, UC_CONTENT_DETECTION},
    {"no-ep-end-detect", 0, NULL, 'n'},
    {"no-song-end",      0, NULL, 'n'},
    {"normalise",        2, NULL, UC_NORMALISE},
    {"ntsc",             0, NULL, UC_NTSC},
    {"one",              0, NULL, '1'},
    {"pal",              0, NULL, UC_PAL},
    {"panning",          1, NULL, 'p'},
    {"recursive",        0, NULL, 'r'},
    {"repeat",           0, NULL, OPT_REPEAT},
    {"resampler",        1, NULL, UC_RESAMPLER},
    {"scan",             0, NULL, OPT_SCAN},
    {"scope",            0, NULL, OPT_SCOPE},
    {"shuffle",          0, NULL, 'z'},
    {"set",              1, NULL, OPT_SET},
    {"silence-timeout",  1, NULL, 'y'},
    {"speed-hack",       0, NULL, UC_SPEED_HACK},
    {"stderr",           0, NULL, OPT_STDERR},
    {"stdout",           0, NULL, 'c'},
    {"subsong",          1, NULL, 's'},
    {"subsong-timeout",  1, NULL, 'w'},
    {"timeout",          1, NULL, 't'},
    {"verbose",          0, NULL, 'v'},
    {"version",          0, NULL, OPT_VERSION},
    {NULL,               0, NULL, 0}
  };

  uade_config_set_defaults(&uc_cmdline);

  if (!playlist_init(&uade_playlist))
    die("Can not initialize playlist.\n");

#define GET_OPT_STRING(x) if (strlcpy((x), optarg, sizeof(x)) >= sizeof(x)) { die("Too long a string for option %c.\n", ret); }

  while ((ret = getopt_long(argc, argv, "@:1cde:f:gG:hij:k:np:P:rs:S:t:u:vw:x:y:z", long_options, 0)) != -1) {
    switch (ret) {

    case '@':
      listfile = fopen(optarg, "r");
      if (listfile == NULL)
    die("Can not open list file: %s\n", optarg);
      break;

    case '1':
      uade_set_config_option(&uc_cmdline, UC_ONE_SUBSONG, NULL);
      break;

    case 'c':
      strlcpy(uade_output_file_name, "/dev/stdout", sizeof uade_output_file_name);
      /* Output sample data to stdout so do not print anything on stdout */
      uade_terminal_file = stderr;
      break;

    case 'd':
      debug_mode = 1;
      uade_debug_trigger = 1;
      break;
    case 'e':
      GET_OPT_STRING(uade_output_file_format);
      break;

    case 'f':
      GET_OPT_STRING(uade_output_file_name);
      break;

    case 'g':
      uade_info_mode = 1;
      uade_no_audio_output = 1;
      uade_no_text_output = 1;
      uade_set_config_option(&uc_cmdline, UC_ACTION_KEYS, "off");
      break;

    case 'G':
      uade_set_config_option(&uc_cmdline, UC_GAIN, optarg);
      break;

    case 'h':
      print_help();
      exit(0);

    case 'i':
      uade_set_config_option(&uc_cmdline, UC_IGNORE_PLAYER_CHECK, NULL);
      break;

    case 'j':
      uade_jump_pos = strtod(optarg, &endptr);
      if (*endptr != 0 || uade_jump_pos < 0.0)
    die("Invalid jump position: %s\n", optarg);
      break;

    case 'k':
      uade_set_config_option(&uc_cmdline, UC_ACTION_KEYS, optarg);
      break;

    case 'n':
      uade_set_config_option(&uc_cmdline, UC_NO_EP_END, NULL);
      break;

    case 'p':
      uade_set_config_option(&uc_cmdline, UC_PANNING_VALUE, optarg);
      break;

    case 'P':
      GET_OPT_STRING(playername);
      playernamegiven = 1;
      have_modules = 1;
      break;

    case 'r':
      uade_set_config_option(&uc_cmdline, UC_RECURSIVE_MODE, NULL);
      break;

    case 's':
      subsong = strtol(optarg, &endptr, 10);
      if (*endptr != 0 || subsong < 0 || subsong > 255)
    die("Invalid subsong string: %s\n", optarg);
      break;

    case 'S':
      GET_OPT_STRING(scorename);
      break;

    case 't':
      uade_set_config_option(&uc_cmdline, UC_TIMEOUT_VALUE, optarg);
      break;

    case 'u':
      GET_OPT_STRING(uadename);
      break;

    case 'v':
      uade_set_config_option(&uc_cmdline, UC_VERBOSE, NULL);
      break;

    case 'w':
      uade_set_config_option(&uc_cmdline, UC_SUBSONG_TIMEOUT_VALUE, optarg);
      break;

    case 'x':
      uade_set_config_option(&uc_cmdline, UC_EAGLEPLAYER_OPTION, optarg);
      break;

    case 'y':
      uade_set_config_option(&uc_cmdline, UC_SILENCE_TIMEOUT_VALUE, optarg);
      break;

    case 'z':
      uade_set_config_option(&uc_cmdline, UC_RANDOM_PLAY, NULL);
      break;

    case '?':
    case ':':
      exit(1);

    case OPT_BASEDIR:
      uade_set_config_option(&uc_cmdline, UC_BASE_DIR, optarg);
      break;

    case OPT_REPEAT:
      playlist_repeat(&uade_playlist);
      break;

    case OPT_SCAN:
      scanmode = 1;
      /* Set recursive mode in scan mode */
      uade_set_config_option(&uc_cmdline, UC_RECURSIVE_MODE, NULL);
      break;

    case OPT_SCOPE:
      uade_no_text_output = 1;
      uade_set_config_option(&uc_cmdline, UC_USE_TEXT_SCOPE, NULL);
      break;

    case OPT_SET:
      have_song_options = 1;
      strlcpy(songoptions, optarg, sizeof songoptions);
      break;

    case OPT_STDERR:
      uade_terminal_file = stderr;
      break;

    case OPT_VERSION:
      printf("uade123 %s\n", UADE_VERSION);
      exit(0);
      break;

    case UC_AO_OPTION:
    case UC_BUFFER_TIME:
    case UC_FILTER_TYPE:
    case UC_FORCE_LED:
    case UC_FREQUENCY:
    case UC_NORMALISE:
    case UC_RESAMPLER:
      uade_set_config_option(&uc_cmdline, ret, optarg);
      break;

    case UC_CONTENT_DETECTION:
    case UC_CYGWIN_DRIVE_WORKAROUND:
    case UC_DISABLE_TIMEOUTS:
    case UC_ENABLE_TIMEOUTS:
    case UC_HEADPHONES:
    case UC_HEADPHONES2:
    case UC_NTSC:
    case UC_PAL:
    case UC_SPEED_HACK:
      uade_set_config_option(&uc_cmdline, ret, NULL);
      break;

    default:
      die("Impossible option.\n");
    }
  }

  basedir = NULL;
  if (uc_cmdline.basedir_set) {
      basedir = uc_cmdline.basedir.name;
  }
  
  uadeconf_loaded = uade_load_initial_config(&state, uadeconfname, sizeof uadeconfname, basedir);

  /* Merge loaded configurations and command line options */
  uade_merge_configs(&state.config, &uc_cmdline);

  if (uadeconf_loaded == 0) {
    debug(state.config.verbose, "Not able to load uade.conf from ~/.uade2/ or %s/.\n", state.config.basedir.name);
  } else {
    debug(state.config.verbose, "Loaded configuration: %s\n", uadeconfname);
  }

  songconf_loaded = uade_load_initial_song_conf(songconfname, sizeof songconfname, &state.permconfig, &uc_cmdline, &state);

  if (songconf_loaded == 0) {
    debug(state.config.verbose, "Not able to load song.conf from ~/.uade2/ or %s/.\n", state.config.basedir.name);
  } else {
    debug(state.config.verbose, "Loaded song.conf: %s\n", songconfname);
  }

  /* Read play list from file */
  if (listfile != NULL) {
    while (xfgets(tmpstr, sizeof(tmpstr), listfile) != NULL) {
      if (tmpstr[0] == '#')
    continue;
      if (tmpstr[strlen(tmpstr) - 1] == '\n')
    tmpstr[strlen(tmpstr) - 1] = 0;
      playlist_add(&uade_playlist, tmpstr, state.config.recursive_mode, state.config.cygwin_drive_workaround);
    }
    fclose(listfile);
    listfile = NULL;
    have_modules = 1;
  }

  /* Read play list from command line parameters */
  for (i = optind; i < argc; i++) {
    /* Play files */
    playlist_add(&uade_playlist, argv[i], uc_eff.recursive_mode, uc_eff.cygwin_drive_workaround);
    have_modules = 1;
  }

  if (scanmode) {
    scan_playlist(&state.config);
    exit(0);
  }

  if (have_song_options) {
    set_song_options(&songconf_loaded, songoptions, songconfname, sizeof songconfname);
    exit(0);
  }

  load_content_db(&state.config, &state);

  if (state.config.random_play)
    playlist_randomize(&uade_playlist);

  if (have_modules == 0) {
    print_help();
    exit(0);
  }

  /* we want to control terminal differently in debug mode */
  if (debug_mode)
    state.config.action_keys = 0;

  if (state.config.action_keys)
    setup_terminal();

  do {
    DIR *bd = opendir(state.config.basedir.name);
    if (bd == NULL)
      dieerror("Could not access dir %s", state.config.basedir.name);

    closedir(bd);

    snprintf(configname, sizeof configname, "%s/uaerc", state.config.basedir.name);

    if (scorename[0] == 0)
      snprintf(scorename, sizeof scorename, "%s/score", state.config.basedir.name);

    if (uadename[0] == 0)
      strlcpy(uadename, UADE_CONFIG_UADE_CORE, sizeof uadename);

    if (access(configname, R_OK))
      dieerror("Could not read %s", configname);

    if (access(scorename, R_OK))
      dieerror("Could not read %s", scorename);

    if (access(uadename, X_OK))
      dieerror("Could not execute %s", uadename);

  } while (0);

  setup_sighandlers();

  uade_spawn(&state, uadename, configname);

  if (!audio_init(&state.config))
    goto cleanup;

  plistdir = UADE_PLAY_CURRENT;

  while (1) {

    ssize_t filesize;

    /* modulename and songname are a bit different. modulename is the name
       of the song from uadecore's point of view and songname is the
       name of the song from user point of view. Sound core considers all
       custom songs to be players (instead of modules) and therefore modulename
       will become a zero-string with custom songs. */
    char modulename[PATH_MAX];
    char songname[PATH_MAX];

    if (!playlist_get(modulename, sizeof modulename, &uade_playlist, plistdir))
      break;

    plistdir = UADE_PLAY_NEXT;

    state.config = state.permconfig;
    state.song = NULL;
    state.ep = NULL;

    if (uc_cmdline.verbose)
      state.config.verbose = 1;

    if (playernamegiven == 0) {
      debug(state.config.verbose, "\n");

      if (!uade_is_our_file(modulename, 0, &state)) {
    __android_log_print(ANDROID_LOG_VERBOSE, "UADE", "Unknown format: %s\n", modulename);
    continue;
      }

      debug(state.config.verbose, "Player candidate: %s\n", state.ep->playername);

      if (strcmp(state.ep->playername, "custom") == 0) {
    strlcpy(playername, modulename, sizeof playername);
    modulename[0] = 0;
      } else {
    snprintf(playername, sizeof playername, "%s/players/%s", uc_cmdline.basedir.name, state.ep->playername);
      }
    }

    if (strlen(playername) == 0) {
      __android_log_print(ANDROID_LOG_VERBOSE, "UADE", "Error: an empty player name given\n");
      goto cleanup;
    }

    /* If no modulename given, try the playername as it can be a custom song */
    strlcpy(songname, modulename[0] ? modulename : playername, sizeof songname);

    if (!uade_alloc_song(&state, songname)) {
      __android_log_print(ANDROID_LOG_VERBOSE, "UADE", "Can not read %s: %s\n", songname, strerror(errno));
      continue;
    }

    /* The order of parameter processing is important:
     * 0. set uade.conf options (done before this)
     * 1. set eagleplayer attributes
     * 2. set song attributes
     * 3. set command line options
     */

    if (state.ep != NULL)
      uade_set_ep_attributes(&state);

    if (uade_set_song_attributes(&state, playername, sizeof playername)) {
      debug(state.config.verbose, "Song rejected based on attributes: %s\n",
        state.song->module_filename);
      uade_unalloc_song(&state);
      continue;
    }

    uade_merge_configs(&state.config, &uc_cmdline);

    /* Now we have the final configuration in "uc". */

    uade_set_effects(&state);

    if ((filesize = stat_file_size(playername)) < 0) {
      __android_log_print(ANDROID_LOG_VERBOSE, "UADE", "Can not find player: %s (%s)\n", playername, strerror(errno));
      uade_unalloc_song(&state);
      continue;
    }

    debug(state.config.verbose, "Player: %s (%zd bytes)\n", playername, filesize);

    __android_log_print(ANDROID_LOG_VERBOSE, "UADE", "Song: %s (%zd bytes)\n", state.song->module_filename, state.song->bufsize);

    ret = uade_song_initialization(scorename, playername, modulename, &state);
    switch (ret) {
    case UADECORE_INIT_OK:
      break;

    case UADECORE_INIT_ERROR:
      uade_unalloc_song(&state);
      goto cleanup;

    case UADECORE_CANT_PLAY:
    debug(state.config.verbose, "Uadecore refuses to play the song.\n");
    uade_unalloc_song(&state);
    continue; /* jump to the beginning of playlist loop */

    default:
      die("Unknown error from uade_song_initialization()\n");
    }

    if (subsong >= 0)
      uade_set_subsong(subsong, &state.ipc);

    plistdir = play_loop(&state);

    uade_unalloc_song(&state);

    if (plistdir == UADE_PLAY_FAILURE)
      goto cleanup;
    else if (plistdir == UADE_PLAY_EXIT)
      break;
  }

  debug(uc_cmdline.verbose || state.permconfig.verbose, "Killing child (%d).\n", uadepid);
  cleanup(&state);
  return 0;

 cleanup:
  cleanup(&state);
  return 1;
}
コード例 #2
0
ファイル: playloop.c プロジェクト: QaDeS/droidsound
int play_loop(struct uade_state *state)
{
  uint16_t *sm;
  int i;
  uint32_t *u32ptr;

  uint8_t space[UADE_MAX_MESSAGE_SIZE];
  struct uade_msg *um = (struct uade_msg *) space;

  uint8_t sampledata[UADE_MAX_MESSAGE_SIZE];
  int left = 0;
  int what_was_left = 0;

  int subsong_end = 0;
  int next_song = 0;
  int ret;
  int new_sub;
  int tailbytes = 0;
  int playbytes;
  char *reason;
  int64_t skip_bytes;

  int record_playtime = 1;

  int64_t subsong_bytes = 0;
  int deciseconds;
  int jump_sub = 0;
  int have_subsong_info = 0;

  const int framesize = UADE_BYTES_PER_SAMPLE * UADE_CHANNELS;
  const int bytes_per_second = UADE_BYTES_PER_FRAME * state->config.frequency;

  enum uade_control_state controlstate = UADE_S_STATE;

  int plistdir = UADE_PLAY_NEXT;

  struct uade_ipc *ipc = &state->ipc;
  struct uade_song *us = state->song;
  struct uade_effect *ue = &state->effects;
  struct uade_config *uc = &state->config;

  uade_effect_reset_internals();

  /* Skip bytes must be a multiple of audio frame size */
  skip_bytes = uade_jump_pos * bytes_per_second;
  skip_bytes = (skip_bytes / framesize) * framesize;

  test_song_end_trigger(); /* clear a pending SIGINT */

  while (next_song == 0) {

    if (uade_terminated) {
      if (!uade_no_text_output)
	tprintf("\n");

      return UADE_PLAY_FAILURE;
    }

    if (controlstate == UADE_S_STATE) {

      if (skip_bytes == 0) {
	deciseconds = subsong_bytes * 10 / bytes_per_second;
	if (!uade_no_text_output) {
	  if (us->playtime >= 0) {
	    int ptimesecs = us->playtime / 1000;
	    int ptimesubsecs = (us->playtime / 100) % 10;
	    tprintf("Playing time position %d.%ds in subsong %d (all subs %d.%ds)  \r", deciseconds / 10, deciseconds % 10, us->cur_subsong == -1 ? 0 : us->cur_subsong, ptimesecs, ptimesubsecs);
	  } else {
	    tprintf("Playing time position %d.%ds in subsong %d                \r", deciseconds / 10, deciseconds % 10,  us->cur_subsong == -1 ? 0 : us->cur_subsong);
	  }
	  fflush(stdout);
	}
      }

      if (uc->action_keys) {
	switch ((ret = poll_terminal())) {
	case 0:
	  break;
	case '<':
	  plistdir = UADE_PLAY_PREVIOUS;
	  uade_song_end_trigger = 1;
	  record_playtime = 0;
	  break;
	case '.':
	  if (skip_bytes == 0) {
	    __android_log_print(ANDROID_LOG_VERBOSE, "UADE", "\nSkipping 10 seconds\n");
	    skip_bytes = bytes_per_second * 10;
	  }
	  break;
	case ' ':
	case 'b':
	  subsong_end = 1;
	  record_playtime = 0;
	  break;
	case 'c':
	  pause_terminal();
	  break;
	case 'f':
	  uade_set_config_option(uc, UC_FORCE_LED, uc->led_state ? "off" : "on");
	  tprintf("\nForcing LED %s\n", (uc->led_state & 1) ? "ON" : "OFF");
	  uade_send_filter_command(state);
	  break;
	case 'g':
	  uade_effect_toggle(ue, UADE_EFFECT_GAIN);
	  tprintf("\nGain effect %s %s\n", uade_effect_is_enabled(ue, UADE_EFFECT_GAIN) ? "ON" : "OFF", (uade_effect_is_enabled(ue, UADE_EFFECT_ALLOW) == 0 && uade_effect_is_enabled(ue, UADE_EFFECT_GAIN)) ? "(Remember to turn ON postprocessing!)" : "");
	  break;
	case 'h':
	  tprintf("\n\n");
	  print_action_keys();
	  tprintf("\n");
	  break;
	case 'H':
	  uade_effect_toggle(ue, UADE_EFFECT_HEADPHONES);
	  tprintf("\nHeadphones effect %s %s\n", uade_effect_is_enabled(ue, UADE_EFFECT_HEADPHONES) ? "ON" : "OFF", (uade_effect_is_enabled(ue, UADE_EFFECT_ALLOW) == 0 && uade_effect_is_enabled(ue, UADE_EFFECT_HEADPHONES) == 1) ? "(Remember to turn ON postprocessing!)" : "");
	  break;
	case 'i':
	  if (!uade_no_text_output)
	    print_song_info(us, UADE_MODULE_INFO);
	  break;
	case 'I':
	  if (!uade_no_text_output)
	    print_song_info(us, UADE_HEX_DUMP_INFO);
	  break;
	case '\n':
	case 'n':
	  uade_song_end_trigger = 1;
	  record_playtime = 0;
	  break;
	case 'N':
	  uade_effect_toggle(ue, UADE_EFFECT_NORMALISE);
	  tprintf("\nNormalise effect %s\n", uade_effect_is_enabled(ue, UADE_EFFECT_NORMALISE) ? "ON" : "OFF");
	  break;
	case 'p':
	  uade_effect_toggle(ue, UADE_EFFECT_ALLOW);
	  tprintf("\nPostprocessing effects %s\n", uade_effect_is_enabled(ue, UADE_EFFECT_ALLOW) ? "ON" : "OFF");
	  break;
	case 'P':
	  uade_effect_toggle(ue, UADE_EFFECT_PAN);
	  tprintf("\nPanning effect %s %s\n", uade_effect_is_enabled(ue, UADE_EFFECT_PAN) ? "ON" : "OFF", (uade_effect_is_enabled(ue, UADE_EFFECT_ALLOW) == 0 && uade_effect_is_enabled(ue, UADE_EFFECT_PAN) == 1) ? "(Remember to turn ON postprocessing!)" : "");
	  break;

	case 'q':
	  tprintf("\n");
	  return UADE_PLAY_EXIT;

	case 's':
	  playlist_random(&uade_playlist, -1);
	  tprintf("\n%s mode\n", uade_playlist.randomize ? "Shuffle" : "Normal");
	  break;
	case 'v':
	  uc->verbose ^= 1;
	  tprintf("\nVerbose mode %s\n", uc->verbose ? "ON" : "OFF");
	  break;
	case 'x':
	  us->cur_subsong--;
	  subsong_end = 1;
	  jump_sub = 1;
	  record_playtime = 0;
	  break;
	case 'z':
	  record_playtime = 0;
	  if (us->cur_subsong == 0 ||
	      (us->min_subsong >= 0 && us->cur_subsong == us->min_subsong)) {
	    plistdir = UADE_PLAY_PREVIOUS;
	    uade_song_end_trigger = 1;
	    break;
	  }
	  new_sub = us->cur_subsong - 2;
	  if (new_sub < 0)
	    new_sub = -1;
	  if (us->min_subsong >= 0 && new_sub < us->min_subsong)
	    new_sub = us->min_subsong - 1;
	  us->cur_subsong = new_sub;
	  subsong_end = 1;
	  jump_sub = 1;
	  break;
	default:
	  if (isdigit(ret)) {
	    new_sub = ret - '0';
	    if (us->min_subsong >= 0 && new_sub < us->min_subsong) {
	      __android_log_print(ANDROID_LOG_VERBOSE, "UADE", "\ntoo low a subsong number\n");
	      break;
	    }
	    if (us->max_subsong >= 0 && new_sub > us->max_subsong) {
	      __android_log_print(ANDROID_LOG_VERBOSE, "UADE", "\ntoo high a subsong number\n");
	      break;
	    }
	    us->cur_subsong = new_sub - 1;
	    subsong_end = 1;
	    jump_sub = 1;
	    us->out_bytes = 0; /* to prevent timeout */
	    record_playtime = 0; /* to not record playtime */
	  } else if (!isspace(ret)) {
	    __android_log_print(ANDROID_LOG_VERBOSE, "UADE", "\n%c is not a valid command\n", ret);
	  }
	}
      }

      if (uade_debug_trigger == 1) {
	if (uade_send_short_message(UADE_COMMAND_ACTIVATE_DEBUGGER, ipc)) {
	  __android_log_print(ANDROID_LOG_VERBOSE, "UADE", "\nCan not active debugger\n");
	  return UADE_PLAY_FAILURE;
	}
	uade_debug_trigger = 0;
      }
      
      if (uade_info_mode && have_subsong_info) {
	/* we assume that subsong info is the last info we get */
	uade_song_end_trigger = 1;
	subsong_end = 0;
      }

      if (uade_song_end_trigger)
	record_playtime = 0;

      if (subsong_end && uade_song_end_trigger == 0) {

	if (jump_sub || (uc->one_subsong == 0 && us->cur_subsong != -1 && us->max_subsong != -1)) {

	  us->cur_subsong++;

	  jump_sub = 0;

	  if (us->cur_subsong > us->max_subsong) {
	    uade_song_end_trigger = 1;
	  } else {
	    subsong_end = 0;
	    subsong_bytes = 0;

	    uade_change_subsong(state);

	    __android_log_print(ANDROID_LOG_VERBOSE, "UADE", "\nChanging to subsong %d from range [%d, %d]\n", us->cur_subsong, us->min_subsong, us->max_subsong);
	  }
	} else {
	  uade_song_end_trigger = 1;
	}
      }

      if (uade_song_end_trigger) {
	next_song = 1;
	if (uade_send_short_message(UADE_COMMAND_REBOOT, ipc)) {
	  __android_log_print(ANDROID_LOG_VERBOSE, "UADE", "\nCan not send reboot\n");
	  return UADE_PLAY_FAILURE;
	}
	goto sendtoken;
      }

      left = uade_read_request(ipc);

    sendtoken:
      if (uade_send_short_message(UADE_COMMAND_TOKEN, ipc)) {
	__android_log_print(ANDROID_LOG_VERBOSE, "UADE", "\nCan not send token\n");
	return UADE_PLAY_FAILURE;
      }

      controlstate = UADE_R_STATE;

      if (what_was_left) {
	if (subsong_end) {
	  /* We can only rely on 'tailbytes' amount which was determined
	     earlier when UADE_REPLY_SONG_END happened */
	  playbytes = tailbytes;
	  tailbytes = 0;
	} else {
	  playbytes = what_was_left;
	}

	if (subsong_end == 0 && uade_song_end_trigger == 0 &&
	    uade_test_silence(um->data, playbytes, state)) {
	    
	  __android_log_print(ANDROID_LOG_VERBOSE, "UADE", "\nsilence detected (%d seconds)\n", uc->silence_timeout);
	  subsong_end = 1;
	}

	us->out_bytes += playbytes;
	subsong_bytes += playbytes;

	if (skip_bytes > 0) {
	  if (playbytes <= skip_bytes) {
	    skip_bytes -= playbytes;
	    playbytes = 0;
	  } else {
	    playbytes -= skip_bytes;
	    memmove(sampledata, sampledata + skip_bytes, playbytes);
	    skip_bytes = 0;
	  }
	}

	uade_effect_run(ue, (int16_t *) sampledata, playbytes / framesize);

	if (!audio_play(sampledata, playbytes)) {
	  __android_log_print(ANDROID_LOG_VERBOSE, "UADE", "\nlibao error detected.\n");
	  return UADE_PLAY_FAILURE;
	}

	if (uc->timeout != -1 && uc->use_timeouts) {
	  if (uade_song_end_trigger == 0) {
	    if (us->out_bytes / bytes_per_second >= uc->timeout) {
	      __android_log_print(ANDROID_LOG_VERBOSE, "UADE", "\nSong end (timeout %ds)\n", uc->timeout);
	      uade_song_end_trigger = 1;
	      record_playtime = 0;
	    }
	  }
	}

	if (uc->subsong_timeout != -1 && uc->use_timeouts) {
	  if (subsong_end == 0 && uade_song_end_trigger == 0) {
	    if (subsong_bytes / bytes_per_second >= uc->subsong_timeout) {
	      __android_log_print(ANDROID_LOG_VERBOSE, "UADE", "\nSong end (subsong timeout %ds)\n", uc->subsong_timeout);
	      subsong_end = 1;
	      record_playtime = 0;
	    }
	  }
	}
      }

    } else {

      /* receive state */

      if (uade_receive_message(um, sizeof(space), ipc) <= 0) {
	__android_log_print(ANDROID_LOG_VERBOSE, "UADE", "\nCan not receive events from uade\n");
	return UADE_PLAY_FAILURE;
      }
      
      switch (um->msgtype) {

      case UADE_COMMAND_TOKEN:
	controlstate = UADE_S_STATE;
	break;

      case UADE_REPLY_DATA:
	sm = (uint16_t *) um->data;
	for (i = 0; i < um->size; i += 2) {
	  *sm = ntohs(*sm);
	  sm++;
	}

	assert (left == um->size);
	assert (sizeof sampledata >= um->size);

	memcpy(sampledata, um->data, um->size);

	what_was_left = left;
	left = 0;
	break;
	
      case UADE_REPLY_FORMATNAME:
	uade_check_fix_string(um, 128);
	debug(uc->verbose, "\nFormat name: %s\n", (uint8_t *) um->data);
	if (uade_info_mode)
	  tprintf("formatname: %s\n", (char *) um->data);
	break;
	
      case UADE_REPLY_MODULENAME:
	uade_check_fix_string(um, 128);
	debug(uc->verbose, "\nModule name: %s\n", (uint8_t *) um->data);
	if (uade_info_mode)
	  tprintf("modulename: %s\n", (char *) um->data);
	break;

      case UADE_REPLY_MSG:
	uade_check_fix_string(um, 128);
	debug(uc->verbose, "\nMessage: %s\n", (char *) um->data);
	break;

      case UADE_REPLY_PLAYERNAME:
	uade_check_fix_string(um, 128);
	debug(uc->verbose, "\nPlayer name: %s\n", (uint8_t *) um->data);
	if (uade_info_mode)
	  tprintf("playername: %s\n", (char *) um->data);
	break;

      case UADE_REPLY_SONG_END:
	if (um->size < 9) {
	  __android_log_print(ANDROID_LOG_VERBOSE, "UADE", "\nInvalid song end reply\n");
	  exit(-1);
	}
	tailbytes = ntohl(((uint32_t *) um->data)[0]);
	/* next ntohl() is only there for a principle. it is not useful */
	if (ntohl(((uint32_t *) um->data)[1]) == 0) {
	  /* normal happy song end. go to next subsong if any */
	  subsong_end = 1;
	} else {
	  /* unhappy song end (error in the 68k side). skip to next song
	     ignoring possible subsongs */
	  uade_song_end_trigger = 1;
	}
	i = 0;
	reason = (char *) &um->data[8];
	while (reason[i] && i < (um->size - 8))
	  i++;
	if (reason[i] != 0 || (i != (um->size - 9))) {
	  __android_log_print(ANDROID_LOG_VERBOSE, "UADE", "\nbroken reason string with song end notice\n");
	  exit(-1);
	}
	__android_log_print(ANDROID_LOG_VERBOSE, "UADE", "\nSong end (%s)\n", reason);
	break;

      case UADE_REPLY_SUBSONG_INFO:
	if (um->size != 12) {
	  __android_log_print(ANDROID_LOG_VERBOSE, "UADE", "\nsubsong info: too short a message\n");
	  exit(-1);
	}

	u32ptr = (uint32_t *) um->data;
	us->min_subsong = ntohl(u32ptr[0]);
	us->max_subsong = ntohl(u32ptr[1]);
	us->cur_subsong = ntohl(u32ptr[2]);

	debug(uc->verbose, "\nsubsong: %d from range [%d, %d]\n", us->cur_subsong, us->min_subsong, us->max_subsong);

	if (!(-1 <= us->min_subsong && us->min_subsong <= us->cur_subsong && us->cur_subsong <= us->max_subsong)) {
	  int tempmin = us->min_subsong, tempmax = us->max_subsong;
	  __android_log_print(ANDROID_LOG_VERBOSE, "UADE", "\nThe player is broken. Subsong info does not match.\n");
	  us->min_subsong = tempmin <= tempmax ? tempmin : tempmax;
	  us->max_subsong = tempmax >= tempmin ? tempmax : tempmin;
	  if (us->cur_subsong > us->max_subsong)
	    us->max_subsong = us->cur_subsong;
	  else if (us->cur_subsong < us->min_subsong)
	    us->min_subsong = us->cur_subsong;
	}

	if ((us->max_subsong - us->min_subsong) != 0)
	  __android_log_print(ANDROID_LOG_VERBOSE, "UADE", "\nThere are %d subsongs in range [%d, %d].\n", 1 + us->max_subsong - us->min_subsong, us->min_subsong, us->max_subsong);

	uade_lookup_volume_normalisation(state);

	have_subsong_info = 1;

	if (uade_info_mode)
	  tprintf("subsong_info: %d %d %d (cur, min, max)\n", us->cur_subsong, us->min_subsong, us->max_subsong);
	break;
	
      default:
	__android_log_print(ANDROID_LOG_VERBOSE, "UADE", "\nExpected sound data. got %u.\n", (unsigned int) um->msgtype);
	return UADE_PLAY_FAILURE;
      }
    }
  }

  if (record_playtime && us->md5[0] != 0) {
    uint32_t playtime = (us->out_bytes * 1000) / bytes_per_second;
    uade_add_playtime(us->md5, playtime);
  }

  do {
    ret = uade_receive_message(um, sizeof(space), ipc);
    if (ret < 0) {
      __android_log_print(ANDROID_LOG_VERBOSE, "UADE", "\nCan not receive events (TOKEN) from uade.\n");
      return UADE_PLAY_FAILURE;
    }
    if (ret == 0) {
      __android_log_print(ANDROID_LOG_VERBOSE, "UADE", "\nEnd of input after reboot.\n");
      return UADE_PLAY_FAILURE;
    }
  } while (um->msgtype != UADE_COMMAND_TOKEN);

  tprintf("\n");

  return plistdir;
}