//===============================================================================================
//CDDARIP.EXE
//Copyright (c), Firelight Technologies Pty, Ltd, 1999-2004.
//
//Use CDDA streaming to rip a CD track to a wav file
//===============================================================================================
/**
* I've ported the C++ FMOD example to use it with NativeFmod
*
* @author J�r�me JOUVIE (Jouvieje)
*
* WANT TO CONTACT ME ?
* E-mail :
* jerome.jouvie@gmail.com
* My web sites :
* http://jerome.jouvie.free.fr/
*/
package org.jouvieje.Fmod.Examples;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import javax.swing.JPanel;
import org.jouvieje.Fmod.Fmod;
import org.jouvieje.Fmod.Init;
import org.jouvieje.Fmod.Callbacks.FSOUND_DSPCALLBACK;
import org.jouvieje.Fmod.Callbacks.FSOUND_STREAMCALLBACK;
import org.jouvieje.Fmod.Defines.FSOUND_DSP_PRIORITIES;
import org.jouvieje.Fmod.Defines.FSOUND_MISC_VALUES;
import org.jouvieje.Fmod.Defines.FSOUND_MODES;
import org.jouvieje.Fmod.Defines.VERSIONS;
import org.jouvieje.Fmod.Enumerations.FSOUND_OUTPUTTYPES;
import org.jouvieje.Fmod.Examples.Util.ConsoleGUI;
import org.jouvieje.Fmod.Examples.Util.FmodExampleFrame;
import org.jouvieje.Fmod.Exceptions.InitException;
import org.jouvieje.Fmod.FileFormat.WavFormat.DataChunk;
import org.jouvieje.Fmod.FileFormat.WavFormat.FmtChunk;
import org.jouvieje.Fmod.FileFormat.WavFormat.RiffChunk;
import org.jouvieje.Fmod.FileFormat.WavFormat.WavHeader;
import org.jouvieje.Fmod.Misc.Pointer;
import org.jouvieje.Fmod.Structures.FSOUND_DSPUNIT;
import org.jouvieje.Fmod.Structures.FSOUND_STREAM;
import org.jouvieje.libloader.LibLoader;
public class CDDARip extends ConsoleGUI implements FSOUND_OUTPUTTYPES, FSOUND_DSP_PRIORITIES, FSOUND_MODES, FSOUND_MISC_VALUES, VERSIONS {
private static final long serialVersionUID = 1L;
public static void main(String[] args) {
new FmodExampleFrame(new CDDARip());
}
public CDDARip() {
super();
initFmod();
initialize();
}
public JPanel getPanel() {
return this;
}
public String getTitle() {
return "FMOD CDDA-Rip example.";
}
private boolean init = false;
private boolean deinit = false;
private boolean stream_ended = false;
private long start_time, elapsed_time;
private RandomAccessFile file = null;
private long byteswritten = 0;
private FSOUND_STREAMCALLBACK endcallback = new FSOUND_STREAMCALLBACK(){
public boolean FSOUND_STREAMCALLBACK(FSOUND_STREAM stream, ByteBuffer buff, int len, Pointer userdata) {
//Use to sop the writting in the file
stream_ended = true;
return true;
}
};
private FSOUND_DSPCALLBACK DSP_RawWriteCallback = new FSOUND_DSPCALLBACK(){
public ByteBuffer FSOUND_DSPCALLBACK(ByteBuffer originalbuffer, ByteBuffer newBuffer, int length,
Pointer userdata) {
if(file != null && !stream_ended) {
/**
* length is the length of newBuffer in SAMPLES. But 1 SAMPLE is 4 bytes, so the length in byte is 4*length
* newbuffer holds 4*length bytes.
*
* First we convert the CPointer (general C++ object) to a byte array (~ byte array).
* After, we get our 4*length elements as a byte array.
*/
byte[] datas = new byte[length * 4];
newBuffer.get(datas, 0, length * 4);
try {
//write the byte array into the file
file.write(datas);
}
catch(Exception e) {}
byteswritten += length * 4;
}
return newBuffer;
}
};
public void initFmod() {
/*
* NativeFmod Init
*/
try {
Init.loadLibraries();
}
catch(InitException e) {
printExit("NativeFmod error! " + e.getMessage() + "\n");
return;
}
/*
* Checking NativeFmodEx version
*/
if(NATIVEFMOD_LIBRARY_VERSION != NATIVEFMOD_JAR_VERSION) {
printExit("Error! NativeFmod library version (" + NATIVEFMOD_LIBRARY_VERSION
+ ") is different to jar version (" + NATIVEFMOD_JAR_VERSION + ")\n");
return;
}
/*==================================================*/
/*
* Checking Fmod version
*/
if(Fmod.FSOUND_GetVersion() < FMOD_VERSION) {
printExit("Error : You are using the wrong DLL version! You should be using FMOD " + FMOD_VERSION + "\n");
return;
}
init = true;
}
public void run() {
if(!init) return;
FSOUND_STREAM stream;
FSOUND_DSPUNIT rawwrite_dsp;
int track_num, read_percent;
start_time = System.currentTimeMillis();
print("-------------------------------------------------------------\n");
print("FMOD CDDA-Rip example.\n");
print("Copyright (c) Firelight Technologies Pty, Ltd, 2001-2004.\n");
print("-------------------------------------------------------------\n");
//Drive letter
setInput("'E' (windows) or '/dev/cdrom1' (linux/mac)");
String drive = readInput("\nAudio CD drive:");
switch (LibLoader.getPlatform()) {
case LibLoader.PLATFORM_WINDOWS:
if(!((drive.charAt(0) >= 'a' && drive.charAt(0) <= 'z') || (drive.charAt(0) >= 'A' && drive.charAt(0) <= 'Z'))) {
printExit("ERROR: Invalid drive");
return;
}
drive += ":*j";
break;
case LibLoader.PLATFORM_LINUX:
case LibLoader.PLATFORM_MACOSX:
if(!drive.startsWith("/")) {
printExit("ERROR: Invalid drive");
return;
}
break;
}
Fmod.FSOUND_SetOutput(FSOUND_OUTPUT_NOSOUND_NONREALTIME);
if(!Fmod.FSOUND_Init(44100, 32, 0)) {
String error = Fmod.FMOD_ErrorString(Fmod.FSOUND_GetError());
print("Shutdown FMOD\n");
Fmod.FSOUND_Close();
printExit(error);
return;
}
//Output file
setInput("track_ripped");
String filename = readInput("Audio track rip file name:") + ".wav";
try {
file = new RandomAccessFile(new File(filename), "rw");
}
catch(IOException e) {
file = null;
}
if(file == null) {
print("Shutdown FMOD\n");
Fmod.FSOUND_Close();
printExit("ERROR: Couldn't open " + filename + " for writing");
return;
}
/*
* Before we've even written the headers for the wav out, seek to the offset the raw data will start from.
*/
try {
file.seek(WavHeader.SIZEOF_WAV_HEADER + FmtChunk.SIZEOF_FMT_CHUNK + DataChunk.SIZEOF_DATA_CHUNK);
}
catch(IOException e) {}
/*
* Create a DSP callback which will capture the mixed data and write it to disk
*/
rawwrite_dsp = Fmod.FSOUND_DSP_Create(DSP_RawWriteCallback, FSOUND_DSP_DEFAULTPRIORITY_USER, null);
Fmod.FSOUND_DSP_SetActive(rawwrite_dsp, true);
Fmod.FSOUND_Stream_SetBufferSize(2000);
stream = Fmod.FSOUND_Stream_Open(drive, FSOUND_NORMAL, 0, 0);
if(stream == null) {
Fmod.FSOUND_DSP_SetActive(rawwrite_dsp, false);
Fmod.FSOUND_DSP_Free(rawwrite_dsp);
print("Shutdown FMOD\n");
Fmod.FSOUND_Close();
printExit("ERROR: Couldn't create CDDA stream");
return;
}
setInput("1");
track_num = Integer.valueOf(readInput("Audio track number ro rip:")).intValue();
if((track_num < 1) || ((track_num - 1) >= Fmod.FSOUND_Stream_GetNumSubStreams(stream))) {
Fmod.FSOUND_Stream_Close(stream);
Fmod.FSOUND_DSP_SetActive(rawwrite_dsp, false);
Fmod.FSOUND_DSP_Free(rawwrite_dsp);
print("Shutdown FMOD\n");
Fmod.FSOUND_Close();
printExit("ERROR: Invalid track number");
return;
}
Fmod.FSOUND_Stream_SetEndCallback(stream, endcallback, null);
Fmod.FSOUND_Stream_SetSubStream(stream, track_num - 1);
Fmod.FSOUND_Stream_Play(FSOUND_FREE, stream);
print("\nRipping " + new String(drive) + " track " + track_num + " ("
+ (Fmod.FSOUND_Stream_GetLengthMs(stream) / 1000 / 60) + ":"
+ (Fmod.FSOUND_Stream_GetLengthMs(stream) / 1000 % 60) + ")\n");
do {
read_percent = (int)(((float)Fmod.FSOUND_Stream_GetTime(stream) / (float)Fmod.FSOUND_Stream_GetLengthMs(stream)) * 100.0f);
byte[] b = " ".getBytes();
for(int i = 0; i < (read_percent >> 1) + (read_percent & 1); i++) {
b[i] = '=';
}
printr("|" + new String(b) + "| " + read_percent + "% ");
Fmod.FSOUND_Update();
}
while(!stream_ended);
Fmod.FSOUND_Stream_Close(stream);
Fmod.FSOUND_DSP_SetActive(rawwrite_dsp, false);
Fmod.FSOUND_DSP_Free(rawwrite_dsp);
/*
* Now finalize the wav file by seeking to the start and putting in the headers.
*/
/*
* WAV Structures
*/
WavHeader wavHeader = new WavHeader(new RiffChunk(new byte[]{'R', 'I', 'F', 'F'},
FmtChunk.SIZEOF_FMT_CHUNK + RiffChunk.SIZEOF_RIFF_CHUNK + (int)byteswritten), new byte[]{'W', 'A', 'V', 'E'});
FmtChunk fmtChunk = new FmtChunk(new RiffChunk(new byte[]{'f', 'm', 't', ' '},
FmtChunk.SIZEOF_FMT_CHUNK - RiffChunk.SIZEOF_RIFF_CHUNK),
(short)1, (short)2, 44100, 44100 * 2 * 16 / 8, (short)(1 * 2 * 16 / 8), (short)16);
DataChunk dataChunk = new DataChunk(new RiffChunk(new byte[]{'d', 'a', 't', 'a'}, (int)byteswritten));
try {
//Go to the beginning of the file
file.seek(0);
WavHeader.writeWavHeader(file, wavHeader);
FmtChunk.writeFmtChunk(file, fmtChunk);
DataChunk.writeDataChunk(file, dataChunk);
//Close the stream
file.close();
}
catch(Exception e) {}
stop();
}
public void stop() {
if(!init || deinit) {
return;
}
deinit = true;
print("\n");
print("Shutdown FMOD\n");
Fmod.FSOUND_Close();
elapsed_time = System.currentTimeMillis() - start_time;
print("\nElapsed time: " + (elapsed_time / 1000 / 60) + ":" + (elapsed_time / 1000 % 60) + "\n");
}
}