Playing sound in the Lotus Notes client

Thu Oct 15 22:47:17 EDT 2009

Tags: notes

From time to time, mostly for fun, I've wanted to figure out how to play arbitrary sound clips in the Notes client. From what I can tell, the "proper" way to do that is to use NotesUIWorkspace.PlayTune(), which, as with so many aspects of Notes, falls far short of what I want. I don't want to do any filesystem or network access (both because that's ugly and also because it should be cross-platform and not depend on being in the office), and so that just won't work.

A while ago, I came up with a method that kind of worked, using Java's "sun.audio.AudioPlayer" class and feeding it data pulled from an HTTP connection. However, this had a couple problems - the audio player returns control as soon as it starts, resulting in the code ending and closing the stream, so I had to either cache the data first or add in a loop to wait for it to finish. More problematically, though, it caused weird problems with the web server wherein the connections didn't properly close, bogging it down to unresponsiveness over time. So... scratch that idea.

Eventually, I came up with something that worked: access the sound files as NotesDocuments (I do it as file resources showing up in a $FormulaClass-modified view, but it should work as attachments to normal documents too), export the docs as DXL, pull out the Base64-encoded sound file data, translate that back, and feed it to the audio player.

I ran into some trouble instantiating some of the objects in a pure-LotusScript implementation, so the library is broken up into two parts: a LotusScript script library with a function that pulls an audio file from the storage DB by name and calls a Java method, and a Java library to do the dirty work of playing the audio. The LotusScript, cleaned slightly, is:

Uselsx "*javacon"
Use "Java Audio"
	
Sub PlayByName(fileName As String)
	Dim jsession As New JavaSession, session As New NotesSession
	Dim storagedb As New NotesDatabase(session.CurrentDatabase.Server, "storagedb.nsf")
		
	If Not storagedb.IsOpen Then Exit Sub
		
	Dim soundEffects As NotesView, effect As NotesDocument
	Set soundEffects = storagedb.GetView("Sound Effects")
	soundEffects.AutoUpdate = False
	Set effect = soundEffects.GetDocumentByKey(fileName, True)
		
	If Not (effect Is Nothing) Then
		Dim exporter As NotesDXLExporter, dxl As String, fileData As String
		Set exporter = session.CreateDXLExporter
		dxl = exporter.Export(effect)
		fileData = Strleftback(Strright(dxl, {<filedata>
}), {
</filedata>})
		Dim audioPlayer As JAVACLASS
		Set audioPlayer = jsession.GetClass("JAudioPlayer")
		Call audioPlayer.play(fileData)
	End If
End Sub

The Java code isn't much worse:

public class JAudioPlayer {
	public static String play(String base64data) {
		AudioStream audioStream;
		try {
			byte[] audioBuffer = new BASE64Decoder().decodeBuffer(base64data);
			ByteArrayInputStream inputStream = new ByteArrayInputStream(audioBuffer);
			
			audioStream = new AudioStream(inputStream);
			AudioPlayer.player.start(audioStream);
		} catch(Exception e) {
			String stackTrace = "";
			for(StackTraceElement element : e.getStackTrace()) {
				stackTrace += element.toString() + "\n";
			}
			return stackTrace;
		}
		return "Success";
	}
	
	public static void stop(AudioStream audioStream) {
		try {
			AudioPlayer.player.stop(audioStream);
		} catch(Exception e) { }
	}
}

Naturally, there's other stuff to worry about, like making the DB, setting up the way you want it stored (if you use normal documents with file attachments, the DXL-searching bit would have to be slightly different), etc., but this is the meat of it.

All in all, this has been working out pretty well. It's not perfect (exporting as DXL would have a lot of waiting time if the audio file is big or the network slow), but it works in Windows and OS X, hasn't caused any server troubles (always a plus), and, since the audio player returns control immediately, the UI doesn't lock up while the file is playing.

New Comment