Beispiel #1
0
END_OPTION_TABLE

static int
sayLine (char *line, void *data) {
  sayString(&spk, line, 0);
  return 1;
}
	string countAndSay(int n)
	{
		string result = "1";
		for(int i=0; i<n-1; ++i)
		{
			string str = sayString(result);
			result = str;
		}
			
		return result;
	}
Beispiel #3
0
static
NAMED_PIPE_INPUT_CALLBACK(handleSpeechInput) {
//SpeechInputObject *obj = parameters->data;

  const unsigned char *buffer = parameters->buffer;
  size_t length = parameters->length;
  char string[length + 1];

  memcpy(string, buffer, length);
  string[length] = 0;
  sayString(&spk, string, 0);

  return length;
}
Beispiel #4
0
void ESpeak::doSay(QString string)
{
    switch(string.length())
    {
    case 0:
        break;

    case 1:
        sayChar(string[0]);
        break;

    default:
        sayString(string);
        break;
    }

    queueLength--;
}
Beispiel #5
0
int speech(char* msg)
{
#ifdef _WIN32
	int size = MultiByteToWideChar(CP_ACP, 0, msg, -1, NULL, 0);
	LPWSTR converted[65536];
	memset(converted, 0, sizeof(LPWSTR)*size);
	MultiByteToWideChar(CP_ACP, 0, msg, strlen(msg), converted, size);
	wchar_t* message;
	message = a2u(msg);
	sayString(message, true);
#else
if(spd_sayf(conn, SPD_MESSAGE, msg))
{
return 1;
}
else
{
return 0;
}
#endif
return 0;
}
Beispiel #6
0
int
main (int argc, char *argv[]) {
  ProgramExitStatus exitStatus;
  const char *driver = NULL;
  void *object;
  float speechRate;
  float speechVolume;

  {
    static const OptionsDescriptor descriptor = {
      OPTION_TABLE(programOptions),
      .applicationName = "spktest",
      .argumentsSummary = "[driver [parameter=value ...]]"
    };
    PROCESS_OPTIONS(descriptor, argc, argv);
  }

  {
    char **const paths[] = {
      &opt_driversDirectory,
      &opt_dataDirectory,
      NULL
    };
    fixInstallPaths(paths);
  }

  speechRate = 1.0;
  if (opt_speechRate && *opt_speechRate) {
    static const float minimum = 0.1;
    static const float maximum = 10.0;
    if (!validateFloat(&speechRate, opt_speechRate, &minimum, &maximum)) {
      logMessage(LOG_ERR, "%s: %s", "invalid rate multiplier", opt_speechRate);
      return PROG_EXIT_SYNTAX;
    }
  }

  speechVolume = 1.0;
  if (opt_speechVolume && *opt_speechVolume) {
    static const float minimum = 0.0;
    static const float maximum = 2.0;
    if (!validateFloat(&speechVolume, opt_speechVolume, &minimum, &maximum)) {
      logMessage(LOG_ERR, "%s: %s", "invalid volume multiplier", opt_speechVolume);
      return PROG_EXIT_SYNTAX;
    }
  }

  if (argc) {
    driver = *argv++;
    --argc;
  }

  if ((speech = loadSpeechDriver(driver, &object, opt_driversDirectory))) {
    const char *const *parameterNames = speech->parameters;
    char **parameterSettings;
    if (!parameterNames) {
      static const char *const noNames[] = {NULL};
      parameterNames = noNames;
    }
    {
      const char *const *name = parameterNames;
      unsigned int count;
      char **setting;
      while (*name) ++name;
      count = name - parameterNames;
      if (!(parameterSettings = malloc((count + 1) * sizeof(*parameterSettings)))) {
        logMallocError();
        return PROG_EXIT_FATAL;
      }
      setting = parameterSettings;
      while (count--) *setting++ = "";
      *setting = NULL;
    }
    while (argc) {
      char *assignment = *argv++;
      int ok = 0;
      char *delimiter = strchr(assignment, '=');
      if (!delimiter) {
        logMessage(LOG_ERR, "missing speech driver parameter value: %s", assignment);
      } else if (delimiter == assignment) {
        logMessage(LOG_ERR, "missing speech driver parameter name: %s", assignment);
      } else {
        size_t nameLength = delimiter - assignment;
        const char *const *name = parameterNames;
        while (*name) {
          if (strncasecmp(assignment, *name, nameLength) == 0) {
            parameterSettings[name - parameterNames] = delimiter + 1;
            ok = 1;
            break;
          }
          ++name;
        }
        if (!ok) logMessage(LOG_ERR, "invalid speech driver parameter: %s", assignment);
      }
      if (!ok) return PROG_EXIT_SYNTAX;
      --argc;
    }

    initializeSpeechSynthesizer(&spk);
    identifySpeechDriver(speech, 0);		/* start-up messages */
    if (speech->construct(&spk, parameterSettings)) {
      if (speech->setVolume) speech->setVolume(&spk, speechVolume);
      if (speech->setRate) speech->setRate(&spk, speechRate);

      if (opt_textString && *opt_textString) {
        sayString(&spk, opt_textString, 0);
      } else {
        processLines(stdin, sayLine, NULL);
      }
      speech->destruct(&spk);		/* finish with the display */
      exitStatus = PROG_EXIT_SUCCESS;
    } else {
      logMessage(LOG_ERR, "can't initialize speech driver.");
      exitStatus = PROG_EXIT_FATAL;
    }
  } else {
    logMessage(LOG_ERR, "can't load speech driver.");
    exitStatus = PROG_EXIT_FATAL;
  }
  return exitStatus;
}
Beispiel #7
0
int
handleSpeechCommand (int command, void *datga) {
  switch (command & BRL_MSK_CMD) {
    case BRL_CMD_RESTARTSPEECH:
      restartSpeechDriver();
      break;
    case BRL_CMD_SPKHOME:
      if (scr.number == spk.track.screenNumber) {
        trackSpeech();
      } else {
        playTune(&tune_command_rejected);
      }
      break;
    case BRL_CMD_AUTOSPEAK:
      toggleFeatureSetting(&prefs.autospeak, command);
      break;

    case BRL_CMD_ASPK_SEL_LINE:
      toggleFeatureSetting(&prefs.autospeakSelectedLine, command);
      break;

    case BRL_CMD_ASPK_SEL_CHAR:
      toggleFeatureSetting(&prefs.autospeakSelectedCharacter, command);
      break;

    case BRL_CMD_ASPK_INS_CHARS:
      toggleFeatureSetting(&prefs.autospeakInsertedCharacters, command);
      break;

    case BRL_CMD_ASPK_DEL_CHARS:
      toggleFeatureSetting(&prefs.autospeakDeletedCharacters, command);
      break;

    case BRL_CMD_ASPK_REP_CHARS:
      toggleFeatureSetting(&prefs.autospeakReplacedCharacters, command);
      break;

    case BRL_CMD_ASPK_CMP_WORDS:
      toggleFeatureSetting(&prefs.autospeakCompletedWords, command);
      break;

    case BRL_CMD_MUTE:
      muteSpeech("command");
      break;

    case BRL_CMD_SAY_LINE:
      sayScreenLines(ses->winy, 1, 0, prefs.sayLineMode);
      break;
    case BRL_CMD_SAY_ABOVE:
      sayScreenLines(0, ses->winy+1, 1, sayImmediate);
      break;
    case BRL_CMD_SAY_BELOW:
      sayScreenLines(ses->winy, scr.rows-ses->winy, 1, sayImmediate);
      break;

    case BRL_CMD_SAY_SLOWER:
      if (!canSetSpeechRate()) {
        playTune(&tune_command_rejected);
      } else if (prefs.speechRate > 0) {
        setSpeechRate(--prefs.speechRate, 1);
      } else {
        playTune(&tune_no_change);
      }
      break;

    case BRL_CMD_SAY_FASTER:
      if (!canSetSpeechRate()) {
        playTune(&tune_command_rejected);
      } else if (prefs.speechRate < SPK_RATE_MAXIMUM) {
        setSpeechRate(++prefs.speechRate, 1);
      } else {
        playTune(&tune_no_change);
      }
      break;

    case BRL_CMD_SAY_SOFTER:
      if (!canSetSpeechVolume()) {
        playTune(&tune_command_rejected);
      } else if (prefs.speechVolume > 0) {
        setSpeechVolume(--prefs.speechVolume, 1);
      } else {
        playTune(&tune_no_change);
      }
      break;

    case BRL_CMD_SAY_LOUDER:
      if (!canSetSpeechVolume()) {
        playTune(&tune_command_rejected);
      } else if (prefs.speechVolume < SPK_VOLUME_MAXIMUM) {
        setSpeechVolume(++prefs.speechVolume, 1);
      } else {
        playTune(&tune_no_change);
      }
      break;

    case BRL_CMD_SPEAK_CURR_CHAR:
      speakCurrentCharacter();
      break;

    case BRL_CMD_SPEAK_PREV_CHAR:
      if (ses->spkx > 0) {
        ses->spkx -= 1;
        speakCurrentCharacter();
      } else if (ses->spky > 0) {
        ses->spky -= 1;
        ses->spkx = scr.cols - 1;
        playTune(&tune_wrap_up);
        speakCurrentCharacter();
      } else {
        playTune(&tune_bounce);
      }
      break;

    case BRL_CMD_SPEAK_NEXT_CHAR:
      if (ses->spkx < (scr.cols - 1)) {
        ses->spkx += 1;
        speakCurrentCharacter();
      } else if (ses->spky < (scr.rows - 1)) {
        ses->spky += 1;
        ses->spkx = 0;
        playTune(&tune_wrap_down);
        speakCurrentCharacter();
      } else {
        playTune(&tune_bounce);
      }
      break;

    case BRL_CMD_SPEAK_FRST_CHAR: {
      ScreenCharacter characters[scr.cols];
      int column;

      readScreen(0, ses->spky, scr.cols, 1, characters);
      if ((column = findFirstNonSpaceCharacter(characters, scr.cols)) >= 0) {
        ses->spkx = column;
        speakDone(characters, column, 1, 0);
      } else {
        playTune(&tune_command_rejected);
      }

      break;
    }

    case BRL_CMD_SPEAK_LAST_CHAR: {
      ScreenCharacter characters[scr.cols];
      int column;

      readScreen(0, ses->spky, scr.cols, 1, characters);
      if ((column = findLastNonSpaceCharacter(characters, scr.cols)) >= 0) {
        ses->spkx = column;
        speakDone(characters, column, 1, 0);
      } else {
        playTune(&tune_command_rejected);
      }

      break;
    }

    {
      int direction;
      int spell;

    case BRL_CMD_SPEAK_PREV_WORD:
      direction = -1;
      spell = 0;
      goto speakWord;

    case BRL_CMD_SPEAK_NEXT_WORD:
      direction = 1;
      spell = 0;
      goto speakWord;

    case BRL_CMD_SPEAK_CURR_WORD:
      direction = 0;
      spell = 0;
      goto speakWord;

    case BRL_CMD_SPELL_CURR_WORD:
      direction = 0;
      spell = 1;
      goto speakWord;

    speakWord:
      {
        int row = ses->spky;
        int column = ses->spkx;

        ScreenCharacter characters[scr.cols];
        ScreenCharacterType type;
        int onCurrentWord;

        int from = column;
        int to = from + 1;

      findWord:
        readScreen(0, row, scr.cols, 1, characters);
        type = (row == ses->spky)? getScreenCharacterType(&characters[column]): SCT_SPACE;
        onCurrentWord = type != SCT_SPACE;

        if (direction < 0) {
          while (1) {
            if (column == 0) {
              if ((type != SCT_SPACE) && !onCurrentWord) {
                ses->spkx = from = column;
                ses->spky = row;
                break;
              }

              if (row == 0) goto noWord;
              if (row-- == ses->spky) playTune(&tune_wrap_up);
              column = scr.cols;
              goto findWord;
            }

            {
              ScreenCharacterType newType = getScreenCharacterType(&characters[--column]);

              if (newType != type) {
                if (onCurrentWord) {
                  onCurrentWord = 0;
                } else if (type != SCT_SPACE) {
                  ses->spkx = from = column + 1;
                  ses->spky = row;
                  break;
                }

                if (newType != SCT_SPACE) to = column + 1;
                type = newType;
              }
            }
          }
        } else if (direction > 0) {
          while (1) {
            if (++column == scr.cols) {
              if ((type != SCT_SPACE) && !onCurrentWord) {
                to = column;
                ses->spkx = from;
                ses->spky = row;
                break;
              }

              if (row == (scr.rows - 1)) goto noWord;
              if (row++ == ses->spky) playTune(&tune_wrap_down);
              column = -1;
              goto findWord;
            }

            {
              ScreenCharacterType newType = getScreenCharacterType(&characters[column]);

              if (newType != type) {
                if (onCurrentWord) {
                  onCurrentWord = 0;
                } else if (type != SCT_SPACE) {
                  to = column;
                  ses->spkx = from;
                  ses->spky = row;
                  break;
                }

                if (newType != SCT_SPACE) from = column;
                type = newType;
              }
            }
          }
        } else if (type != SCT_SPACE) {
          while (from > 0) {
            if (getScreenCharacterType(&characters[--from]) != type) {
              from += 1;
              break;
            }
          }

          while (to < scr.cols) {
            if (getScreenCharacterType(&characters[to]) != type) break;
            to += 1;
          }
        }

        speakDone(characters, from, to-from, spell);
        break;
      }

    noWord:
      playTune(&tune_bounce);
      break;
    }

    case BRL_CMD_SPEAK_CURR_LINE:
      speakCurrentLine();
      break;

    {
      int increment;
      int limit;

    case BRL_CMD_SPEAK_PREV_LINE:
      increment = -1;
      limit = 0;
      goto speakLine;

    case BRL_CMD_SPEAK_NEXT_LINE:
      increment = 1;
      limit = scr.rows - 1;
      goto speakLine;

    speakLine:
      if (ses->spky == limit) {
        playTune(&tune_bounce);
      } else {
        if (prefs.skipIdenticalLines) {
          ScreenCharacter original[scr.cols];
          ScreenCharacter current[scr.cols];
          int count = 0;

          readScreen(0, ses->spky, scr.cols, 1, original);

          do {
            readScreen(0, ses->spky+=increment, scr.cols, 1, current);
            if (!isSameRow(original, current, scr.cols, isSameText)) break;

            if (!count) {
              playTune(&tune_skip_first);
            } else if (count < 4) {
              playTune(&tune_skip);
            } else if (!(count % 4)) {
              playTune(&tune_skip_more);
            }

            count += 1;
          } while (ses->spky != limit);
        } else {
          ses->spky += increment;
        }

        speakCurrentLine();
      }

      break;
    }

    case BRL_CMD_SPEAK_FRST_LINE: {
      ScreenCharacter characters[scr.cols];
      int row = 0;

      while (row < scr.rows) {
        readScreen(0, row, scr.cols, 1, characters);
        if (!isAllSpaceCharacters(characters, scr.cols)) break;
        row += 1;
      }

      if (row < scr.rows) {
        ses->spky = row;
        ses->spkx = 0;
        speakCurrentLine();
      } else {
        playTune(&tune_command_rejected);
      }

      break;
    }

    case BRL_CMD_SPEAK_LAST_LINE: {
      ScreenCharacter characters[scr.cols];
      int row = scr.rows - 1;

      while (row >= 0) {
        readScreen(0, row, scr.cols, 1, characters);
        if (!isAllSpaceCharacters(characters, scr.cols)) break;
        row -= 1;
      }

      if (row >= 0) {
        ses->spky = row;
        ses->spkx = 0;
        speakCurrentLine();
      } else {
        playTune(&tune_command_rejected);
      }

      break;
    }

    case BRL_CMD_DESC_CURR_CHAR: {
      char description[0X50];
      formatCharacterDescription(description, sizeof(description), ses->spkx, ses->spky);
      sayString(description, 1);
      break;
    }

    case BRL_CMD_ROUTE_CURR_LOCN:
      if (routeCursor(ses->spkx, ses->spky, scr.number)) {
        playTune(&tune_routing_started);
      } else {
        playTune(&tune_command_rejected);
      }
      break;

    case BRL_CMD_SPEAK_CURR_LOCN: {
      char buffer[0X50];
      snprintf(buffer, sizeof(buffer), "%s %d, %s %d",
               gettext("line"), ses->spky+1,
               gettext("column"), ses->spkx+1);
      sayString(buffer, 1);
      break;
    }

    case BRL_CMD_SHOW_CURR_LOCN:
      toggleFeatureSetting(&prefs.showSpeechCursor, command);
      break;

    default:
      return 0;
  }

  return 1;
}
Beispiel #8
0
static void
sayStringSetting (SpeechSynthesizer *spk, const char *name, const char *string) {
  char statement[0X40];
  snprintf(statement, sizeof(statement), "%s %s", name, string);
  sayString(spk, statement, 1);
}
Beispiel #9
0
static int
handleSpeechCommands (int command, void *data) {
  switch (command & BRL_MSK_CMD) {
    case BRL_CMD_RESTARTSPEECH:
      restartSpeechDriver();
      break;

    case BRL_CMD_SPKHOME:
      if (scr.number == spk.track.screenNumber) {
        trackSpeech();
      } else {
        alert(ALERT_COMMAND_REJECTED);
      }
      break;

    case BRL_CMD_MUTE:
      muteSpeech(&spk, "command");
      break;

    case BRL_CMD_SAY_LINE:
      sayScreenLines(ses->winy, 1, 0, prefs.sayLineMode);
      break;
    case BRL_CMD_SAY_ABOVE:
      sayScreenLines(0, ses->winy+1, 1, sayImmediate);
      break;
    case BRL_CMD_SAY_BELOW:
      sayScreenLines(ses->winy, scr.rows-ses->winy, 1, sayImmediate);
      break;

    case BRL_CMD_SAY_SLOWER:
      if (!canSetSpeechRate(&spk)) {
        alert(ALERT_COMMAND_REJECTED);
      } else if (prefs.speechRate > 0) {
        setSpeechRate(&spk, --prefs.speechRate, 1);
      } else {
        alert(ALERT_NO_CHANGE);
      }
      break;

    case BRL_CMD_SAY_FASTER:
      if (!canSetSpeechRate(&spk)) {
        alert(ALERT_COMMAND_REJECTED);
      } else if (prefs.speechRate < SPK_RATE_MAXIMUM) {
        setSpeechRate(&spk, ++prefs.speechRate, 1);
      } else {
        alert(ALERT_NO_CHANGE);
      }
      break;

    case BRL_CMD_SAY_SOFTER:
      if (!canSetSpeechVolume(&spk)) {
        alert(ALERT_COMMAND_REJECTED);
      } else if (prefs.speechVolume > 0) {
        setSpeechVolume(&spk, --prefs.speechVolume, 1);
      } else {
        alert(ALERT_NO_CHANGE);
      }
      break;

    case BRL_CMD_SAY_LOUDER:
      if (!canSetSpeechVolume(&spk)) {
        alert(ALERT_COMMAND_REJECTED);
      } else if (prefs.speechVolume < SPK_VOLUME_MAXIMUM) {
        setSpeechVolume(&spk, ++prefs.speechVolume, 1);
      } else {
        alert(ALERT_NO_CHANGE);
      }
      break;

    case BRL_CMD_SPEAK_CURR_CHAR:
      speakCurrentCharacter();
      break;

    case BRL_CMD_SPEAK_PREV_CHAR:
      if (ses->spkx > 0) {
        ses->spkx -= 1;
        speakCurrentCharacter();
      } else if (ses->spky > 0) {
        ses->spky -= 1;
        ses->spkx = scr.cols - 1;
        alert(ALERT_WRAP_UP);
        speakCurrentCharacter();
      } else {
        alert(ALERT_BOUNCE);
      }
      break;

    case BRL_CMD_SPEAK_NEXT_CHAR:
      if (ses->spkx < (scr.cols - 1)) {
        ses->spkx += 1;
        speakCurrentCharacter();
      } else if (ses->spky < (scr.rows - 1)) {
        ses->spky += 1;
        ses->spkx = 0;
        alert(ALERT_WRAP_DOWN);
        speakCurrentCharacter();
      } else {
        alert(ALERT_BOUNCE);
      }
      break;

    case BRL_CMD_SPEAK_FRST_CHAR: {
      ScreenCharacter characters[scr.cols];
      int column;

      readScreen(0, ses->spky, scr.cols, 1, characters);
      if ((column = findFirstNonSpaceCharacter(characters, scr.cols)) >= 0) {
        ses->spkx = column;
        speakDone(characters, column, 1, 0);
      } else {
        alert(ALERT_COMMAND_REJECTED);
      }

      break;
    }

    case BRL_CMD_SPEAK_LAST_CHAR: {
      ScreenCharacter characters[scr.cols];
      int column;

      readScreen(0, ses->spky, scr.cols, 1, characters);
      if ((column = findLastNonSpaceCharacter(characters, scr.cols)) >= 0) {
        ses->spkx = column;
        speakDone(characters, column, 1, 0);
      } else {
        alert(ALERT_COMMAND_REJECTED);
      }

      break;
    }

    {
      int direction;
      int spell;

    case BRL_CMD_SPEAK_PREV_WORD:
      direction = -1;
      spell = 0;
      goto speakWord;

    case BRL_CMD_SPEAK_NEXT_WORD:
      direction = 1;
      spell = 0;
      goto speakWord;

    case BRL_CMD_SPEAK_CURR_WORD:
      direction = 0;
      spell = 0;
      goto speakWord;

    case BRL_CMD_SPELL_CURR_WORD:
      direction = 0;
      spell = 1;
      goto speakWord;

    speakWord:
      {
        int row = ses->spky;
        int column = ses->spkx;

        ScreenCharacter characters[scr.cols];
        ScreenCharacterType type;
        int onCurrentWord;

        int from = column;
        int to = from + 1;

      findWord:
        readScreen(0, row, scr.cols, 1, characters);
        type = (row == ses->spky)? getScreenCharacterType(&characters[column]): SCT_SPACE;
        onCurrentWord = type != SCT_SPACE;

        if (direction < 0) {
          while (1) {
            if (column == 0) {
              if ((type != SCT_SPACE) && !onCurrentWord) {
                ses->spkx = from = column;
                ses->spky = row;
                break;
              }

              if (row == 0) goto noWord;
              if (row-- == ses->spky) alert(ALERT_WRAP_UP);
              column = scr.cols;
              goto findWord;
            }

            {
              ScreenCharacterType newType = getScreenCharacterType(&characters[--column]);

              if (newType != type) {
                if (onCurrentWord) {
                  onCurrentWord = 0;
                } else if (type != SCT_SPACE) {
                  ses->spkx = from = column + 1;
                  ses->spky = row;
                  break;
                }

                if (newType != SCT_SPACE) to = column + 1;
                type = newType;
              }
            }
          }
        } else if (direction > 0) {
          while (1) {
            if (++column == scr.cols) {
              if ((type != SCT_SPACE) && !onCurrentWord) {
                to = column;
                ses->spkx = from;
                ses->spky = row;
                break;
              }

              if (row == (scr.rows - 1)) goto noWord;
              if (row++ == ses->spky) alert(ALERT_WRAP_DOWN);
              column = -1;
              goto findWord;
            }

            {
              ScreenCharacterType newType = getScreenCharacterType(&characters[column]);

              if (newType != type) {
                if (onCurrentWord) {
                  onCurrentWord = 0;
                } else if (type != SCT_SPACE) {
                  to = column;
                  ses->spkx = from;
                  ses->spky = row;
                  break;
                }

                if (newType != SCT_SPACE) from = column;
                type = newType;
              }
            }
          }
        } else if (type != SCT_SPACE) {
          while (from > 0) {
            if (getScreenCharacterType(&characters[--from]) != type) {
              from += 1;
              break;
            }
          }

          while (to < scr.cols) {
            if (getScreenCharacterType(&characters[to]) != type) break;
            to += 1;
          }
        }

        speakDone(characters, from, to-from, spell);
        break;
      }

    noWord:
      alert(ALERT_BOUNCE);
      break;
    }

    case BRL_CMD_SPEAK_CURR_LINE:
      speakCurrentLine();
      break;

    {
      int increment;
      int limit;

    case BRL_CMD_SPEAK_PREV_LINE:
      increment = -1;
      limit = 0;
      goto speakLine;

    case BRL_CMD_SPEAK_NEXT_LINE:
      increment = 1;
      limit = scr.rows - 1;
      goto speakLine;

    speakLine:
      if (ses->spky == limit) {
        alert(ALERT_BOUNCE);
      } else {
        if (prefs.skipIdenticalLines) {
          ScreenCharacter original[scr.cols];
          ScreenCharacter current[scr.cols];
          int count = 0;

          readScreen(0, ses->spky, scr.cols, 1, original);

          do {
            readScreen(0, ses->spky+=increment, scr.cols, 1, current);
            if (!isSameRow(original, current, scr.cols, isSameText)) break;

            if (!count) {
              alert(ALERT_SKIP_FIRST);
            } else if (count < 4) {
              alert(ALERT_SKIP);
            } else if (!(count % 4)) {
              alert(ALERT_SKIP_MORE);
            }

            count += 1;
          } while (ses->spky != limit);
        } else {
          ses->spky += increment;
        }

        speakCurrentLine();
      }

      break;
    }

    case BRL_CMD_SPEAK_FRST_LINE: {
      ScreenCharacter characters[scr.cols];
      int row = 0;

      while (row < scr.rows) {
        readScreen(0, row, scr.cols, 1, characters);
        if (!isAllSpaceCharacters(characters, scr.cols)) break;
        row += 1;
      }

      if (row < scr.rows) {
        ses->spky = row;
        ses->spkx = 0;
        speakCurrentLine();
      } else {
        alert(ALERT_COMMAND_REJECTED);
      }

      break;
    }

    case BRL_CMD_SPEAK_LAST_LINE: {
      ScreenCharacter characters[scr.cols];
      int row = scr.rows - 1;

      while (row >= 0) {
        readScreen(0, row, scr.cols, 1, characters);
        if (!isAllSpaceCharacters(characters, scr.cols)) break;
        row -= 1;
      }

      if (row >= 0) {
        ses->spky = row;
        ses->spkx = 0;
        speakCurrentLine();
      } else {
        alert(ALERT_COMMAND_REJECTED);
      }

      break;
    }

    case BRL_CMD_DESC_CURR_CHAR: {
      char description[0X50];
      formatCharacterDescription(description, sizeof(description), ses->spkx, ses->spky);
      sayString(&spk, description, SAY_OPT_MUTE_FIRST);
      break;
    }

    case BRL_CMD_ROUTE_CURR_LOCN:
      if (routeCursor(ses->spkx, ses->spky, scr.number)) {
        alert(ALERT_ROUTING_STARTED);
      } else {
        alert(ALERT_COMMAND_REJECTED);
      }
      break;

    case BRL_CMD_SPEAK_CURR_LOCN: {
      char buffer[0X50];
      snprintf(buffer, sizeof(buffer), "%s %d, %s %d",
               gettext("line"), ses->spky+1,
               gettext("column"), ses->spkx+1);
      sayString(&spk, buffer, SAY_OPT_MUTE_FIRST);
      break;
    }

    default:
      return 0;
  }

  return 1;
}