Beispiel #1
0
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;
}
Beispiel #2
0
static int run_client()
{

	// __android_log_print(ANDROID_LOG_VERBOSE, "UADE", "UADE STATE %d", ctrlstate);

	if(ctrlstate == UADE_S_STATE)
	{
		if(uade_song_end_trigger) {
			__android_log_print(ANDROID_LOG_VERBOSE, "UADE", "song_end_trigger");
			return -1;
		}
		/*if(uade_song_end_trigger)
		{
			next_song = 1;
			if(uade_send_short_message(UADE_COMMAND_REBOOT, &uadeipc))
			{
				__android_log_print(ANDROID_LOG_VERBOSE, "UADE", "\nCan not send reboot\n");
				return 0;
			}
		}
		else */

		if(new_subsong >= 0)
		{
			state.song->cur_subsong = state.song->min_subsong + new_subsong;
			__android_log_print(ANDROID_LOG_VERBOSE, "UADE", "New subsong %d", state.song->cur_subsong);
			uade_change_subsong(&state);
			//uade_change_subsong(new_subsong, &uadeipc);
			new_subsong = -1;
		}

		if(state.config.no_filter_set) {
			uade_send_filter_command(&state);
			state.config.no_filter_set = 0;
		}

		if(state.config.panning_enable_set || state.config.panning_set) {
			uade_set_effects(&state);
			state.config.no_filter_set = 0;
			state.config.panning_set = 0;
			state.config.panning_enable_set = 0;
		}

		//if(state.config.use_ntsc_set) {
		//	uade_set_effects(&state);
		//	state.config.use_ntsc_set = 0;
		//}


		left = uade_read_request(&uadeipc); /* Request another batch of sample data from uadecore */

		if(uade_send_short_message(UADE_COMMAND_TOKEN, &uadeipc))
		{
			__android_log_print(ANDROID_LOG_VERBOSE, "UADE", "\nCan not send token\n");
			return 0;
		}
		ctrlstate = UADE_R_STATE;
	}

	/* receive ctrlstate */
	if(ctrlstate == UADE_R_STATE)
	{
		uint16_t *sm;

		if (uade_receive_message(um, sizeof(space), &uadeipc) <= 0)
		{
			__android_log_print(ANDROID_LOG_VERBOSE, "UADE", "\nCan not receive events from uade\n");
			return 0;
		}

		//__android_log_print(ANDROID_LOG_VERBOSE, "UADE", "Got msg %d", um->msgtype);

		switch (um->msgtype)
		{
		case UADE_COMMAND_TOKEN:
			ctrlstate = UADE_S_STATE;
			//__android_log_print(ANDROID_LOG_VERBOSE, "UADE", "Got token");
			break;

		case UADE_REPLY_DATA:
			sm = (uint16_t *)um->data;

			if(!soundPtr) {
				__android_log_print(ANDROID_LOG_VERBOSE, "UADE", "No soundPtr");
				return -1;
			}

			if(subsong_end)
			{
				playbytes = tailbytes; /* Determined by UADE_REPLY_SONG_END */
				tailbytes = 0;
			}
			else
				playbytes = um->size;


			if(1) //plugin.freq == 44100)
			{
				for (int i = 0; i < um->size/2; i++)
					soundPtr[i] = ntohs(sm[i]);

				soundPtr += (playbytes/2);
			}
			else
			if(0) // plugin.freq == 22050)
			{
				for (int i = 0; i < um->size/2; i+=4)
				{
					soundPtr[i/2] = (ntohs(sm[i]) + ntohs(sm[i+2])) / 2;
					soundPtr[i/2+1] = (ntohs(sm[i+1]) + ntohs(sm[i+3])) / 2;
				}

				soundPtr += (playbytes/4);
			}

			//dbprintf("Got %d bytes sampledata\n", playbytes);
			//__android_log_print(ANDROID_LOG_VERBOSE, "UADEPlugin", "Got %d bytes sampledata\n", playbytes);

//			time_bytes += playbytes;
			//assert(left >= um->size);
			left -= um->size;

			return 1;

			break;

		case UADE_REPLY_FORMATNAME:
			uade_check_fix_string(um, MAX_LEN);
			__android_log_print(ANDROID_LOG_VERBOSE, "UADE", "Format name: %s\n", (char*) um->data);
			strcpy(current_format, (char *)um->data);
			break;

		case UADE_REPLY_MODULENAME:
			uade_check_fix_string(um, MAX_LEN);
			__android_log_print(ANDROID_LOG_VERBOSE, "UADE", "Module name: %s\n", (char*) um->data);
			break;

		case UADE_REPLY_MSG:
			uade_check_fix_string(um, MAX_LEN);
			__android_log_print(ANDROID_LOG_VERBOSE, "UADE", "Message: %s\n", (char *) um->data);
			break;

		case UADE_REPLY_PLAYERNAME:
			uade_check_fix_string(um, MAX_LEN);
			__android_log_print(ANDROID_LOG_VERBOSE, "UADE", "Player name: %s\n", (char*) um->data);
			strcpy(current_format, (char *)um->data);
			break;

		case UADE_REPLY_SONG_END:
			if (um->size < 9)
			{
				__android_log_print(ANDROID_LOG_VERBOSE, "UADE", "Invalid song end reply\n");
				//exit(-1);
				return -1;
			}

			tailbytes = ntohl(((uint32_t *) um->data)[0]);
			uade_song_end_trigger = 1;
			__android_log_print(ANDROID_LOG_VERBOSE, "UADE", "Song end: %s", (char*)um->data+8);
			break;

		case UADE_REPLY_SUBSONG_INFO:
			if(um->size != 12)
			{
				__android_log_print(ANDROID_LOG_VERBOSE, "UADE", "subsong info: too short a message\n");
			}
			{
				unsigned int *u32ptr = (unsigned int *)um->data;
				int min_sub = ntohl(u32ptr[0]);
				int max_sub = ntohl(u32ptr[1]);
				int cur_sub = ntohl(u32ptr[2]);

				state.song->min_subsong = min_sub;
				state.song->max_subsong = max_sub;
				state.song->cur_subsong = cur_sub;
				startSong = cur_sub - min_sub;

				__android_log_print(ANDROID_LOG_VERBOSE, "UADE", "subsong: %d from range [%d, %d]\n", cur_sub, min_sub, max_sub);
			}
			break;
		case UADE_REPLY_CANT_PLAY:
		case UADE_REPLY_CAN_PLAY:
		default:
			__android_log_print(ANDROID_LOG_VERBOSE, "UADE", "Expected sound data. got %d.\n", um->msgtype);
			return 0;
		}
	}
	return 0;
}
Beispiel #3
0
static void *play_loop(void *arg)
{
  enum uade_control_state controlstate = UADE_S_STATE;
  int ret;
  int left = 0;
  uint8_t space[UADE_MAX_MESSAGE_SIZE];
  struct uade_msg *um = (struct uade_msg *) space;
  int subsong_end = 0;
  uint16_t *sm;
  int i;
  unsigned int play_bytes, tailbytes = 0;
  uint64_t subsong_bytes = 0;
  char *reason;
  uint32_t *u32ptr;
  int writeoffs;
  int framesize = UADE_CHANNELS * UADE_BYTES_PER_SAMPLE;
  int song_end_trigger = 0;
  int64_t skip_bytes = 0;

  uade_lock();
  record_playtime = 1;
  uade_unlock();

  while (1) {
    if (controlstate == UADE_S_STATE) {

      assert(left == 0);

      if (abort_playing) {
    uade_lock();
    record_playtime = 0;
    uade_unlock();
    break;
      }

      uade_lock();
      if (uade_seek_forward) {
    skip_bytes += uade_seek_forward * UADE_BYTES_PER_FRAME * state.config.frequency;
    uade_ip.output->flush(uade_ip.output->written_time() + uade_seek_forward * 1000);
    uade_seek_forward = 0;
      }
      if (uade_select_sub != -1) {
    state.song->cur_subsong = uade_select_sub;

    uade_change_subsong(&state);

    uade_ip.output->flush(0);
    uade_select_sub = -1;
    subsong_end = 0;
    subsong_bytes = 0;

    /* we do this to avoid timeout, and to not record playtime */
    state.song->out_bytes = 0;
    record_playtime = 0;

    uade_info_string();
      }

      if (subsong_end && song_end_trigger == 0) {

    if (state.song->cur_subsong == -1 || state.song->max_subsong == -1) {
      song_end_trigger = 1;

    } else {

      state.song->cur_subsong++;

      if (state.song->cur_subsong > state.song->max_subsong) {
        song_end_trigger = 1;
      } else {
        int x = 0;

        uade_change_subsong(&state);

        while (uade_ip.output->buffer_playing()) {
          /* Sleep at most 5 secs */
          if (x >= 500) {
        __android_log_print(ANDROID_LOG_VERBOSE, "UADE", "UADE: blocking work-around activated.\n");
        break;
          }
          x++;
          xmms_usleep(10000);
        }
        uade_ip.output->flush(0);
        subsong_end = 0;
        subsong_bytes = 0;

        uade_gui_subsong_changed(state.song->cur_subsong);

        uade_info_string();
      }
    }
      }
      uade_unlock();

      if (song_end_trigger) {
    /* We must drain the audio fast if abort_playing happens (e.g.
       the user changes song when we are here waiting the sound device) */
    while (uade_ip.output->buffer_playing() && abort_playing == 0)

      xmms_usleep(10000);
    break;
      }

      left = uade_read_request(&state.ipc);

      if (uade_send_short_message(UADE_COMMAND_TOKEN, &state.ipc)) {
    __android_log_print(ANDROID_LOG_VERBOSE, "UADE", "Can not send token.\n");
    return NULL;
      }
      controlstate = UADE_R_STATE;

    } else {

      if (uade_receive_message(um, sizeof(space), &state.ipc) <= 0) {
    __android_log_print(ANDROID_LOG_VERBOSE, "UADE", "Can not receive events from uade\n");
    exit(1);
      }

      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++;
    }

    if (subsong_end) {
      play_bytes = tailbytes;
      tailbytes = 0;
    } else {
      play_bytes = um->size;
    }

    if (subsong_end == 0 && song_end_trigger == 0 &&
        uade_test_silence(um->data, play_bytes, &state)) {
      subsong_end = 1;
    }

    subsong_bytes += play_bytes;
    uade_lock();
    state.song->out_bytes += play_bytes;
    uade_unlock();

    if (skip_bytes > 0) {
      if (play_bytes <= skip_bytes) {
        skip_bytes -= play_bytes;
        play_bytes = 0;
      } else {
        play_bytes -= skip_bytes;
        skip_bytes = 0;
      }
    }

    uade_effect_run(&state.effects, (int16_t *) um->data, play_bytes / framesize);
    uade_ip.add_vis_pcm(uade_ip.output->written_time(), sample_format, UADE_CHANNELS, play_bytes, um->data);

    writeoffs = 0;
    while (writeoffs < play_bytes) {
      int writable;
      while ((writable = uade_ip.output->buffer_free()) <= 0) {
        if (abort_playing)
          goto nowrite;
        xmms_usleep(10000);
      }

      if (writable > (play_bytes - writeoffs))
        writable = play_bytes - writeoffs;

      uade_ip.output->write_audio(&um->data[writeoffs], writable);

      writeoffs += writable;
    }


      nowrite:

    if (state.config.timeout != -1 && state.config.use_timeouts) {
      if (song_end_trigger == 0) {
        uade_lock();
        if (state.song->out_bytes / (UADE_BYTES_PER_FRAME * state.config.frequency) >= state.config.timeout) {
          song_end_trigger = 1;
          record_playtime = 0;
        }
        uade_unlock();
      }
    }

    if (state.config.subsong_timeout != -1 && state.config.use_timeouts) {
      if (subsong_end == 0 && song_end_trigger == 0) {
        if (subsong_bytes / (UADE_BYTES_PER_FRAME * state.config.frequency) >= state.config.subsong_timeout) {
          subsong_end = 1;
          record_playtime = 0;
        }
      }
    }

    assert (left >= um->size);
    left -= um->size;
    break;

      case UADE_REPLY_FORMATNAME:
    uade_check_fix_string(um, 128);
    strlcpy(gui_formatname, (char *) um->data, sizeof gui_formatname);
    strlcpy(state.song->formatname, (char *) um->data, sizeof state.song->formatname);
    break;

      case UADE_REPLY_MODULENAME:
    uade_check_fix_string(um, 128);
    strlcpy(gui_modulename, (char *) um->data, sizeof gui_modulename);
    strlcpy(state.song->modulename, (char *) um->data, sizeof state.song->modulename);
    break;

      case UADE_REPLY_MSG:
    uade_check_fix_string(um, 128);
    plugindebug("Message: %s\n", (char *) um->data);
    break;

      case UADE_REPLY_PLAYERNAME:
    uade_check_fix_string(um, 128);
    strlcpy(gui_playername, (char *) um->data, sizeof gui_playername);
    strlcpy(state.song->playername, (char *) um->data, sizeof state.song->playername);
    break;

      case UADE_REPLY_SONG_END:
    if (um->size < 9) {
      __android_log_print(ANDROID_LOG_VERBOSE, "UADE", "Invalid 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 */
      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", "Broken reason string with song end notice\n");
      exit(1);
    }
    break;

      case UADE_REPLY_SUBSONG_INFO:
    if (um->size != 12) {
      __android_log_print(ANDROID_LOG_VERBOSE, "UADE", "subsong info: too short a message\n");
      exit(1);
    }
    u32ptr = (uint32_t *) um->data;
    uade_lock();
    state.song->min_subsong = ntohl(u32ptr[0]);
    state.song->max_subsong = ntohl(u32ptr[1]);
    state.song->cur_subsong = ntohl(u32ptr[2]);

    if (!(-1 <= state.song->min_subsong && state.song->min_subsong <= state.song->cur_subsong && state.song->cur_subsong <= state.song->max_subsong)) {
      int tempmin = state.song->min_subsong, tempmax = state.song->max_subsong;
      __android_log_print(ANDROID_LOG_VERBOSE, "UADE", "uade: The player is broken. Subsong info does not match with %s.\n", gui_filename);
      state.song->min_subsong = tempmin <= tempmax ? tempmin : tempmax;
      state.song->max_subsong = tempmax >= tempmin ? tempmax : tempmin;
      if (state.song->cur_subsong > state.song->max_subsong)
        state.song->max_subsong = state.song->cur_subsong;
      else if (state.song->cur_subsong < state.song->min_subsong)
        state.song->min_subsong = state.song->cur_subsong;
    }
    uade_unlock();
    break;

      default:
    __android_log_print(ANDROID_LOG_VERBOSE, "UADE", "Expected sound data. got %d.\n", um->msgtype);
    plugin_disabled = 1;
    return NULL;
      }
    }
  }

  last_beat_played = 1;

  if (uade_send_short_message(UADE_COMMAND_REBOOT, &state.ipc)) {
    __android_log_print(ANDROID_LOG_VERBOSE, "UADE", "Can not send reboot.\n");
    return NULL;
  }

  if (uade_send_short_message(UADE_COMMAND_TOKEN, &state.ipc)) {
    __android_log_print(ANDROID_LOG_VERBOSE, "UADE", "Can not send token.\n");
    return NULL;
  }

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

  return NULL;
}