Search code examples
c++curllibcurlspeech-to-text

How to use libcurl with Google Speech API (what is the equivalent for --data-binary or --upload-file)?


I have figured out how to do it on the command line:

curl -X POST --upload-file audio/test.wav 
--header "Content-Type: audio/l16; rate=44100;" 
"https://www.google.com/speech-api/v2/recognize?output=json&lang=en-us&key=MYKEY"

or

curl -X POST --data-binary @audio/test.wav 
--header "Content-Type: audio/l16; rate=44100;" 
"https://www.google.com/speech-api/v2/recognize?output=json&lang=en-us&key=MYKEY"

Where 'MYKEY' is my developer key.

How do I do this in C++, with libcurl? I have searched for hours but cannot figure out how to properly attach my audio file to a post request using CURLOPT_POSTFIELDS (or other parameters).

Here is what I have so far. It breaks with an access violation.

#include <iostream>
#include <stdio.h> 
#include <curl/curl.h> 

int main(void)
{
    CURL *curl;         // curl handle
    CURLcode res;

    curl = curl_easy_init();
    if (curl) 
    {
        struct curl_slist *chunk = NULL;

        chunk = curl_slist_append(chunk, "Content-Type: audio/l16; rate=44100");

        std::cout << curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "file=@audio/test.wav") << std::endl;
        std::cout << curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, 177644) << std::endl;
        std::cout << curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk) << std::endl;
        std::cout << curl_easy_setopt(curl, CURLOPT_CAINFO, "ca-bundle.crt") << std::endl;
        std::cout << curl_easy_setopt(curl, CURLOPT_URL, "https://www.google.com/speech-api/v2/recognize?output=json&lang=en-us&key=MYKEY") << std::endl;

        res = curl_easy_perform(curl);

        std::cout << res;

        curl_easy_cleanup(curl);

    }
    return 0;
}

I don't think the "file=@audio/test.wav" is correct, I saw that syntax on the PHP documentation for curl and decided to try it with libcurl.

I'm really at a loss, any help would be appreciated.

EDIT: Here is a working example [pastebin]. It took me a ridiculous amount of time to figure out. It uses the example functions given in http://curl.haxx.se/libcurl/c/post-callback.html, so its not a final solution, but is a good starting point. Make sure to put your API key into the speech API link.

#include <iostream>
#include <stdio.h> 
#include <curl/curl.h> 
#include <direct.h>
#include <string>

struct WriteThis {
    const char *readptr;
    long sizeleft;
};

// callback function from http://curl.haxx.se/libcurl/c/post-callback.html
static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *userp)
{
    struct WriteThis *pooh = (struct WriteThis *)userp;

    if (size*nmemb < 1)
        return 0;

    if (pooh->sizeleft) {
        *(char *)ptr = pooh->readptr[0]; /* copy one single byte */
        pooh->readptr++;                 /* advance pointer */
        pooh->sizeleft--;                /* less data left */
        return 1;                        /* we return 1 byte at a time! */
    }

    return 0;                          /* no more data left to deliver */
}

int main(void)
{
    CURL *curl;         // curl handle
    CURLcode res;

    curl = curl_easy_init();
    if (curl) 
    {
        FILE *file;
        errno_t err = fopen_s(&file, "testaudio.wav", "r");
        fseek(file, 0, SEEK_END);
        int fileSize = ftell(file);
        fseek(file, 0, SEEK_SET);

        std::cout << "file open status: " << err << std::endl;
        std::cout << "file " << fileSize << std::endl;

        struct curl_slist *chunk = NULL;
        chunk = curl_slist_append(chunk, "Content-Type: audio/l16; rate=44100");

        char *audioData = (char*)malloc(fileSize);
        struct WriteThis pooh;
        fread(audioData, fileSize, 1, file);
        fclose(file);

        pooh.readptr = audioData;
        pooh.sizeleft = fileSize;

        std::string sizeHeader = "Content-Length: ";
        sizeHeader += std::to_string(fileSize);
        chunk = curl_slist_append(chunk, sizeHeader.c_str());

        std::cout << curl_easy_setopt(curl, CURLOPT_POST, 1L) << std::endl;
        curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback);
        curl_easy_setopt(curl, CURLOPT_READDATA, &pooh);
        curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);

        std::cout << curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk) << std::endl;
        std::cout << curl_easy_setopt(curl, CURLOPT_CAINFO, "ca-bundle.crt") << std::endl;
        std::cout << curl_easy_setopt(curl, CURLOPT_URL, "https://www.google.com/speech-api/v2/recognize?output=json&lang=en-us&key=yourkey") << std::endl;

        res = curl_easy_perform(curl);
        std::cout << res;
        curl_easy_cleanup(curl);

    }
    return 0;
}

Solution

  • You need to use callback to upload the data file. You can find example here:

    http://curl.haxx.se/libcurl/c/post-callback.html

    data-binary is irrelevant in this example because you don't do anything with the file passing the data in callback, so it will be posted as is.