Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow request.Audio reader be streaming and minor API changes #2

Merged
merged 3 commits into from
Feb 9, 2017
Merged

Allow request.Audio reader be streaming and minor API changes #2

merged 3 commits into from
Feb 9, 2017

Conversation

brentnd
Copy link
Contributor

@brentnd brentnd commented Feb 9, 2017

When uploading a file, request.Audio (io.Reader) is used in an io.Copy to copy to the audio part of the upload. This works fine when request.Audio is a file but when request.Audio is a data stream (recording live) the HTTP request reads from the Body (a byte.Buffer) very quickly with a large buffer size and after 1 or 2 reads, gets an io.EOF because no more recorded data has been added to the buffer and finishes the request prematurely.

The implementation uses a blockingBuffer which returns the data in the buffer if any exists and blocks if the body is not finished (EOF) and there is no data. The blocking behavior in this case is desired to prevent the HTTP request from reading until more Audio data is written to the buffer.

The additional benefit of not blocking on the io.Copy is that the request can begin and (in NEAR_FIELD and FAR_FIELD profiles), AVS can begin processing the data and even return a directive before all of the audio is POSTed.

Additionally, two API changes were made.

  1. Add Token to Speak directive (so SpeechStarted and SpeechFinished can re-use the token)
    https://developer.amazon.com/public/solutions/alexa/alexa-voice-service/reference/speechsynthesizer#speak
  2. Enumerate and allow use of different Recognize Profiles. Previously was defaulted to "CLOSE_TALK"
    https://developer.amazon.com/public/solutions/alexa/alexa-voice-service/reference/speechrecognizer#profiles

Copy link
Member

@blixt blixt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like where this is going. I have a few concerns about the blockingReader type, and I think there's actually standard library way of solving it. See my comments in client.go.

client.go Outdated
@@ -67,7 +119,7 @@ func (c *Client) CreateDownchannel(accessToken string) (<-chan *Message, error)

// Do posts a request to the AVS service's /events endpoint.
func (c *Client) Do(request *Request) (*Response, error) {
body := &bytes.Buffer{}
body := NewBlockingReader()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The blockingReader type has quite a bit going on inside of it for the purposes of avoiding bytes.Buffer's EOF on empty. Do you think io.Pipe() would work here? It's used for purposes like these where you need an intermediate in-memory buffer.

Here's a quick example:

out, in := io.Pipe()
writer := multipart.NewWriter(in)
// ...
// Send the request to AVS.
req, err := http.NewRequest("POST", EventsURL, out)

Inside the goroutine I think you need to call out.Close() when done copying, or out.CloseWithError(err) if an error occurs.

Copy link
Contributor Author

@brentnd brentnd Feb 9, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback. I too was slightly dissatisfied with having to roll something custom for this.

I did start with using an io.Pipe() but the problem is that io.Pipe() is always unbuffered. To use it, the entire writing section would need to be in a goroutine to avoid a deadlock before the HTTP body read is called.

An issue similar to this was discussed here: https://groups.google.com/forum/#!topic/golang-dev/k0bSal8eDyE and a buffered pipe was incorporated into net/http2/pipe.go but not exported.

I'm open to suggestions.

client.go Outdated
mut *sync.Mutex
eof bool
err error
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a pretty complex type for piping data. See my comment below in the Do function about a possible alternative.

RecognizeProfileCloseTalk = RecognizeProfile("CLOSE_TALK")
RecognizeProfileNearField = RecognizeProfile("NEAR_FIELD")
RecognizeProfileFarField = RecognizeProfile("FAR_FIELD")
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great, thanks for adding these! :)

Copy link
Contributor Author

@brentnd brentnd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The implementation with io.Pipe worked out pretty cleanly. I can squash these commits if you're happy with it this way.

@blixt
Copy link
Member

blixt commented Feb 9, 2017

I think this looks great! You can go ahead and squash if you want. I added two comments but they're not super important to address.

@brentnd
Copy link
Contributor Author

brentnd commented Feb 9, 2017

I addressed your two comments and squashed the latest two commits.

@blixt blixt merged commit ab3c102 into rogertalk:master Feb 9, 2017
@blixt
Copy link
Member

blixt commented Feb 9, 2017

Excellent, merged!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants