#include #include #include #include #include "com_jogamp_audio_windows_waveout_Mixer.h" static HANDLE event = NULL; static HWAVEOUT output = NULL; // We use only two buffers to keep latency down #define NUM_BUFFERS 2 //#define NUM_BUFFERS 4 // This is about 20 ms of data for WAVE_FORMAT_PCM: // (44100 samples / sec) * (20 ms / 1000 ms) * (2 bytes / sample) * (2 channels) //#define BUFFER_SIZE 3528 // This is about 50 ms of data for WAVE_FORMAT_PCM: // (44100 samples / sec) * (50 ms / 1000 ms) * (2 bytes / sample) * (1 channel) //#define BUFFER_SIZE 4410 // This is about 200 ms of data for WAVE_FORMAT_PCM: // (44100 samples / sec) * (200 ms / 1000 ms) * (2 bytes / sample) * (1 channel) //#define BUFFER_SIZE 17640 // This is about 200 ms of data for WAVE_FORMAT_PCM: // (44100 samples / sec) * (200 ms / 1000 ms) * (2 bytes / sample) * (2 channel) //#define BUFFER_SIZE 35280 // This is about 1000 ms of data for WAVE_FORMAT_PCM: // (44100 samples / sec) * (1000 ms / 1000 ms) * (2 bytes / sample) * (1 channel) //#define BUFFER_SIZE 88200 // This is about 50 ms of data for WAVE_FORMAT_PCM: // (44100 samples / sec) * (50 ms / 1000 ms) * (2 bytes / sample) * (2 channels) //#define BUFFER_SIZE 8820 // This is about 50 ms of data for WAVE_FORMAT_IEEE_FLOAT: // (44100 samples / sec) * (50 ms / 1000 ms) * (4 bytes / sample) * (2 channels) //#define BUFFER_SIZE 17640 // This is about 200 ms of data for WAVE_FORMAT_PCM: // (11025 samples / sec) * (200 ms / 1000 ms) * (2 bytes / sample) * (2 channel) #define BUFFER_SIZE 8820 //#define BUFFER_SIZE 8192 static WAVEHDR** buffers = NULL; void CALLBACK playbackCallback(HWAVEOUT output, UINT msg, DWORD_PTR userData, DWORD_PTR param1, DWORD_PTR param2) { if (msg == WOM_DONE) { WAVEHDR* hdr = (WAVEHDR*) param1; hdr->dwFlags |= WHDR_DONE; SetEvent(event); } } JNIEXPORT jboolean JNICALL Java_com_jogamp_audio_windows_waveout_Mixer_initializeWaveOut (JNIEnv *env, jclass unused, jlong eventObject) { event = (HANDLE) eventObject; // Note the hard requirements on the RawSoundConverter's output format WAVEFORMATEX format; format.wFormatTag = WAVE_FORMAT_PCM; // format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; format.nChannels = 2; // format.nChannels = 1; // format.nSamplesPerSec = 44100; format.nSamplesPerSec = 11025; format.wBitsPerSample = 16; // format.wBitsPerSample = 32; format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8; format.nAvgBytesPerSec = format.nBlockAlign * format.nSamplesPerSec; format.cbSize = 0; MMRESULT res = waveOutOpen(&output, WAVE_MAPPER, &format, /* NULL, */ (DWORD_PTR) &playbackCallback, NULL, // No user data right now /* CALLBACK_NULL */ CALLBACK_FUNCTION); if (res != MMSYSERR_NOERROR) { return JNI_FALSE; } buffers = (WAVEHDR**) calloc(NUM_BUFFERS, sizeof(WAVEHDR)); for (int i = 0; i < NUM_BUFFERS; i++) { char* data = (char*) calloc(BUFFER_SIZE, 1); WAVEHDR* hdr = (WAVEHDR*) calloc(1, sizeof(WAVEHDR)); hdr->lpData = data; hdr->dwBufferLength = BUFFER_SIZE; hdr->dwFlags |= WHDR_DONE; buffers[i] = hdr; } return JNI_TRUE; } JNIEXPORT void JNICALL Java_com_jogamp_audio_windows_waveout_Mixer_shutdownWaveOut (JNIEnv *env, jclass unused) { // writeString("Pausing\n"); waveOutPause(output); // writeString("Resetting\n"); waveOutReset(output); // writeString("Closing output\n"); waveOutClose(output); } JNIEXPORT jlong JNICALL Java_com_jogamp_audio_windows_waveout_Mixer_getNextMixerBuffer (JNIEnv *env, jclass unused) { WAVEHDR* hdr = NULL; for (int i = 0; i < NUM_BUFFERS; i++) { if (buffers[i] != NULL && ((buffers[i]->dwFlags & WHDR_DONE) != 0)) { hdr = buffers[i]; hdr->dwFlags &= ~WHDR_DONE; break; } } return (jlong) hdr; } JNIEXPORT jobject JNICALL Java_com_jogamp_audio_windows_waveout_Mixer_getMixerBufferData (JNIEnv *env, jclass unused, jlong mixerBuffer) { WAVEHDR* hdr = (WAVEHDR*) mixerBuffer; return env->NewDirectByteBuffer(hdr->lpData, hdr->dwBufferLength); } JNIEXPORT jlong JNICALL Java_com_jogamp_audio_windows_waveout_Mixer_getMixerBufferDataAddress (JNIEnv *env, jclass unused, jlong mixerBuffer) { WAVEHDR* hdr = (WAVEHDR*) mixerBuffer; return (jlong) hdr->lpData; } JNIEXPORT jint JNICALL Java_com_jogamp_audio_windows_waveout_Mixer_getMixerBufferDataCapacity (JNIEnv *env, jclass unused, jlong mixerBuffer) { WAVEHDR* hdr = (WAVEHDR*) mixerBuffer; return (jint) hdr->dwBufferLength; } JNIEXPORT jboolean JNICALL Java_com_jogamp_audio_windows_waveout_Mixer_prepareMixerBuffer (JNIEnv *env, jclass unused, jlong mixerBuffer) { MMRESULT res = waveOutPrepareHeader(output, (WAVEHDR*) mixerBuffer, sizeof(WAVEHDR)); if (res == MMSYSERR_NOERROR) { return JNI_TRUE; } return JNI_FALSE; } JNIEXPORT jboolean JNICALL Java_com_jogamp_audio_windows_waveout_Mixer_writeMixerBuffer (JNIEnv *env, jclass unused, jlong mixerBuffer) { MMRESULT res = waveOutWrite(output, (WAVEHDR*) mixerBuffer, sizeof(WAVEHDR)); if (res == MMSYSERR_NOERROR) { waveOutRestart(output); return JNI_TRUE; } return JNI_FALSE; } JNIEXPORT jlong JNICALL Java_com_jogamp_audio_windows_waveout_Mixer_CreateEvent (JNIEnv *env, jclass unused) { return (jlong) CreateEvent(NULL, FALSE, TRUE, NULL); } JNIEXPORT jboolean JNICALL Java_com_jogamp_audio_windows_waveout_Mixer_WaitForSingleObject (JNIEnv *env, jclass unused, jlong eventObject) { DWORD res = WaitForSingleObject((HANDLE) eventObject, INFINITE); if (res == WAIT_OBJECT_0) { return JNI_TRUE; } return JNI_FALSE; } JNIEXPORT void JNICALL Java_com_jogamp_audio_windows_waveout_Mixer_SetEvent (JNIEnv *env, jclass unused, jlong eventObject) { SetEvent((HANDLE) eventObject); } JNIEXPORT void JNICALL Java_com_jogamp_audio_windows_waveout_Mixer_CloseHandle (JNIEnv *env, jclass unused, jlong eventObject) { CloseHandle((HANDLE) eventObject); }