diff --git a/.gitignore b/.gitignore index 6a50d0a..958a6aa 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,8 @@ /*/build/ /local.properties /browser/profile/extensions/universal_music@regis.edu.xpi +/browser/profile/cache2/* +/browser/profile/datareporting/* +/browser/profile/startupCache/* +/browser/profile/sessionstore-backups/* +/browser/profile/lock \ No newline at end of file diff --git a/WavReader/README.md b/WavReader/README.md new file mode 100644 index 0000000..6de5907 --- /dev/null +++ b/WavReader/README.md @@ -0,0 +1 @@ +These files were originally posted by Andrii Tkachenko on GitHub in 2016. They have been refactored to function in a Gradle environment. \ No newline at end of file diff --git a/WavReader/build.gradle b/WavReader/build.gradle new file mode 100644 index 0000000..9679f1a --- /dev/null +++ b/WavReader/build.gradle @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2021 William Hubbard. All Rights Reserved. + */ + +apply plugin: "java" +apply plugin: "application" + +sourceCompatibility = 1.8 +targetCompatibility = 1.8 + +mainClassName = "MetaReader" \ No newline at end of file diff --git a/WavReader/src/main/java/MetaReader.java b/WavReader/src/main/java/MetaReader.java new file mode 100644 index 0000000..11d107f --- /dev/null +++ b/WavReader/src/main/java/MetaReader.java @@ -0,0 +1,28 @@ +import wave.WavHeader; +import wave.WavHeaderReader; + +import java.io.FileNotFoundException; +import java.io.IOException; + +/** + * Console utility for reading meta data of the wave files + */ +public class MetaReader { + public static void main(String[] args) { + if (args.length == 0) { + System.out.println("Illegal command line arguments.\n" + + "Usage MetaReader\n\t MetaReader "); + return; + } + WavHeader wavHeader; + try { + WavHeaderReader wavHeaderReader = new WavHeaderReader(args[0]); + wavHeader = wavHeaderReader.read(); + System.out.println(wavHeader.toString()); + } catch (FileNotFoundException e) { + System.out.println("Error: File " + args[0] + " not found!"); + } catch (IOException e) { + System.out.println("Error: " + e.getMessage()); + } + } +} diff --git a/WavReader/src/main/java/wave/WavHeader.java b/WavReader/src/main/java/wave/WavHeader.java new file mode 100644 index 0000000..e4657ac --- /dev/null +++ b/WavReader/src/main/java/wave/WavHeader.java @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2016. Andrii Tkachenko. https://github.com/tkaczenko/WavReader + */ + +package wave; + +/** + * This class contains a structure of WAVE file. + * + * @see + * Structure of wave format + * + */ +public class WavHeader { + private byte[] chunkID = new byte[4]; + private int chunkSize; + private byte[] format = new byte[4]; + private byte[] subChunk1ID = new byte[4]; + private int subChunk1Size; + private short audioFormat; + private short numChannels; + private int sampleRate; + private int byteRate; + private short blockAlign; + private short bitsPerSample; + private byte[] subChunk2ID = new byte[4]; + private int subChunk2Size; + + @Override + public String toString() { + return "The RIFF chunk desriptor: " + new String(this.getChunkID()) + "\n" + + "Size of this chunk: " + this.getChunkSize() + "\n" + + "Format: " + new String(this.getFormat()) + "\n" + "\n" + + "fmt subchunk: " + new String(this.getSubChunk1ID()) + "\n" + + "Size of this chunk: " + this.getSubChunk1Size() + "\n" + + "Audio format: " + this.getAudioFormat() + "\n" + + "Number of channels: " + this.getNumChannels() + "\n" + + "Sample rate: " + this.getSampleRate() + "\n" + + "Byte rate: " + this.getByteRate() + "\n" + + "Block align: " + this.getBlockAlign() + "\n" + + "Bits per sample: " + this.getBitsPerSample() + "\n" + "\n" + + "data subchunk: " + new String(this.getSubChunk2ID()) + "\n" + + "Size of this chunk: " + this.getSubChunk2Size(); + } + + public byte[] getChunkID() { + return chunkID; + } + + public void setChunkID(byte[] chunkID) { + this.chunkID = chunkID; + } + + public int getChunkSize() { + return chunkSize; + } + + public void setChunkSize(int chunkSize) { + this.chunkSize = chunkSize; + } + + public byte[] getFormat() { + return format; + } + + public void setFormat(byte[] format) { + this.format = format; + } + + public byte[] getSubChunk1ID() { + return subChunk1ID; + } + + public void setSubChunk1ID(byte[] subChunk1ID) { + this.subChunk1ID = subChunk1ID; + } + + public int getSubChunk1Size() { + return subChunk1Size; + } + + public void setSubChunk1Size(int subChunk1Size) { + this.subChunk1Size = subChunk1Size; + } + + public short getAudioFormat() { + return audioFormat; + } + + public void setAudioFormat(short audioFormat) { + this.audioFormat = audioFormat; + } + + public short getNumChannels() { + return numChannels; + } + + public void setNumChannels(short numChannels) { + this.numChannels = numChannels; + } + + public int getSampleRate() { + return sampleRate; + } + + public void setSampleRate(int sampleRate) { + this.sampleRate = sampleRate; + } + + public int getByteRate() { + return byteRate; + } + + public void setByteRate(int byteRate) { + this.byteRate = byteRate; + } + + public short getBlockAlign() { + return blockAlign; + } + + public void setBlockAlign(short blockAlign) { + this.blockAlign = blockAlign; + } + + public short getBitsPerSample() { + return bitsPerSample; + } + + public void setBitsPerSample(short bitsPerSample) { + this.bitsPerSample = bitsPerSample; + } + + public byte[] getSubChunk2ID() { + return subChunk2ID; + } + + public void setSubChunk2ID(byte[] subChunk2ID) { + this.subChunk2ID = subChunk2ID; + } + + public int getSubChunk2Size() { + return subChunk2Size; + } + + public void setSubChunk2Size(int subChunk2Size) { + this.subChunk2Size = subChunk2Size; + } +} \ No newline at end of file diff --git a/WavReader/src/main/java/wave/WavHeaderReader.java b/WavReader/src/main/java/wave/WavHeaderReader.java new file mode 100644 index 0000000..93813c7 --- /dev/null +++ b/WavReader/src/main/java/wave/WavHeaderReader.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2016. Andrii Tkachenko. https://github.com/tkaczenko/WavReader + */ + +package wave; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; + +/** + * Custom wave header reader which use class + * + * @see WavHeader + */ +public class WavHeaderReader { + /** + * Size for the wave header + */ + private static final int HEADER_SIZE = 44; + + /** + * Buffer which contain bytes of the wave header + */ + private byte[] buf = new byte[HEADER_SIZE]; + + /** + * Wave header + */ + private WavHeader header = new WavHeader(); + + /** + * InputStream to the wave file + */ + private InputStream inputStream; + + /** + * Empty constructor + */ + public WavHeaderReader() { + + } + + /** + * Constructor which create own FileInputStream + * + * @param source absolute path to the file + * @throws IOException + */ + public WavHeaderReader(String source) throws IOException { + inputStream = new FileInputStream(source); + } + + /** + * Constructor which use created InputStream + * + * @param inputStream InputStream to the wave file + */ + public WavHeaderReader(InputStream inputStream) { + this.inputStream = inputStream; + } + + /** + * Read wave header of the file + * + * @return wave.WavHeader object + * @throws IOException + * @see WavHeader + */ + public WavHeader read() throws IOException { + int res = inputStream.read(buf); + if (res != HEADER_SIZE) { + throw new IOException("Could not read header."); + } + header.setChunkID(Arrays.copyOfRange(buf, 0, 4)); + if (new String(header.getChunkID()).compareTo("RIFF") != 0) { + throw new IOException("Illegal format."); + } + header.setChunkSize(toInt(4, false)); + header.setFormat(Arrays.copyOfRange(buf, 8, 12)); + header.setSubChunk1ID(Arrays.copyOfRange(buf, 12, 16)); + header.setSubChunk1Size(toInt(16, false)); + header.setAudioFormat(toShort(20, false)); + header.setNumChannels(toShort(22, false)); + header.setSampleRate(toInt(24, false)); + header.setByteRate(toInt(28, false)); + header.setBlockAlign(toShort(32, false)); + header.setBitsPerSample(toShort(34, false)); + header.setSubChunk2ID(Arrays.copyOfRange(buf, 36, 40)); + header.setSubChunk2Size(toInt(40, false)); + return header; + } + + /** + * Convert byte[] array to int number + * + * @param start start position of the buffer + * @param endian true for big-endian + * false for little-endian + * @return converted number + */ + private int toInt(int start, boolean endian) { + int k = (endian) ? 1 : -1; + if (!endian) { + start += 3; + } + return (buf[start] << 24) + (buf[start + k * 1] << 16) + + (buf[start + k * 2] << 8) + buf[start + k * 3]; + } + + /** + * Convert byte[] array to short number + * + * @param start start position of the buffer + * @param endian true for big-endian + * false for little-endian + * @return converted number + */ + private short toShort(int start, boolean endian) { + short k = (endian) ? (short) 1 : -1; + if (!endian) { + start++; + } + return (short) ((buf[start] << 8) + (buf[start + k * 1])); + } + + /** + * Return the buffer which contain wave hader + * + * @return buffer + */ + public byte[] getBuf() { + return buf; + } + + /** + * Return wave.WavHeader object which contain wave header + * + * @return wave.WavHeader object + */ + public WavHeader getHeader() { + return header; + } + + /** + * Return input stream to the wave file + * + * @return Input steam to the wave file + */ + public InputStream getInputStream() { + return inputStream; + } + + public void setInputStream(InputStream inputStream) { + this.inputStream = inputStream; + } + +} diff --git a/interface/build.gradle b/interface/build.gradle index bfcbd74..bbdb210 100644 --- a/interface/build.gradle +++ b/interface/build.gradle @@ -17,6 +17,7 @@ dependencies { // The production code uses the SLF4J logging API at compile time implementation 'org.slf4j:slf4j-api:1.7.25' implementation 'com.google.code.gson:gson:2.8.7' + implementation project(":WavReader") // Declare the dependency for your favourite test framework you want to use in your tests. // TestNG is also supported by the Gradle Test task. Just change the @@ -27,5 +28,12 @@ dependencies { // Define the main class for the application mainClassName = defaultPackage + '.player.Interface' +//mainClassName = defaultPackage + '.localPlayer.Player' -compileJava.dependsOn rootProject.bundleAddOn \ No newline at end of file +compileJava.dependsOn rootProject.bundleAddOn + +run { + systemProperty "java.library.path", file("${project(":player").buildDir}/lib/main/debug").absolutePath +} + +compileJava.dependsOn ':player:linkDebug' \ No newline at end of file diff --git a/interface/src/main/java/edu/regis/universeplayer/localPlayer/AudioFile.java b/interface/src/main/java/edu/regis/universeplayer/localPlayer/AudioFile.java new file mode 100644 index 0000000..d8ddff7 --- /dev/null +++ b/interface/src/main/java/edu/regis/universeplayer/localPlayer/AudioFile.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2021 William Hubbard. All Rights Reserved. + */ + +package edu.regis.universeplayer.localPlayer; + +import java.io.IOException; +import java.io.InputStream; + +import wave.WavHeader; +import wave.WavHeaderReader; + +/** + * Contains information on a WAV audio file, along with a reference to the stream of raw data. + * + * @author William Hubbard + */ +public class AudioFile extends InputStream +{ + /** + * Contains header information. + */ + private final WavHeaderReader header; + /** + * Contains a link to the process reading the audio file. + */ + private final Process process; + /** + * The stream for the actual audio data. + */ + private InputStream stream; + + /** + * Creates an audio file from a stream + * + * @param stream - The stream to create it from. + * @throws IOException - Thrown when an error occurs creating the input stream. + */ + AudioFile(Process stream) throws IOException + { + this.process = stream; + this.stream = stream.getInputStream(); + this.header = new WavHeaderReader(this.stream); + this.header.read(); + } + + public WavHeader getHeader() + { + return this.header.getHeader(); + } + + /** + * Obtains the byte string representation of this header. + * @return A byte string containing this file's header. + */ + public byte[] getByteStream() + { + return this.header.getBuf(); + } + + @Override + public int read() throws IOException + { + return this.stream.read(); + } + + @Override + public int available() throws IOException + { + return this.process.isAlive() ? Integer.MAX_VALUE : super.available(); + } +} diff --git a/interface/src/main/java/edu/regis/universeplayer/localPlayer/Player.java b/interface/src/main/java/edu/regis/universeplayer/localPlayer/Player.java new file mode 100644 index 0000000..be263ae --- /dev/null +++ b/interface/src/main/java/edu/regis/universeplayer/localPlayer/Player.java @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2021 William Hubbard. All Rights Reserved. + */ +package edu.regis.universeplayer.localPlayer; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.LinkedList; + +import wave.WavHeader; + +/** + * This allows for the control of playback of files on the local file system + * + * @author William Hubbard + * @version 0.1 + */ +public class Player +{ + static + { + System.loadLibrary("player"); + } + + private int currentId; + + private AudioFile currentFile; + + /** + * Sets the file currently being used. + * + * @param file - The current audio file. + */ + public void setCurrentFile(AudioFile file) + { + WavHeader header = file.getHeader(); + this.currentFile = file; + // TODO - Ensure that bytes per sample and bits per sample don't bother things + this.currentId = this.setCurrentFile(this.currentFile, header.getNumChannels(), header + .getBitsPerSample(), header.getSampleRate()); + } + + /** + * Updates the file currently being played. + * + * @param stream - A reference to the audio data stream. + * @param numChannels - The number of audio channels contained in the file. + * @param bitsPerSample - The number if bits in every sample. + * @param sampleRate - How many samples need to play every second. + * @return An ID for the current song. + */ + private native int setCurrentFile(InputStream stream, short numChannels, short bitsPerSample, int sampleRate); + + /** + * Saves the current file to + * + * @param file - The file to save to. + */ + public native void save(String file); + + /** + * Saves the current file as a WAVE file somewhere else. This method is meant for testing only. + * + * @param file - The file to save to. + */ + public void saveJava(String file) + { + try (FileOutputStream out = new FileOutputStream(file)) + { + out.write(this.getAudioFile().getByteStream()); + byte[] buffer = new byte[256]; + int read = this.getAudioFile().read(buffer); + while (read > 0) + { + out.write(buffer, 0, read); + read = this.getAudioFile().read(buffer); + } + } + catch (IOException e) + { + e.printStackTrace(); + } + } + + /** + * Obtains the current file. + * + * @return The current audio file we are working with. + */ + public AudioFile getAudioFile() + { + return this.currentFile; + } + + /** + * Plays the current audio file. + */ + public native void play(); + + /** + * Pauses playback for the current audio file. + */ + public native void pause(); + + /** + * Checks to see whether the current audio file is paused. + * + * @return True if there is an audio file present and it is paused, false otherwise. + */ + public native boolean isPaused(); + + /** + * Obtains an input stream for the requested file. + * + * @param file - The file to read + * @return An raw stream for the file + * @throws FileNotFoundException - Thrown should the file not exist. + * @throws IOException - Thrown should an error occur when reading the file + */ + public static AudioFile getAudioStream(File file) throws IOException + { + return new AudioFile(convertFile(file)); + } + + /** + * Converts any audio file to a stream containing WAV audio file data (courtesy of FFMPEG). + * + * @param file - The file to convert + * @return An input stream containing the file data + * @throws FileNotFoundException - Thrown should the file not exist. + * @throws IOException - Thrown should an error occur when reading the file + */ + protected static Process convertFile(File file) throws FileNotFoundException, IOException + { + LinkedList args = new LinkedList<>(); + if (!file.isFile()) + { + throw new FileNotFoundException("Must provide a file"); + } + args.add("ffmpeg"); + args.add("-hide_banner"); + args.add("-loglevel"); + args.add("error"); + args.add("-y"); + args.add("-i"); + args.add(file.getAbsolutePath()); + args.add("-f"); + args.add("wav"); + args.add("pipe:1"); + return Runtime.getRuntime().exec(args.toArray(new String[args.size()])); + } + + public static void main(String[] args) + { + byte[] buffer = new byte[200]; + int len; + File file = new File(args[0]); + try (AudioFile stream = getAudioStream(new File(args[0]))) + { + Player player = new Player(); + player.setCurrentFile(stream); + player.save(args[0] + ".wav"); + } + catch (IOException e) + { + e.printStackTrace(); + } + // Compare JNI vs Java + try (AudioFile stream = getAudioStream(new File(args[0]))) + { + Player player = new Player(); + player.setCurrentFile(stream); + player.saveJava(args[0] + ".orig.wav"); + } + catch (IOException e) + { + e.printStackTrace(); + } + } +} diff --git a/player/build.gradle b/player/build.gradle new file mode 100644 index 0000000..d9cb4c3 --- /dev/null +++ b/player/build.gradle @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 William Hubbard. All Rights Reserved. + */ +import org.gradle.internal.jvm.Jvm + +/* + * There is currently no "C application" plugin, so this build uses the "C++ application" plugin and then reconfigures it + * to build C instead. + */ +plugins { + id 'cpp-library' +} + +library { + binaries.configureEach { CppBinary binary -> + def compileTask = binary.compileTask.get() + compileTask.includes.from("${Jvm.current().javaHome}/include") + + def osFamily = binary.targetPlatform.targetMachine.operatingSystemFamily + if (osFamily.macOs) { + compileTask.includes.from("${Jvm.current().javaHome}/include/darwin") + } else if (osFamily.linux) { + compileTask.includes.from("${Jvm.current().javaHome}/include/linux") + } else if (osFamily.windows) { + compileTask.includes.from("${Jvm.current().javaHome}/include/win32") + } + + compileTask.source.from fileTree(dir: "src/main/c", include: "**/*.c") + + def toolChain = binary.toolChain + if (toolChain instanceof VisualCpp) { + compileTask.compilerArgs.addAll(["/TC"]) + } else if (toolChain instanceof GccCompatibleToolChain) { + compileTask.compilerArgs.addAll(["-x", "c", "-std=c11"]) + } + } +} \ No newline at end of file diff --git a/player/src/main/c/player.c b/player/src/main/c/player.c new file mode 100644 index 0000000..26ba753 --- /dev/null +++ b/player/src/main/c/player.c @@ -0,0 +1,311 @@ +/* + * Copyright (c) 2021 William Hubbard. All Rights Reserved. + */ +#include +#include + +static jclass InputStream; +static jclass AudioStream; +static jmethodID AudioStream_getHeader; +static jmethodID AudioStream_getByteStream; +static jmethodID AudioStream_readInt; +static jmethodID InputStream_readBuffer; + +static jobject currentFile = NULL; + +/** + * This function is used to initialize the static reference to java.io.InputStream class. + * + * @param env - A reference to the JVM. + * @param audio - An InputStream instance + * @return A reference to the InputStream class. + */ +jclass getInputStreamClass(JNIEnv *env) +{ + if (InputStream == 0 ) + { + jclass tempClass = (*env)->FindClass(env, "java/io/InputStream"); + if (tempClass == 0) + { + printf("Could not find class java/io/InputStream"); + return 0; + } + InputStream = (*env)->NewGlobalRef(env, tempClass); + } + return InputStream; +} + +/** + * This function is used to initialize the static reference to the AudioStream class. + * + * @param env - A reference to the JVM. + * @param audio - An AudioStream instance + * @return A reference to the AudioStream class. + */ +jclass getAudioStreamClass(JNIEnv *env, jobject audio) +{ + if (AudioStream == 0 ) + { + jclass tempClass = (*env)->GetObjectClass(env, audio); + if (tempClass == 0) + { + printf("Could not find class edu/regis/universeplayer_localPlayer/AudioFile"); + return 0; + } + AudioStream = (*env)->NewGlobalRef(env, tempClass); + } + return AudioStream; +} + +/** + * This function is used to initialize the static reference to AudioStream's getHeader method. + * + * @param env - A reference to the JVM. + * @param audio - An AudioStream instance + * @return A reference to the getHeader method. + */ +jmethodID getHeaderMethod(JNIEnv *env, jobject audio) +{ + if (AudioStream_getHeader == 0 ) + { + jclass tempClass = getAudioStreamClass(env, audio); + if (tempClass == 0) + { + return 0; + } + AudioStream_getHeader = (*env)->GetMethodID(env, tempClass, "getHeader", "()Lwave/WavHeader;"); + } + return AudioStream_getHeader; +} + +/** + * This function is used to initialize the static reference to AudioStream's getByteStream method. + * + * @param env - A reference to the JVM. + * @param audio - An AudioStream instance + * @return A reference to the getByteStream method. + */ +jmethodID getByteStreamMethod(JNIEnv *env, jobject audio) +{ + if (AudioStream_getByteStream == 0 ) + { + jclass tempClass = getAudioStreamClass(env, audio); + if (tempClass == 0) + { + return 0; + } + AudioStream_getByteStream = (*env)->GetMethodID(env, tempClass, "getByteStream", "()[B"); + } + return AudioStream_getByteStream; +} + +/** + * This function is used to initialize the static references to AudioStream's read methods. + * + * @param env - A reference to the JVM. + * @param audio - An AudioStream instance + * @return A reference to the getByteStream method. + */ +jmethodID getReadMethod(JNIEnv *env, jobject audio) +{ + jclass tempClass; + if (AudioStream_readInt == 0) + { + tempClass = getAudioStreamClass(env, audio); + if (tempClass == 0) + { + printf("Could not find AudioStream class"); + } + else + { + AudioStream_readInt = (*env)->GetMethodID(env, tempClass, "read", "()I"); + printf("Found class AudioStream"); + } + } + if (InputStream_readBuffer == 0 ) + { + tempClass = getInputStreamClass(env); + if (tempClass == 0) + { + printf("Could not find InputStream class"); + } + else + { + InputStream_readBuffer = (*env)->GetMethodID(env, tempClass, "read", "([B)I"); + printf("Found class java/io/InputStream"); + } + } + return AudioStream_readInt; +} + +/** + * Obtains an audio stream's header. + * @param env - A reference to the JVM. + * @param stream - The audio stream to obtain the header from + * @return The header information. + */ +jobject getHeader(JNIEnv *env, jobject stream) +{ + if (AudioStream_getHeader == 0) + { + getHeaderMethod(env, stream); + if (AudioStream_getHeader == 0) + { + return 0; + } + } + jobject returnValue = (*env)->CallObjectMethod(env, stream, AudioStream_getHeader); + return returnValue; +} + +/** + * Obtains the original raw byte data for an audio stream's header. + * @param env - A reference to the JVM. + * @param stream - The audio stream to obtain the header from. + * @return The header information. + */ +jbyteArray getByteStream(JNIEnv *env, jobject stream) +{ + if (AudioStream_getByteStream == 0) + { + getByteStreamMethod(env, stream); + if (AudioStream_getByteStream == 0) + { + return 0; + } + } + jbyteArray returnValue = (jbyteArray) (*env)->CallObjectMethod(env, stream, AudioStream_getByteStream); + return returnValue; +} + +/** + * Reads a single integer from the audio stream. + * @param env - A reference to the JVM. + * @param stream - The audio stream to obtain the header from. + * @return The integer read. + */ +int readInt(JNIEnv *env, jobject stream) +{ + if (AudioStream_readInt == 0) + { + getReadMethod(env, stream); + if (AudioStream_readInt == 0) + { + return 0; + } + } + jint returnValue = (*env)->CallIntMethod(env, stream, AudioStream_readInt); + return (int) returnValue; +} + +/** + * Fills a provided buffer with input from the audio stream. + * @param env - A reference to the JVM. + * @param stream - The audio stream to obtain the header from. + * @param buffer - The buffer to read information to + * @param bufferSize - The size of the buffer + * @return How much information was actually read. + */ +int readBuffer(JNIEnv *env, jobject stream, char* buffer, int bufferSize) +{ + if (InputStream_readBuffer == 0) + { + getReadMethod(env, stream); + if (InputStream_readBuffer == 0) + { + printf("readBuffer method not found\n"); + return 0; + } + if (AudioStream_readInt == 0) + { + printf("readInt method not found\n"); + return 0; + } + } + + jbyteArray jbuffer = (*env)->NewByteArray(env, bufferSize); + jint returnValue = (*env)->CallIntMethod(env, stream, InputStream_readBuffer, jbuffer); + jbyte *jbufferEl = (*env)->GetByteArrayElements(env, jbuffer, 0); + for (int i = 0; i < returnValue; i++) + { + buffer[i] = jbufferEl[i]; + } + (*env)->ReleaseByteArrayElements(env, jbuffer, jbufferEl, 0); + return returnValue; +} + +/** + * Sets the file currently being used + */ +JNIEXPORT jint JNICALL Java_edu_regis_universeplayer_localPlayer_Player_setCurrentFile(JNIEnv *env, jobject obj, jobject stream, jshort numChannels, jshort bitsPerSample, jint sampleRate) +{ + if (currentFile != NULL) + { + (*env)->DeleteLocalRef(env, currentFile); + } + currentFile = (*env)->NewGlobalRef(env, stream); + printf("Setting File\n"); + return 0; +} + +JNIEXPORT void JNICALL Java_edu_regis_universeplayer_localPlayer_Player_save(JNIEnv *env, jobject obj, jstring location) +{ + int BUFFER_SIZE = 256; + char buffer[BUFFER_SIZE]; + int read; + + FILE *output; + + char buf[128]; + const char *locationPath = (*env)->GetStringUTFChars(env, location, 0); + output = fopen(locationPath, "w"); + + if (output == NULL) + { + printf("File %s can't be opened\n", locationPath); + return; + } + + // Obtains header data + jbyteArray headerData = getByteStream(env, currentFile); + jsize headerLength = (*env)->GetArrayLength(env, headerData); + jbyte* header = (*env)->GetByteArrayElements(env, headerData, 0); + char headerBuff[headerLength]; + for (int i = 0; i < headerLength; i++) + { + headerBuff[i] = header[i]; + } + fwrite(headerBuff, headerLength, 1, output); + (*env)->ReleaseByteArrayElements(env, headerData, header, 0); + + // Writes the actual file + read = readBuffer(env, currentFile, headerBuff, BUFFER_SIZE); + while (read > 0) + { + fwrite(headerBuff, read, 1, output); + read = readBuffer(env, currentFile, headerBuff, BUFFER_SIZE); + } + + fclose(output); + (*env)->ReleaseStringUTFChars(env, location, locationPath); + + return; +} + +JNIEXPORT void JNICALL Java_edu_regis_universeplayer_localPlayer_Player_play(JNIEnv *env, jobject obj) +{ + printf("Playing\n"); + return; +} + +JNIEXPORT void JNICALL Java_edu_regis_universeplayer_localPlayer_Player_pause(JNIEnv *env, jobject obj) +{ + printf("Pausing\n"); + return; +} + +JNIEXPORT jboolean JNICALL Java_edu_regis_universeplayer_localPlayer_Player_isPaused(JNIEnv *env, jobject obj) +{ + printf("Is Paused?\n"); + return JNI_FALSE; +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 385f921..2b935ce 100644 --- a/settings.gradle +++ b/settings.gradle @@ -22,3 +22,5 @@ include 'services:webservice' rootProject.name = 'UniversalMusicPlayer' include ':interface' include ':add-on' +include ':player' +include ':WavReader'