Added mp3 exporting

This commit is contained in:
2018-09-09 21:40:05 +01:00
parent a40f22be16
commit 0bd794785e
6 changed files with 313 additions and 13 deletions

BIN
deps/jave-1.0.2.jar vendored Normal file

Binary file not shown.

BIN
deps/mp3agic-0.9.1.jar vendored Normal file

Binary file not shown.

View File

@@ -11,6 +11,8 @@ import java.lang.reflect.*;
import java.util.*;
import java.util.prefs.*;
import java.io.*;
import it.sauronsoftware.jave.*;
import com.mpatric.mp3agic.*;
public class AudiobookRecorder extends JFrame {
@@ -34,7 +36,7 @@ public class AudiobookRecorder extends JFrame {
JPanel statusBar;
JLabel statusFormat;
JLabel statusLabel;
JScrollPane mainScroll;
@@ -57,6 +59,8 @@ public class AudiobookRecorder extends JFrame {
Thread playingThread = null;
Random rng = new Random();
public static AudiobookRecorder window;
void buildToolbar(Container ob) {
@@ -78,7 +82,18 @@ public class AudiobookRecorder extends JFrame {
}
});
fileOpenBook = new JMenuItem("Open Book...");
fileOpenBook.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
openBook();
}
});
fileExit = new JMenuItem("Exit");
fileExit.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
saveBookStructure();
System.exit(0);
}
});
fileMenu.add(fileNewBook);
fileMenu.add(fileOpenBook);
@@ -91,7 +106,18 @@ public class AudiobookRecorder extends JFrame {
bookMenu = new JMenu("Book");
bookNewChapter = new JMenuItem("New Chapter");
bookExportAudio = new JMenuItem("Export Audio...");
bookNewChapter.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
addChapter();
}
});
bookExportAudio = new JMenuItem("Export Audio");
bookExportAudio.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
exportAudio();
}
});
bookMenu.add(bookNewChapter);
bookMenu.add(bookExportAudio);
@@ -158,6 +184,7 @@ public class AudiobookRecorder extends JFrame {
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
saveBookStructure();
System.exit(0);
}
});
@@ -336,8 +363,8 @@ public class AudiobookRecorder extends JFrame {
statusBar = new JPanel();
add(statusBar, BorderLayout.SOUTH);
statusFormat = new JLabel(Options.getAudioFormat().toString());
statusBar.add(statusFormat);
statusLabel = new JLabel("Noise floor: " + getNoiseFloor());
statusBar.add(statusLabel);
buildToolbar(centralPanel);
@@ -474,6 +501,19 @@ public class AudiobookRecorder extends JFrame {
}
});
process.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JMenuObject o = (JMenuObject)e.getSource();
Sentence s = (Sentence)o.getObject();
s.autoTrimSample();
sampleWaveform.setData(s.getAudioData());
sampleWaveform.setMarkers(s.getStartOffset(), s.getEndOffset());
startOffset.setValue(s.getStartOffset());
endOffset.setValue(s.getEndOffset());
postSentenceGap.setValue(s.getPostGap());
}
});
menu.add(del);
menu.add(edit);
menu.add(process);
@@ -554,6 +594,7 @@ public class AudiobookRecorder extends JFrame {
toolBar.enableBook();
toolBar.enableSentence();
toolBar.disableStop();
centralPanel.setFlash(false);
recording = null;
@@ -594,7 +635,11 @@ public class AudiobookRecorder extends JFrame {
if (s != null) {
bookTree.setSelectionPath(new TreePath(s.getPath()));
bookTree.scrollPathToVisible(new TreePath(s.getPath()));
} else {
sampleWaveform.clearData();
}
selectedSentence = s;
saveBookStructure();
}
public void addChapter() {
@@ -607,6 +652,8 @@ public class AudiobookRecorder extends JFrame {
@SuppressWarnings("unchecked")
public void saveBookStructure() {
if (book == null) return;
File bookRoot = new File(Options.get("path.storage"), book.getName());
if (!bookRoot.exists()) {
bookRoot.mkdirs();
@@ -694,12 +741,14 @@ public class AudiobookRecorder extends JFrame {
((SteppedNumericSpinnerModel)endOffset.getModel()).setMaximum(samples);
toolBar.enableSentence();
toolBar.disableStop();
} else {
selectedSentence = null;
toolBar.disableSentence();
sampleWaveform.clearData();
startOffset.setValue(0);
endOffset.setValue(0);
toolBar.disableStop();
postSentenceGap.setValue(0);
}
}
@@ -783,11 +832,11 @@ public class AudiobookRecorder extends JFrame {
bookTree.expandPath(new TreePath(book.getPath()));
toolBar.enableBook();
statusLabel.setText("Noise floor: " + getNoiseFloor());
}
public void openBook() {
OpenBookPanel info = new OpenBookPanel();
int r = JOptionPane.showConfirmDialog(this, info, "Open Book", JOptionPane.OK_CANCEL_OPTION);
if (r == JOptionPane.OK_OPTION) {
@@ -831,6 +880,7 @@ public class AudiobookRecorder extends JFrame {
}
public int getNoiseFloor() {
if (roomNoise == null) return 0;
int[] samples = roomNoise.getAudioData();
if (samples == null) {
return 0;
@@ -855,10 +905,10 @@ public class AudiobookRecorder extends JFrame {
ticker.schedule(new TimerTask() {
public void run() {
roomNoise.stopRecording();
centralPanel.setFlash(false);
centralPanel.setFlash(false);
statusLabel.setText("Noise floor: " + getNoiseFloor());
}
}, 5000); // 5 seconds of recording
}
}
@@ -869,11 +919,16 @@ public class AudiobookRecorder extends JFrame {
playing = selectedSentence;
toolBar.disableSentence();
toolBar.enableStop();
playingThread = new Thread(new Runnable() {
public void run() {
playing.play();
playing = null;
toolBar.enableSentence();
toolBar.disableStop();
}
});
@@ -881,4 +936,202 @@ public class AudiobookRecorder extends JFrame {
playingThread.start();
}
@SuppressWarnings("unchecked")
public void exportAudio() {
try {
File bookRoot = new File(Options.get("path.storage"), book.getName());
if (!bookRoot.exists()) {
bookRoot.mkdirs();
}
File export = new File(bookRoot, "export");
if (!export.exists()) {
export.mkdirs();
}
Encoder encoder = new Encoder();
EncodingAttributes attributes = new EncodingAttributes();
AudioAttributes audioAttributes = new AudioAttributes();
audioAttributes.setCodec("libmp3lame");
audioAttributes.setBitRate(new Integer(256000));
audioAttributes.setSamplingRate(new Integer(44100));
audioAttributes.setChannels(new Integer(2));
attributes.setFormat("mp3");
attributes.setAudioAttributes(audioAttributes);
AudioFormat format = roomNoise.getAudioFormat();
byte[] data;
for (Enumeration<Chapter> o = book.children(); o.hasMoreElements();) {
int fullLength = 0;
Chapter c = o.nextElement();
String name = c.getName();
File exportFile = new File(export, name + ".wax");
File wavFile = new File(export, name + ".wav");
File mp3File = new File(export, name + "-untagged.mp3");
File taggedFile = new File(export, name + ".mp3");
FileOutputStream fos = new FileOutputStream(exportFile);
data = getRoomNoise(s2i(Options.get("catenation.pre-chapter")));
fullLength += data.length;
fos.write(data);
for (Enumeration<Sentence> s = c.children(); s.hasMoreElements();) {
Sentence snt = s.nextElement();
data = snt.getRawAudioData();
fullLength += data.length;
fos.write(data);
if (s.hasMoreElements()) {
data = getRoomNoise(snt.getPostGap());
} else {
data = getRoomNoise(s2i(Options.get("catenation.post-chapter")));
}
fullLength += data.length;
fos.write(data);
}
fos.close();
FileInputStream fis = new FileInputStream(exportFile);
AudioInputStream ais = new AudioInputStream(fis, format, fullLength);
AudioSystem.write(ais, AudioFileFormat.Type.WAVE, wavFile);
fis.close();
exportFile.delete();
encoder.encode(wavFile, mp3File, attributes);
Mp3File id3 = new Mp3File(mp3File);
ID3v2 tags = new ID3v24Tag();
id3.setId3v2Tag(tags);
tags.setTrack(Integer.toString(s2i(c.getId()) - 0));
tags.setTitle(c.getName());
tags.setArtist(book.getAuthor());
// ID3v2TextFrameData g = new ID3v2TextFrameData(false, new EncodedText(book.getGenre()));
// tags.addFrame(tags.createFrame("TCON", g.toBytes(), true));
tags.setComment(book.getComment());
id3.save(taggedFile.getAbsolutePath());
mp3File.delete();
wavFile.delete();
}
JOptionPane.showMessageDialog(this, "Book exported.", "Done", JOptionPane.INFORMATION_MESSAGE);
} catch (Exception e) {
e.printStackTrace();
}
}
public void playFromSelectedSentence() {
if (selectedSentence == null) return;
if (playing != null) return;
playing = selectedSentence;
toolBar.disableSentence();
toolBar.enableStop();
playingThread = new Thread(new Runnable() {
public void run() {
Sentence s = playing;
byte[] data;
try {
AudioFormat format = s.getAudioFormat();
SourceDataLine play = AudioSystem.getSourceDataLine(format, Options.getPlaybackMixer());
play.open(format);
play.start();
while (playing != null) {
DefaultMutableTreeNode prev = s.getPreviousSibling();
boolean first = false;
if (prev == null) {
first = true;
} else if (!(prev instanceof Sentence)) {
first = true;
}
if (first) {
data = getRoomNoise(s2i(Options.get("catenation.pre-chapter")));
play.write(data, 0, data.length);
}
data = s.getRawAudioData();
play.write(data, 0, data.length);
DefaultMutableTreeNode next = s.getNextSibling();
boolean last = false;
if (next == null) {
last = true;
} else if (!(next instanceof Sentence)) {
last = true;
}
if (last) {
data = getRoomNoise(s2i(Options.get("catenation.post-chapter")));
play.write(data, 0, data.length);
playing = null;
} else {
data = getRoomNoise(s.getPostGap());
play.write(data, 0, data.length);
}
s = (Sentence)next;
}
} catch (Exception e) {
e.printStackTrace();
playing = null;
}
toolBar.enableSentence();
toolBar.disableStop();
}
});
playingThread.setDaemon(true);
playingThread.start();
}
public byte[] getRoomNoise(int ms) {
if (roomNoise == null) return null;
int len = roomNoise.getSampleSize();
if (len == 0) return null;
AudioFormat f = roomNoise.getAudioFormat();
int frameSize = f.getFrameSize();
float sr = f.getSampleRate();
int samples = (int)(ms * (sr / 1000f));
int start = rng.nextInt(len - samples);
int end = start + samples;
roomNoise.setStartOffset(start);
roomNoise.setEndOffset(end);
byte[] data = roomNoise.getRawAudioData();
return data;
}
public void stopPlaying() {
playing = null;
}
}

View File

@@ -15,6 +15,7 @@ public class MainToolBar extends JToolBar {
JButton playSentence;
JButton playonSentence;
JButton recordSentence;
JButton stopPlaying;
AudiobookRecorder root;
@@ -83,12 +84,26 @@ public class MainToolBar extends JToolBar {
playonSentence = new JButton(Icons.playon);
playonSentence.setToolTipText("Play from sentence");
playonSentence.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
root.playFromSelectedSentence();
}
});
add(playonSentence);
recordSentence = new JButton(Icons.record);
recordSentence.setToolTipText("Re-record sentence");
add(recordSentence);
stopPlaying = new JButton(Icons.stop);
stopPlaying.setToolTipText("Stop playing / recording");
stopPlaying.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
root.stopPlaying();
}
});
add(stopPlaying);
setFloatable(false);
}
@@ -113,4 +128,12 @@ public class MainToolBar extends JToolBar {
playonSentence.setEnabled(false);
recordSentence.setEnabled(false);
}
public void enableStop() {
stopPlaying.setEnabled(true);
}
public void disableStop() {
stopPlaying.setEnabled(false);
}
}

View File

@@ -308,8 +308,8 @@ public class Options extends JDialog {
set("audio.playback.device", ((KVPair)playbackList.getSelectedItem()).key);
set("path.storage", storageFolder.getText());
set("catenation.pre-chapter", "" + preChapterGap.getValue());
set("catenation.post-chapter", "" + preChapterGap.getValue());
set("catenation.post-sentence", "" + preChapterGap.getValue());
set("catenation.post-chapter", "" + postChapterGap.getValue());
set("catenation.post-sentence", "" + postSentenceGap.getValue());
savePreferences();
}

View File

@@ -86,7 +86,7 @@ public class Sentence extends DefaultMutableTreeNode {
line.start();
File audioFile = new File(AudiobookRecorder.window.getBookFolder(), id + ".wav");
File audioFile = getFile();
recordingThread = new Thread(new Runnable() {
public void run() {
@@ -169,7 +169,11 @@ public class Sentence extends DefaultMutableTreeNode {
}
public File getFile() {
return new File(AudiobookRecorder.window.getBookFolder(), id + ".wav");
File b = new File(AudiobookRecorder.window.getBookFolder(), "files");
if (!b.exists()) {
b.mkdirs();
}
return new File(b, id + ".wav");
}
public void editText() {
@@ -205,7 +209,7 @@ public class Sentence extends DefaultMutableTreeNode {
}
public void deleteFiles() {
File audioFile = new File(AudiobookRecorder.window.getBookFolder(), id + ".wav");
File audioFile = getFile();
if (audioFile.exists()) {
audioFile.delete();
}
@@ -243,7 +247,6 @@ public class Sentence extends DefaultMutableTreeNode {
sampleSize = samples.length;
return samples;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@@ -332,4 +335,25 @@ public class Sentence extends DefaultMutableTreeNode {
e.printStackTrace();
}
}
public byte[] getRawAudioData() {
File f = getFile();
try {
AudioInputStream s = AudioSystem.getAudioInputStream(f);
AudioFormat format = s.getFormat();
int frameSize = format.getFrameSize();
int length = endOffset - startOffset;
byte[] data = new byte[length * frameSize];
s.skip(startOffset * frameSize);
s.read(data);
return data;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}