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