Adds basic support for native APIs for a local song player.

This commit is contained in:
Markil3
2021-07-17 07:20:14 -06:00
parent 704ab06f85
commit 94ffd80c8d
12 changed files with 968 additions and 1 deletions

View File

@@ -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
compileJava.dependsOn rootProject.bundleAddOn
run {
systemProperty "java.library.path", file("${project(":player").buildDir}/lib/main/debug").absolutePath
}
compileJava.dependsOn ':player:linkDebug'

View File

@@ -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();
}
}

View File

@@ -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<String> 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();
}
}
}