Some more work with playback.
WARNING: UNSTABLE
This commit is contained in:
@@ -16,6 +16,9 @@ library {
|
||||
def compileTask = binary.compileTask.get()
|
||||
compileTask.includes.from("${Jvm.current().javaHome}/include")
|
||||
|
||||
compileTask.source.from fileTree(dir: "src/main/c", include: "interface.h")
|
||||
compileTask.source.from fileTree(dir: "src/main/c", include: "interface.c")
|
||||
compileTask.source.from fileTree(dir: "src/main/c", include: "player.h")
|
||||
compileTask.source.from fileTree(dir: "src/main/c", include: "player.c")
|
||||
|
||||
def osFamily = binary.targetPlatform.targetMachine.operatingSystemFamily
|
||||
@@ -28,6 +31,7 @@ library {
|
||||
} else if (osFamily.windows) {
|
||||
compileTask.includes.from("${Jvm.current().javaHome}/include/win32")
|
||||
compileTask.source.from fileTree(dir: "src/main/c", include: "player_win.c")
|
||||
compileTask.source.from fileTree(dir: "src/main/c", include: "windows_mutex.c")
|
||||
}
|
||||
|
||||
def toolChain = binary.toolChain
|
||||
|
||||
259
player/src/main/c/interface.c
Normal file
259
player/src/main/c/interface.c
Normal file
@@ -0,0 +1,259 @@
|
||||
#include "interface.h"
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
fprintf(stderr, "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)
|
||||
{
|
||||
fprintf(stderr, "Could not find class edu/regis/universeplayer_localPlayer/AudioFile");
|
||||
return 0;
|
||||
}
|
||||
AudioStream = (*env)->NewGlobalRef(env, tempClass);
|
||||
}
|
||||
return AudioStream;
|
||||
}
|
||||
|
||||
jclass getHeaderClass(JNIEnv *env)
|
||||
{
|
||||
if (Header == 0)
|
||||
{
|
||||
jclass tempClass = (*env)->FindClass(env, "wave/WavHeader");
|
||||
if (tempClass == 0)
|
||||
{
|
||||
fprintf(stderr, "Could not find wave/WavHeader");
|
||||
return 0;
|
||||
}
|
||||
Header = (*env)->NewGlobalRef(env, tempClass);
|
||||
}
|
||||
return Header;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
fprintf(stderr, "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)
|
||||
{
|
||||
fprintf(stderr, "Could not find InputStream class");
|
||||
}
|
||||
else
|
||||
{
|
||||
InputStream_readBuffer = (*env)->GetMethodID(env, tempClass, "read", "([B)I");
|
||||
printf("Found class java/io/InputStream");
|
||||
}
|
||||
}
|
||||
return AudioStream_readInt;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is used to initialize the static reference to WavHeader's various methods.
|
||||
*
|
||||
* @param env - A reference to the JVM.
|
||||
*/
|
||||
void getHeaderMethods(JNIEnv *env)
|
||||
{
|
||||
jclass tempClass = getHeaderClass(env);
|
||||
if (tempClass == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (Header_getChunkId == 0)
|
||||
{
|
||||
Header_getChunkId = (*env)->GetMethodID(env, tempClass, "getChunkID", "()[B");
|
||||
if (Header_getChunkId == 0)
|
||||
{
|
||||
fprintf(stderr, "Could not find wave/WavHeader#getChunkID\n");
|
||||
}
|
||||
}
|
||||
if (Header_getChunkSize == 0)
|
||||
{
|
||||
Header_getChunkSize = (*env)->GetMethodID(env, tempClass, "getChunkSize", "()I");
|
||||
if (Header_getChunkSize == 0)
|
||||
{
|
||||
fprintf(stderr, "Could not find wave/WavHeader#getChunkSize\n");
|
||||
}
|
||||
}
|
||||
if (Header_getFormat == 0)
|
||||
{
|
||||
Header_getFormat = (*env)->GetMethodID(env, tempClass, "getFormat", "()[B");
|
||||
if (Header_getFormat == 0)
|
||||
{
|
||||
fprintf(stderr, "Could not find wave/WavHeader#getFormat\n");
|
||||
}
|
||||
}
|
||||
if (Header_getSubChunk1ID == 0)
|
||||
{
|
||||
Header_getSubChunk1ID = (*env)->GetMethodID(env, tempClass, "getSubChunk1ID", "()[B");
|
||||
if (Header_getSubChunk1ID == 0)
|
||||
{
|
||||
fprintf(stderr, "Could not find wave/WavHeader#getSubChunk1ID\n");
|
||||
}
|
||||
}
|
||||
if (Header_getSubChunk1Size == 0)
|
||||
{
|
||||
Header_getSubChunk1Size = (*env)->GetMethodID(env, tempClass, "getSubChunk1Size", "()I");
|
||||
if (Header_getSubChunk1Size == 0)
|
||||
{
|
||||
fprintf(stderr, "Could not find wave/WavHeader#getSubChunk1Size\n");
|
||||
}
|
||||
}
|
||||
if (Header_getAudioFormat == 0)
|
||||
{
|
||||
Header_getAudioFormat = (*env)->GetMethodID(env, tempClass, "getAudioFormat", "()S");
|
||||
if (Header_getAudioFormat == 0)
|
||||
{
|
||||
fprintf(stderr, "Could not find wave/WavHeader#getAudioFormat\n");
|
||||
}
|
||||
}
|
||||
if (Header_getNumChannels == 0)
|
||||
{
|
||||
Header_getNumChannels = (*env)->GetMethodID(env, tempClass, "getNumChannels", "()S");
|
||||
if (Header_getNumChannels == 0)
|
||||
{
|
||||
fprintf(stderr, "Could not find wave/WavHeader#getNumChannels\n");
|
||||
}
|
||||
}
|
||||
if (Header_getSampleRate == 0)
|
||||
{
|
||||
Header_getSampleRate = (*env)->GetMethodID(env, tempClass, "getSampleRate", "()I");
|
||||
if (Header_getSampleRate == 0)
|
||||
{
|
||||
fprintf(stderr, "Could not find wave/WavHeader#getSampleRate\n");
|
||||
}
|
||||
}
|
||||
if (Header_getByteRate == 0)
|
||||
{
|
||||
Header_getByteRate = (*env)->GetMethodID(env, tempClass, "getByteRate", "()I");
|
||||
if (Header_getByteRate == 0)
|
||||
{
|
||||
fprintf(stderr, "Could not find wave/WavHeader#getByteRate\n");
|
||||
}
|
||||
}
|
||||
if (Header_getBlockAlign == 0)
|
||||
{
|
||||
Header_getBlockAlign = (*env)->GetMethodID(env, tempClass, "getBlockAlign", "()S");
|
||||
if (Header_getBlockAlign == 0)
|
||||
{
|
||||
fprintf(stderr, "Could not find wave/WavHeader#getBlockAlign\n");
|
||||
}
|
||||
}
|
||||
if (Header_getBitsPerSample == 0)
|
||||
{
|
||||
Header_getBitsPerSample = (*env)->GetMethodID(env, tempClass, "getBitsPerSample", "()S");
|
||||
if (Header_getBitsPerSample == 0)
|
||||
{
|
||||
fprintf(stderr, "Could not find wave/WavHeader#getBitsPerSample\n");
|
||||
}
|
||||
}
|
||||
if (Header_getSubChunk2ID == 0)
|
||||
{
|
||||
Header_getSubChunk2ID = (*env)->GetMethodID(env, tempClass, "getSubChunk2ID", "()[B");
|
||||
if (Header_getSubChunk2ID == 0)
|
||||
{
|
||||
fprintf(stderr, "Could not find wave/WavHeader#getSubChunk2ID\n");
|
||||
}
|
||||
}
|
||||
if (Header_getSubChunk2Size == 0)
|
||||
{
|
||||
Header_getSubChunk2Size = (*env)->GetMethodID(env, tempClass, "getSubChunk2Size", "()I");
|
||||
if (Header_getSubChunk2Size == 0)
|
||||
{
|
||||
fprintf(stderr, "Could not find wave/WavHeader#getSubChunk2Size\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
88
player/src/main/c/interface.h
Normal file
88
player/src/main/c/interface.h
Normal file
@@ -0,0 +1,88 @@
|
||||
#ifndef INTERFACE_H
|
||||
#define INTERFACE_H
|
||||
|
||||
#include <jni.h>
|
||||
#include <stdio.h>
|
||||
|
||||
typedef struct {
|
||||
short numChannels;
|
||||
short bytesPerChannel;
|
||||
int sampleRate;
|
||||
} HeaderData;
|
||||
|
||||
static jclass InputStream;
|
||||
static jclass AudioStream;
|
||||
static jclass Header;
|
||||
static jmethodID AudioStream_getHeader;
|
||||
static jmethodID AudioStream_getByteStream;
|
||||
static jmethodID AudioStream_readInt;
|
||||
static jmethodID InputStream_readBuffer;
|
||||
static jmethodID Header_getChunkId;
|
||||
static jmethodID Header_getChunkSize;
|
||||
static jmethodID Header_getFormat;
|
||||
static jmethodID Header_getSubChunk1ID;
|
||||
static jmethodID Header_getSubChunk1Size;
|
||||
static jmethodID Header_getAudioFormat;
|
||||
static jmethodID Header_getNumChannels;
|
||||
static jmethodID Header_getSampleRate;
|
||||
static jmethodID Header_getByteRate;
|
||||
static jmethodID Header_getBlockAlign;
|
||||
static jmethodID Header_getBitsPerSample;
|
||||
static jmethodID Header_getSubChunk2ID;
|
||||
static jmethodID Header_getSubChunk2Size;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
jclass getHeaderClass(JNIEnv *env);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* This function is used to initialize the static reference to WavHeader's various methods.
|
||||
*
|
||||
* @param env - A reference to the JVM.
|
||||
*/
|
||||
void getHeaderMethods(JNIEnv *env);
|
||||
|
||||
#endif /* INTERFACE_H */
|
||||
@@ -1,297 +1,20 @@
|
||||
/*
|
||||
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
||||
*/
|
||||
#include <jni.h>
|
||||
#include <stdio.h>
|
||||
#include "interface.h"
|
||||
#include "player.h"
|
||||
|
||||
typedef struct {
|
||||
short numChannels;
|
||||
short bytesPerChannel;
|
||||
int sampleRate;
|
||||
} HeaderData;
|
||||
|
||||
static jclass InputStream;
|
||||
static jclass AudioStream;
|
||||
static jclass Header;
|
||||
static jmethodID AudioStream_getHeader;
|
||||
static jmethodID AudioStream_getByteStream;
|
||||
static jmethodID AudioStream_readInt;
|
||||
static jmethodID InputStream_readBuffer;
|
||||
static jmethodID Header_getChunkId;
|
||||
static jmethodID Header_getChunkSize;
|
||||
static jmethodID Header_getFormat;
|
||||
static jmethodID Header_getSubChunk1ID;
|
||||
static jmethodID Header_getSubChunk1Size;
|
||||
static jmethodID Header_getAudioFormat;
|
||||
static jmethodID Header_getNumChannels;
|
||||
static jmethodID Header_getSampleRate;
|
||||
static jmethodID Header_getByteRate;
|
||||
static jmethodID Header_getBlockAlign;
|
||||
static jmethodID Header_getBitsPerSample;
|
||||
static jmethodID Header_getSubChunk2ID;
|
||||
static jmethodID Header_getSubChunk2Size;
|
||||
play_item_t play_list_head = {
|
||||
0, /* play_id */
|
||||
SA_CLEAR, /* stop_flag */
|
||||
NULL, /* prev_item */
|
||||
NULL, /* next_item */
|
||||
NULL /* mutex */
|
||||
};
|
||||
|
||||
static jobject currentFile = NULL;
|
||||
static HeaderData currentHeader;
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
fprintf(stderr, "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)
|
||||
{
|
||||
fprintf(stderr, "Could not find class edu/regis/universeplayer_localPlayer/AudioFile");
|
||||
return 0;
|
||||
}
|
||||
AudioStream = (*env)->NewGlobalRef(env, tempClass);
|
||||
}
|
||||
return AudioStream;
|
||||
}
|
||||
|
||||
jclass getHeaderClass(JNIEnv *env)
|
||||
{
|
||||
if (Header == 0)
|
||||
{
|
||||
jclass tempClass = (*env)->FindClass(env, "wave/WavHeader");
|
||||
if (tempClass == 0)
|
||||
{
|
||||
fprintf(stderr, "Could not find wave/WavHeader");
|
||||
return 0;
|
||||
}
|
||||
Header = (*env)->NewGlobalRef(env, tempClass);
|
||||
}
|
||||
return Header;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
fprintf(stderr, "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)
|
||||
{
|
||||
fprintf(stderr, "Could not find InputStream class");
|
||||
}
|
||||
else
|
||||
{
|
||||
InputStream_readBuffer = (*env)->GetMethodID(env, tempClass, "read", "([B)I");
|
||||
printf("Found class java/io/InputStream");
|
||||
}
|
||||
}
|
||||
return AudioStream_readInt;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is used to initialize the static reference to WavHeader's various methods.
|
||||
*
|
||||
* @param env - A reference to the JVM.
|
||||
*/
|
||||
void getHeaderMethods(JNIEnv *env)
|
||||
{
|
||||
jclass tempClass = getHeaderClass(env);
|
||||
if (tempClass == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (Header_getChunkId == 0)
|
||||
{
|
||||
Header_getChunkId = (*env)->GetMethodID(env, tempClass, "getChunkID", "()[B");
|
||||
if (Header_getChunkId == 0)
|
||||
{
|
||||
fprintf(stderr, "Could not find wave/WavHeader#getChunkID\n");
|
||||
}
|
||||
}
|
||||
if (Header_getChunkSize == 0)
|
||||
{
|
||||
Header_getChunkSize = (*env)->GetMethodID(env, tempClass, "getChunkSize", "()I");
|
||||
if (Header_getChunkSize == 0)
|
||||
{
|
||||
fprintf(stderr, "Could not find wave/WavHeader#getChunkSize\n");
|
||||
}
|
||||
}
|
||||
if (Header_getFormat == 0)
|
||||
{
|
||||
Header_getFormat = (*env)->GetMethodID(env, tempClass, "getFormat", "()[B");
|
||||
if (Header_getFormat == 0)
|
||||
{
|
||||
fprintf(stderr, "Could not find wave/WavHeader#getFormat\n");
|
||||
}
|
||||
}
|
||||
if (Header_getSubChunk1ID == 0)
|
||||
{
|
||||
Header_getSubChunk1ID = (*env)->GetMethodID(env, tempClass, "getSubChunk1ID", "()[B");
|
||||
if (Header_getSubChunk1ID == 0)
|
||||
{
|
||||
fprintf(stderr, "Could not find wave/WavHeader#getSubChunk1ID\n");
|
||||
}
|
||||
}
|
||||
if (Header_getSubChunk1Size == 0)
|
||||
{
|
||||
Header_getSubChunk1Size = (*env)->GetMethodID(env, tempClass, "getSubChunk1Size", "()I");
|
||||
if (Header_getSubChunk1Size == 0)
|
||||
{
|
||||
fprintf(stderr, "Could not find wave/WavHeader#getSubChunk1Size\n");
|
||||
}
|
||||
}
|
||||
if (Header_getAudioFormat == 0)
|
||||
{
|
||||
Header_getAudioFormat = (*env)->GetMethodID(env, tempClass, "getAudioFormat", "()S");
|
||||
if (Header_getAudioFormat == 0)
|
||||
{
|
||||
fprintf(stderr, "Could not find wave/WavHeader#getAudioFormat\n");
|
||||
}
|
||||
}
|
||||
if (Header_getNumChannels == 0)
|
||||
{
|
||||
Header_getNumChannels = (*env)->GetMethodID(env, tempClass, "getNumChannels", "()S");
|
||||
if (Header_getNumChannels == 0)
|
||||
{
|
||||
fprintf(stderr, "Could not find wave/WavHeader#getNumChannels\n");
|
||||
}
|
||||
}
|
||||
if (Header_getSampleRate == 0)
|
||||
{
|
||||
Header_getSampleRate = (*env)->GetMethodID(env, tempClass, "getSampleRate", "()I");
|
||||
if (Header_getSampleRate == 0)
|
||||
{
|
||||
fprintf(stderr, "Could not find wave/WavHeader#getSampleRate\n");
|
||||
}
|
||||
}
|
||||
if (Header_getByteRate == 0)
|
||||
{
|
||||
Header_getByteRate = (*env)->GetMethodID(env, tempClass, "getByteRate", "()I");
|
||||
if (Header_getByteRate == 0)
|
||||
{
|
||||
fprintf(stderr, "Could not find wave/WavHeader#getByteRate\n");
|
||||
}
|
||||
}
|
||||
if (Header_getBlockAlign == 0)
|
||||
{
|
||||
Header_getBlockAlign = (*env)->GetMethodID(env, tempClass, "getBlockAlign", "()S");
|
||||
if (Header_getBlockAlign == 0)
|
||||
{
|
||||
fprintf(stderr, "Could not find wave/WavHeader#getBlockAlign\n");
|
||||
}
|
||||
}
|
||||
if (Header_getBitsPerSample == 0)
|
||||
{
|
||||
Header_getBitsPerSample = (*env)->GetMethodID(env, tempClass, "getBitsPerSample", "()S");
|
||||
if (Header_getBitsPerSample == 0)
|
||||
{
|
||||
fprintf(stderr, "Could not find wave/WavHeader#getBitsPerSample\n");
|
||||
}
|
||||
}
|
||||
if (Header_getSubChunk2ID == 0)
|
||||
{
|
||||
Header_getSubChunk2ID = (*env)->GetMethodID(env, tempClass, "getSubChunk2ID", "()[B");
|
||||
if (Header_getSubChunk2ID == 0)
|
||||
{
|
||||
fprintf(stderr, "Could not find wave/WavHeader#getSubChunk2ID\n");
|
||||
}
|
||||
}
|
||||
if (Header_getSubChunk2Size == 0)
|
||||
{
|
||||
Header_getSubChunk2Size = (*env)->GetMethodID(env, tempClass, "getSubChunk2Size", "()I");
|
||||
if (Header_getSubChunk2Size == 0)
|
||||
{
|
||||
fprintf(stderr, "Could not find wave/WavHeader#getSubChunk2Size\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains an audio stream's header.
|
||||
* @param env - A reference to the JVM.
|
||||
@@ -409,7 +132,7 @@ HeaderData readHeader(JNIEnv *env, jobject header, HeaderData *headerStruct)
|
||||
/**
|
||||
* 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)
|
||||
JNIEXPORT jint JNICALL Java_edu_regis_universeplayer_localPlayer_LocalPlayer_setCurrentFile(JNIEnv *env, jobject obj, jobject stream, jshort numChannels, jshort bitsPerSample, jint sampleRate)
|
||||
{
|
||||
jobject header;
|
||||
if (currentFile != NULL)
|
||||
@@ -423,7 +146,7 @@ JNIEXPORT jint JNICALL Java_edu_regis_universeplayer_localPlayer_Player_setCurre
|
||||
return updatePlayer(env, obj, stream, numChannels, bitsPerSample, sampleRate);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_edu_regis_universeplayer_localPlayer_Player_save(JNIEnv *env, jobject obj, jstring location)
|
||||
JNIEXPORT void JNICALL Java_edu_regis_universeplayer_localPlayer_LocalPlayer_save(JNIEnv *env, jobject obj, jstring location)
|
||||
{
|
||||
const int BUFFER_SIZE = 256;
|
||||
/*
|
||||
@@ -471,19 +194,19 @@ JNIEXPORT void JNICALL Java_edu_regis_universeplayer_localPlayer_Player_save(JNI
|
||||
return;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_edu_regis_universeplayer_localPlayer_Player_play(JNIEnv *env, jobject obj)
|
||||
JNIEXPORT void JNICALL Java_edu_regis_universeplayer_localPlayer_LocalPlayer_playSong(JNIEnv *env, jobject obj)
|
||||
{
|
||||
printf("Playing\n");
|
||||
return;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_edu_regis_universeplayer_localPlayer_Player_pause(JNIEnv *env, jobject obj)
|
||||
JNIEXPORT void JNICALL Java_edu_regis_universeplayer_localPlayer_LocalPlayer_pauseSong(JNIEnv *env, jobject obj)
|
||||
{
|
||||
printf("Pausing\n");
|
||||
return;
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL Java_edu_regis_universeplayer_localPlayer_Player_isPaused(JNIEnv *env, jobject obj)
|
||||
JNIEXPORT jboolean JNICALL Java_edu_regis_universeplayer_localPlayer_LocalPlayer_isSongPaused(JNIEnv *env, jobject obj)
|
||||
{
|
||||
printf("Is Paused?\n");
|
||||
return JNI_FALSE;
|
||||
|
||||
65
player/src/main/c/player.h
Normal file
65
player/src/main/c/player.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
Simpleaudio Python Extension
|
||||
Copyright (C) 2015, Joe Hamilton
|
||||
MIT License (see LICENSE.txt)
|
||||
*/
|
||||
|
||||
#ifndef SIMPLEAUDIO_H
|
||||
#define SIMPLEAUDIO_H
|
||||
|
||||
#include <jni.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define SA_ERR_STR_LEN (256)
|
||||
|
||||
#define SA_CLEAR (0)
|
||||
#define SA_STOP (1)
|
||||
|
||||
enum {
|
||||
NOT_LAST_ITEM = 0,
|
||||
LAST_ITEM = 1
|
||||
};
|
||||
|
||||
typedef unsigned long long play_id_t;
|
||||
|
||||
/* linked list structure used to track the active playback items/threads */
|
||||
typedef struct play_item_s {
|
||||
/* the play_id of the list head is used to store the next play_id value
|
||||
used by a new play list item */
|
||||
play_id_t play_id;
|
||||
int stop_flag;
|
||||
struct play_item_s* prev_item;
|
||||
struct play_item_s* next_item;
|
||||
/* the mutex of the list head is used as a 'global' mutex for modifying
|
||||
and accessing the list itself */
|
||||
void* mutex;
|
||||
} play_item_t;
|
||||
|
||||
typedef struct {
|
||||
jobject buffer_obj;
|
||||
void* handle;
|
||||
int used_bytes;
|
||||
int len_bytes;
|
||||
int num_buffers;
|
||||
int frame_size;
|
||||
int buffer_size;
|
||||
play_item_t* play_list_item;
|
||||
void* list_mutex;
|
||||
} audio_blob_t;
|
||||
|
||||
jint* play_os(jobject buffer_obj, int len_samples, int num_channels, int bytes_per_chan, int sample_rate, play_item_t* play_list_head, int latency_us);
|
||||
|
||||
void delete_list_item(play_item_t* play_item);
|
||||
play_item_t* new_list_item(play_item_t* list_head);
|
||||
|
||||
void destroy_audio_blob(audio_blob_t* audio_blob);
|
||||
audio_blob_t* create_audio_blob(void);
|
||||
|
||||
int get_buffer_size(int latency_us, int sample_rate, int frame_size);
|
||||
|
||||
void* create_mutex(void);
|
||||
void destroy_mutex(void* mutex);
|
||||
void grab_mutex(void* mutex);
|
||||
void release_mutex(void* mutex);
|
||||
|
||||
#endif /* SIMPLEAUDIO_H */
|
||||
@@ -1,11 +1,112 @@
|
||||
/*
|
||||
* Copyright (c) 2021 William Hubbard. All Rights Reserved.
|
||||
*/
|
||||
#include <jni.h>
|
||||
#include <stdio.h>
|
||||
Simpleaudio Python Extension
|
||||
Copyright (C) 2015, Joe Hamilton
|
||||
MIT License (see LICENSE.txt)
|
||||
*/
|
||||
#include "simpleaudio.h"
|
||||
#include <Windows.h>
|
||||
#include <mmreg.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int updatePlayer(JNIEnv *env, jobject obj, jobject stream, jshort numChannels, jshort bitsPerSample, jint sampleRate)
|
||||
{
|
||||
printf("Updating File\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
jint play_os(jobject buffer_obj, int len_samples, int num_channels, int bytes_per_chan, int sample_rate, play_item_t* play_list_head, int latency_us)
|
||||
{
|
||||
char err_msg_buf[SA_ERR_STR_LEN];
|
||||
char sys_msg_buf[SA_ERR_STR_LEN / 2];
|
||||
audio_blob_t* audio_blob;
|
||||
|
||||
WAVEFORMATEX audio_format;
|
||||
MMRESULT result;
|
||||
HANDLE thread_handle = NULL;
|
||||
DWORD thread_id;
|
||||
int bytes_per_frame = bytes_per_chan * num_channels;
|
||||
WAVEHDR* temp_wave_hdr;
|
||||
int buffer_size;
|
||||
int i;
|
||||
|
||||
DBG_PLAY_OS_CALL
|
||||
|
||||
buffer_size = get_buffer_size(latency_us / NUM_BUFS, sample_rate, bytes_per_chan * num_channels);
|
||||
|
||||
audio_blob = create_audio_blob();
|
||||
audio_blob->buffer_obj = buffer_obj;
|
||||
audio_blob->list_mutex = play_list_head->mutex;
|
||||
audio_blob->len_bytes = len_samples * bytes_per_frame;
|
||||
audio_blob->num_buffers = NUM_BUFS;
|
||||
|
||||
/* setup the linked list item for this playback buffer */
|
||||
grab_mutex(play_list_head->mutex);
|
||||
audio_blob->play_list_item = new_list_item(play_list_head);
|
||||
release_mutex(play_list_head->mutex);
|
||||
|
||||
/* windows audio device and format headers setup */
|
||||
if (bytes_per_chan < 4) {
|
||||
audio_format.wFormatTag = WAVE_FORMAT_PCM;
|
||||
} else {
|
||||
audio_format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
|
||||
}
|
||||
audio_format.nChannels = num_channels;
|
||||
|
||||
audio_format.nSamplesPerSec = sample_rate;
|
||||
audio_format.nBlockAlign = bytes_per_frame;
|
||||
/* per MSDN WAVEFORMATEX documentation */
|
||||
audio_format.nAvgBytesPerSec = audio_format.nSamplesPerSec * audio_format.nBlockAlign;
|
||||
audio_format.wBitsPerSample = bytes_per_chan * 8;
|
||||
audio_format.cbSize = 0;
|
||||
|
||||
/* create the cleanup thread so we can return after calling waveOutWrite
|
||||
SEE :http://msdn.microsoft.com/en-us/library/windows/desktop/ms682516(v=vs.85).aspx
|
||||
*/
|
||||
thread_handle = CreateThread(NULL, 0, bufferThread, audio_blob, 0, &thread_id);
|
||||
if (thread_handle != NULL) {
|
||||
/* Close so we don't leak handles - similar to detatched POSIX threads */
|
||||
CloseHandle(thread_handle);
|
||||
} else {
|
||||
DWORD lastError = GetLastError();
|
||||
/* lang code : US En */
|
||||
FormatMessage((FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS), NULL, lastError, 0x0409, sys_msg_buf, SYS_STR_LEN, NULL);
|
||||
WIN_EXCEPTION("Failed to start cleanup thread.", 0, sys_msg_buf, err_msg_buf);
|
||||
|
||||
destroy_audio_blob(audio_blob);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* open a handle to the default audio device */
|
||||
result = waveOutOpen((HWAVEOUT*)&audio_blob->handle, WAVE_MAPPER, &audio_format, thread_id, 0, CALLBACK_THREAD);
|
||||
if (result != MMSYSERR_NOERROR) {
|
||||
waveOutGetErrorText(result, sys_msg_buf, SYS_STR_LEN);
|
||||
WIN_EXCEPTION("Failed to open audio device.", result, sys_msg_buf, err_msg_buf);
|
||||
|
||||
PostThreadMessage(thread_id, WM_QUIT, 0, 0);
|
||||
destroy_audio_blob(audio_blob);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dbg1("allocating %d buffers of %d bytes\n", NUM_BUFS, buffer_size);
|
||||
|
||||
for (i = 0; i < NUM_BUFS; i++) {
|
||||
temp_wave_hdr = PyMem_Malloc(sizeof(WAVEHDR));
|
||||
memset(temp_wave_hdr, 0, sizeof(WAVEHDR));
|
||||
temp_wave_hdr->lpData = PyMem_Malloc(buffer_size);
|
||||
temp_wave_hdr->dwBufferLength = buffer_size;
|
||||
|
||||
result = fill_buffer(temp_wave_hdr, audio_blob);
|
||||
if (result != MMSYSERR_NOERROR) {
|
||||
waveOutGetErrorText(result, sys_msg_buf, SYS_STR_LEN);
|
||||
WIN_EXCEPTION("Failed to buffer audio.", result, sys_msg_buf, err_msg_buf);
|
||||
|
||||
PostThreadMessage(thread_id, WM_QUIT, 0, 0);
|
||||
waveOutUnprepareHeader(audio_blob->handle, temp_wave_hdr, sizeof(WAVEHDR));
|
||||
waveOutClose(audio_blob->handle);
|
||||
destroy_audio_blob(audio_blob);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return PyLong_FromUnsignedLongLong(audio_blob->play_list_item->play_id);
|
||||
}
|
||||
27
player/src/main/c/windows_mutex.c
Normal file
27
player/src/main/c/windows_mutex.c
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
Simpleaudio Python Extension
|
||||
Copyright (C) 2015, Joe Hamilton
|
||||
MIT License (see LICENSE.txt)
|
||||
*/
|
||||
|
||||
#include "player.h"
|
||||
#include <stdlib.h>
|
||||
#include <Windows.h>
|
||||
|
||||
void* create_mutex() {
|
||||
void* mutex;
|
||||
mutex = (void*)CreateMutex(NULL, FALSE, NULL);
|
||||
return mutex;
|
||||
}
|
||||
|
||||
void destroy_mutex(void* mutex) {
|
||||
CloseHandle((HANDLE)mutex);
|
||||
}
|
||||
|
||||
void grab_mutex(void* mutex) {
|
||||
WaitForSingleObject((HANDLE)mutex, INFINITE);
|
||||
}
|
||||
|
||||
void release_mutex(void* mutex) {
|
||||
ReleaseMutex((HANDLE)mutex);
|
||||
}
|
||||
Reference in New Issue
Block a user