/*
* ASAPMIDlet.java - ASAP midlet
*
* Copyright (C) 2007-2009 Piotr Fusik
*
* This file is part of ASAP (Another Slight Atari Player),
* see http://asap.sourceforge.net
*
* ASAP is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2 of the License,
* or (at your option) any later version.
*
* ASAP is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ASAP; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Vector;
import javax.microedition.io.Connector;
import javax.microedition.io.file.FileConnection;
import javax.microedition.io.file.FileSystemRegistry;
import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.AlertType;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Gauge;
import javax.microedition.lcdui.List;
import javax.microedition.lcdui.StringItem;
import javax.microedition.media.Manager;
import javax.microedition.media.MediaException;
import javax.microedition.media.Player;
import javax.microedition.media.PlayerListener;
import javax.microedition.midlet.MIDlet;
import net.sf.asap.ASAP;
import net.sf.asap.ASAP_ModuleInfo;
class FileList
{
final String caption;
final String path;
final String[] names;
final FileList parent;
final FileList[] sublists;
private static boolean lessOrEqual(String name1, String name2)
{
boolean dir1 = name1.endsWith("/");
boolean dir2 = name2.endsWith("/");
if (dir1 != dir2)
return dir1;
return (name1.compareTo(name2) <= 0);
}
private void sort(int start, int end)
{
while (start + 1 < end) {
int left = start + 1;
int right = end;
String pivot = names[start];
String tmp;
while (left < right) {
if (lessOrEqual(names[left], pivot))
left++;
else {
right--;
tmp = names[left];
names[left] = names[right];
names[right] = tmp;
}
}
left--;
tmp = names[left];
names[left] = names[start];
names[start] = tmp;
sort(start, left);
start = right;
}
}
FileList(String caption, String path, FileList parent, Enumeration contents)
{
this.caption = caption;
this.path = path;
this.parent = parent;
Vector v = new Vector();
int start = 0;
if (parent != null) {
v.addElement("..");
start = 1;
}
while (contents.hasMoreElements()) {
String name = (String) contents.nextElement();
if (name.endsWith("/") || ASAP.isOurFile(name))
v.addElement(name);
}
int n = v.size();
names = new String[n];
sublists = new FileList[n];
for (int i = 0; i < n; i++)
names[i] = (String) v.elementAt(i);
sort(start, n);
}
}
class ASAPInputStream extends InputStream
{
private final ASAP asap;
private final ASAPMIDlet midlet;
private final byte[] buffer = new byte[16384];
private int buffer_len = ASAP.WAV_HEADER_BYTES;
private int buffer_offset = 0;
private static final int FORMAT = ASAP.FORMAT_U8;
ASAPInputStream(ASAP asap, ASAPMIDlet midlet)
{
this.asap = asap;
this.midlet = midlet;
asap.getWavHeader(buffer, FORMAT);
}
public int read()
{
int i = buffer_offset;
if (i >= buffer_len) {
buffer_len = asap.generate(buffer, FORMAT);
if (buffer_len == 0)
return -1;
midlet.updatePosition();
i = 0;
}
buffer_offset = i + 1;
return buffer[i] & 0xff;
}
public int read(byte[] b, int off, int len)
{
int i = buffer_offset;
if (i >= buffer_len) {
buffer_len = asap.generate(buffer, FORMAT);
if (buffer_len == 0)
return -1;
midlet.updatePosition();
i = 0;
}
if (len > buffer_len - i)
len = buffer_len - i;
System.arraycopy(buffer, i, b, off, len);
buffer_offset = i + len;
return len;
}
}
public class ASAPMIDlet extends MIDlet implements CommandListener, PlayerListener
{
private boolean useSmallStreams = false; // TODO: work in progress
private static final int SMALL_STREAM_BLOCKS = 16384;
private Command selectCommand;
private Command backCommand;
private Command stopCommand;
private Command exitCommand;
private FileList fileList;
private List fileListControl;
private byte[] module;
private ASAP asap;
private Player player;
private Player nextPlayer;
private List songListControl;
private Gauge gauge;
public ASAPMIDlet()
{
}
private void displayFileList(FileList fileList)
{
this.fileList = fileList;
this.fileListControl = new List(fileList.caption, List.IMPLICIT, fileList.names, null);
fileListControl.addCommand(selectCommand);
fileListControl.addCommand(exitCommand);
fileListControl.setCommandListener(this);
Display.getDisplay(this).setCurrent(fileListControl);
}
private void displayError(String message)
{
Alert alert = new Alert("Error", message, null, AlertType.ERROR);
Display.getDisplay(this).setCurrent(alert);
}
private static void appendStringItem(Form form, String label, String text)
{
if (text.length() == 0)
return;
StringItem si = new StringItem(label, text);
si.setLayout(StringItem.LAYOUT_NEWLINE_AFTER);
form.append(si);
}
public void updatePosition()
{
gauge.setValue(asap.getPosition());
}
private Player createSmallPlayer() throws IOException, MediaException
{
int len = asap.getModuleInfo().channels * SMALL_STREAM_BLOCKS;
byte[] wav = new byte[ASAP.WAV_HEADER_BYTES + len];
asap.getWavHeaderForPart(wav, ASAP.FORMAT_U8, SMALL_STREAM_BLOCKS);
len = asap.generate(wav, ASAP.WAV_HEADER_BYTES, len, ASAP.FORMAT_U8);
if (len == 0)
return null;
updatePosition();
InputStream is = new ByteArrayInputStream(wav, 0, ASAP.WAV_HEADER_BYTES + len);
return Manager.createPlayer(is, "audio/x-wav");
}
private void playSong(int song) throws IOException, MediaException
{
ASAP_ModuleInfo module_info = asap.getModuleInfo();
int duration = module_info.durations[song];
gauge = new Gauge(null, false, 1, 0);
Form playForm = new Form("ASAP " + ASAP.VERSION);
appendStringItem(playForm, "Name: ", module_info.name);
appendStringItem(playForm, "Author: ", module_info.author);
appendStringItem(playForm, "Date: ", module_info.date);
appendStringItem(playForm, "Time: ", ASAP.durationToString(duration));
playForm.append(gauge);
playForm.addCommand(stopCommand);
playForm.addCommand(exitCommand);
playForm.setCommandListener(this);
Display.getDisplay(this).setCurrent(playForm);
if (duration < 0)
duration = 180000;
gauge.setMaxValue(duration);
asap.playSong(song, duration);
if (useSmallStreams) {
player = createSmallPlayer();
player.realize();
nextPlayer = createSmallPlayer();
if (nextPlayer != null)
nextPlayer.realize();
}
else {
InputStream is = new ASAPInputStream(asap, this);
player = Manager.createPlayer(is, "audio/x-wav");
}
player.addPlayerListener(this);
player.start();
}
public void startApp()
{
selectCommand = new Command("Select", Command.ITEM, 1);
backCommand = new Command("Back", Command.BACK, 1);
stopCommand = new Command("Stop", Command.STOP, 1);
exitCommand = new Command("Exit", Command.EXIT, 2);
module = new byte[ASAP.MODULE_MAX];
asap = new ASAP();
displayFileList(new FileList("Select File", "", null, FileSystemRegistry.listRoots()));
}
public void commandAction(Command c, Displayable s)
{
if (c == selectCommand || c == List.SELECT_COMMAND) {
try {
if (songListControl != null) {
playSong(songListControl.getSelectedIndex());
songListControl = null;
return;
}
int index = fileListControl.getSelectedIndex();
String filename = fileList.names[index];
if (filename.equals("..")) {
displayFileList(fileList.parent);
return;
}
FileList newList = fileList.sublists[index];
if (newList != null) {
displayFileList(newList);
return;
}
String path = fileList.path + filename;
FileConnection fc = (FileConnection) Connector.open("file:///" + path, Connector.READ);
if (fc.isDirectory()) {
newList = new FileList(filename, path, fileList, fc.list());
fileList.sublists[index] = newList;
displayFileList(newList);
return;
}
InputStream is = fc.openInputStream();
int module_len = 0;
for (;;) {
int i = is.read(module, module_len, ASAP.MODULE_MAX - module_len);
if (i <= 0)
break;
module_len += i;
}
is.close();
asap.load(filename, module, module_len);
int songs = asap.getModuleInfo().songs;
if (songs > 1) {
songListControl = new List(asap.getModuleInfo().name, List.IMPLICIT);
for (int i = 1; i <= songs; i++)
songListControl.append("Song " + i, null);
songListControl.setSelectedIndex(asap.getModuleInfo().default_song, true);
songListControl.addCommand(selectCommand);
songListControl.addCommand(backCommand);
songListControl.setCommandListener(this);
Display.getDisplay(this).setCurrent(songListControl);
}
else
playSong(0);
} catch (Exception ex) {
displayError(ex.toString());
}
}
else if (c == backCommand) {
songListControl = null;
Display.getDisplay(this).setCurrent(fileListControl);
}
else if (c == stopCommand) {
nextPlayer = null;
try {
player.stop();
} catch (MediaException ex) {
}
Display.getDisplay(this).setCurrent(fileListControl);
}
else if (c == exitCommand)
notifyDestroyed();
}
public void playerUpdate(Player player, String event, Object eventData)
{
if (event == PlayerListener.END_OF_MEDIA) {
if (nextPlayer != null) {
player = nextPlayer;
player.addPlayerListener(this);
try {
player.start();
nextPlayer = createSmallPlayer();
if (nextPlayer != null)
nextPlayer.realize();
} catch (Exception ex) {
displayError(ex.toString());
}
}
else
Display.getDisplay(this).setCurrent(fileListControl);
}
else if (event == PlayerListener.ERROR)
displayError((String) eventData);
}
public void pauseApp()
{
}
public void destroyApp(boolean unconditional)
{
}
}