~alcinnz/rhapsode

d57e119d70319c7a6ae80b9d0f18cdb74dd26833 — Adrian Cochrane 3 years ago 0844f28
Refactor to use GLib event-based input.

So I can receive PocketSphinx's events via GStreamer!
1 files changed, 148 insertions(+), 129 deletions(-)

M src/main.c
M src/main.c => src/main.c +148 -129
@@ 194,7 194,8 @@ int levenshtein_distance(const char *a, const char* b) {
}

int MAX_DIST = 3; // Is this ideal?
char *select_link(char **links, char *command) {
char **links = NULL;
char *select_link(char *command) {
    // Pass 1, min distance
    int score = INT_MAX;
    for (int i = 0; strcmp(links[i], " ") != 0; i++) {


@@ 210,7 211,7 @@ char *select_link(char **links, char *command) {

    // Pass 3: Retrieve answer
    for (int i = 0; strcmp(links[i], " ") != 0; i += 3) {
        if (command[0] != '\0') {// "" to read entire link table...
        if (command[0] != '\0' || command[0] == '\n') {// "" to read entire link table...
            if (score < levenshtein_distance(command, links[i]) &&
                score < levenshtein_distance(command, links[i+1]) &&
                score < levenshtein_distance(command, links[i+2])) continue;


@@ 229,103 230,150 @@ char *select_link(char **links, char *command) {

struct termios stored_settings, no_echo;
int read_keyboard = 0;
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];
            espeak_Cancel();
            char c = getc(stdin);
            if (c == 0 || c == -1 || c == '\033' || c == 'q') goto close; // skip [
            switch (getc(stdin)) {
            case 'A':
                // 🠕
                if (in_table) {
                    tablerow--;
                    if (tablerow > 0) {
                        sprintf(mark, "-rhaps-tablecell%i:%ix%i", tableno, tablerow, tablecol);
                        speak(ssml, mark, NULL);
                        break;
                    } else in_table = 0;
                }
                section_no--;
                sprintf(mark, "-rhaps-section%i", section_no);
                speak(ssml, section_no > 0 ? mark : NULL, NULL);
                break;
            case 'B':
                // 🠗
                if (in_table) {
                    tablerow++;

void read_page(char *uri, int read_input);
gboolean read_stdin(GIOChannel *source, GIOCondition condition, gpointer data) {
    char *ssml = data;

    if (getc(stdin) == '\033') {
        char mark[200];
        char fallback[200];
        espeak_Cancel();
        char c = getc(stdin);
        if (c == 0 || c == -1 || c == '\033' || c == 'q') return 1; // skip [
        switch (getc(stdin)) {
        case 'A':
            // 🠕
            if (in_table) {
                tablerow--;
                if (tablerow > 0) {
                    sprintf(mark, "-rhaps-tablecell%i:%ix%i", tableno, tablerow, tablecol);
                    sprintf(fallback, "-rhaps-section%i", section_no+1);
                    speak(ssml, mark, fallback);
                    break; // FIXME What if that mark doesn't exist?
                }
                section_no++;
                sprintf(mark, "-rhaps-section%i", section_no);
                speak(ssml, section_no > 0 ? mark : NULL, NULL);
                break;
            case 'C':
                // ➔
                if (in_table) {
                    tablecol++;
                    speak(ssml, mark, NULL);
                    break;
                } else in_table = 0;
            }
            section_no--;
            sprintf(mark, "-rhaps-section%i", section_no);
            speak(ssml, section_no > 0 ? mark : NULL, NULL);
            break;
        case 'B':
            // 🠗
            if (in_table) {
                tablerow++;
                sprintf(mark, "-rhaps-tablecell%i:%ix%i", tableno, tablerow, tablecol);
                sprintf(fallback, "-rhaps-section%i", section_no+1);
                speak(ssml, mark, fallback);
                break; // FIXME What if that mark doesn't exist?
            }
            section_no++;
            sprintf(mark, "-rhaps-section%i", section_no);
            speak(ssml, section_no > 0 ? mark : NULL, NULL);
            break;
        case 'C':
            // ➔
            if (in_table) {
                tablecol++;
                sprintf(mark, "-rhaps-tablecell%i:%ix%i", tableno, tablerow, tablecol);
                sprintf(fallback, "-rhaps-paragraph%i", paragraph_no+1);
                speak(ssml, mark, fallback);
                break; // FIXME What if that mark doesn't exist?
            }
            paragraph_no++;
            sprintf(mark, "-rhaps-paragraph%i", paragraph_no);
            speak(ssml, paragraph_no > 0 ? mark : NULL, NULL);
            break;
        case 'D':
            // 🠔
            if (in_table) {
                tablecol--;
                if (tablecol > 0) {
                    sprintf(mark, "-rhaps-tablecell%i:%ix%i", tableno, tablerow, tablecol);
                    sprintf(fallback, "-rhaps-paragraph%i", paragraph_no+1);
                    speak(ssml, mark, fallback);
                    break; // FIXME What if that mark doesn't exist?
                }
                paragraph_no++;
                sprintf(mark, "-rhaps-paragraph%i", paragraph_no);
                speak(ssml, paragraph_no > 0 ? mark : NULL, NULL);
                break;
            case 'D':
                // 🠔
                if (in_table) {
                    tablecol--;
                    if (tablecol > 0) {
                        sprintf(mark, "-rhaps-tablecell%i:%ix%i", tableno, tablerow, tablecol);
                        speak(ssml, mark, NULL);
                        break;
                    } else in_table = 0;
                }
                paragraph_no--;
                sprintf(mark, "-rhaps-paragraph%i", paragraph_no);
                speak(ssml, paragraph_no > 0 ? mark : NULL, NULL);
                break;
                    speak(ssml, mark, NULL);
                    break;
                } else in_table = 0;
            }
        } else {
            // Read in a line
            tcsetattr(0, TCSANOW, &stored_settings);
            char *line = NULL;
            size_t len = 0;
            if (getline(&line, &len, stdin) < 0)
                fprintf(stderr, "Failed to read stdin line!\n");
            else if (*line == '\n') {
                espeak_Cancel();
                return 0;
            } else if (out_link != NULL)
                *out_link = select_link(links, line);
            tcsetattr(0, TCSANOW, &no_echo);
            paragraph_no--;
            sprintf(mark, "-rhaps-paragraph%i", paragraph_no);
            speak(ssml, paragraph_no > 0 ? mark : NULL, NULL);
            break;
        }
    } else {
        // Read in a line, with full terminal emulator i18n.
        tcsetattr(0, TCSANOW, &stored_settings);
        char *line = NULL;
        size_t len = 0;
        if (getline(&line, &len, stdin) < 0) {
            fprintf(stderr, "Failed to read stdin line!\n");
            return TRUE;
        }
        tcsetattr(0, TCSANOW, &no_echo);
        read_page(select_link(line), 1);
        return FALSE;
    }
    espeak_ng_STATUS result = espeak_ng_Synchronize();
    if (result != ENS_OK) {
        espeak_ng_PrintStatusCodeMessage(result, stderr, context);
        return 4;
    return TRUE;
}

int speak_finalize(char *ssml) {
    while (read_keyboard) {
        if (!read_stdin(NULL, 0, ssml)) return 0;
    }

close:
    if (path_wav != NULL) sf_close(fd_wav);
    espeak_ng_Terminate();
    return 0;
}

/* Main driver */
void write_links(FILE *dest, char **links) {
int speak_err = 0;
struct session *session = NULL;
struct page *referer = NULL;

int use_espeak = 0;
char *logpath = NULL;
char *mimes = "text/html text/xml application/xml application/xhtml+xml text/plain";
FILE *fd_ssml = NULL;
FILE *fd_links = NULL;

void write_links() {
    for (int i = 0; strcmp(links[i], " ") != 0; i++) {
        fprintf(dest, "%s%c", links[i], (i % 3) == 2 ? '\n' : '\t');
        fprintf(fd_links, "%s%c", links[i], (i % 3) == 2 ? '\n' : '\t');
    }
}

void read_page(char *uri, int read_input) {
    for (int i = 0; strcmp(links[i], " ") != 0; i++) free(links[i]);
    free(links);

    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, uri);
    #endif
    else printf("%s\n", uri);

    struct page *page = c_fetchURL(session, mimes, referer, uri);
    referer = page;
    char *ssml = c_renderDoc(session, page, use_espeak);
    links = c_extractLinks(page);

    if (logpath != NULL) c_writeLog(logpath, session);

    if (fd_ssml != NULL) fprintf(fd_ssml, "%s\n", ssml);
    if (read_keyboard && strcmp(uri, "about:welcome")) select_link("");
    if (fd_links != NULL) write_links(fd_links, links);
    if (use_espeak & speak_err == 0) speak(ssml, "main", NULL);
    #ifdef WITH_SPEECHD
    if (spd_conn != NULL) spd_say(spd_conn, SPD_MESSAGE, ssml);
    #endif

    c_freePage(page);
    free(uri);

    if (read_input) {
        g_io_add_watch(g_io_channel_unix_new(0), G_IO_IN|G_IO_HUP, read_stdin, ssml);
        // if (use_espeak & speak_err == 0) speak_err = speak_finalize(ssml);
    } else {
        free(ssml);
        for (int i = 0; strcmp(links[i], " ") != 0; i++) free(links[i]);
        free(links);
    }
}



@@ 340,16 388,10 @@ FILE *parse_opt_file() {
}

int main(int argc, char **argv) {
    int speak_err = 0;
    hs_init(&argc, &argv);
    gst_init(&argc, &argv);
    GMainLoop *loop = g_main_loop_new(NULL, FALSE);

    char *mimes = "text/html text/xml application/xml application/xhtml+xml text/plain";
    char *logpath = NULL;
    FILE *fd_ssml = NULL;
    FILE *fd_links = NULL;
    int use_espeak = 0;
    tcgetattr(0, &stored_settings);
    #ifdef WITH_SPEECHD
    SPDConnection *spd_conn = NULL;


@@ 462,53 504,30 @@ int main(int argc, char **argv) {
    if (dist != 3) return 11;
#endif

    struct session *session = c_newSession();
    struct page *referer = c_initialReferer();
    session = c_newSession();
    if (logpath != NULL) session = c_enableLogging(session);
    referer = c_initialReferer();

    if (use_espeak) speak_err = speak_initialize();
    char *ssml, **links, *uri;
    if (optind >= argc) {
        // No URLs specified, restore previous session or goto about:welcome
        uri = c_lastVisited("about:welcome");
        goto read_uri;
    }
    for (int i = optind; i < argc; i++) {
        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, uri);
        #endif
        else printf("%s\n", argv[i]);

        if (logpath != NULL) session = c_enableLogging(session);
        read_page(c_lastVisited("about:welcome"), 1);
    } else for (int i = optind; i < argc; i++) read_page(argv[i], i+1 == argc);

        struct page *page = c_fetchURL(session, mimes, referer, uri);
        ssml = c_renderDoc(session, page, use_espeak);
        links = c_extractLinks(page);

        if (logpath != NULL) c_writeLog(logpath, session);

        if (fd_ssml != NULL) fprintf(fd_ssml, "%s\n", ssml);
        if (read_keyboard && strcmp(uri, "about:welcome")) select_link(links, "");
        if (fd_links != NULL) write_links(fd_links, links);
        if (use_espeak & speak_err == 0) speak(ssml, "main", NULL);
        #ifdef WITH_SPEECHD
        if (spd_conn != NULL) spd_say(spd_conn, SPD_MESSAGE, ssml);
        #endif

        c_freePage(page);
    }

    uri = NULL;
    if (use_espeak & speak_err == 0) speak_err = speak_finalize(ssml, links, &uri);
    if (uri != NULL) goto read_uri;
    g_main_loop_run(loop);

    #ifdef WITH_SPEECHD
    if (spd_conn != NULL) spd_close(spd_conn);
    #endif

    espeak_ng_STATUS result = espeak_ng_Synchronize();
    if (result != ENS_OK) {
        espeak_ng_PrintStatusCodeMessage(result, stderr, context);
        return 4;
    }
    if (path_wav != NULL) sf_close(fd_wav);
    espeak_ng_Terminate();

    c_freePage(referer);
    c_freeSession(session);
    hs_exit();