From b762df03a8b89e55a9960a347f2a804ebb8026dc Mon Sep 17 00:00:00 2001 From: Adrian Cochrane Date: Fri, 30 Oct 2020 19:55:51 +1300 Subject: [PATCH] Navigate to selected link. --- src/main.c | 86 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 63 insertions(+), 23 deletions(-) diff --git a/src/main.c b/src/main.c index 1dc3928..d8b4c1e 100644 --- a/src/main.c +++ b/src/main.c @@ -132,11 +132,20 @@ void speak(char *ssml, char *mark, char* fallback) { int flags = espeakCHARS_AUTO | espeakPHONEMES | espeakENDPAUSE | espeakCHARS_UTF8 | espeakSSML; if (mark != NULL && c_ssmlHasMark(mark, ssml)) espeak_Synth_Mark(ssml, strlen(ssml)+1, mark, 0, flags, NULL, NULL); - else if (fallback != NULL) + else if (fallback != NULL && c_ssmlHasMark(fallback, ssml)) espeak_Synth_Mark(ssml, strlen(ssml)+1, fallback, 0, flags, NULL, NULL); else espeak_Synth(ssml, strlen(ssml)+1, 0, POS_CHARACTER, 0, flags, NULL, NULL); } +void speak_text(char *text, espeak_PARAMETER param, int value) { + if (param != 0) espeak_SetParameter(param, value, /* relative */1); + + int flags = espeakCHARS_AUTO | espeakPHONEMES | espeakENDPAUSE | espeakCHARS_UTF8; + espeak_Synth(text, strlen(text)+1, 0, POS_CHARACTER, 0, flags, NULL, NULL); + + if (param != 0) espeak_SetParameter(param, espeak_GetParameter(param, /* current */0), /* relative */0); +} + /* Utilities */ #define SWAP(type, a, b) {type temp = a; a = b; b = temp;} int min(int a, int b) {return a < b ? a : b;} @@ -149,8 +158,8 @@ int levenshtein_distance(const char *a, const char* b) { SWAP(int, a_len, b_len); SWAP(const char*, a, b); } - int *v0 = malloc(sizeof(int)*b_len); - int *v1 = malloc(sizeof(int)*b_len); + int *v0 = malloc(sizeof(int)*(b_len+1)); + int *v1 = malloc(sizeof(int)*(b_len+1)); // Initialize v0 for an edit distance of an empty a for (int i = 0; i < b_len; i++) v0[i] = i; @@ -160,7 +169,7 @@ int levenshtein_distance(const char *a, const char* b) { // Edit distance from empty string is delete i characters. v1[0] = i; - for (int j = 0; j < b_len - 1; j++) { + for (int j = 0; j < b_len; j++) { int deletion_cost = v0[j+1] + 1; // above cell + deletion int insertion_cost = v1[j] + 1; // left cell + insertion // top left cell + maybe substitution @@ -172,39 +181,50 @@ int levenshtein_distance(const char *a, const char* b) { // Progress to next row SWAP(int*, v0, v1); } - return v0[b_len-1]; + + int ret = v0[b_len]; + free(v0); free(v1); + return ret; } int MAX_DIST = 3; // Is this ideal? char *select_link(char **links, char *command) { // Pass 1, min distance int score = INT_MAX; - for (int i = 0; strcmp(links[i], " ") != 0; i++) + for (int i = 0; strcmp(links[i], " ") != 0; i++) { score = min(score, levenshtein_distance(command, links[i])); + } // Pass 2: Is ambiguous? - int is_ambiguous = 0; + int num_matches = 0; for (int i = 0; strcmp(links[i], " ") != 0; i++) - is_ambiguous |= score >= levenshtein_distance(command, links[i]) - MAX_DIST; + if (score >= levenshtein_distance(command, links[i]) - MAX_DIST) num_matches++; + + espeak_Cancel(); // Pass 3: Retrieve answer for (int i = 0; strcmp(links[i], " ") != 0; i += 3) { if (score < levenshtein_distance(command, links[i]) && score < levenshtein_distance(command, links[i+1]) && score < levenshtein_distance(command, links[i+2])) continue; - if (!is_ambiguous) return links[i+2]; + if (num_matches == 1) return links[i+2]; - // TODO Apply formatting! - speak(links[i+2], NULL, NULL); speak(links[i], NULL, NULL); speak(links[i+1], NULL, NULL); + // Communicate + printf("%s\t%s\t%s\n", links[i+2], links[i], links[i+1]); + speak_text(links[i+2], espeakRATE, 20); // URL, fast + speak_text(links[i], espeakPITCH, -40); // Label, low + speak_text(links[i+1], espeakVOLUME, -40); // Title, quite } - return ""; + return NULL; } struct termios stored_settings, no_echo; int read_keyboard = 0; -int speak_finalize(char *ssml) { +int speak_finalize(char *ssml, char **links, char **out_link) { while (read_keyboard) { + if (out_link != NULL && *out_link != NULL) return 0; + if (getc(stdin) == '\033') { char mark[200]; char fallback[200]; @@ -272,9 +292,13 @@ int speak_finalize(char *ssml) { tcsetattr(0, TCSANOW, &stored_settings); char *line = NULL; size_t len = 0; - if (getline(&line, &len, stdin) < 0) { + if (getline(&line, &len, stdin) < 0) fprintf(stderr, "Failed to read stdin line!\n"); - } else printf("%s\n", line); // TODO Do something with this! + else if (*line == '\n') { + espeak_Cancel(); + return 0; + } else if (out_link != NULL) + *out_link = select_link(links, line); tcsetattr(0, TCSANOW, &no_echo); } } @@ -347,8 +371,8 @@ int main(int argc, char **argv) { no_echo = stored_settings; no_echo.c_lflag &= (~ICANON); no_echo.c_lflag &= (~ECHO); - no_echo.c_cc[VTIME] = 1; - no_echo.c_cc[VMIN] = 0; + no_echo.c_cc[VTIME] = 0; + no_echo.c_cc[VMIN] = 1; tcsetattr(0, TCSANOW, &no_echo); break; case 'w': @@ -391,23 +415,35 @@ int main(int argc, char **argv) { #endif use_espeak = 1; +#if 0 // Unit-test levenshtein_distance + int dist = levenshtein_distance("kitten", "kitten"); + printf("kitten => kitten: %i\n", dist); + if (dist != 0) return 10; + dist = levenshtein_distance("kitten", "sitting"); + printf("kitten => sitting: %i\n", dist); + if (dist != 3) return 11; +#endif + struct session *session = c_newSession(); struct page *referer = c_initialReferer(); if (use_espeak) speak_err = speak_initialize(); - char *ssml; + char *ssml, **links, *uri; for (int i = optind; i < argc; i++) { - if (use_espeak && speak_err == 0) speak(argv[i], NULL, NULL); + uri = argv[i]; + +read_uri: + if (use_espeak && speak_err == 0) speak_text(uri, espeakRATE, 10); #ifdef WITH_SPEECHD - else if (spd_conn != NULL) spd_say(spd_conn, SPD_MESSAGE, argv[i]); + else if (spd_conn != NULL) spd_say(spd_conn, SPD_MESSAGE, uri); #endif else printf("%s\n", argv[i]); if (logpath != NULL) c_enableLogging(session); - struct page *page = c_fetchURL(session, mimes, referer, argv[i]); + struct page *page = c_fetchURL(session, mimes, referer, uri); ssml = c_renderDoc(session, page, use_espeak); - char **links = c_extractLinks(page); + links = c_extractLinks(page); if (logpath != NULL) c_writeLog(logpath, session); @@ -420,7 +456,11 @@ int main(int argc, char **argv) { c_freePage(page); } - if (use_espeak & speak_err == 0) speak_err = speak_finalize(ssml); + + uri = NULL; + if (use_espeak & speak_err == 0) speak_err = speak_finalize(ssml, links, &uri); + if (uri != NULL) goto read_uri; + #ifdef WITH_SPEECHD if (spd_conn != NULL) spd_close(spd_conn); #endif -- 2.30.2