-
Notifications
You must be signed in to change notification settings - Fork 229
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
Dictation API #109
Dictation API #109
Changes from 10 commits
f06b0c1
bcb58f5
1da117c
8fe5cf9
da59fb6
57a6c82
884f39e
5dcb30d
c1a88c0
ff21db3
3fbf578
1fc5c11
e439bf7
947348d
0f9eeff
fe1c771
da2afba
fa169a0
b6a487a
533eeb0
0fad8e4
a2ece17
93ce3b6
9d191fe
ffbf49c
33faf29
0bc9e80
f97bea8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,7 +13,7 @@ Pebble.js applications run on your phone. They have access to all the resources | |
|
||
## Getting Started | ||
|
||
* In CloudPebble | ||
* In CloudPebble | ||
|
||
The easiest way to use Pebble.js is in [CloudPebble](https://cloudpebble.net). Select the 'Pebble.js' project type when creating a new project. | ||
|
||
|
@@ -146,16 +146,6 @@ wind.add(textfield); | |
wind.show(); | ||
```` | ||
|
||
## Examples | ||
|
||
Coming Soon! | ||
|
||
## Acknowledgements | ||
|
||
Pebble.js started as [Simply.JS](http://simplyjs.io), a project by [Meiguro](http://github.com/meiguro). It is now part of the Pebble SDK and supported by Pebble. Contact [[email protected]](mailto:[email protected]) with any questions! | ||
|
||
This documentation uses [Flatdoc](http://ricostacruz.com/flatdoc/#flatdoc). | ||
|
||
# API Reference | ||
|
||
## Global namespace | ||
|
@@ -407,14 +397,6 @@ You can use the accelerometer in two different ways: | |
var Accel = require('ui/accel'); | ||
```` | ||
|
||
#### Accel.init() | ||
|
||
Before you can use the accelerometer, you must call `Accel.init()`. | ||
|
||
````js | ||
Accel.init(); | ||
```` | ||
|
||
#### Accel.config(accelConfig) | ||
|
||
This function configures the accelerometer `data` events to your liking. The `tap` event requires no configuration for use. Configuring the accelerometer is a very error prone process, so it is recommended to not configure the accelerometer and use `data` events with the default configuration without calling `Accel.config`. | ||
|
@@ -494,6 +476,34 @@ wind.on('accelData', function(e) { | |
}); | ||
```` | ||
|
||
### Voice | ||
|
||
The `Voice` module allows you to interact with Pebble's dictation API on supported platforms (Basalt and Chalk). | ||
|
||
````js | ||
var Voice = require('ui/voice'); | ||
```` | ||
|
||
#### Voice.startDictationSession(callback) | ||
|
||
This function starts the dictation UI, and invokes the callback upon completion. The callback is invoked with an event with the following fields: | ||
|
||
* `status`: The [DictationSessionStatus](https://developer.getpebble.com/docs/c/Foundation/Dictation/#DictationSessionStatus) (or -1 if the platform is not supported). | ||
* `transcription`: The transcribed string | ||
|
||
```js | ||
Voice.startDictationSession(function(e) { | ||
if (e.status != 0) { | ||
// if there was an error | ||
console.log('Error: ' + e.status); | ||
return; | ||
} | ||
|
||
// Log the result | ||
console.log('Success: ' + e.transcription); | ||
}); | ||
``` | ||
|
||
### Window | ||
|
||
`Window` is the basic building block in your Pebble.js application. All windows share some common properties and methods. | ||
|
@@ -1338,3 +1348,13 @@ For more information, see [Vector2 in the three.js reference documentation][thre | |
[Text]: #text | ||
[TimeText]: #timetext | ||
[three.js Vector2]: http://threejs.org/docs/#Reference/Math/Vector2 | ||
|
||
## Examples | ||
|
||
Coming Soon! | ||
|
||
## Acknowledgements | ||
|
||
Pebble.js started as [Simply.JS](http://simplyjs.io), a project by [Meiguro](http://github.com/meiguro). It is now part of the Pebble SDK and supported by Pebble. Contact [[email protected]](mailto:[email protected]) with any questions! | ||
|
||
This documentation uses [Flatdoc](http://ricostacruz.com/flatdoc/#flatdoc). |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ var Wakeup = require('wakeup'); | |
var Timeline = require('timeline'); | ||
var Resource = require('ui/resource'); | ||
var Accel = require('ui/accel'); | ||
var Voice = require('ui/voice'); | ||
var ImageService = require('ui/imageservice'); | ||
var WindowStack = require('ui/windowstack'); | ||
var Window = require('ui/window'); | ||
|
@@ -764,6 +765,21 @@ var ElementAnimateDonePacket = new struct([ | |
['uint32', 'id'], | ||
]); | ||
|
||
var NumCommandsPacket = new struct([ | ||
[Packet, 'packet'], | ||
['uint32', 'id'], | ||
]); | ||
|
||
var VoiceDictationStartPacket = new struct([ | ||
[Packet, 'packet'], | ||
]); | ||
|
||
var VoiceDictationDataPacket = new struct([ | ||
[Packet, 'packet'], | ||
['int8', 'status'], | ||
['cstring', 'transcription'], | ||
]); | ||
|
||
var CommandPackets = [ | ||
Packet, | ||
SegmentPacket, | ||
|
@@ -815,6 +831,9 @@ var CommandPackets = [ | |
ElementImagePacket, | ||
ElementAnimatePacket, | ||
ElementAnimateDonePacket, | ||
NumCommandsPacket, | ||
VoiceDictationStartPacket, | ||
VoiceDictationDataPacket, | ||
]; | ||
|
||
var accelAxes = [ | ||
|
@@ -1113,6 +1132,35 @@ SimplyPebble.accelConfig = function(def) { | |
SimplyPebble.sendPacket(AccelConfigPacket.prop(def)); | ||
}; | ||
|
||
SimplyPebble.voiceDictationSession = function(callback) { | ||
// If there's a transcription in progress | ||
if (SimplyPebble.dictationCallback) { | ||
callback( { 'status': -1, 'transcription': null } ); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No spaces for round parens, but the spaces for curly is good. I have to admit I'm not sure what you'd call this style. No quotes necessary, but you did use the right kind of quotes!
|
||
return; | ||
} | ||
|
||
// Grab the current window to re-show once we're done | ||
SimplyPebble.window = WindowStack.top(); | ||
|
||
// Set the callback and send the packet | ||
SimplyPebble.dictationCallback = callback; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use an array of callbacks similar to You'll probably want to start more information than just the callback, such as the top window, so you would have something like:
|
||
SimplyPebble.sendPacket(VoiceDictationStartPacket); | ||
} | ||
|
||
SimplyPebble.onVoiceData = function(packet) { | ||
if (!SimplyPebble.dictationCallback) { | ||
// Something bad happened | ||
console.log("No callback specified for dictation session"); | ||
} else { | ||
// invoke and clear the callback | ||
SimplyPebble.dictationCallback( { 'status': packet.status(), 'transcription': packet.transcription() } ); | ||
SimplyPebble.dictationCallback = null; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. accelPeek is a little weird in how it handles listeners since it tries to do a best effort of throwing the most recent accel data it was given to all listeners. For here, you would just get the callback info with |
||
|
||
// show the top window to re-register handlers, etc. | ||
SimplyPebble.window.show(); | ||
} | ||
|
||
SimplyPebble.menuClear = function() { | ||
SimplyPebble.sendPacket(MenuClearPacket); | ||
}; | ||
|
@@ -1381,11 +1429,15 @@ SimplyPebble.onPacket = function(buffer, offset) { | |
case ElementAnimateDonePacket: | ||
StageElement.emitAnimateDone(packet.id()); | ||
break; | ||
case VoiceDictationDataPacket: | ||
SimplyPebble.onVoiceData(packet); | ||
break; | ||
} | ||
}; | ||
|
||
SimplyPebble.onAppMessage = function(e) { | ||
var data = e.payload[0]; | ||
|
||
Packet._view = toArrayBuffer(data); | ||
|
||
var offset = 0; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
var simply = require('ui/simply'); | ||
|
||
var Voice = {}; | ||
|
||
Voice.startDictationSession = function(e) { | ||
simply.impl.voiceDictationSession(e); | ||
}; | ||
|
||
module.exports = Voice; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -53,4 +53,6 @@ enum Command { | |
CommandElementAnimate, | ||
CommandElementAnimateDone, | ||
NumCommands, | ||
CommandVoiceStart, | ||
CommandVoiceData, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add these before NumCommands and remove the NumCommands packet type in simply-pebble.js -- it isn't a real packet type, it's just to get the number of commands. |
||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
#include "simply_voice.h" | ||
|
||
#include "simply_msg.h" | ||
|
||
#include "simply.h" | ||
|
||
#include <pebble.h> | ||
|
||
typedef struct VoiceDataPacket VoiceDataPacket; | ||
|
||
struct __attribute__((__packed__)) VoiceDataPacket { | ||
Packet packet; | ||
int8_t status; | ||
char result[SIMPLY_VOICE_BUFFER_LENGTH]; | ||
}; | ||
|
||
static SimplyVoice *s_voice; | ||
|
||
static bool send_voice_data(int status, char *transcription) { | ||
VoiceDataPacket packet = { | ||
.packet.type = CommandVoiceData, | ||
.packet.length = sizeof(packet), | ||
.status = (uint8_t) status, | ||
}; | ||
snprintf(packet.result, sizeof(packet.result), "%s", transcription); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, it would be better to allocate a packet that is the exact size of the string, such as
You would also change Bonus points if you use strncpy, but I'll accept it without it (too much to type here). |
||
|
||
return simply_msg_send_packet(&packet.packet); | ||
} | ||
|
||
#ifndef PBL_SDK_2 | ||
// Define a callback for the dictation session | ||
static void dictation_session_callback(DictationSession *session, DictationSessionStatus status, char *transcription, void *context) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This codebase has max line of 120 (although I'm starting to think about whether it should be 100) following Google C++ Style. Long story short, you can format this like this.
|
||
s_voice->inProgress = false; | ||
|
||
// Send the result | ||
send_voice_data(status, transcription); | ||
} | ||
#endif | ||
|
||
static void timer_callback_start_dictation(void *data) { | ||
dictation_session_start(s_voice->session); | ||
} | ||
|
||
|
||
static void handle_voice_start_packet(Simply *simply, Packet *data) { | ||
#ifdef PBL_SDK_2 | ||
// send an immediate reply if we don't support voice | ||
send_voice_data(-1, ""); | ||
#else | ||
|
||
// Send an immediate response if there's already a dictation session in progress | ||
if (s_voice->inProgress) { | ||
send_voice_data(-1, ""); | ||
return; | ||
} | ||
|
||
// Otherwise, start the timer as soon as possible | ||
// (we start a timer so we can return true as quickly as possible) | ||
s_voice->inProgress = true; | ||
s_voice->timer = app_timer_register(0, timer_callback_start_dictation, NULL); | ||
#endif | ||
} | ||
|
||
bool simply_voice_handle_packet(Simply *simply, Packet *packet) { | ||
switch (packet->type) { | ||
case CommandVoiceStart: | ||
handle_voice_start_packet(simply, packet); | ||
return true; | ||
} | ||
|
||
return false; | ||
} | ||
|
||
SimplyVoice *simply_voice_create(Simply *simply) { | ||
if(s_voice) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Space between |
||
return s_voice; | ||
} | ||
|
||
SimplyVoice *self = malloc(sizeof(*self)); | ||
*self = (SimplyVoice) { | ||
.simply = simply, | ||
.inProgress = false, | ||
}; | ||
|
||
#ifndef PBL_SDK_2 | ||
self->session = dictation_session_create(SIMPLY_VOICE_BUFFER_LENGTH, dictation_session_callback, NULL), | ||
#endif | ||
|
||
s_voice = self; | ||
return self; | ||
} | ||
|
||
void simply_voice_destroy(SimplyVoice *self) { | ||
if (!self) { | ||
return; | ||
} | ||
|
||
free(self); | ||
s_voice = NULL; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
#pragma once | ||
|
||
#include "simply_msg.h" | ||
#include "simply.h" | ||
|
||
#include <pebble.h> | ||
|
||
#define SIMPLY_VOICE_BUFFER_LENGTH 512 | ||
|
||
#ifdef PBL_SDK_2 | ||
typedef struct DictationSession DictationSession; | ||
typedef struct DictationSessionStatus DictationSessionStatus; | ||
void dictation_session_start(DictationSession *session); | ||
#endif | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can move this into util/compat.h, there is a section for |
||
|
||
typedef struct SimplyVoice SimplyVoice; | ||
|
||
struct SimplyVoice { | ||
Simply *simply; | ||
DictationSession *session; | ||
AppTimer *timer; | ||
|
||
bool inProgress; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We use |
||
}; | ||
|
||
SimplyVoice *simply_voice_create(Simply *simply); | ||
void simply_voice_destroy(SimplyVoice *self); | ||
|
||
bool simply_voice_handle_packet(Simply *simply, Packet *packet); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extra whitespace