parent
b9f2ae2bff
commit
ec0f90125d
@ -1,206 +0,0 @@ |
||||
<?php |
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
/** |
||||
* This file allows creating new svg and png documents with an online editor. |
||||
* |
||||
* @package chamilo.document |
||||
* |
||||
* @author Juan Carlos Raña Trabado herodoto@telefonica.net |
||||
* @since 5/mar/2011 |
||||
*/ |
||||
/** |
||||
* Code |
||||
*/ |
||||
|
||||
/* INIT SECTION */ |
||||
|
||||
// Name of the language file that needs to be included |
||||
$language_file = array('document'); |
||||
|
||||
require_once '../inc/global.inc.php'; |
||||
|
||||
$_SESSION['whereami'] = 'document/voicerecord'; |
||||
$this_section = SECTION_COURSES; |
||||
|
||||
require_once api_get_path(SYS_CODE_PATH).'document/document.inc.php'; |
||||
require_once api_get_path(LIBRARY_PATH).'groupmanager.lib.php'; |
||||
|
||||
$nameTools = get_lang('VoiceRecord'); |
||||
|
||||
api_protect_course_script(); |
||||
api_block_anonymous_users(); |
||||
|
||||
$document_data = DocumentManager::get_document_data_by_id($_GET['id'], api_get_course_id(), true); |
||||
if (empty($document_data)) { |
||||
if (api_is_in_group()) { |
||||
$group_properties = GroupManager::get_group_properties(api_get_group_id()); |
||||
$document_id = DocumentManager::get_document_id(api_get_course_info(), $group_properties['directory']); |
||||
$document_data = DocumentManager::get_document_data_by_id($document_id, api_get_course_id()); |
||||
} |
||||
} |
||||
|
||||
$document_id = $document_data['id']; |
||||
$dir = $document_data['path']; |
||||
|
||||
$is_allowed_to_edit = api_is_allowed_to_edit(null, true); |
||||
|
||||
// Please, do not modify this dirname formatting |
||||
|
||||
if (strstr($dir, '..')) { |
||||
$dir = '/'; |
||||
} |
||||
|
||||
if ($dir[0] == '.') { |
||||
$dir = substr($dir, 1); |
||||
} |
||||
|
||||
if ($dir[0] != '/') { |
||||
$dir = '/'.$dir; |
||||
} |
||||
|
||||
if ($dir[strlen($dir) - 1] != '/') { |
||||
$dir .= '/'; |
||||
} |
||||
|
||||
$filepath = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document'.$dir; |
||||
|
||||
if (!is_dir($filepath)) { |
||||
$filepath = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document/'; |
||||
$dir = '/'; |
||||
} |
||||
|
||||
//groups //TODO: clean |
||||
if (isset ($_SESSION['_gid']) && $_SESSION['_gid'] != 0) { |
||||
$req_gid = '&gidReq='.$_SESSION['_gid']; |
||||
$interbreadcrumb[] = array ("url" => "../group/group_space.php?gidReq=".$_SESSION['_gid'], "name" => get_lang('GroupSpace')); |
||||
$noPHP_SELF = true; |
||||
$to_group_id = $_SESSION['_gid']; |
||||
$group = GroupManager :: get_group_properties($to_group_id); |
||||
$path = explode('/', $dir); |
||||
if ('/'.$path[1] != $group['directory']) { |
||||
api_not_allowed(true); |
||||
} |
||||
} |
||||
|
||||
$interbreadcrumb[] = array ("url" => "./document.php?id=".$document_id.$req_gid, "name" => get_lang('Documents')); |
||||
|
||||
if (!$is_allowed_in_course) { |
||||
api_not_allowed(true); |
||||
} |
||||
|
||||
if (!($is_allowed_to_edit || $_SESSION['group_member_with_upload_rights'] || is_my_shared_folder(api_get_user_id(), Security::remove_XSS($dir),api_get_session_id()))) { |
||||
api_not_allowed(true); |
||||
} |
||||
|
||||
|
||||
/* Header */ |
||||
event_access_tool(TOOL_DOCUMENT); |
||||
|
||||
$display_dir = $dir; |
||||
if (isset ($group)) { |
||||
$display_dir = explode('/', $dir); |
||||
unset ($display_dir[0]); |
||||
unset ($display_dir[1]); |
||||
$display_dir = implode('/', $display_dir); |
||||
} |
||||
|
||||
// Interbreadcrumb for the current directory root path |
||||
$counter = 0; |
||||
if (isset($document_data['parents'])) { |
||||
foreach($document_data['parents'] as $document_sub_data) { |
||||
//fixing double group folder in breadcrumb |
||||
if (api_get_group_id()) { |
||||
if ($counter == 0) { |
||||
$counter++; |
||||
continue; |
||||
} |
||||
} |
||||
$interbreadcrumb[] = array('url' => $document_sub_data['document_url'], 'name' => $document_sub_data['title']); |
||||
$counter++; |
||||
} |
||||
} |
||||
Display :: display_header($nameTools, 'Doc'); |
||||
|
||||
echo '<div class="actions">'; |
||||
echo '<a href="document.php?'.api_get_cidreq().'&id='.$document_id.'">'.Display::return_icon('back.png',get_lang('BackTo').' '.get_lang('DocumentsOverview'),'',ICON_SIZE_MEDIUM).'</a>'; |
||||
echo '</div>'; |
||||
|
||||
?> |
||||
<script language="JavaScript"> |
||||
function submitVoice() { |
||||
//lang vars |
||||
var lang_no_applet="<?php echo get_lang('NanogongNoApplet'); ?>";
|
||||
var lang_record_before_save="<?php echo get_lang('NanogongRecordBeforeSave'); ?>";
|
||||
var lang_give_a_title="<?php echo get_lang('NanogongGiveTitle'); ?>";
|
||||
var lang_failled_to_submit="<?php echo get_lang('NanogongFailledToSubmit'); ?>";
|
||||
var lang_submitted="<?php echo get_lang('NanogongSubmitted'); ?>";
|
||||
|
||||
//path, url and filename |
||||
var filename = document.getElementById("audio_title").value+".wav"; |
||||
var filename = filename.replace(/\s/g, "_");//replace spaces by _ |
||||
var filename = encodeURIComponent(filename); |
||||
var filepath="<?php echo urlencode($filepath); ?>";
|
||||
var dir="<?php echo urlencode($dir); ?>";
|
||||
var course_code="<?php echo urlencode($course_code); ?>";
|
||||
var urlnanogong="../inc/lib/nanogong/receiver.php?filename="+filename+"&filepath="+filepath+"&dir="+dir+"&course_code="+course_code; |
||||
|
||||
//check |
||||
var recorder |
||||
if (!(recorder = document.getElementById("nanogong"))) { |
||||
alert(lang_no_applet) |
||||
return |
||||
} |
||||
var duration = parseInt(recorder.sendGongRequest("GetMediaDuration", "audio")) || 0 |
||||
if (duration <= 0) { |
||||
alert(lang_record_before_save) |
||||
return |
||||
} |
||||
if (!document.getElementById("audio_title").value) { |
||||
alert(lang_give_a_title) |
||||
return |
||||
} |
||||
// |
||||
var applet = document.getElementById("nanogong"); |
||||
var ret = applet.sendGongRequest( "PostToForm", urlnanogong, "voicefile", "", "temp");//'PostToForm', postURL, inputname, cookie, filename |
||||
if (ret == null) { |
||||
alert(lang_failled_to_submit); |
||||
} else { |
||||
alert(lang_submitted+"\n"+ret); |
||||
$("#status").attr('value', '1'); |
||||
} |
||||
} |
||||
</script> |
||||
|
||||
<?php |
||||
|
||||
echo '<div align="center">'; |
||||
|
||||
Display::display_icon('microphone.png', get_lang('PressRecordButton'),'','128'); |
||||
echo '<br/>'; |
||||
echo '<applet id="nanogong" archive="'.api_get_path(WEB_LIBRARY_PATH).'nanogong/nanogong.jar" code="gong.NanoGong" width="250" height="40" ALIGN="middle">'; |
||||
//echo '<param name="ShowRecordButton" value="false" />'; // default true |
||||
// echo '<param name="ShowSaveButton" value="false" />'; //you can save in local computer | (default true) |
||||
//echo '<param name="ShowSpeedButton" value="false" />'; // default true |
||||
//echo '<param name="ShowAudioLevel" value="false" />'; // it displays the audiometer | (default true) |
||||
echo '<param name="ShowTime" value="true" />'; // default false |
||||
//echo '<param name="Color" value="#C0E0FF" />'; // default #FFFFFF |
||||
//echo '<param name="StartTime" value="10.5" />'; |
||||
//echo '<param name="EndTime" value="65" />'; |
||||
echo '<param name="AudioFormat" value="ImaADPCM" />';// ImaADPCM (more speed), Speex (more compression)|(default Speex) |
||||
//echo '<param name="SamplingRate" value="32000" />';//Quality for ImaADPCM (low 8000, medium 11025, normal 22050, hight 44100) OR Quality for Speex (low 8000, medium 16000, normal 32000, hight 44100) | (default 44100) |
||||
//echo '<param name="MaxDuration" value="60" />'; |
||||
//echo '<param name="SoundFileURL" value="http://somewhere.com/mysoundfile.wav" />';//load a file |(default "") |
||||
|
||||
echo '</applet>'; |
||||
|
||||
echo '<form name="form_nanogong">'; |
||||
echo '<input placeholder="'.get_lang('Filename').'" type="text" id="audio_title">'; |
||||
echo '<input type="hidden" name="cidReq" value="'.$_course['id'].'">'; |
||||
echo '<input type="hidden" name="id" value="'.$document_id.'">'; |
||||
echo '<input id="status" type="hidden" name="status" value="0">'; |
||||
echo '<button class="upload" type="submit" value="'.get_lang('Send').'" onClick="submitVoice()" />'.get_lang('Send').'</button>'; |
||||
echo '</form>'; |
||||
|
||||
echo '</div>'; |
||||
Display :: display_footer(); |
@ -0,0 +1,199 @@ |
||||
<?php |
||||
/* For licensing terms, see /license.txt */ |
||||
|
||||
/** |
||||
* This file allows record wav files. |
||||
* |
||||
* @package chamilo.document |
||||
* |
||||
* @author Juan Carlos Raña Trabado herodoto@telefonica.net |
||||
* @since 5/april/2012 |
||||
*/ |
||||
/** |
||||
* Code |
||||
*/ |
||||
|
||||
/* INIT SECTION */ |
||||
|
||||
// Name of the language file that needs to be included |
||||
$language_file = array('document'); |
||||
|
||||
require_once '../inc/global.inc.php'; |
||||
|
||||
$_SESSION['whereami'] = 'document/voicerecord'; |
||||
$this_section = SECTION_COURSES; |
||||
|
||||
require_once api_get_path(SYS_CODE_PATH).'document/document.inc.php'; |
||||
require_once api_get_path(LIBRARY_PATH).'groupmanager.lib.php'; |
||||
|
||||
$nameTools = get_lang('VoiceRecord'); |
||||
|
||||
api_protect_course_script(); |
||||
api_block_anonymous_users(); |
||||
|
||||
$document_data = DocumentManager::get_document_data_by_id($_GET['id'], api_get_course_id(), true); |
||||
if (empty($document_data)) { |
||||
if (api_is_in_group()) { |
||||
$group_properties = GroupManager::get_group_properties(api_get_group_id()); |
||||
$document_id = DocumentManager::get_document_id(api_get_course_info(), $group_properties['directory']); |
||||
$document_data = DocumentManager::get_document_data_by_id($document_id, api_get_course_id()); |
||||
} |
||||
} |
||||
|
||||
$document_id = $document_data['id']; |
||||
$dir = $document_data['path']; |
||||
|
||||
//make some vars |
||||
$wamidir=$dir; |
||||
if($wamidir=="/"){ |
||||
$wamidir=""; |
||||
} |
||||
$wamiurlplay=api_get_path(WEB_COURSE_PATH).api_get_course_path().'/document'.$wamidir."/"; |
||||
|
||||
|
||||
$is_allowed_to_edit = api_is_allowed_to_edit(null, true); |
||||
|
||||
// Please, do not modify this dirname formatting |
||||
|
||||
if (strstr($dir, '..')) { |
||||
$dir = '/'; |
||||
} |
||||
|
||||
if ($dir[0] == '.') { |
||||
$dir = substr($dir, 1); |
||||
} |
||||
|
||||
if ($dir[0] != '/') { |
||||
$dir = '/'.$dir; |
||||
} |
||||
|
||||
if ($dir[strlen($dir) - 1] != '/') { |
||||
$dir .= '/'; |
||||
} |
||||
|
||||
$filepath = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document'.$dir; |
||||
|
||||
if (!is_dir($filepath)) { |
||||
$filepath = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document/'; |
||||
$dir = '/'; |
||||
} |
||||
//make some vars |
||||
//$waminame="audiorecord.wav"; |
||||
$wamiuserid=api_get_user_id(); |
||||
$userinfo=Database::get_user_info_from_id($wamiuserid); |
||||
$waminame = api_get_person_name($userinfo['firstname'], $userinfo['lastname']).'.wav'; |
||||
$waminame = replace_dangerous_char($waminame, 'strict'); |
||||
|
||||
|
||||
$waminame_noex=basename($waminame, ".wav"); |
||||
$dirBaseDocuments = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document'; |
||||
$saveDir=$dirBaseDocuments.$wamidir; |
||||
if (file_exists($saveDir.'/'.$waminame)){ |
||||
$i = 1; |
||||
while (file_exists($saveDir.'/'.$waminame_noex.'_'.$i.'.wav')) $i++; |
||||
$waminame = $waminame_noex . '_' . $i . '.wav'; |
||||
} |
||||
|
||||
//groups //TODO: clean |
||||
if (isset ($_SESSION['_gid']) && $_SESSION['_gid'] != 0) { |
||||
$req_gid = '&gidReq='.$_SESSION['_gid']; |
||||
$interbreadcrumb[] = array ("url" => "../group/group_space.php?gidReq=".$_SESSION['_gid'], "name" => get_lang('GroupSpace')); |
||||
$noPHP_SELF = true; |
||||
$to_group_id = $_SESSION['_gid']; |
||||
$group = GroupManager :: get_group_properties($to_group_id); |
||||
$path = explode('/', $dir); |
||||
if ('/'.$path[1] != $group['directory']) { |
||||
api_not_allowed(true); |
||||
} |
||||
} |
||||
|
||||
$interbreadcrumb[] = array ("url" => "./document.php?id=".$document_id.$req_gid, "name" => get_lang('Documents')); |
||||
|
||||
if (!$is_allowed_in_course) { |
||||
api_not_allowed(true); |
||||
} |
||||
|
||||
if (!($is_allowed_to_edit || $_SESSION['group_member_with_upload_rights'] || is_my_shared_folder(api_get_user_id(), Security::remove_XSS($dir),api_get_session_id()))) { |
||||
api_not_allowed(true); |
||||
} |
||||
|
||||
|
||||
/* Header */ |
||||
event_access_tool(TOOL_DOCUMENT); |
||||
|
||||
$display_dir = $dir; |
||||
if (isset ($group)) { |
||||
$display_dir = explode('/', $dir); |
||||
unset ($display_dir[0]); |
||||
unset ($display_dir[1]); |
||||
$display_dir = implode('/', $display_dir); |
||||
} |
||||
|
||||
// Interbreadcrumb for the current directory root path |
||||
$counter = 0; |
||||
if (isset($document_data['parents'])) { |
||||
foreach($document_data['parents'] as $document_sub_data) { |
||||
//fixing double group folder in breadcrumb |
||||
if (api_get_group_id()) { |
||||
if ($counter == 0) { |
||||
$counter++; |
||||
continue; |
||||
} |
||||
} |
||||
$interbreadcrumb[] = array('url' => $document_sub_data['document_url'], 'name' => $document_sub_data['title']); |
||||
$counter++; |
||||
} |
||||
} |
||||
|
||||
Display :: display_header($nameTools, 'Doc'); |
||||
echo '<div class="actions">'; |
||||
echo '<a href="document.php?id='.$document_id.'">'.Display::return_icon('back.png',get_lang('BackTo').' '.get_lang('DocumentsOverview'),'',ICON_SIZE_MEDIUM).'</a>'; |
||||
echo '</div>'; |
||||
|
||||
|
||||
?> |
||||
<!-- swfobject is a commonly used library to embed Flash content https://ajax.googleapis.com/ajax/libs/swfobject/2.2/ --> |
||||
<script type="text/javascript" src="<?php echo api_get_path(WEB_LIBRARY_PATH) ?>swfobject/swfobject.js"></script>
|
||||
|
||||
<!-- Setup the recorder interface --> |
||||
<script type="text/javascript" src="<?php echo api_get_path(WEB_LIBRARY_PATH) ?>wami-recorder/recorder.js"></script>
|
||||
|
||||
<!-- GUI code... take it or leave it --> |
||||
<script type="text/javascript" src="<?php echo api_get_path(WEB_LIBRARY_PATH) ?>wami-recorder/gui.js"></script>
|
||||
|
||||
<script> |
||||
function setupRecorder() { |
||||
Wami.setup({ |
||||
id : "wami", |
||||
onReady : setupGUI |
||||
}); |
||||
} |
||||
|
||||
function setupGUI() { |
||||
var gui = new Wami.GUI({ |
||||
id : "wami", |
||||
recordUrl : "<?php echo api_get_path(WEB_LIBRARY_PATH) ?>wami-recorder/record_document.php?waminame=<?php echo $waminame; ?>&wamidir=<?php echo $wamidir; ?>&wamiuserid=<?php echo $wamiuserid; ?>",
|
||||
playUrl : "<?php echo $wamiurlplay.$waminame; ?>",
|
||||
buttonUrl : "<?php echo api_get_path(WEB_LIBRARY_PATH) ?>wami-recorder/buttons.png",
|
||||
swfUrl : "<?php echo api_get_path(WEB_LIBRARY_PATH) ?>wami-recorder/Wami.swf",
|
||||
}); |
||||
|
||||
gui.setPlayEnabled(false); |
||||
} |
||||
|
||||
</script> |
||||
|
||||
<div id="wami" style="margin-left: 460px; margin-top:50px;"></div> |
||||
<div align="center" style="margin-top:200px;"> |
||||
<?php |
||||
|
||||
Display::display_normal_message(get_lang('VoiceRecord'), false); |
||||
|
||||
?> |
||||
|
||||
</div> |
||||
<body onLoad="setupRecorder()"> |
||||
|
||||
<?php |
||||
|
||||
Display :: display_footer(); |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
@ -1,44 +0,0 @@ |
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> |
||||
<html> |
||||
<head> |
||||
<!-- swfobject is a commonly used library to embed Flash content --> |
||||
<script type="text/javascript" |
||||
src="https://ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js"></script> |
||||
|
||||
<!-- Setup the recorder interface --> |
||||
<script type="text/javascript" src="recorder.js"></script> |
||||
|
||||
<script> |
||||
function setup() { |
||||
Wami.setup("wami"); |
||||
} |
||||
|
||||
function record() { |
||||
status("Recording..."); |
||||
Wami.startRecording("https://wami-recorder.appspot.com/audio"); |
||||
} |
||||
|
||||
function play() { |
||||
Wami.startPlaying("https://wami-recorder.appspot.com/audio"); |
||||
} |
||||
|
||||
function stop() { |
||||
status(""); |
||||
Wami.stopRecording(); |
||||
Wami.stopPlaying(); |
||||
} |
||||
|
||||
function status(msg) { |
||||
document.getElementById('status').innerHTML = msg; |
||||
} |
||||
</script> |
||||
</head> |
||||
|
||||
<body onload="setup()"> |
||||
<input type="button" value="Record" onclick="record()"></input> |
||||
<input type="button" value="Stop" onclick="stop()"></input> |
||||
<input type="button" value="Play" onclick="play()"></input> |
||||
<div id="status"></div> |
||||
<div id="wami"></div> |
||||
</body> |
||||
</html> |
@ -1,52 +0,0 @@ |
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> |
||||
<html> |
||||
<head> |
||||
<!-- swfobject is a commonly used library to embed Flash content --> |
||||
<script type="text/javascript" |
||||
src="https://ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js"></script> |
||||
|
||||
<!-- Setup the recorder interface --> |
||||
<script type="text/javascript" src="recorder.js"></script> |
||||
|
||||
<!-- GUI code... take it or leave it --> |
||||
<script type="text/javascript" src="gui.js"></script> |
||||
|
||||
<script> |
||||
function setupRecorder() { |
||||
Wami.setup({ |
||||
id : "wami", |
||||
onReady : setupGUI |
||||
}); |
||||
} |
||||
|
||||
function setupGUI() { |
||||
var gui = new Wami.GUI({ |
||||
id : "wami", |
||||
recordUrl : "https://wami-recorder.appspot.com/audio", |
||||
playUrl : "https://wami-recorder.appspot.com/audio" |
||||
}); |
||||
|
||||
gui.setPlayEnabled(false); |
||||
} |
||||
</script> |
||||
</head> |
||||
|
||||
<body onload="setupRecorder()"> |
||||
<div id="wami" style="margin-left: 100px;"></div> |
||||
<noscript>WAMI requires Javascript</noscript> |
||||
|
||||
<div |
||||
style="position: absolute; left: 400px; top: 20px; font-family: arial, sans-serif; font-size: 82%"> |
||||
Right-click to Download<br /> <br /> <a |
||||
href="https://wami-recorder.googlecode.com/hg/example/client/index.html">index.html</a><br /> |
||||
<a |
||||
href="https://wami-recorder.googlecode.com/hg/example/client/Wami.swf">Wami.swf</a><br /> |
||||
<a |
||||
href="https://wami-recorder.googlecode.com/hg/example/client/buttons.png">buttons.png</a><br /> |
||||
<a |
||||
href="https://wami-recorder.googlecode.com/hg/example/client/recorder.js">recorder.js</a><br /> |
||||
<a |
||||
href="https://wami-recorder.googlecode.com/hg/example/client/gui.js">gui.js</a><br /> |
||||
</div> |
||||
</body> |
||||
</html> |
@ -1,12 +0,0 @@ |
||||
Each directory (or sub-directory of 'gae') contains an example server |
||||
in a particular language designed to handle audio incoming from the |
||||
Wami recorder. |
||||
|
||||
The PHP example requires the configuration of a PHP-enabled |
||||
web-server, while the python server can be run on any machine with |
||||
python installed. |
||||
|
||||
If you do not wish to host your own server, you can try out the Google |
||||
App Engine (GAE) examples. The GAE python example stores data in a |
||||
"blobstore", for which Google provides a handy web-based management |
||||
console. |
@ -1,9 +0,0 @@ |
||||
<?php |
||||
# Save the audio to a URL-accessible directory for playback. |
||||
parse_str($_SERVER['QUERY_STRING'], $params); |
||||
$name = isset($params['name']) ? $params['name'] : 'output.wav'; |
||||
$content = file_get_contents('php://input'); |
||||
$fh = fopen($name, 'w') or die("can't open file"); |
||||
fwrite($fh, $content); |
||||
fclose($fh); |
||||
?> |
@ -1,53 +0,0 @@ |
||||
# Run from the commandline: |
||||
# |
||||
# python server.py |
||||
# POST audio to http://localhost:9000 |
||||
# GET audio from http://localhost:9000 |
||||
# |
||||
# A simple server to collect audio using python. To be more secure, |
||||
# you might want to check the file names and place size restrictions |
||||
# on the incoming data. |
||||
|
||||
import cgi |
||||
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer |
||||
|
||||
class WamiHandler(BaseHTTPRequestHandler): |
||||
dirname = "/tmp/" |
||||
|
||||
def do_GET(self): |
||||
f = open(self.get_name()) |
||||
self.send_response(200) |
||||
self.send_header('content-type','audio/x-wav') |
||||
self.end_headers() |
||||
self.wfile.write(f.read()) |
||||
f.close() |
||||
|
||||
def do_POST(self): |
||||
f = open(self.get_name(), "wb") |
||||
# Note that python's HTTPServer doesn't support chunked transfer. |
||||
# Thus, it requires a content-length. |
||||
length = int(self.headers.getheader('content-length')) |
||||
print "POST of length " + str(length) |
||||
f.write(self.rfile.read(length)) |
||||
f.close(); |
||||
|
||||
def get_name(self): |
||||
filename = 'output.wav'; |
||||
qs = self.path.split('?',1); |
||||
if len(qs) == 2: |
||||
params = cgi.parse_qs(qs[1]) |
||||
if params['name']: |
||||
filename = params['name'][0]; |
||||
return WamiHandler.dirname + filename |
||||
|
||||
def main(): |
||||
try: |
||||
server = HTTPServer(('', 9000), WamiHandler) |
||||
print 'Started server...' |
||||
server.serve_forever() |
||||
except KeyboardInterrupt: |
||||
print 'Stopping server' |
||||
server.socket.close() |
||||
|
||||
if __name__ == '__main__': |
||||
main() |
@ -1,9 +0,0 @@ |
||||
<?xml version="1.0" encoding="UTF-8" ?> |
||||
<!-- This is just a Google gadget to toss an example onto the code.google.com page. --> |
||||
<Module> |
||||
<ModulePrefs title="Wami Recorder Example" |
||||
author="Ian McGraw" |
||||
author_email="wami-toolkit@csail.mit.edu" |
||||
description="A Google gadget to exemplify the simplest wami-recorder use-case." /> |
||||
<Content type="url" href="https://wami-recorder.appspot.com/client/index.html"/> |
||||
</Module> |
@ -0,0 +1,6 @@ |
||||
<html> |
||||
<head> |
||||
</head> |
||||
<body> |
||||
</body> |
||||
</html> |
@ -0,0 +1,72 @@ |
||||
<?php |
||||
require_once '../../../inc/global.inc.php'; |
||||
require_once api_get_path(LIBRARY_PATH).'fileUpload.lib.php'; |
||||
require_once api_get_path(LIBRARY_PATH).'document.lib.php'; |
||||
////Add security from Chamilo |
||||
api_protect_course_script(); |
||||
api_block_anonymous_users(); |
||||
// |
||||
# Save the audio to a URL-accessible directory for playback. |
||||
parse_str($_SERVER['QUERY_STRING'], $params); |
||||
|
||||
if(isset($params['waminame']) && isset($params['wamidir']) && isset($params['wamiuserid'])) { |
||||
$waminame = $params['waminame']; |
||||
$wamidir = $params['wamidir']; |
||||
$wamiuserid = $params['wamiuserid']; |
||||
} |
||||
else { |
||||
api_not_allowed(); |
||||
die(); |
||||
} |
||||
|
||||
if ($wamiuserid!= api_get_user_id()){ |
||||
api_not_allowed(); |
||||
die(); |
||||
} |
||||
|
||||
|
||||
//clean |
||||
$waminame = Security::remove_XSS($waminame); |
||||
$waminame = addslashes(trim($waminame)); |
||||
$waminame = replace_dangerous_char($waminame, 'strict'); |
||||
$waminame = disable_dangerous_file($waminame); |
||||
$wamidir = Security::remove_XSS($wamidir); |
||||
|
||||
|
||||
$content = file_get_contents('php://input'); |
||||
|
||||
//security extension |
||||
$ext = explode('.', $waminame); |
||||
$ext = strtolower($ext[sizeof($ext) - 1]); |
||||
|
||||
if($ext!= 'wav'){ |
||||
die(); |
||||
} |
||||
|
||||
/* |
||||
//Security verify that the file is audio |
||||
$headers = get_headers($content, 1); |
||||
$content_type = explode("/", $headers['Content-Type']); |
||||
if ($content_type[0] != "audio"){ |
||||
echo "Invalid file type"; |
||||
exit; |
||||
} |
||||
*/ |
||||
|
||||
$dirBaseDocuments = api_get_path(SYS_COURSE_PATH).$_course['path'].'/document'; |
||||
$saveDir=$dirBaseDocuments.$wamidir; |
||||
$current_session_id = api_get_session_id(); |
||||
$groupId=$_SESSION['_gid']; |
||||
$documentPath = $saveDir.'/'.$waminame; |
||||
$title=str_replace('_',' ',$waminame); |
||||
//$title=basename($waminame, ".wav");//save title whitout extension |
||||
|
||||
//add to disk |
||||
$fh = fopen($documentPath, 'w') or die("can't open file"); |
||||
fwrite($fh, $content); |
||||
fclose($fh); |
||||
|
||||
//add document to database |
||||
$doc_id = add_document($_course, $wamidir.'/'.$waminame, 'file', filesize($documentPath), $title); |
||||
api_item_property_update($_course, TOOL_DOCUMENT, $doc_id, 'DocumentAdded', $_user['user_id'], $groupId, null, null, null, $current_session_id); |
||||
?> |
@ -1,171 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2011 |
||||
* Spoken Language Systems Group |
||||
* MIT Computer Science and Artificial Intelligence Laboratory |
||||
* Massachusetts Institute of Technology |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person |
||||
* obtaining a copy of this software and associated documentation |
||||
* files (the "Software"), to deal in the Software without |
||||
* restriction, including without limitation the rights to use, copy, |
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies |
||||
* of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice shall be |
||||
* included in all copies or substantial portions of the Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
*/ |
||||
package edu.mit.csail.wami.audio |
||||
{ |
||||
import edu.mit.csail.wami.utils.External; |
||||
|
||||
import flash.utils.ByteArray; |
||||
import flash.utils.Endian; |
||||
|
||||
/** |
||||
* This container is better for streaming, because it explicitly |
||||
* says what to do when the length of the audio is unknown. It's typically |
||||
* associated with mu-law compression (which wouldn't be too hard too implement) |
||||
* but here we're using linear PCM. |
||||
*/ |
||||
public class AuContainer implements IAudioContainer |
||||
{ |
||||
public function isLengthRequired():Boolean { |
||||
return false; |
||||
} |
||||
|
||||
public function toByteArray(format:AudioFormat, length:int = -1):ByteArray |
||||
{ |
||||
var dataLength:uint = 0xffffffff; |
||||
if (length > -1) |
||||
{ |
||||
dataLength = length; |
||||
} |
||||
|
||||
if (format.endian != Endian.BIG_ENDIAN) |
||||
{ |
||||
throw new Error("AU is a container for big endian data"); |
||||
} |
||||
// http://en.wikipedia.org/wiki/Au_file_format |
||||
var header:ByteArray = new ByteArray(); |
||||
header.endian = format.endian; |
||||
header.writeUTFBytes(".snd"); |
||||
header.writeInt(24); // Data offset |
||||
header.writeInt(dataLength); |
||||
|
||||
var bits:uint = getEncodingFromBits(format); |
||||
header.writeInt(bits); |
||||
header.writeInt(format.rate); |
||||
header.writeInt(format.channels); |
||||
header.position = 0; |
||||
External.debugBytes(header); |
||||
return header; |
||||
} |
||||
|
||||
private function getEncodingFromBits(format:AudioFormat):uint |
||||
{ |
||||
if (format.bits == 16) |
||||
{ |
||||
return 3; |
||||
} |
||||
else if (format.bits == 24) |
||||
{ |
||||
return 4; |
||||
} |
||||
else if (format.bits == 32) |
||||
{ |
||||
return 5; |
||||
} |
||||
|
||||
throw new Error("Bits not supported"); |
||||
} |
||||
|
||||
private function getBitsFromEncoding(encoding:uint):uint |
||||
{ |
||||
if (encoding == 3) |
||||
{ |
||||
return 16; |
||||
} |
||||
else if (encoding == 4) |
||||
{ |
||||
return 24; |
||||
} |
||||
else if (encoding == 5) |
||||
{ |
||||
return 32; |
||||
} |
||||
|
||||
throw new Error("Encoding not supported: " + encoding); |
||||
} |
||||
|
||||
public function fromByteArray(header:ByteArray):AudioFormat |
||||
{ |
||||
if (header.bytesAvailable < 24) |
||||
{ |
||||
return notAu(header, "Header not yet long enough for Au"); |
||||
} |
||||
|
||||
var b:ByteArray = new ByteArray(); |
||||
header.readBytes(b, 0, 24); |
||||
External.debugBytes(b); |
||||
header.position = 0; |
||||
|
||||
header.endian = Endian.BIG_ENDIAN; // Header is big-endian |
||||
|
||||
var magic:String = header.readUTFBytes(4); |
||||
if (magic != ".snd") |
||||
{ |
||||
return notAu(header, "Not an AU header, first bytes should be .snd"); |
||||
} |
||||
|
||||
var dataOffset:uint = header.readInt(); |
||||
var dataLength:uint = header.readInt(); |
||||
|
||||
if (header.bytesAvailable < dataOffset - 12) |
||||
{ |
||||
return notAu(header, "Header of length " + header.bytesAvailable + " not long enough yet to include offset of length " + dataOffset); |
||||
} |
||||
|
||||
var encoding:uint = header.readInt(); |
||||
|
||||
var bits:uint; |
||||
try { |
||||
bits = getBitsFromEncoding(encoding); |
||||
} catch (e:Error) { |
||||
return notAu(header, e.message); |
||||
} |
||||
|
||||
var rate:uint = header.readInt(); |
||||
var channels:uint = header.readInt(); |
||||
|
||||
header.position = dataOffset; |
||||
|
||||
var format:AudioFormat; |
||||
try |
||||
{ |
||||
format = new AudioFormat(rate, channels, bits, Endian.BIG_ENDIAN); |
||||
} catch (e:Error) |
||||
{ |
||||
return notAu(header, e.message); |
||||
} |
||||
|
||||
return format; |
||||
} |
||||
|
||||
private function notAu(header:ByteArray, msg:String):AudioFormat |
||||
{ |
||||
External.debug("Not Au: " + msg); |
||||
header.position = 0; |
||||
return null; |
||||
} |
||||
} |
||||
} |
@ -1,158 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2011 |
||||
* Spoken Language Systems Group |
||||
* MIT Computer Science and Artificial Intelligence Laboratory |
||||
* Massachusetts Institute of Technology |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person |
||||
* obtaining a copy of this software and associated documentation |
||||
* files (the "Software"), to deal in the Software without |
||||
* restriction, including without limitation the rights to use, copy, |
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies |
||||
* of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice shall be |
||||
* included in all copies or substantial portions of the Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
*/ |
||||
package edu.mit.csail.wami.audio |
||||
{ |
||||
import flash.utils.Endian; |
||||
|
||||
/** |
||||
* This class keeps track of all the information that defines the |
||||
* audio format independent of the actual audio container |
||||
* (e.g. .wav or .au) |
||||
*/ |
||||
public class AudioFormat |
||||
{ |
||||
// Allow 8 and 16 kHz as well. |
||||
public static var allrates:Boolean = false; |
||||
|
||||
public var channels:uint; |
||||
public var rate:uint; |
||||
public var bits:uint; |
||||
public var endian:String; |
||||
|
||||
public function AudioFormat(rate:uint, channels:uint, bits:uint, endian:String) |
||||
{ |
||||
this.rate = rate; |
||||
this.channels = channels; |
||||
this.endian = endian; |
||||
this.bits = bits; |
||||
|
||||
validate(); |
||||
} |
||||
|
||||
// flash.media.Microphone quasi-rounds sample rates in kHz |
||||
public static function toRoundedRate(rate:uint):uint |
||||
{ |
||||
if (rate == 5512) |
||||
{ |
||||
return 5; |
||||
} |
||||
else if (rate == 8000) |
||||
{ |
||||
return 8; |
||||
} |
||||
else if (rate == 11025) |
||||
{ |
||||
return 11; |
||||
} |
||||
else if (rate == 16000) |
||||
{ |
||||
return 16; |
||||
} |
||||
else if (rate == 22050) |
||||
{ |
||||
return 22; |
||||
} |
||||
else if (rate == 44100) |
||||
{ |
||||
return 44; |
||||
} |
||||
|
||||
throw new Error("Unsupported sample rate in Hz: " + rate); |
||||
} |
||||
|
||||
|
||||
public static function fromRoundedRate(rate:uint):uint |
||||
{ |
||||
if (rate == 5) |
||||
{ |
||||
return 5512; |
||||
} |
||||
else if (rate == 8) |
||||
{ |
||||
return 8000; |
||||
} |
||||
else if (rate == 11) |
||||
{ |
||||
return 11025; |
||||
} |
||||
else if (rate == 16) |
||||
{ |
||||
return 16000; |
||||
} |
||||
else if (rate == 22) |
||||
{ |
||||
return 22050; |
||||
} |
||||
else if (rate == 44) |
||||
{ |
||||
return 44100; |
||||
} |
||||
|
||||
throw new Error("Unsupported sample rate rounded in kHz: " + rate); |
||||
} |
||||
|
||||
public function validate():void |
||||
{ |
||||
if (bits != 8 && bits != 16 && bits != 32) |
||||
{ |
||||
throw new Error("Unsupported number of bits per sample: " + bits); |
||||
} |
||||
|
||||
if (channels != 1 && channels != 2) |
||||
{ |
||||
throw new Error("Unsupported number of channels: " + channels); |
||||
} |
||||
|
||||
if (endian != Endian.BIG_ENDIAN && endian != Endian.LITTLE_ENDIAN) |
||||
{ |
||||
throw new Error("Unsupported endian type: " + endian); |
||||
} |
||||
|
||||
var msg:String = ""; |
||||
if (rate < 100) |
||||
{ |
||||
throw new Error("Rate should be in Hz"); |
||||
} |
||||
else if (rate != 5512 && rate != 8000 && rate != 11025 && rate != 16000 && rate != 22050 && rate != 44100) |
||||
{ |
||||
msg = "Sample rate of " + rate + " is not supported."; |
||||
msg += " See flash.media.Microphone documentation." |
||||
throw new Error(msg); |
||||
} |
||||
else if (!allrates && (rate == 8000 || rate == 16000 || rate == 11025)) { |
||||
msg = "8kHz and 16kHz are supported for recording but not playback. 11kHz doesn't work in Ubuntu."; |
||||
msg += " Enable all rates via a parameter passed into the Flash." |
||||
throw new Error(msg); |
||||
} |
||||
} |
||||
|
||||
public function toString():String |
||||
{ |
||||
return "Rate: " + rate + " Channels " + channels + " Bits: " + bits + " Endian: " + endian; |
||||
} |
||||
} |
||||
} |
@ -1,125 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2011 |
||||
* Spoken Language Systems Group |
||||
* MIT Computer Science and Artificial Intelligence Laboratory |
||||
* Massachusetts Institute of Technology |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person |
||||
* obtaining a copy of this software and associated documentation |
||||
* files (the "Software"), to deal in the Software without |
||||
* restriction, including without limitation the rights to use, copy, |
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies |
||||
* of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice shall be |
||||
* included in all copies or substantial portions of the Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
*/ |
||||
package edu.mit.csail.wami.audio |
||||
{ |
||||
import edu.mit.csail.wami.audio.AudioFormat; |
||||
import edu.mit.csail.wami.audio.IAudioContainer; |
||||
import edu.mit.csail.wami.utils.External; |
||||
import edu.mit.csail.wami.utils.Pipe; |
||||
|
||||
import flash.utils.ByteArray; |
||||
import flash.utils.Endian; |
||||
|
||||
/** |
||||
* Convert WAVE data coming in to the float-based format flash uses. |
||||
*/ |
||||
public class DecodePipe extends Pipe |
||||
{ |
||||
private var format:AudioFormat; |
||||
private var header:ByteArray = new ByteArray(); |
||||
private var containers:Vector.<IAudioContainer>; |
||||
|
||||
public function DecodePipe(containers:Vector.<IAudioContainer>) { |
||||
if (containers.length == 0) { |
||||
throw new Error("Must have at least one container."); |
||||
} |
||||
this.containers = containers; |
||||
} |
||||
|
||||
override public function write(bytes:ByteArray):void |
||||
{ |
||||
if (format == null) |
||||
{ |
||||
// Try to get header by parsing from each container |
||||
bytes.readBytes(header, header.length, bytes.length); |
||||
for each (var container:IAudioContainer in containers) { |
||||
format = container.fromByteArray(header); |
||||
if (format != null) { |
||||
// Put the leftover bytes back |
||||
bytes = new ByteArray(); |
||||
header.readBytes(bytes); |
||||
External.debug("Format: " + format); |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
if (format != null && bytes.bytesAvailable) |
||||
{ |
||||
bytes.endian = format.endian; |
||||
super.write(decode(bytes)); |
||||
} |
||||
} |
||||
|
||||
private function decode(bytes:ByteArray):ByteArray |
||||
{ |
||||
var decoded:ByteArray = new ByteArray(); |
||||
while (bytes.bytesAvailable) |
||||
{ |
||||
var sample1:Number = getSample(bytes); |
||||
var sample2:Number = sample1; |
||||
if (format.channels == 2) |
||||
{ |
||||
sample2 = getSample(bytes); |
||||
} |
||||
|
||||
// cheap way to upsample |
||||
var repeat:uint = 44100 / format.rate; |
||||
while (repeat-- > 0) { |
||||
decoded.writeFloat(sample1); |
||||
decoded.writeFloat(sample2); |
||||
} |
||||
} |
||||
decoded.position = 0; |
||||
return decoded; |
||||
} |
||||
|
||||
private function getSample(bytes:ByteArray):Number |
||||
{ |
||||
var sample:Number; |
||||
|
||||
if (format.bits == 8) |
||||
{ |
||||
sample = bytes.readByte()/0x7f; |
||||
} |
||||
else if (format.bits == 16) |
||||
{ |
||||
sample = bytes.readShort()/0x7fff; |
||||
} |
||||
else if (format.bits == 32) |
||||
{ |
||||
sample = bytes.readInt()/0x7fffffff; |
||||
} |
||||
else |
||||
{ |
||||
throw new Error("Unsupported bits per sample: " + format.bits); |
||||
} |
||||
|
||||
return sample; |
||||
} |
||||
} |
||||
} |
@ -1,126 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2011 |
||||
* Spoken Language Systems Group |
||||
* MIT Computer Science and Artificial Intelligence Laboratory |
||||
* Massachusetts Institute of Technology |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person |
||||
* obtaining a copy of this software and associated documentation |
||||
* files (the "Software"), to deal in the Software without |
||||
* restriction, including without limitation the rights to use, copy, |
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies |
||||
* of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice shall be |
||||
* included in all copies or substantial portions of the Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
*/ |
||||
package edu.mit.csail.wami.audio |
||||
{ |
||||
import edu.mit.csail.wami.audio.AudioFormat; |
||||
import edu.mit.csail.wami.audio.IAudioContainer; |
||||
import edu.mit.csail.wami.utils.Pipe; |
||||
|
||||
import flash.utils.ByteArray; |
||||
|
||||
/** |
||||
* Convert float format to raw audio accoring to the audio format passed in. |
||||
*/ |
||||
public class EncodePipe extends Pipe |
||||
{ |
||||
private var format:AudioFormat; |
||||
private var container:IAudioContainer; |
||||
|
||||
// Buffer if container requires length. In this case, |
||||
// we cannot write the data to the sink until the very end. |
||||
private var buffer:ByteArray; |
||||
private var headerWritten:Boolean; |
||||
|
||||
function EncodePipe(format:AudioFormat, container:IAudioContainer) |
||||
{ |
||||
this.format = format; |
||||
this.container = container; |
||||
this.buffer = new ByteArray(); |
||||
headerWritten = false; |
||||
} |
||||
|
||||
override public function write(bytes:ByteArray):void |
||||
{ |
||||
var transcoded:ByteArray = new ByteArray(); |
||||
transcoded.endian = format.endian; |
||||
|
||||
while (bytes.bytesAvailable >= 4) |
||||
{ |
||||
var sample:int; |
||||
if (format.bits == 16) |
||||
{ |
||||
sample = bytes.readFloat()*0x7fff; |
||||
transcoded.writeShort(sample); |
||||
if (format.channels == 2) |
||||
{ |
||||
transcoded.writeShort(sample); |
||||
} |
||||
} |
||||
else if (format.bits == 32) |
||||
{ |
||||
sample = bytes.readFloat()*0x7fffffff; |
||||
transcoded.writeInt(sample); |
||||
if (format.channels == 2) |
||||
{ |
||||
transcoded.writeInt(sample); |
||||
} |
||||
} |
||||
else |
||||
{ |
||||
throw new Error("Unsupported bits per sample: " + format.bits); |
||||
} |
||||
} |
||||
transcoded.position = 0; |
||||
handleEncoded(transcoded); |
||||
} |
||||
|
||||
private function handleEncoded(bytes:ByteArray):void { |
||||
if (container == null) { |
||||
// No container, just stream it on |
||||
super.write(bytes); |
||||
return; |
||||
} |
||||
|
||||
if (container.isLengthRequired()) |
||||
{ |
||||
buffer.writeBytes(bytes, bytes.position, bytes.bytesAvailable); |
||||
return; |
||||
} |
||||
|
||||
if (!headerWritten) |
||||
{ |
||||
var header:ByteArray = container.toByteArray(format); |
||||
super.write(header); |
||||
headerWritten = true; |
||||
} |
||||
super.write(bytes); |
||||
} |
||||
|
||||
override public function close():void |
||||
{ |
||||
if (container != null && container.isLengthRequired()) |
||||
{ |
||||
// Write the audio (including the header). |
||||
buffer.position = 0; |
||||
super.write(container.toByteArray(format, buffer.length)); |
||||
super.write(buffer); |
||||
} |
||||
|
||||
super.close(); |
||||
} |
||||
} |
||||
} |
@ -1,54 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2011 |
||||
* Spoken Language Systems Group |
||||
* MIT Computer Science and Artificial Intelligence Laboratory |
||||
* Massachusetts Institute of Technology |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person |
||||
* obtaining a copy of this software and associated documentation |
||||
* files (the "Software"), to deal in the Software without |
||||
* restriction, including without limitation the rights to use, copy, |
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies |
||||
* of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice shall be |
||||
* included in all copies or substantial portions of the Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
*/ |
||||
package edu.mit.csail.wami.audio |
||||
{ |
||||
import flash.utils.ByteArray; |
||||
|
||||
/** |
||||
* There are a number of ways to store raw audio. WAV is a container from |
||||
* Microsoft. AU is a container from Sun Microsystems. This interface |
||||
* helps us separate the container format from the audio format itself. |
||||
*/ |
||||
public interface IAudioContainer |
||||
{ |
||||
function toByteArray(format:AudioFormat, length:int = -1):ByteArray; |
||||
|
||||
/** |
||||
* If successful, the position is left at the first byte after |
||||
* the header. If the bytes do not represent the expected container |
||||
* header null is returned and the position is returned to 0. |
||||
*/ |
||||
function fromByteArray(bytes:ByteArray):AudioFormat; |
||||
|
||||
/** |
||||
* Some containers (e.g. WAV) require the length of the data to be specified, |
||||
* and thus are not amenable to streaming. Others (e.g. AU) have well |
||||
* defined ways of dealing with data of unknown length. |
||||
*/ |
||||
function isLengthRequired():Boolean; |
||||
} |
||||
} |
@ -1,131 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2011 |
||||
* Spoken Language Systems Group |
||||
* MIT Computer Science and Artificial Intelligence Laboratory |
||||
* Massachusetts Institute of Technology |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person |
||||
* obtaining a copy of this software and associated documentation |
||||
* files (the "Software"), to deal in the Software without |
||||
* restriction, including without limitation the rights to use, copy, |
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies |
||||
* of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice shall be |
||||
* included in all copies or substantial portions of the Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
*/ |
||||
package edu.mit.csail.wami.audio |
||||
{ |
||||
import edu.mit.csail.wami.utils.External; |
||||
|
||||
import flash.utils.ByteArray; |
||||
import flash.utils.Endian; |
||||
|
||||
/** |
||||
* This class builds a WAVE header formt the audio format. |
||||
*/ |
||||
public class WaveContainer implements IAudioContainer |
||||
{ |
||||
public function isLengthRequired():Boolean { |
||||
return true; |
||||
} |
||||
|
||||
public function toByteArray(audioFormat:AudioFormat, length:int = -1):ByteArray |
||||
{ |
||||
// https://ccrma.stanford.edu/courses/422/projects/WaveFormat/ |
||||
var id:String = (audioFormat.endian == Endian.LITTLE_ENDIAN) ? "RIFF" : "RIFX"; |
||||
var bytesPerSample:uint = audioFormat.channels*audioFormat.bits/8; |
||||
|
||||
var header:ByteArray = new ByteArray(); |
||||
|
||||
// Little-endian is generally the way to go for WAVs |
||||
header.endian = Endian.LITTLE_ENDIAN; |
||||
header.writeUTFBytes(id); |
||||
header.writeInt(length > 0 ? 36 + length : 0); |
||||
header.writeUTFBytes("WAVE"); |
||||
header.writeUTFBytes("fmt "); |
||||
header.writeInt(16); |
||||
header.writeShort(1); |
||||
header.writeShort(audioFormat.channels); |
||||
header.writeInt(audioFormat.rate); |
||||
header.writeInt(audioFormat.rate*bytesPerSample); |
||||
header.writeShort(bytesPerSample); |
||||
header.writeShort(audioFormat.bits); |
||||
header.writeUTFBytes('data'); |
||||
header.writeInt(length); |
||||
header.position = 0; |
||||
return header; |
||||
} |
||||
|
||||
public function fromByteArray(header:ByteArray):AudioFormat |
||||
{ |
||||
if (header.bytesAvailable < 44) { |
||||
var msg:String = "This header is not yet long enough "; |
||||
msg += "(need 44 bytes only have " + header.bytesAvailable + ")." |
||||
return notWav(header, msg); |
||||
} |
||||
|
||||
var endian:String = Endian.LITTLE_ENDIAN; |
||||
var chunkID:String = header.readUTFBytes(4); |
||||
if (chunkID == "RIFX") |
||||
{ |
||||
endian = Endian.BIG_ENDIAN; |
||||
} |
||||
else if (chunkID != "RIFF") |
||||
{ |
||||
return notWav(header, "Does not look like a WAVE header: " + chunkID); |
||||
} |
||||
|
||||
header.endian = Endian.LITTLE_ENDIAN; // Header is little-endian |
||||
var totalLength:uint = header.readInt() + 8; |
||||
var waveFmtStr:String = header.readUTFBytes(8); // "WAVEfmt " |
||||
if (waveFmtStr != "WAVEfmt ") |
||||
{ |
||||
return notWav(header, "RIFF header, but not a WAV."); |
||||
} |
||||
var subchunkSize:uint = header.readUnsignedInt(); // 16 |
||||
var audioFormat:uint = header.readShort(); // 1 |
||||
if (audioFormat != 1) { |
||||
return notWav(header, "Currently we only support linear PCM"); |
||||
} |
||||
var channels:uint = header.readShort(); |
||||
var rate:uint = header.readInt(); |
||||
var bps:uint = header.readInt(); |
||||
var bytesPerSample:uint = header.readShort(); |
||||
var bits:uint = header.readShort(); |
||||
var dataStr:String = header.readUTFBytes(4); // "data" |
||||
var length:uint = header.readInt(); |
||||
|
||||
var format:AudioFormat; |
||||
try |
||||
{ |
||||
format = new AudioFormat(rate, channels, bits, endian); |
||||
} catch (e:Error) |
||||
{ |
||||
return notWav(header, e.message); |
||||
} |
||||
return format; |
||||
} |
||||
|
||||
/** |
||||
* Emit error message for debugging, reset the ByteArray and |
||||
* return null. |
||||
*/ |
||||
private function notWav(header:ByteArray, msg:String):AudioFormat |
||||
{ |
||||
External.debug("Not WAV: " + msg); |
||||
header.position = 0; |
||||
return null; |
||||
} |
||||
} |
||||
} |
@ -1,104 +0,0 @@ |
||||
<?xml version="1.0" encoding="utf-8"?> |
||||
<mx:Application xmlns:fx="http://ns.adobe.com/mxml/2009" |
||||
xmlns:mx="library://ns.adobe.com/flex/mx" |
||||
minWidth="1" minHeight="1" backgroundAlpha="0" |
||||
applicationComplete="applicationCompleteHandler(event)"> |
||||
<!--- |
||||
The WAMI recorder is not meant to be seen, except for when the security panel |
||||
is in view. There is, however, an interface for the times you want to run it |
||||
in a debugger. |
||||
--> |
||||
<fx:Script> |
||||
<![CDATA[ |
||||
import edu.mit.csail.wami.utils.External; |
||||
|
||||
import mx.controls.Button; |
||||
import mx.controls.Label; |
||||
import mx.core.UIComponent; |
||||
|
||||
private function applicationCompleteHandler(event:Event):void { |
||||
External.debug('WAMI Flash Version 1.0: ' + Security.sandboxType); |
||||
Security.allowInsecureDomain("*"); |
||||
var params:WamiParams = new WamiParams(LoaderInfo(this.root.loaderInfo).parameters); |
||||
|
||||
var audio:WamiAudio = new WamiAudio(params); |
||||
var settings:FlashSettings = new FlashSettings(stage); |
||||
|
||||
if (params.visible) |
||||
{ |
||||
showDebugInterface(params, audio); |
||||
} |
||||
|
||||
External.call(params.loadedCallback); |
||||
} |
||||
|
||||
private function showDebugInterface(params:WamiParams, audio:WamiAudio):void |
||||
{ |
||||
this.horizontalScrollPolicy = "off"; |
||||
this.verticalScrollPolicy = "off"; |
||||
|
||||
var label:Label = new Label(); |
||||
var interval:uint; |
||||
|
||||
var recordButton:Button = new Button(); |
||||
recordButton.label = "Record"; |
||||
addElement(recordButton); |
||||
recordButton.addEventListener(MouseEvent.CLICK, function(evt:MouseEvent):void |
||||
{ |
||||
audio.startRecording(params.testRecordUrl); |
||||
label.setStyle("color", "#500000"); |
||||
interval = setInterval(function():void |
||||
{ |
||||
label.text = "Activity: " + audio.getRecordingLevel(); |
||||
}, 250); |
||||
}); |
||||
|
||||
var stopButton:Button = new Button(); |
||||
stopButton.label = "Stop"; |
||||
addElement(stopButton); |
||||
stopButton.addEventListener(MouseEvent.CLICK, function(evt:MouseEvent):void |
||||
{ |
||||
if (interval) clearInterval(interval); |
||||
interval = 0; |
||||
label.text = ""; |
||||
audio.stopRecording(); |
||||
audio.stopPlaying(); |
||||
}); |
||||
|
||||
var playButton:Button = new Button(); |
||||
playButton.label = "Play"; |
||||
addElement(playButton); |
||||
var uic:UIComponent = new UIComponent(); |
||||
var video:Video = new Video(); |
||||
uic.addChild(video); |
||||
addChild(uic); |
||||
playButton.addEventListener(MouseEvent.CLICK, function(evt:MouseEvent):void |
||||
{ |
||||
function ns_onMetaData(item:Object):void { |
||||
trace("metaData"); |
||||
// Resize video instance. |
||||
video.width = item.width; |
||||
video.height = item.height; |
||||
// Center video instance on Stage. |
||||
video.x = (stage.stageWidth - video.width) / 2; |
||||
video.y = (stage.stageHeight - video.height) / 2; |
||||
} |
||||
|
||||
function ns_onCuePoint(item:Object):void { |
||||
trace("cuePoint"); |
||||
trace(item.name + "\t" + item.time); |
||||
} |
||||
|
||||
audio.startPlaying(params.testPlayUrl); |
||||
label.setStyle("color", "#000500"); |
||||
interval = setInterval(function():void |
||||
{ |
||||
label.text = "Activity: " + audio.getPlayingLevel(); |
||||
}, 250); |
||||
}); |
||||
|
||||
addChild(label); |
||||
} |
||||
]]> |
||||
</fx:Script> |
||||
</mx:Application> |
@ -1,109 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2011 |
||||
* Spoken Language Systems Group |
||||
* MIT Computer Science and Artificial Intelligence Laboratory |
||||
* Massachusetts Institute of Technology |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person |
||||
* obtaining a copy of this software and associated documentation |
||||
* files (the "Software"), to deal in the Software without |
||||
* restriction, including without limitation the rights to use, copy, |
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies |
||||
* of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice shall be |
||||
* included in all copies or substantial portions of the Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
*/ |
||||
package edu.mit.csail.wami.client |
||||
{ |
||||
import edu.mit.csail.wami.play.IPlayer; |
||||
import edu.mit.csail.wami.play.WamiPlayer; |
||||
import edu.mit.csail.wami.record.IRecorder; |
||||
import edu.mit.csail.wami.record.WamiRecorder; |
||||
import edu.mit.csail.wami.utils.External; |
||||
|
||||
import flash.display.MovieClip; |
||||
|
||||
public class WamiAudio extends MovieClip |
||||
{ |
||||
private var recorder:IRecorder; |
||||
private var player:IPlayer; |
||||
|
||||
private var checkSettingsIntervalID:int = 0; |
||||
private var checkSettingsInterval:int = 1000; |
||||
|
||||
function WamiAudio(params:WamiParams) |
||||
{ |
||||
recorder = new WamiRecorder(params.getMicrophone(), params); |
||||
player = new WamiPlayer(); |
||||
|
||||
External.addCallback("startListening", startListening); |
||||
External.addCallback("stopListening", stopListening); |
||||
External.addCallback("startRecording", startRecording); |
||||
External.addCallback("stopRecording",stopRecording); |
||||
External.addCallback("getRecordingLevel", getRecordingLevel); |
||||
|
||||
External.addCallback("startPlaying",startPlaying); |
||||
External.addCallback("stopPlaying",stopPlaying); |
||||
External.addCallback("getPlayingLevel", getPlayingLevel); |
||||
} |
||||
|
||||
internal function startPlaying(url:String, |
||||
startedCallback:String = null, |
||||
finishedCallback:String = null, |
||||
failedCallback:String = null):void |
||||
{ |
||||
recorder.stop(true); |
||||
player.start(url, new WamiListener(startedCallback, finishedCallback, failedCallback)); |
||||
} |
||||
|
||||
internal function stopPlaying():void |
||||
{ |
||||
player.stop(); |
||||
} |
||||
|
||||
internal function getPlayingLevel():int |
||||
{ |
||||
return player.level(); |
||||
} |
||||
|
||||
private function startListening(paddingMillis:uint = 200):void |
||||
{ |
||||
recorder.listen(paddingMillis); |
||||
} |
||||
|
||||
private function stopListening():void |
||||
{ |
||||
recorder.unlisten(); |
||||
} |
||||
|
||||
internal function startRecording(url:String, |
||||
startedCallback:String = null, |
||||
finishedCallback:String = null, |
||||
failedCallback:String = null):void |
||||
{ |
||||
recorder.start(url, new WamiListener(startedCallback, finishedCallback, failedCallback)); |
||||
} |
||||
|
||||
internal function stopRecording():void |
||||
{ |
||||
recorder.stop(); |
||||
} |
||||
|
||||
internal function getRecordingLevel():int |
||||
{ |
||||
return recorder.level(); |
||||
} |
||||
} |
||||
} |
||||
|
@ -1,61 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2011 |
||||
* Spoken Language Systems Group |
||||
* MIT Computer Science and Artificial Intelligence Laboratory |
||||
* Massachusetts Institute of Technology |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person |
||||
* obtaining a copy of this software and associated documentation |
||||
* files (the "Software"), to deal in the Software without |
||||
* restriction, including without limitation the rights to use, copy, |
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies |
||||
* of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice shall be |
||||
* included in all copies or substantial portions of the Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
*/ |
||||
package edu.mit.csail.wami.client |
||||
{ |
||||
import edu.mit.csail.wami.utils.External; |
||||
import edu.mit.csail.wami.utils.StateListener; |
||||
|
||||
/** |
||||
* Translates audio events into Javascript callbacks. |
||||
*/ |
||||
public class WamiListener implements StateListener |
||||
{ |
||||
private var startCallback:String, finishedCallback:String, failedCallback:String; |
||||
|
||||
function WamiListener(startCallback:String, finishedCallback:String, failedCallback:String) |
||||
{ |
||||
this.startCallback = startCallback; |
||||
this.finishedCallback = finishedCallback; |
||||
this.failedCallback = failedCallback; |
||||
} |
||||
|
||||
public function started():void |
||||
{ |
||||
External.call(startCallback); |
||||
} |
||||
|
||||
public function finished():void |
||||
{ |
||||
External.call(finishedCallback); |
||||
} |
||||
|
||||
public function failed(error:Error):void |
||||
{ |
||||
External.call(failedCallback, error.message); |
||||
} |
||||
} |
||||
} |
@ -1,180 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2011 |
||||
* Spoken Language Systems Group |
||||
* MIT Computer Science and Artificial Intelligence Laboratory |
||||
* Massachusetts Institute of Technology |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person |
||||
* obtaining a copy of this software and associated documentation |
||||
* files (the "Software"), to deal in the Software without |
||||
* restriction, including without limitation the rights to use, copy, |
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies |
||||
* of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice shall be |
||||
* included in all copies or substantial portions of the Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
*/ |
||||
package edu.mit.csail.wami.client |
||||
{ |
||||
import edu.mit.csail.wami.audio.AudioFormat; |
||||
import edu.mit.csail.wami.utils.External; |
||||
|
||||
import flash.media.Microphone; |
||||
import flash.utils.ByteArray; |
||||
import flash.utils.Endian; |
||||
|
||||
/** |
||||
* A class documents the possible parameters and sets a few defaults. |
||||
* The defaults are set up to stream to localhost. |
||||
*/ |
||||
public class WamiParams |
||||
{ |
||||
private var mic:Microphone; |
||||
|
||||
// Show the debug interface. |
||||
public var visible:Boolean = true; |
||||
|
||||
// Append this many milliseconds of audio before |
||||
// and after calls to startRecording/stopRecording. |
||||
public var paddingMillis:uint = 250; |
||||
|
||||
// Send the audio using multiple HTTP Posts. |
||||
public var stream:Boolean = false; |
||||
|
||||
// The URLs used in the debugging interface. |
||||
public var testRecordUrl:String = "https://wami-recorder.appspot.com/audio"; |
||||
public var testPlayUrl:String = "https://wami-recorder.appspot.com/audio"; |
||||
|
||||
// Callbacks for loading the client. |
||||
public var loadedCallback:String; |
||||
public var format:AudioFormat; |
||||
|
||||
public function WamiParams(params:Object):void |
||||
{ |
||||
mic = Microphone.getMicrophone(); |
||||
|
||||
External.addCallback("setSettings", setSettings); |
||||
External.addCallback("getSettings", getSettings); |
||||
|
||||
if (params.stream != undefined) |
||||
{ |
||||
stream = params.stream == "true"; |
||||
} |
||||
|
||||
if (params.visible != undefined) |
||||
{ |
||||
visible = params.visible == "true"; |
||||
} |
||||
|
||||
if (params.console != undefined) { |
||||
External.debugToConsole = params.console == "true"; |
||||
} |
||||
|
||||
// Override to allow recording at 8000 and 16000 as well. |
||||
// Note that playback at these sample-rates will be sped up. |
||||
if (params.allrates != undefined) { |
||||
AudioFormat.allrates = params.allrates == "true"; |
||||
} |
||||
|
||||
loadedCallback = params.loadedCallback; |
||||
|
||||
var rate:uint = 22050; |
||||
if (params.rate != undefined) |
||||
{ |
||||
rate = uint(params.rate); |
||||
} |
||||
format = new AudioFormat(rate, 1, 16, Endian.LITTLE_ENDIAN); |
||||
} |
||||
|
||||
public function getMicrophone():Microphone { |
||||
return mic; |
||||
} |
||||
|
||||
// Settings (including microphone security) are passed back here. |
||||
internal function getSettings():Object |
||||
{ |
||||
var json:Object = { |
||||
"container" : (stream) ? "au" : "wav", |
||||
"encoding" : "pcm", |
||||
"signed" : true, |
||||
"sampleSize" : format.bits, |
||||
"bigEndian" : format.endian == Endian.BIG_ENDIAN, |
||||
"sampleRate" : format.rate, |
||||
"numChannels" : format.channels, |
||||
"interleaved" : true, |
||||
"microphone" : { |
||||
"granted" : (mic != null && !mic.muted) |
||||
} |
||||
}; |
||||
|
||||
return json; |
||||
} |
||||
|
||||
internal function setSettings(json:Object):void |
||||
{ |
||||
if (json) |
||||
{ |
||||
// For now the type also specifies streaming or not. |
||||
if (json.container == "au") |
||||
{ |
||||
stream = true; |
||||
} |
||||
else if (json.container == "wav") |
||||
{ |
||||
stream = false; |
||||
} |
||||
|
||||
if (json.encoding) |
||||
{ |
||||
throw new Error("Encodings such as mu-law could be implemented."); |
||||
} |
||||
|
||||
if (json.signed) |
||||
{ |
||||
throw new Error("Not implemented yet."); |
||||
} |
||||
|
||||
if (json.bigEndian) |
||||
{ |
||||
throw new Error("Automatically determined."); |
||||
} |
||||
|
||||
if (json.numChannels) |
||||
{ |
||||
format.channels = json.numChannels; |
||||
} |
||||
|
||||
if (json.sampleSize) |
||||
{ |
||||
format.bits = json.sampleSize; |
||||
} |
||||
|
||||
if (json.interleaved) |
||||
{ |
||||
throw new Error("Always true."); |
||||
} |
||||
|
||||
if (json.microphone) |
||||
{ |
||||
throw new Error("Only the user can change the microphone security settings."); |
||||
} |
||||
|
||||
if (json.sampleRate) |
||||
{ |
||||
format.rate = json.sampleRate; |
||||
} |
||||
|
||||
} |
||||
} |
||||
} |
||||
} |
@ -1,39 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2011 |
||||
* Spoken Language Systems Group |
||||
* MIT Computer Science and Artificial Intelligence Laboratory |
||||
* Massachusetts Institute of Technology |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person |
||||
* obtaining a copy of this software and associated documentation |
||||
* files (the "Software"), to deal in the Software without |
||||
* restriction, including without limitation the rights to use, copy, |
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies |
||||
* of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice shall be |
||||
* included in all copies or substantial portions of the Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
*/ |
||||
package edu.mit.csail.wami.play |
||||
{ |
||||
import edu.mit.csail.wami.utils.StateListener; |
||||
|
||||
public interface IPlayer |
||||
{ |
||||
function start(url:String, listener:StateListener):void; |
||||
function stop():void; |
||||
|
||||
// Audio level (between 0 and 100) |
||||
function level():int; |
||||
} |
||||
} |
@ -1,179 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2011 |
||||
* Spoken Language Systems Group |
||||
* MIT Computer Science and Artificial Intelligence Laboratory |
||||
* Massachusetts Institute of Technology |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person |
||||
* obtaining a copy of this software and associated documentation |
||||
* files (the "Software"), to deal in the Software without |
||||
* restriction, including without limitation the rights to use, copy, |
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies |
||||
* of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice shall be |
||||
* included in all copies or substantial portions of the Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
*/ |
||||
package edu.mit.csail.wami.play |
||||
{ |
||||
import edu.mit.csail.wami.audio.AuContainer; |
||||
import edu.mit.csail.wami.audio.DecodePipe; |
||||
import edu.mit.csail.wami.audio.IAudioContainer; |
||||
import edu.mit.csail.wami.audio.WaveContainer; |
||||
import edu.mit.csail.wami.utils.BytePipe; |
||||
import edu.mit.csail.wami.utils.External; |
||||
import edu.mit.csail.wami.utils.Pipe; |
||||
import edu.mit.csail.wami.utils.StateListener; |
||||
|
||||
import flash.events.Event; |
||||
import flash.events.HTTPStatusEvent; |
||||
import flash.events.IOErrorEvent; |
||||
import flash.events.ProgressEvent; |
||||
import flash.events.SampleDataEvent; |
||||
import flash.events.SecurityErrorEvent; |
||||
import flash.media.Sound; |
||||
import flash.media.SoundChannel; |
||||
import flash.net.URLLoader; |
||||
import flash.net.URLLoaderDataFormat; |
||||
import flash.net.URLRequest; |
||||
import flash.net.URLRequestMethod; |
||||
import flash.utils.ByteArray; |
||||
|
||||
public class WamiPlayer implements IPlayer |
||||
{ |
||||
private var currentChannel:SoundChannel = null; |
||||
private var currentAudio:ByteArray; |
||||
private var listener:StateListener; |
||||
|
||||
public function start(url:String, listener:StateListener):void |
||||
{ |
||||
this.listener = listener; |
||||
var loader:URLLoader = new URLLoader(); |
||||
loader.dataFormat = URLLoaderDataFormat.BINARY; |
||||
|
||||
loader.addEventListener(Event.COMPLETE, completeHandler); |
||||
loader.addEventListener(Event.OPEN, openHandler); |
||||
loader.addEventListener(ProgressEvent.PROGRESS, progressHandler); |
||||
loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler); |
||||
loader.addEventListener(HTTPStatusEvent.HTTP_STATUS, httpStatusHandler); |
||||
loader.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler); |
||||
|
||||
var request:URLRequest = new URLRequest(url); |
||||
request.method = URLRequestMethod.GET; |
||||
|
||||
try { |
||||
loader.load(request); |
||||
} catch (error:Error) { |
||||
listener.failed(error); |
||||
} |
||||
|
||||
function completeHandler(event:Event):void { |
||||
listener.started(); |
||||
|
||||
loader.removeEventListener(Event.COMPLETE, completeHandler); |
||||
loader.removeEventListener(Event.OPEN, openHandler); |
||||
loader.removeEventListener(ProgressEvent.PROGRESS, progressHandler); |
||||
loader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler); |
||||
loader.removeEventListener(HTTPStatusEvent.HTTP_STATUS, httpStatusHandler); |
||||
loader.removeEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler) |
||||
|
||||
play(loader.data); |
||||
} |
||||
|
||||
function openHandler(event:Event):void { |
||||
External.debug("openHandler: " + event); |
||||
} |
||||
|
||||
function progressHandler(event:ProgressEvent):void { |
||||
//External.debug("progressHandler loaded:" + event.bytesLoaded + " total: " + event.bytesTotal); |
||||
} |
||||
|
||||
function securityErrorHandler(event:SecurityErrorEvent):void { |
||||
listener.failed(new Error("Security error while playing: " + event.errorID)); |
||||
} |
||||
|
||||
function httpStatusHandler(event:HTTPStatusEvent):void { |
||||
External.debug("httpStatusHandler: " + event); |
||||
} |
||||
|
||||
function ioErrorHandler(event:IOErrorEvent):void { |
||||
listener.failed(new Error("IO error while playing: " + event.errorID)); |
||||
} |
||||
} |
||||
|
||||
public function stop():void |
||||
{ |
||||
if (currentChannel != null) |
||||
{ |
||||
External.debug("Stop playing."); |
||||
currentChannel.removeEventListener(Event.SOUND_COMPLETE, stop); |
||||
currentChannel.stop(); |
||||
External.debug("Listener finished."); |
||||
listener.finished(); |
||||
currentChannel = null; |
||||
} |
||||
} |
||||
|
||||
public function level():int |
||||
{ |
||||
if (currentChannel != null) { |
||||
return 100 * ((currentChannel.leftPeak + currentChannel.rightPeak) / 2.0); |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
protected function play(audio:ByteArray):void |
||||
{ |
||||
stop(); // Make sure we're stopped |
||||
|
||||
var containers:Vector.<IAudioContainer> = new Vector.<IAudioContainer>(); |
||||
containers.push(new WaveContainer()); |
||||
containers.push(new AuContainer()); |
||||
|
||||
External.debug("Playing audio of " + audio.length + " bytes."); |
||||
var decoder:Pipe = new DecodePipe(containers); |
||||
var pipe:BytePipe = new BytePipe(); |
||||
decoder.setSink(pipe); |
||||
decoder.write(audio); |
||||
decoder.close(); |
||||
|
||||
currentAudio = pipe.getByteArray(); |
||||
External.debug("Playing audio with " + currentAudio.length/4 + " samples."); |
||||
|
||||
var sound:Sound = new Sound(); |
||||
sound.addEventListener(SampleDataEvent.SAMPLE_DATA, handleSampleEvent); |
||||
|
||||
currentChannel = sound.play(); |
||||
currentChannel.addEventListener(Event.SOUND_COMPLETE, function(event:Event):void { |
||||
sound.removeEventListener(SampleDataEvent.SAMPLE_DATA, handleSampleEvent); |
||||
stop(); |
||||
}); |
||||
} |
||||
|
||||
private function handleSampleEvent(event:SampleDataEvent):void |
||||
{ |
||||
if (currentAudio == null) return; |
||||
|
||||
var MAX_SAMPLES_PER_EVENT:uint = 4000; |
||||
var count:uint = 0; |
||||
// External.debug("Audio " + currentAudio.bytesAvailable + " " + event.data.endian); |
||||
while (currentAudio.bytesAvailable && count < MAX_SAMPLES_PER_EVENT) |
||||
{ |
||||
event.data.writeFloat(currentAudio.readFloat()); |
||||
event.data.writeFloat(currentAudio.readFloat()); |
||||
count += 1; |
||||
} |
||||
} |
||||
} |
||||
} |
@ -1,49 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2011 |
||||
* Spoken Language Systems Group |
||||
* MIT Computer Science and Artificial Intelligence Laboratory |
||||
* Massachusetts Institute of Technology |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person |
||||
* obtaining a copy of this software and associated documentation |
||||
* files (the "Software"), to deal in the Software without |
||||
* restriction, including without limitation the rights to use, copy, |
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies |
||||
* of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice shall be |
||||
* included in all copies or substantial portions of the Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
*/ |
||||
package edu.mit.csail.wami.record |
||||
{ |
||||
import edu.mit.csail.wami.utils.StateListener; |
||||
|
||||
public interface IRecorder |
||||
{ |
||||
// Start and stop recording. Calling start while recording |
||||
// or calling stop when not recording should have no effect. |
||||
function start(url:String, listener:StateListener):void; |
||||
function stop(force:Boolean = false):void; |
||||
|
||||
// It can be helpful to buffer a certain amount of audio to |
||||
// prepend (and append) to the audio collected between start |
||||
// and stop. This means, Flash needs to constantly listen. |
||||
// There are other times when it's obvious no recording will |
||||
// be done, and so listening is unnecesary. |
||||
function listen(paddingMillis:uint):void; |
||||
function unlisten():void; |
||||
|
||||
// Audio level (between 0 and 100) |
||||
function level():int; |
||||
} |
||||
} |
@ -1,111 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2011 |
||||
* Spoken Language Systems Group |
||||
* MIT Computer Science and Artificial Intelligence Laboratory |
||||
* Massachusetts Institute of Technology |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person |
||||
* obtaining a copy of this software and associated documentation |
||||
* files (the "Software"), to deal in the Software without |
||||
* restriction, including without limitation the rights to use, copy, |
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies |
||||
* of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice shall be |
||||
* included in all copies or substantial portions of the Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
*/ |
||||
package edu.mit.csail.wami.record |
||||
{ |
||||
import edu.mit.csail.wami.utils.External; |
||||
import edu.mit.csail.wami.utils.Pipe; |
||||
import edu.mit.csail.wami.utils.StateListener; |
||||
|
||||
import flash.utils.ByteArray; |
||||
|
||||
public class MultiPost extends Pipe implements StateListener |
||||
{ |
||||
private var url:String; |
||||
private var contentType:String = null; |
||||
private var partIndex:int = 0; |
||||
private var timeoutMillis:int; |
||||
private var totalBytes:int = 0; |
||||
private var totalPostsMade:int = 0; |
||||
private var totalPostsDone:int = 0; |
||||
private var error:Error = null; |
||||
private var listener:StateListener; |
||||
|
||||
/** |
||||
* Does of POST of the data passed in to every call to "write" |
||||
*/ |
||||
public function MultiPost(url:String, type:String, timeoutMillis:int, listener:StateListener) |
||||
{ |
||||
this.url = url; |
||||
this.contentType = type; |
||||
this.timeoutMillis = timeoutMillis; |
||||
this.listener = listener; |
||||
} |
||||
|
||||
override public function write(bytes:ByteArray):void |
||||
{ |
||||
if (getError() != null) { |
||||
throw getError(); |
||||
} |
||||
|
||||
var type:String = contentType.replace("%s", partIndex++); |
||||
var post:Pipe = new SinglePost(url, type, timeoutMillis, this); |
||||
post.write(bytes); |
||||
post.close(); |
||||
totalBytes += bytes.length; |
||||
totalPostsMade++; |
||||
} |
||||
|
||||
// A final POST containing a -1 signifies the end of the MultiPost stream. |
||||
override public function close():void |
||||
{ |
||||
External.debug("Total multi-posted bytes: " + totalBytes); |
||||
var arr:ByteArray = new ByteArray(); |
||||
arr.writeInt(-1); |
||||
arr.position = 0; |
||||
write(arr); |
||||
super.close(); |
||||
} |
||||
|
||||
public function started():void |
||||
{ |
||||
// nothing to do |
||||
} |
||||
|
||||
public function finished():void |
||||
{ |
||||
totalPostsDone++; |
||||
checkFinished(); |
||||
} |
||||
|
||||
public function failed(error:Error):void |
||||
{ |
||||
this.error = error; |
||||
} |
||||
|
||||
public function getError():Error |
||||
{ |
||||
return error; |
||||
} |
||||
|
||||
private function checkFinished():void |
||||
{ |
||||
if (totalPostsDone == totalPostsMade && super.isClosed()) { |
||||
listener.finished(); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -1,159 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2011 |
||||
* Spoken Language Systems Group |
||||
* MIT Computer Science and Artificial Intelligence Laboratory |
||||
* Massachusetts Institute of Technology |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person |
||||
* obtaining a copy of this software and associated documentation |
||||
* files (the "Software"), to deal in the Software without |
||||
* restriction, including without limitation the rights to use, copy, |
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies |
||||
* of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice shall be |
||||
* included in all copies or substantial portions of the Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
*/ |
||||
package edu.mit.csail.wami.record |
||||
{ |
||||
import edu.mit.csail.wami.utils.External; |
||||
import edu.mit.csail.wami.utils.Pipe; |
||||
import edu.mit.csail.wami.utils.StateListener; |
||||
|
||||
import flash.events.Event; |
||||
import flash.events.HTTPStatusEvent; |
||||
import flash.events.IOErrorEvent; |
||||
import flash.events.ProgressEvent; |
||||
import flash.events.SecurityErrorEvent; |
||||
import flash.net.URLLoader; |
||||
import flash.net.URLRequest; |
||||
import flash.net.URLRequestMethod; |
||||
import flash.utils.ByteArray; |
||||
import flash.utils.setInterval; |
||||
|
||||
/** |
||||
* Write data and POST on close. |
||||
*/ |
||||
public class SinglePost extends Pipe |
||||
{ |
||||
private var url:String; |
||||
private var contentType:String = null; |
||||
private var listener:StateListener; |
||||
|
||||
private var finished:Boolean = false; |
||||
private var buffer:ByteArray = new ByteArray(); |
||||
private var timeoutMillis:int; |
||||
|
||||
public function SinglePost(url:String, type:String, timeoutMillis:int, listener:StateListener) |
||||
{ |
||||
this.url = url; |
||||
this.contentType = type; |
||||
this.listener = listener; |
||||
this.timeoutMillis = timeoutMillis; |
||||
} |
||||
|
||||
override public function write(bytes:ByteArray):void |
||||
{ |
||||
bytes.readBytes(buffer, buffer.length, bytes.bytesAvailable); |
||||
} |
||||
|
||||
override public function close():void |
||||
{ |
||||
buffer.position = 0; |
||||
External.debug("POST " + buffer.length + " bytes of type " + contentType); |
||||
buffer.position = 0; |
||||
var loader:URLLoader = new URLLoader(); |
||||
|
||||
loader.addEventListener(Event.COMPLETE, completeHandler); |
||||
loader.addEventListener(Event.OPEN, openHandler); |
||||
loader.addEventListener(ProgressEvent.PROGRESS, progressHandler); |
||||
loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler); |
||||
loader.addEventListener(HTTPStatusEvent.HTTP_STATUS, httpStatusHandler); |
||||
loader.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler); |
||||
|
||||
var request:URLRequest = new URLRequest(url); |
||||
request.method = URLRequestMethod.POST; |
||||
request.contentType = contentType; |
||||
request.data = buffer; |
||||
if (buffer.bytesAvailable == 0) { |
||||
External.debug("Note that flash does a GET request if bytes.length == 0"); |
||||
} |
||||
|
||||
try { |
||||
loader.load(request); |
||||
} catch (error:Error) { |
||||
if (listener) |
||||
{ |
||||
listener.failed(error); |
||||
} |
||||
} |
||||
|
||||
super.close(); |
||||
} |
||||
|
||||
private function completeHandler(event:Event):void { |
||||
External.debug("POST: completeHandler"); |
||||
var loader:URLLoader = URLLoader(event.target); |
||||
loader.removeEventListener(Event.COMPLETE, completeHandler); |
||||
loader.removeEventListener(Event.OPEN, openHandler); |
||||
loader.removeEventListener(ProgressEvent.PROGRESS, progressHandler); |
||||
loader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler); |
||||
loader.removeEventListener(HTTPStatusEvent.HTTP_STATUS, httpStatusHandler); |
||||
loader.removeEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler); |
||||
listener.finished(); |
||||
finished = true; |
||||
} |
||||
|
||||
private function openHandler(event:Event):void { |
||||
External.debug("POST openHandler: " + event); |
||||
setInterval(checkFinished, timeoutMillis); |
||||
} |
||||
|
||||
private function checkFinished():void { |
||||
if (!finished && listener) { |
||||
listener.failed(new Error("POST is taking too long.")); |
||||
} |
||||
finished = true; |
||||
} |
||||
|
||||
private function progressHandler(event:ProgressEvent):void { |
||||
External.debug("POST progressHandler loaded:" + event.bytesLoaded + " total: " + event.bytesTotal); |
||||
} |
||||
|
||||
private function securityErrorHandler(event:SecurityErrorEvent):void { |
||||
if (!finished && listener) |
||||
{ |
||||
listener.failed(new Error("Record security error: " + event.errorID)); |
||||
} |
||||
finished = true; |
||||
} |
||||
|
||||
private function httpStatusHandler(event:HTTPStatusEvent):void { |
||||
// Apparently the event.status can be zero in some environments where nothing is wrong: |
||||
// http://johncblandii.com/2008/04/flex-3-firefox-beta-3-returns-0-for-http-status-codes.html |
||||
if (!finished && listener && event.status != 200 && event.status != 0) |
||||
{ |
||||
listener.failed(new Error("HTTP status error: " + event.status)); |
||||
} |
||||
finished = true; |
||||
} |
||||
|
||||
private function ioErrorHandler(event:IOErrorEvent):void { |
||||
if (!finished && listener) |
||||
{ |
||||
listener.failed(new Error("Record IO error: " + event.errorID)); |
||||
} |
||||
finished = true; |
||||
} |
||||
} |
||||
} |
@ -1,269 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2011 |
||||
* Spoken Language Systems Group |
||||
* MIT Computer Science and Artificial Intelligence Laboratory |
||||
* Massachusetts Institute of Technology |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person |
||||
* obtaining a copy of this software and associated documentation |
||||
* files (the "Software"), to deal in the Software without |
||||
* restriction, including without limitation the rights to use, copy, |
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies |
||||
* of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice shall be |
||||
* included in all copies or substantial portions of the Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
*/ |
||||
package edu.mit.csail.wami.record |
||||
{ |
||||
import edu.mit.csail.wami.audio.AuContainer; |
||||
import edu.mit.csail.wami.audio.AudioFormat; |
||||
import edu.mit.csail.wami.audio.EncodePipe; |
||||
import edu.mit.csail.wami.audio.IAudioContainer; |
||||
import edu.mit.csail.wami.audio.WaveContainer; |
||||
import edu.mit.csail.wami.client.WamiParams; |
||||
import edu.mit.csail.wami.utils.BytePipe; |
||||
import edu.mit.csail.wami.utils.External; |
||||
import edu.mit.csail.wami.utils.Pipe; |
||||
import edu.mit.csail.wami.utils.StateListener; |
||||
|
||||
import flash.events.SampleDataEvent; |
||||
import flash.events.StatusEvent; |
||||
import flash.media.Microphone; |
||||
import flash.media.SoundCodec; |
||||
import flash.utils.Endian; |
||||
import flash.utils.clearInterval; |
||||
import flash.utils.setInterval; |
||||
|
||||
public class WamiRecorder implements IRecorder |
||||
{ |
||||
private static var CHUNK_DURATION_MILLIS:Number = 200; |
||||
|
||||
private var mic:Microphone = null; |
||||
private var params:WamiParams; |
||||
private var audioPipe:Pipe; |
||||
|
||||
// For adding some audio padding to start and stop. |
||||
private var circularBuffer:BytePipe; |
||||
private var stopInterval:uint; |
||||
private var paddingMillis:uint = 0; // initially 0, but listen changes it. |
||||
private var listening:Boolean = false; |
||||
|
||||
// To determine if the amount of audio recorded matches up with |
||||
// the length of time we've recorded (i.e. not dropping any frames) |
||||
private var handled:uint; |
||||
private var startTime:Date; |
||||
private var stopTime:Date; |
||||
private var listener:StateListener; |
||||
|
||||
public function WamiRecorder(mic:Microphone, params:WamiParams) |
||||
{ |
||||
this.params = params; |
||||
this.circularBuffer = new BytePipe(getPaddingBufferSize()); |
||||
this.mic = mic; |
||||
mic.addEventListener(StatusEvent.STATUS, onMicStatus); |
||||
|
||||
if (getChunkSize() <= 0) |
||||
{ |
||||
throw new Error("Desired duration is too small, even for streaming chunks: " + getChunkSize()); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* The WAMI recorder can listen constantly, keeping a buffer of the last |
||||
* few milliseconds of audio. Often people start talking before they click the |
||||
* button, so we prepend paddingMillis milliseconds to the audio. |
||||
*/ |
||||
public function listen(paddingMillis:uint):void { |
||||
if (!listening) { |
||||
this.paddingMillis = paddingMillis; |
||||
mic.rate = AudioFormat.toRoundedRate(params.format.rate); |
||||
mic.codec = SoundCodec.NELLYMOSER; // Just to clarify 5, 8, 11, 16, 22 and 44 kHz |
||||
mic.setSilenceLevel(0, 10000); |
||||
mic.addEventListener(SampleDataEvent.SAMPLE_DATA, sampleHandler); |
||||
External.debug("Listening..."); |
||||
listening = true; |
||||
} |
||||
} |
||||
|
||||
public function unlisten():void { |
||||
if (listening) { |
||||
mic.removeEventListener(SampleDataEvent.SAMPLE_DATA, sampleHandler); |
||||
listening = false; |
||||
if (paddingMillis > 0) { |
||||
circularBuffer = new BytePipe(getPaddingBufferSize()); |
||||
} |
||||
External.debug("Unlistening."); |
||||
} |
||||
} |
||||
|
||||
protected function onMicStatus(event:StatusEvent):void |
||||
{ |
||||
External.debug("status: " + event.code); |
||||
if (event.code == "Microphone.Unmuted") |
||||
{ |
||||
listen(this.paddingMillis); |
||||
} else if (event.code == "Microphone.Muted") { |
||||
unlisten(); |
||||
} |
||||
} |
||||
|
||||
public function start(url:String, listener:StateListener):void |
||||
{ |
||||
// Forces security if mic is still muted in debugging mode. |
||||
listen(this.paddingMillis); |
||||
|
||||
// Flash might be able to decide on a different sample rate |
||||
// than the one you suggest depending on your audio card... |
||||
params.format.rate = AudioFormat.fromRoundedRate(mic.rate); |
||||
External.debug("Recording at rate: " + params.format.rate); |
||||
|
||||
stop(true); |
||||
audioPipe = createAudioPipe(url, listener); |
||||
|
||||
if (paddingMillis > 0) { |
||||
// Prepend a small amount of audio we've already recorded. |
||||
circularBuffer.close(); |
||||
audioPipe.write(circularBuffer.getByteArray()); |
||||
circularBuffer = new BytePipe(getPaddingBufferSize()); |
||||
} |
||||
|
||||
listener.started(); |
||||
|
||||
handled = 0; |
||||
startTime = new Date(); |
||||
} |
||||
|
||||
public function createAudioPipe(url:String, listener:StateListener):Pipe |
||||
{ |
||||
this.listener = listener; |
||||
|
||||
var post:Pipe; |
||||
var container:IAudioContainer; |
||||
if (params.stream) |
||||
{ |
||||
// The chunk parameter is something I made up. It would need |
||||
// to be handled on the server-side to piece all the chunks together. |
||||
post = new MultiPost(url, "audio/basic; chunk=%s", 3*1000, listener); |
||||
params.format.endian = Endian.BIG_ENDIAN; |
||||
container = new AuContainer(); |
||||
} |
||||
else |
||||
{ |
||||
post = new SinglePost(url, "audio/x-wav", 30*1000, listener); |
||||
container = new WaveContainer(); |
||||
} |
||||
|
||||
// Setup the audio pipes. A transcoding pipe converts floats |
||||
// to shorts and passes them on to a chunking pipe, which spits |
||||
// out chunks to a pipe that possibly adds a WAVE header |
||||
// before passing the chunks on to a pipe that does HTTP posts. |
||||
var pipe:Pipe = new EncodePipe(params.format, container); |
||||
pipe.setSink(new ChunkPipe(getChunkSize())) |
||||
.setSink(post); |
||||
|
||||
return pipe; |
||||
} |
||||
|
||||
internal function sampleHandler(evt:SampleDataEvent):void |
||||
{ |
||||
evt.data.position = 0; |
||||
try |
||||
{ |
||||
if (audioPipe) |
||||
{ |
||||
audioPipe.write(evt.data); |
||||
handled += evt.data.length / 4; |
||||
} |
||||
else if (paddingMillis > 0) |
||||
{ |
||||
circularBuffer.write(evt.data); |
||||
} |
||||
} |
||||
catch (error:Error) |
||||
{ |
||||
audioPipe = null; |
||||
stop(true); |
||||
listener.failed(error); |
||||
} |
||||
} |
||||
|
||||
public function stop(force:Boolean = false):void |
||||
{ |
||||
clearInterval(stopInterval); |
||||
|
||||
if (force) |
||||
{ |
||||
reallyStop(); |
||||
} |
||||
else |
||||
{ |
||||
stopInterval = setInterval(function():void { |
||||
clearInterval(stopInterval); |
||||
reallyStop(); |
||||
}, paddingMillis); |
||||
} |
||||
} |
||||
|
||||
public function level():int |
||||
{ |
||||
if (!audioPipe) return 0; |
||||
return mic.activityLevel; |
||||
} |
||||
|
||||
private function reallyStop():void |
||||
{ |
||||
if (!audioPipe) return; |
||||
|
||||
try { |
||||
audioPipe.close(); |
||||
} catch(error:Error) { |
||||
listener.failed(error); |
||||
} |
||||
|
||||
audioPipe = null; |
||||
validateAudioLength(); |
||||
|
||||
if (this.paddingMillis == 0) { |
||||
// No need if we're not padding the audio |
||||
unlisten(); |
||||
} |
||||
} |
||||
|
||||
private function validateAudioLength():void |
||||
{ |
||||
stopTime = new Date(); |
||||
var seconds:Number = ((stopTime.time - startTime.time + paddingMillis) / 1000.0); |
||||
var expectedSamples:uint = uint(seconds*params.format.rate); |
||||
External.debug("Expected Samples: " + expectedSamples + " Actual Samples: " + handled); |
||||
startTime = null; |
||||
stopTime = null; |
||||
} |
||||
|
||||
private function getBytesPerSecond():uint |
||||
{ |
||||
return params.format.channels * (params.format.bits/8) * params.format.rate; |
||||
} |
||||
|
||||
private function getChunkSize():uint |
||||
{ |
||||
return params.stream ? getBytesPerSecond() * CHUNK_DURATION_MILLIS / 1000.0 : int.MAX_VALUE; |
||||
} |
||||
|
||||
private function getPaddingBufferSize():uint |
||||
{ |
||||
return uint(getBytesPerSecond()*params.paddingMillis/1000.0); |
||||
} |
||||
|
||||
} |
||||
} |
@ -1,93 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2011 |
||||
* Spoken Language Systems Group |
||||
* MIT Computer Science and Artificial Intelligence Laboratory |
||||
* Massachusetts Institute of Technology |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person |
||||
* obtaining a copy of this software and associated documentation |
||||
* files (the "Software"), to deal in the Software without |
||||
* restriction, including without limitation the rights to use, copy, |
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies |
||||
* of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice shall be |
||||
* included in all copies or substantial portions of the Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
*/ |
||||
package edu.mit.csail.wami.utils |
||||
{ |
||||
import flash.utils.ByteArray; |
||||
|
||||
/** |
||||
* Accumulates the written bytes into an array. If a max number of bytes |
||||
* is specified this will act as a circular buffer. |
||||
*/ |
||||
public class BytePipe extends Pipe |
||||
{ |
||||
private var buffer:ByteArray = new ByteArray(); |
||||
private var done:Boolean = false; |
||||
private var maxBytes:uint; |
||||
private var start:uint = 0; |
||||
|
||||
function BytePipe(maxBytes:uint = uint.MAX_VALUE):void |
||||
{ |
||||
this.maxBytes = maxBytes; |
||||
} |
||||
|
||||
override public function write(bytes:ByteArray):void |
||||
{ |
||||
if (maxBytes <= 0) return; // no room! |
||||
|
||||
var available:uint = Math.min(maxBytes - buffer.length, bytes.bytesAvailable); |
||||
if (available > 0) |
||||
{ |
||||
bytes.readBytes(buffer, buffer.length, available); |
||||
} |
||||
|
||||
while (bytes.bytesAvailable) |
||||
{ |
||||
// Read bytes into the circular buffer. |
||||
available = Math.min(buffer.length - start, bytes.bytesAvailable); |
||||
bytes.readBytes(buffer, start, available); |
||||
start = (start + available) % maxBytes; |
||||
} |
||||
|
||||
buffer.position = 0; |
||||
} |
||||
|
||||
override public function close():void |
||||
{ |
||||
super.close(); |
||||
done = true; |
||||
} |
||||
|
||||
public function getByteArray():ByteArray |
||||
{ |
||||
if (!done) |
||||
{ |
||||
throw new Error("BytePipe should be done before accessing byte array."); |
||||
} |
||||
|
||||
var array:ByteArray = new ByteArray(); |
||||
buffer.position = start; |
||||
buffer.readBytes(array); |
||||
buffer.position = 0; |
||||
if (start > 0) |
||||
{ |
||||
buffer.readBytes(array, array.length, start); |
||||
} |
||||
array.position = 0; |
||||
return array; |
||||
} |
||||
} |
||||
} |
@ -1,144 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2011 |
||||
* Spoken Language Systems Group |
||||
* MIT Computer Science and Artificial Intelligence Laboratory |
||||
* Massachusetts Institute of Technology |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person |
||||
* obtaining a copy of this software and associated documentation |
||||
* files (the "Software"), to deal in the Software without |
||||
* restriction, including without limitation the rights to use, copy, |
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies |
||||
* of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice shall be |
||||
* included in all copies or substantial portions of the Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
*/ |
||||
package edu.mit.csail.wami.utils |
||||
{ |
||||
import flash.external.ExternalInterface; |
||||
import flash.utils.ByteArray; |
||||
|
||||
/** |
||||
* Make external calls only if available. |
||||
*/ |
||||
public class External |
||||
{ |
||||
public static var debugToConsole:Boolean = false; |
||||
|
||||
public static function call(functionName:String, ... arguments):void |
||||
{ |
||||
if (ExternalInterface.available && functionName) |
||||
{ |
||||
try |
||||
{ |
||||
trace("External.call: " + functionName + "(" + arguments + ")"); |
||||
ExternalInterface.call(functionName, arguments); |
||||
} |
||||
catch (e:Error) |
||||
{ |
||||
trace("Error calling external function: " + e.message); |
||||
} |
||||
} |
||||
else |
||||
{ |
||||
trace("No ExternalInterface - External.call: " + functionName + "(" + arguments + ")"); |
||||
} |
||||
} |
||||
|
||||
public static function addCallback(functionName:String, closure:Function):void |
||||
{ |
||||
if (ExternalInterface.available && functionName) |
||||
{ |
||||
try |
||||
{ |
||||
External.debug("External.addCallback: " + functionName); |
||||
ExternalInterface.addCallback(functionName, closure); |
||||
} |
||||
catch (e:Error) |
||||
{ |
||||
External.debug("Error calling external function: " + e.message); |
||||
} |
||||
} |
||||
else |
||||
{ |
||||
External.debug("No ExternalInterface - External.addCallback: " + functionName); |
||||
} |
||||
} |
||||
|
||||
public static function debug(msg:String):void |
||||
{ |
||||
if (debugToConsole) { |
||||
ExternalInterface.call("console.log", "FLASH: " + msg); |
||||
} |
||||
else |
||||
{ |
||||
trace(msg); |
||||
} |
||||
} |
||||
|
||||
public static function debugBytes(bytes:ByteArray):void |
||||
{ |
||||
debug(bytesToHex(bytes)); |
||||
} |
||||
|
||||
public static function bytesToHex(bytes:ByteArray):String |
||||
{ |
||||
var position:int = bytes.position; |
||||
var count:int = 0; |
||||
var str:String = "<"; |
||||
while (bytes.bytesAvailable) |
||||
{ |
||||
if (count%4 == 0) |
||||
{ |
||||
str += " 0x"; |
||||
} |
||||
var byte:uint = bytes.readUnsignedByte(); |
||||
var nib1:uint = byte/16; |
||||
var nib2:uint = byte%16; |
||||
str += getHex(nib1) + getHex(nib2); |
||||
count++; |
||||
} |
||||
str += " >"; |
||||
|
||||
// Reset position |
||||
bytes.position = position; |
||||
return str; |
||||
} |
||||
|
||||
private static function getHex(nibble:uint):String |
||||
{ |
||||
switch(nibble) |
||||
{ |
||||
case 0: return '0'; |
||||
case 1: return '1'; |
||||
case 2: return '2'; |
||||
case 3: return '3'; |
||||
case 4: return '4'; |
||||
case 5: return '5'; |
||||
case 6: return '6'; |
||||
case 7: return '7'; |
||||
case 8: return '8'; |
||||
case 9: return '9'; |
||||
case 10: return 'a'; |
||||
case 11: return 'b'; |
||||
case 12: return 'c'; |
||||
case 13: return 'd'; |
||||
case 14: return 'e'; |
||||
case 15: return 'f'; |
||||
} |
||||
|
||||
return "ERROR(" + nibble + ")"; |
||||
} |
||||
} |
||||
} |
@ -1,35 +0,0 @@ |
||||
/* |
||||
* Copyright (c) 2011 |
||||
* Spoken Language Systems Group |
||||
* MIT Computer Science and Artificial Intelligence Laboratory |
||||
* Massachusetts Institute of Technology |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person |
||||
* obtaining a copy of this software and associated documentation |
||||
* files (the "Software"), to deal in the Software without |
||||
* restriction, including without limitation the rights to use, copy, |
||||
* modify, merge, publish, distribute, sublicense, and/or sell copies |
||||
* of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice shall be |
||||
* included in all copies or substantial portions of the Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
*/ |
||||
package edu.mit.csail.wami.utils |
||||
{ |
||||
public interface StateListener |
||||
{ |
||||
function started():void; |
||||
function finished():void; |
||||
function failed(error:Error):void; |
||||
} |
||||
} |
Loading…
Reference in new issue