I'm trying to write a simple GTK GUI for the command line program auCDtect. It is supposed to take .flac files, convert them to .wav temporarily, pass the to the auCDtect command line program to check they are lossless and then delete the temporary .wav file and output the results.
For around 10+ files it's rather slow (though admittedly so are the command line tools), so I was hoping somebody could review the code and let me know of any obvious improvements or mistakes I'm making.
All the code can be found here.
GUI class
//The user interface
import java.io.*;
import java.util.*;
import org.apache.commons.io.*;
import org.gnome.gdk.*;
import org.gnome.glib.*;
import org.gnome.gtk.*;
public class GTK extends Window implements DeleteEvent {
private String directoryPath = null;
private String filePath = null;
private Collection<String> fileList = new ArrayList<String>();
private auCDtect auCDtect;
private TextBuffer resultsBuffer;
private ProgressBar progressBar;
private double progress = 0;
private String output;
private String summary;
boolean outputUpdated = false;
public GTK() {
//Set window title
setTitle("Dan's AudioChecker");
//Initialise user interface
initUI(this);
//Exit GUI cleanly if close pressed
connect(this);
//Set default size, position and make window visible
setDefaultSize(800, 800);
setPosition(WindowPosition.CENTER);
showAll();
}
public void initUI(final GTK gtk) {
setWindowIcon();
//Create container to vertically stack widgets
VBox vBox = new VBox(false, 5);
//Set alignment and size of action buttons container
Alignment halign = new Alignment(0, 0, 1, 0);
//Create hoziontal box for action buttons (homogenous spacing - false, default spacing 10)
HBox actionButtons = new HBox(false, 10);
//Create horizontal box for view panes
HBox viewPanes = new HBox(false, 10);
//Create horizontal box for progress bar
HBox progressBarBox = new HBox(false, 10);
//create scrollable text box for file queue
final TextBuffer queueBuffer = new TextBuffer();
TextTag label = new TextTag();
label.setBackground("#EAE8E3");
queueBuffer.insert(queueBuffer.getIterStart(), "Queue \n", label);
queueBuffer.insert(queueBuffer.getIterEnd(), "\n");
TextView queue = new TextView(queueBuffer);
queue.setEditable(false);
queue.setCursorVisible(false);
ScrolledWindow queueWindow = new ScrolledWindow();
queueWindow.add(queue);
//create scrollable text box for results info
resultsBuffer = new TextBuffer();
resultsBuffer.insert(resultsBuffer.getIterStart(), "Results \n", label);
resultsBuffer.insert(resultsBuffer.getIterEnd(), "\n");
TextView results = new TextView(resultsBuffer);
results.setEditable(false);
results.setCursorVisible(false);
ScrolledWindow resultsWindow = new ScrolledWindow();
resultsWindow.add(results);
//Create buttons to user interface
final FileChooserDialog directoryDialog = new FileChooserDialog("Directory", null, FileChooserAction.SELECT_FOLDER);
Button directory = new Button("Directory");
final FileChooserDialog filesDialog = new FileChooserDialog("Files", null, FileChooserAction.OPEN);
FileFilter allLosslessMusic = new FileFilter("Lossless Music");
allLosslessMusic.addMimeType("audio/flac");allLosslessMusic.addMimeType("audio/wav");
Button files = new Button("File(s)");
filesDialog.addFilter(allLosslessMusic);
Label emptyLabel = new Label("");
Button start = new Button("Start");
//Create progress bar and add to hbox
progressBar = new ProgressBar();
progressBarBox.add(progressBar);
//Make fileChooser dialog come up when directories is clicked
directory.connect(new Button.Clicked(){
@Override
public void onClicked(Button directory) {
directoryDialog.run();
directoryPath = directoryDialog.getFilename();
String[] extensions = {".flac", ".wav"};
if(!(directoryPath == null)){
Iterator<File> files = FileUtils.iterateFiles(new File(directoryPath), new SuffixFileFilter(extensions, IOCase.INSENSITIVE),DirectoryFileFilter.DIRECTORY);
while(files.hasNext()){
File j = files.next();
fileList.add(j.getPath());
queueBuffer.insert(queueBuffer.getIterEnd(), j+"\n");
}
}
directoryDialog.hide();
}
});
//Make fileChooser dialog come up when files is clicked
files.connect(new Button.Clicked(){
@Override
public void onClicked(Button files) {
filesDialog.run();
if (!(filesDialog.getFilename() == null)){
filePath = filesDialog.getFilename();
fileList.add(filePath);
queueBuffer.insert(queueBuffer.getIterEnd(), filePath+"\n");
}
filesDialog.hide();
}
});
//Make start button work
start.connect(new Button.Clicked(){
@Override
public void onClicked(Button start){
auCDtect = new auCDtect(fileList, gtk);
Thread auCDtectThread = new Thread(auCDtect);
auCDtectThread.start();
Glib.idleAdd(new Handler(){
public boolean run(){
progress = auCDtect.getProgress();
output = auCDtect.getOutput();
summary = auCDtect.getSummary();
if(summary == null){
progressBar.setFraction(progress);
if((outputUpdated == true)&&(output != null)){
resultsBuffer.insert(resultsBuffer.getIterEnd(), output+"\n");
setOutputUpdated(false);
}
return true;
}
else{
progressBar.setFraction(progress);
if(summary != null){
resultsBuffer.insert(resultsBuffer.getIterEnd(), "\n"+"Results summary:"+"\n\n"+summary);
}
return false;
}
}
});
}
});
//Position buttons and add to fix
actionButtons.packStart(directory, false, false, 0);
actionButtons.packStart(files, false, false, 0);
actionButtons.packStart(emptyLabel, true, true, 0);
actionButtons.packStart(start, false, false, 0);
//Add
viewPanes.add(queueWindow);
viewPanes.add(resultsWindow);
halign.add(actionButtons);
vBox.packStart(halign, false, false, 10);
vBox.packStart(viewPanes, true, true, 10);
vBox.packStart(progressBarBox, false, false, 10);
add(vBox);
setBorderWidth(15);
}
//Method to exit application
public boolean onDeleteEvent(Widget widget, Event event) {
Gtk.mainQuit();
return false;
}
//Method to set window icon
public void setWindowIcon(){
//Set the audiochecker icon that appears in the start bar
Pixbuf icon = null;
try {
icon = new Pixbuf("audiochecker.png");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
setIcon(icon);
}
public void setOutputUpdated(boolean outputUpdated){
this.outputUpdated = outputUpdated;
}
public static void main(String[] args) {
//Initialise and run GUI
Gtk.init(args);
new GTK();
Gtk.main();
}
}
The audiochecker Class
//Checks the wav files using auCDtect command line
import java.io.*;
import java.util.*;
public class auCDtect implements Runnable {
private String processingLog;
private String output;
private String summary;
private Collection<String> fileList;
private Collection<String> tempWavList = new ArrayList<String>();
private double progress = 0.0;
private GTK gtk;
auCDtect(Collection<String> fileList, GTK gtk){this.fileList = fileList; this.gtk = gtk;}
public void run () {
List<String> command = new ArrayList<String>();
command.add("./auCDtect");
command.add("-d");
command.add("-m10");
//Add each song passed to this class to the auCDtect command
for(String file:fileList){
if(file.toLowerCase().contains("flac")){
AudioConverter audioConverter = new AudioConverter();
String tempWav = "tempWav.wav"+fileList.iterator();
tempWavList.add(tempWav);
try {
audioConverter.decode(file, tempWav);
} catch (IOException e) {
e.printStackTrace();
}
file = tempWav;
}
command.add(file);
}
ProcessBuilder processBuilder = new ProcessBuilder(command);
Process process = null;
try {
process = processBuilder.start();
} catch (IOException e) {
e.printStackTrace();
}
//Set up error stream thread
StreamGobbler errorGobbler = new StreamGobbler(process.getErrorStream(), "ERROR", this);
//Set up output stream thread
StreamGobbler outputGobbler = new StreamGobbler(process.getInputStream(), "OUTPUT", this);
// Start error and input stream threads
new Thread(errorGobbler).start();
new Thread(outputGobbler).start();
}
public void update(double progress, String processingLog, String output, String summary){
this.processingLog = processingLog;
this.output = output;
this.summary = summary;
this.progress = progress/(fileList.size());
gtk.setOutputUpdated(true);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(this.progress == 1){
deleteTempTracks();
}
}
public double getProgress(){
return progress;
}
public String getProcessingLog(){
return processingLog;
}
public String getOutput(){
return output;
}
public String getSummary(){
return summary;
}
public void deleteTempTracks(){
for(String tempFileName:tempWavList){
File tempFile = new File(tempFileName);
if(tempFile.exists()){
tempFile.deleteOnExit();
}
}
}
}
The class to convert flac to WAV
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import org.kc7bfi.jflac.PCMProcessor;
import org.kc7bfi.jflac.FLACDecoder;
import org.kc7bfi.jflac.metadata.StreamInfo;
import org.kc7bfi.jflac.util.ByteData;
import org.kc7bfi.jflac.util.WavWriter;
/**
* Decode FLAC file to WAV file application.
* @author kc7bfi
*/
public class AudioConverter implements PCMProcessor {
private WavWriter wav;
/**
* Decode a FLAC file to a WAV file.
* @param inFileName The input FLAC file name
* @param outFileName The output WAV file name
* @throws IOException Thrown if error reading or writing files
*/
public void decode(String flacFile, String tempWav) throws IOException {
FileInputStream inputStream = new FileInputStream(flacFile);
FileOutputStream outputStream = new FileOutputStream(tempWav);
wav = new WavWriter(outputStream);
FLACDecoder decoder = new FLACDecoder(inputStream);
decoder.addPCMProcessor(this);
decoder.decode();
}
/**
* Process the StreamInfo block.
* @param info the StreamInfo block
* @see org.kc7bfi.jflac.PCMProcessor#processStreamInfo(org.kc7bfi.jflac.metadata.StreamInfo)
*/
public void processStreamInfo(StreamInfo info) {
try {
wav.writeHeader(info);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Process the decoded PCM bytes.
* @param pcm The decoded PCM data
* @see org.kc7bfi.jflac.PCMProcessor#processPCM(org.kc7bfi.jflac.util.ByteSpace)
*/
public void processPCM(ByteData pcm) {
try {
wav.writePCM(pcm);
} catch (IOException e) {
e.printStackTrace();
}
}
}
The streamGobbler class to handle output
//Processes the output of the auCDtect command line program
import java.io.InputStream;
import java.util.Scanner;
public class StreamGobbler implements Runnable {
InputStream inputStream;
String type;
private String processingLog = null;
private String output = null;
private String summary = null;
protected boolean finished = false;
private auCDtect auCDtect;
private int progress = 0;
StreamGobbler(InputStream inputStream, String type, auCDtect auCDtect){this.inputStream = inputStream; this.type = type; this.auCDtect = auCDtect;}
public void run(){
Scanner scanner = new Scanner(inputStream);
while (((scanner.hasNextLine())&&(type == "OUTPUT"))){
String line = scanner.nextLine();
if(line.contains("Processing file:")){
processingLog = line.substring(line.indexOf("P"), (line.indexOf("]")+1));
}
if(line.contains("This track looks like")){
output = line.substring(line.indexOf("This track"), (line.indexOf("%")+1));
progress = progress + 1;
}
if(line.contains("These")){
summary = line.substring(line.indexOf("These tracks"));
}
if((type == "OUTPUT")&&(progress > 0)){
auCDtect.update(progress, processingLog, output, summary);
processingLog = null;
output = null;
summary = null;
}
}
}
}