2010-08-02 20:34:47 +00:00
|
|
|
/***************************************************************************
|
|
|
|
* __________ __ ___.
|
|
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
|
|
* \/ \/ \/ \/ \/
|
|
|
|
* $Id$
|
|
|
|
*
|
|
|
|
* Copyright (c) 2010 Thomas Martitz
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
|
|
* KIND, either express or implied.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#include <jni.h>
|
|
|
|
#include <stdbool.h>
|
2011-03-16 14:33:55 +00:00
|
|
|
#define _SYSTEM_WITH_JNI /* for getJavaEnvironment */
|
2010-08-02 20:34:47 +00:00
|
|
|
#include <system.h>
|
2010-09-20 17:38:47 +00:00
|
|
|
#include "debug.h"
|
2010-08-02 20:34:47 +00:00
|
|
|
#include "pcm.h"
|
|
|
|
|
2011-03-16 14:33:55 +00:00
|
|
|
extern JNIEnv *env_ptr;
|
|
|
|
|
2010-08-02 20:34:47 +00:00
|
|
|
/* infos about our pcm chunks */
|
|
|
|
static size_t pcm_data_size;
|
|
|
|
static char *pcm_data_start;
|
|
|
|
|
|
|
|
/* cache frequently called methods */
|
|
|
|
static jmethodID play_pause_method;
|
|
|
|
static jmethodID stop_method;
|
|
|
|
static jmethodID set_volume_method;
|
2011-06-05 09:44:57 +00:00
|
|
|
static jmethodID write_method;
|
2011-03-16 15:17:24 +00:00
|
|
|
static jclass RockboxPCM_class;
|
2010-08-02 20:34:47 +00:00
|
|
|
static jobject RockboxPCM_instance;
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2011-06-05 09:44:57 +00:00
|
|
|
* write pcm samples to the hardware. Calls AudioTrack.write directly (which
|
|
|
|
* is usually a blocking call)
|
2010-08-02 20:34:47 +00:00
|
|
|
*
|
2011-06-05 09:44:57 +00:00
|
|
|
* temp_array is not strictly needed as a parameter as we could
|
|
|
|
* create it here, but that would result in frequent garbage collection
|
2010-08-02 20:34:47 +00:00
|
|
|
*
|
|
|
|
* it is called from the PositionMarker callback of AudioTrack
|
|
|
|
**/
|
2011-06-05 09:44:57 +00:00
|
|
|
JNIEXPORT jint JNICALL
|
|
|
|
Java_org_rockbox_RockboxPCM_nativeWrite(JNIEnv *env, jobject this,
|
|
|
|
jbyteArray temp_array, jint max_size)
|
2010-08-02 20:34:47 +00:00
|
|
|
{
|
2011-06-05 09:44:57 +00:00
|
|
|
jint left = max_size;
|
|
|
|
|
|
|
|
if (!pcm_data_size) /* get some initial data */
|
|
|
|
pcm_play_get_more_callback((void**) &pcm_data_start, &pcm_data_size);
|
|
|
|
|
|
|
|
while(left > 0 && pcm_data_size)
|
|
|
|
{
|
|
|
|
jint ret;
|
|
|
|
jsize transfer_size = MIN((size_t)left, pcm_data_size);
|
|
|
|
/* decrement both by the amount we're going to write */
|
|
|
|
pcm_data_size -= transfer_size; left -= transfer_size;
|
|
|
|
(*env)->SetByteArrayRegion(env, temp_array, 0,
|
|
|
|
transfer_size, (jbyte*)pcm_data_start);
|
|
|
|
|
|
|
|
ret = (*env)->CallIntMethod(env, this, write_method,
|
|
|
|
temp_array, 0, transfer_size);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
if (pcm_data_size == 0) /* need new data */
|
|
|
|
pcm_play_get_more_callback((void**)&pcm_data_start, &pcm_data_size);
|
|
|
|
else /* increment data pointer and feed more */
|
|
|
|
pcm_data_start += transfer_size;
|
2010-08-02 20:34:47 +00:00
|
|
|
}
|
2011-06-05 09:44:57 +00:00
|
|
|
return max_size - left;
|
2010-08-02 20:34:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void pcm_play_lock(void)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void pcm_play_unlock(void)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void pcm_dma_apply_settings(void)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void pcm_play_dma_start(const void *addr, size_t size)
|
|
|
|
{
|
|
|
|
pcm_data_start = (char*)addr;
|
|
|
|
pcm_data_size = size;
|
|
|
|
|
|
|
|
pcm_play_dma_pause(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void pcm_play_dma_stop(void)
|
|
|
|
{
|
2011-03-16 14:33:55 +00:00
|
|
|
/* NOTE: due to how pcm_play_get_more_callback() works, this is
|
2011-06-05 09:44:57 +00:00
|
|
|
* possibly called from writeNative(), i.e. another thread.
|
2011-03-16 14:33:55 +00:00
|
|
|
* => We need to discover the env_ptr */
|
|
|
|
JNIEnv* env = getJavaEnvironment();
|
|
|
|
(*env)->CallVoidMethod(env,
|
|
|
|
RockboxPCM_instance,
|
|
|
|
stop_method);
|
2010-08-02 20:34:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void pcm_play_dma_pause(bool pause)
|
|
|
|
{
|
|
|
|
(*env_ptr)->CallVoidMethod(env_ptr,
|
|
|
|
RockboxPCM_instance,
|
|
|
|
play_pause_method,
|
|
|
|
(int)pause);
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t pcm_get_bytes_waiting(void)
|
|
|
|
{
|
|
|
|
return pcm_data_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
const void * pcm_play_dma_get_peak_buffer(int *count)
|
|
|
|
{
|
|
|
|
uintptr_t addr = (uintptr_t)pcm_data_start;
|
|
|
|
*count = pcm_data_size / 4;
|
|
|
|
return (void *)((addr + 3) & ~3);
|
|
|
|
}
|
|
|
|
|
|
|
|
void pcm_play_dma_init(void)
|
|
|
|
{
|
|
|
|
/* in order to have background music playing after leaving the activity,
|
|
|
|
* we need to allocate the PCM object from the Rockbox thread (the Activity
|
|
|
|
* runs in a separate thread because it would otherwise kill us when
|
|
|
|
* stopping it)
|
|
|
|
*
|
|
|
|
* Luckily we only reference the PCM object from here, so it's safe (and
|
|
|
|
* clean) to allocate it here
|
|
|
|
**/
|
|
|
|
JNIEnv e = *env_ptr;
|
|
|
|
/* get the class and its constructor */
|
2011-03-16 15:17:24 +00:00
|
|
|
RockboxPCM_class = e->FindClass(env_ptr, "org/rockbox/RockboxPCM");
|
2010-08-02 20:34:47 +00:00
|
|
|
jmethodID constructor = e->GetMethodID(env_ptr, RockboxPCM_class, "<init>", "()V");
|
|
|
|
/* instance = new RockboxPCM() */
|
2011-03-11 19:23:00 +00:00
|
|
|
RockboxPCM_instance = e->NewObject(env_ptr, RockboxPCM_class, constructor);
|
2010-08-02 20:34:47 +00:00
|
|
|
/* cache needed methods */
|
|
|
|
play_pause_method = e->GetMethodID(env_ptr, RockboxPCM_class, "play_pause", "(Z)V");
|
|
|
|
set_volume_method = e->GetMethodID(env_ptr, RockboxPCM_class, "set_volume", "(I)V");
|
|
|
|
stop_method = e->GetMethodID(env_ptr, RockboxPCM_class, "stop", "()V");
|
2011-06-05 09:44:57 +00:00
|
|
|
write_method = e->GetMethodID(env_ptr, RockboxPCM_class, "write", "([BII)I");
|
2010-08-02 20:34:47 +00:00
|
|
|
/* get initial pcm data, if any */
|
|
|
|
pcm_play_get_more_callback((void*)&pcm_data_start, &pcm_data_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
void pcm_postinit(void)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void pcm_set_mixer_volume(int volume)
|
|
|
|
{
|
|
|
|
(*env_ptr)->CallVoidMethod(env_ptr, RockboxPCM_instance, set_volume_method, volume);
|
|
|
|
}
|
2011-03-14 12:25:48 +00:00
|
|
|
|
2011-03-16 15:17:24 +00:00
|
|
|
/*
|
|
|
|
* release audio resources */
|
|
|
|
void pcm_shutdown(void)
|
|
|
|
{
|
|
|
|
JNIEnv e = *env_ptr;
|
|
|
|
jmethodID release = e->GetMethodID(env_ptr, RockboxPCM_class, "release", "()V");
|
|
|
|
e->CallVoidMethod(env_ptr, RockboxPCM_instance, release);
|
|
|
|
}
|
|
|
|
|
2011-03-14 12:25:48 +00:00
|
|
|
/* Due to limitations of default_event_handler(), parameters gets swallowed when
|
|
|
|
* being posted with queue_broadcast(), so workaround this by caching the last
|
|
|
|
* value.
|
|
|
|
*/
|
|
|
|
static int lastPostedVolume = -1;
|
|
|
|
int hosted_get_volume(void)
|
|
|
|
{
|
|
|
|
return lastPostedVolume;
|
|
|
|
}
|
|
|
|
|
|
|
|
JNIEXPORT void JNICALL
|
|
|
|
Java_org_rockbox_RockboxPCM_postVolumeChangedEvent(JNIEnv *env,
|
|
|
|
jobject this,
|
|
|
|
jint volume)
|
|
|
|
{
|
|
|
|
(void) env;
|
|
|
|
(void) this;
|
|
|
|
|
|
|
|
if (volume != lastPostedVolume)
|
|
|
|
{
|
|
|
|
lastPostedVolume = volume;
|
|
|
|
queue_broadcast(SYS_VOLUME_CHANGED, 0);
|
|
|
|
}
|
|
|
|
}
|