~alcinnz/rhapsode

b762df03a8b89e55a9960a347f2a804ebb8026dc — Adrian Cochrane 4 years ago 723aea0
Navigate to selected link.
1 files changed, 63 insertions(+), 23 deletions(-)

M src/main.c
M src/main.c => src/main.c +63 -23
@@ 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