Free DNS provides easy shared DNS hosting & URL forwarding

Wednesday, July 29, 2009

Nanogong applet how-to

This is a compilation of the notes I tookon nanogong appet. I think I got everything here and I didn't missed anything.

The nanogong applet accepts the following (optional) parameters:

  • SoundFileURL = URL = address of a sound file that should be loaded on applet init (default "")
  • Start = true|false = if true, the applet will start playing the file from SoundFileURL after loading (default false)
  • ShowAudioLevel = true|false = if true, it displays the audiometer (i.e. VU meter) (default true)
  • ShowRecordButton = true|false = if true, it displays the record button (default true)
  • ShowSpeedButton = true|false = if true, it displays the faster/slower playback buttons (default true)
  • ShowSaveButton = true|false = if true, it displays the saved file locally button (default true)
  • Color = HTML color (including # prefix) = the color of the applet's background (default #FFFFFF)
  • AudioFormat = ImaADPCM|Speex = the codec used for sound encoding (default Speex)
  • SamplingRate = integer = the sampling rate of the sound recording (default 44100); allowed values depend on the AudioFormat:
    • 8000|11025|22050|44100 = for IMA_ADPCM audioformat
    • 8000|16000|32000|44100 = for Speex audioformat
  • SpeexQuality = 1...10 = quality of sound compression for the speex codec (1 = lowest, 10 = highest quality) (default: 10)

Appart from SoundFileURL and Start parameters, which allow sound to be played as soon as the applet is loaded, the nanogong applet can be controlled using the sendGongRequest method. In order to do this, the applet should have an id, which makes it easy to get a reference to it in JavaScript. Here's an example:

HTML:
<applet id="nanogong" archive="nanogong.jar" code="gong.NanoGong" width="180" height="40"></applet>

JavaScript:
var recorder = document.getElementById('nanogong');
if (recorder == null) {
 alert("recorder not found");
 return;
}
var result = recorder.sendGongRequest(...request name..., ...parameters...);

The following requests names (and parameters) are accepted (including returned values, but without quotes):

  • playMedia StartTime EndTime - plays the media between the StartTime (optional; default: current position) and EndTime (optional; default: end of sound) seconds; return value is "StartTime;EndTime" (values parsed by the applet)
  • recordMedia Duration - record Duration (optional; default: maximum accepted) seconds of sound; return value is "Duration" (values parsed by the applet)
  • pauseMedia - pause the media playback or recording; return value is "Time" (current playback time)
  • stopMedia - stops the media playback or recording; return value is "" (if playback) or "Duration" (total recording time, if recording)
  • setMediaTime Time - sets the current playback time; return value is "Time" (value parsed by the script)
  • getMediaTime - gets the current playback time; return value is "Time" (current playback time)
  • getMediaDuration - gets the total playback time; return value is "Time" (total playback time)
  • setMediaRate Rate - sets the playback rate (between 0.5 and 1.5); return value is "Rate" (value parsed by the script)
  • getMediaRate - gets the playback rate (between 0.5 and 1.5); return value is "Rate" (current playback rate)
  • getMediaStatus - gets the current status: playing, recording,paused, paused recording, stopping, stopped, closing, or closed; return value is "Status"
  • getAudioLevel - gets the current audio level (in 0.00 format); return value is "Audio level"
  • saveMessage Type Filename Path - saves the audio file (many features)
  • postToForm URL Parameter Cookies Filename - send the sound file via HTTP post to the URL address (including the Cookies), with the file send as the Parameter form item and with local name Filename; return value is the response of the server.
  • loadFromURL URL Start - load the sound file from the URL and playsit if Start=true; return value is "URL" (value parsed by the applet)
  • getVersion - gets the applet version number; return value is "Version"

Here's an exemple that also uploads the registered message (2 files in the same directory):

File: record.php
------------------------
<?php
if(isset($_GET['save'])) {
  move_uploaded_file($_FILES['sndfile']['tmp_name'], '/tmp/' .
$_FILES['sndfile']['name']);
  echo 'Your file have been saved.';
  exit;
}
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title>The NanoGong Applet</title>
  <script src="nanogong.js" type="text/javascript" language="javascript"/>
</head>
<body>
  <h1>The NanoGong Applet</h1>
  <applet id="nanogong" archive="nanogong.jar" code="gong.NanoGong"
width="180" height="40">
  <param name="AudioFormat" value="ImaADPCM" />
  </applet>
  <input type="button" onclick="sendFile('nanogong')" value="Send" />
</body>
</html>

nanogong.js
------------------------
function sendFile(applet_id) {
var recorder = document.getElementById(applet_id);
if (recorder == null) {
  alert("recorder not found");
  return;
}

var duration =parseInt(recorder.sendGongRequest("GetMediaDuration", "audio")) || 0;
if (duration <= 0) {
  alert("no recording found");
  return;
}

// upload the voice file to the server
var msg = recorder.sendGongRequest("PostToForm","record.php?save=1", "sndfile", "cookie=SomeText", "myfile");
alert(msg);
}

The problem with the current applet is that it is not signed and it contains code for saving files locally. This makes browsers to ask the user if they trust the applet publisher. This is annoying and it may raise concerns for unfamiliar users. There are three solutions to this problem:

  • leave it as it is (maybe show a message to the user, explaining what the browser is complaining about)
  • sign a copy of the nanogong.jar file with some valid certificate (maybe a moodle certificate)
  • delete the local file saving code (not very important), recompile the applet and hope that the browser will not complain anymore.

Tuesday, July 21, 2009

Almost there

The riffly plugin repository is almost completed. Jerome is helping me with QA and squashing bugs. The plugin now supports multiple instances (per site, per course and/or per user) and each of this instances can be setup to support only audio media, only video media or audio and video. Also, each instance can be setup to download the riff files locally (for management using Moodle's File API) or to link to the files on riffly.com (very useful, if you have limited storage space or bandwidth).

Meanwhile, I started work on the second plugin, which is using the Java applet nanogong.

Wednesday, July 15, 2009

Code is in CVS

The code up to now it's in CVS. I did some changes to the recording scripts, factoring out the common part in record.php. After Jerome noticed that recording is limited to 10s, I fixed that and set a limit to 1 hour. I also fixed some other bugs that Jerome reported and made some changes to the UI (the recording links placed at the top now; no longer mixed with the recorded content). I'm currently working on integrating this plugin with File API.

Thursday, July 9, 2009

CVS write access

My application for CVS write access was accepted and now I have write access to contrib/plugins/repository/riffly. I'll commit the plugin as soon as possible (probably after I'll do the changes Jerome proposed).

Monday, July 6, 2009

Midterm plugin

This week I "finished" the repository plugin for integration of riffly.com web services. I got carried away so I added more features than I initially planned for (like video recording). Before going into more details, you can go and download the plugin from the ticket #MDL-18341.

Instalation: Inside the tgz'ed file you'll find only one folder (called riffly) that you'll have to extract in the repository folder of your Moodle instalation. After that, go to Moodle's Site Administration menu and drill into Plugins/Repositories/Manage repositories. Click on the "Add Riffly" link and then enable the Riffly repository plugin.

Usage: As I said, the plugin has many features (although some of them are not as visible as they could be), so I will not present them all and let you discover them by yourself. Just a few tips:

  • how to insert a riff in your document: you'll have to use TinyMCE's "Insert/edit embeded media" button. Click on it and then click on the Browse button (to the right of the File/URL textbox). In the File Picker, select the riffly source. There you'll see the riff manager option, the record audio option, the record video option and any riffs you recorded previously (or added manually through the manager). Select the riff you want or record a new one, enter its name (or a description) and click the "Select this file" button. The rest is business as usual.
  • how to manage your riffs: follow the previous steps, but select the "Manage collection" instead of an riff. You'll see a list with all the riffs in your repository (including their name/description, ID and preview areas). You can remove items from the repository or you can add new items if you know their IDs (Yes, you can share riffs with your friends by sharing these IDs)

Things that might go wrong: The code was developed against the HEAD of Moodle's CVS and it works ok. However, I spent an entire day tracking down a bug that prevented the file picker to show its content (repository categories and items). It seems a bug found its way into commit 1.19 of lib/form/editor.php (Petr was so kind to help me find it). Either check it was fixed in a following version or use the 1.18 version from HEAD.

Things that didn't go as planned: I broke my Moodle working enviroment twice this week with CVS updates. It's normal for a product in development phase, but that doesn't make it less annoying. 

I planned to create a TinyMCE to ease the proces of recording (and open the file picker directly). I never developed for TinyMCE before. I got maybe 60% of it done, but yesterday I got stuck in some JavaScript issues. Today, I found some ways to make it work, but I'll have to postpone work on it for a while.

Because of the TinyMCE plugin, I didn't have time to write the text filter, which would be probably much more easier and straightforward (I went with the challenging/fun part).