Pastebin launched a little side project called VERYVIRAL.com, check it out ;-) Want more features on Pastebin? Sign Up, it's FREE!
Guest

Untitled

By: a guest on May 31st, 2011  |  syntax: C  |  size: 7.33 KB  |  views: 29  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. /***************************************************************************
  2.  *             __________               __   ___.
  3.  *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
  4.  *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
  5.  *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
  6.  *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
  7.  *                     \/            \/     \/    \/            \/
  8.  * $Id: pcm-android.c 29826 2011-05-05 19:20:58Z bluebrother $
  9.  *
  10.  * Copyright (c) 2010 Thomas Martitz
  11.  *
  12.  * This program is free software; you can redistribute it and/or
  13.  * modify it under the terms of the GNU General Public License
  14.  * as published by the Free Software Foundation; either version 2
  15.  * of the License, or (at your option) any later version.
  16.  *
  17.  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
  18.  * KIND, either express or implied.
  19.  *
  20.  ****************************************************************************/
  21.  
  22. #include <jni.h>
  23. #include <stdbool.h>
  24. #define _SYSTEM_WITH_JNI /* for getJavaEnvironment */
  25. #include <system.h>
  26. #include <pthread.h>
  27. #include "debug.h"
  28. #include "pcm.h"
  29.  
  30. extern JNIEnv *env_ptr;
  31.  
  32. /* infos about our pcm chunks */
  33. static size_t  pcm_data_size;
  34. static char   *pcm_data_start;
  35. static int     audio_locked = 0;
  36. static struct pthread_mutex audio_lock_mutex = PTHREAD_MUTEX_INITIALIZER;
  37.  
  38. /* cache frequently called methods */
  39. static jmethodID play_pause_method;
  40. static jmethodID stop_method;
  41. static jmethodID set_volume_method;
  42. static jclass    RockboxPCM_class;
  43. static jobject   RockboxPCM_instance;
  44.  
  45.  
  46. /*
  47.  * mutex lock/unlock wrappers neatness' sake
  48.  */
  49. static inline void lock_audio(void)
  50. {
  51.     pthread_mutex_lock(&audio_lock_mutex);
  52. }
  53.  
  54. static inline void unlock_audio(void)
  55. {
  56.     pthread_mutex_unlock(&audio_lock_mutex);
  57. }
  58.  
  59.  
  60. /*
  61.  * transfer our raw data into a java array
  62.  *
  63.  * a bit of a monster functions, but it should cover all cases to overcome
  64.  * the issue that the chunk size of the java layer and our pcm chunks are
  65.  * differently sized
  66.  *
  67.  * afterall, it only copies the raw pcm data from pcm_data_start to
  68.  * the passed byte[]-array
  69.  *
  70.  * it is called from the PositionMarker callback of AudioTrack
  71.  **/
  72. JNIEXPORT void JNICALL
  73. Java_org_rockbox_RockboxPCM_pcmSamplesToByteArray(JNIEnv *env,
  74.                                                   jobject this,
  75.                                                   jbyteArray arr)
  76. {
  77.     lock_audio();
  78.  
  79.     (void)this;
  80.     size_t len;
  81.     size_t array_size = (*env)->GetArrayLength(env, arr);
  82.     if (array_size > pcm_data_size)
  83.         len = pcm_data_size;
  84.     else
  85.         len = array_size;
  86.  
  87.     (*env)->SetByteArrayRegion(env, arr, 0, len, pcm_data_start);
  88.  
  89.     if (array_size > pcm_data_size)
  90.     {   /* didn't have enough data for the array ? */
  91.         size_t remaining = array_size - pcm_data_size;
  92.         size_t offset = len;
  93.     retry:
  94.         pcm_play_get_more_callback((void**)&pcm_data_start, &pcm_data_size);
  95.         if (pcm_data_size == 0)
  96.         {
  97.             DEBUGF("out of data\n");
  98.             unlock_audio();
  99.             return;
  100.         }
  101.         if (remaining > pcm_data_size)
  102.         {   /* got too little data, get more ... */
  103.             (*env)->SetByteArrayRegion(env, arr, offset, pcm_data_size, pcm_data_start);
  104.             /* advance in the java array by the amount we copied */
  105.             offset += pcm_data_size;
  106.             /* we copied at least a bit */
  107.             remaining -= pcm_data_size;
  108.             pcm_data_size = 0;
  109.             pcm_play_dma_started_callback();
  110.             /* let's get another buch of data and try again */
  111.             goto retry;
  112.         }
  113.         else
  114.         {
  115.             (*env)->SetByteArrayRegion(env, arr, offset, remaining, pcm_data_start);
  116.             pcm_play_dma_started_callback();
  117.         }
  118.         len = remaining;
  119.     }
  120.     pcm_data_start += len;
  121.     pcm_data_size -= len;
  122.     unlock_audio();
  123. }
  124.  
  125. void pcm_play_lock(void)
  126. {
  127.     if (++audio_locked == 1)
  128.         lock_audio();
  129. }
  130.  
  131. void pcm_play_unlock(void)
  132. {
  133.     if (--audio_locked == 0);
  134.         unlock_audio();
  135. }
  136.  
  137. void pcm_dma_apply_settings(void)
  138. {
  139. }
  140.  
  141. void pcm_play_dma_start(const void *addr, size_t size)
  142. {
  143.     pcm_data_start = (char*)addr;
  144.     pcm_data_size = size;
  145.    
  146.     pcm_play_dma_pause(false);
  147. }
  148.  
  149. void pcm_play_dma_stop(void)
  150. {
  151.     /* NOTE: due to how pcm_play_get_more_callback() works, this is
  152.      * possibly called from pcmSamplesToByteArray(), i.e. another thread.
  153.      * => We need to discover the env_ptr */
  154.     JNIEnv* env = getJavaEnvironment();
  155.     (*env)->CallVoidMethod(env,
  156.                            RockboxPCM_instance,
  157.                            stop_method);
  158. }
  159.  
  160. void pcm_play_dma_pause(bool pause)
  161. {
  162.     (*env_ptr)->CallVoidMethod(env_ptr,
  163.                                RockboxPCM_instance,
  164.                                play_pause_method,
  165.                                (int)pause);
  166. }
  167.  
  168. size_t pcm_get_bytes_waiting(void)
  169. {
  170.     return pcm_data_size;
  171. }
  172.  
  173. const void * pcm_play_dma_get_peak_buffer(int *count)
  174. {
  175.     uintptr_t addr = (uintptr_t)pcm_data_start;
  176.     *count = pcm_data_size / 4;
  177.     return (void *)((addr + 3) & ~3);
  178. }
  179.  
  180. void pcm_play_dma_init(void)
  181. {
  182.     /* in order to have background music playing after leaving the activity,
  183.      * we need to allocate the PCM object from the Rockbox thread (the Activity
  184.      * runs in a separate thread because it would otherwise kill us when
  185.      * stopping it)
  186.      *
  187.      * Luckily we only reference the PCM object from here, so it's safe (and
  188.      * clean) to allocate it here
  189.      **/
  190.     JNIEnv e = *env_ptr;
  191.     /* get the class and its constructor */
  192.     RockboxPCM_class = e->FindClass(env_ptr, "org/rockbox/RockboxPCM");
  193.     jmethodID constructor = e->GetMethodID(env_ptr, RockboxPCM_class, "<init>", "()V");
  194.     /* instance = new RockboxPCM() */
  195.     RockboxPCM_instance = e->NewObject(env_ptr, RockboxPCM_class, constructor);
  196.     /* cache needed methods */
  197.     play_pause_method = e->GetMethodID(env_ptr, RockboxPCM_class, "play_pause", "(Z)V");
  198.     set_volume_method = e->GetMethodID(env_ptr, RockboxPCM_class, "set_volume", "(I)V");
  199.     stop_method       = e->GetMethodID(env_ptr, RockboxPCM_class, "stop", "()V");
  200. }
  201.  
  202. void pcm_postinit(void)
  203. {
  204. }
  205.  
  206. void pcm_set_mixer_volume(int volume)
  207. {
  208.     (*env_ptr)->CallVoidMethod(env_ptr, RockboxPCM_instance, set_volume_method, volume);
  209. }
  210.  
  211. /*
  212.  * release audio resources */
  213. void pcm_shutdown(void)
  214. {
  215.     JNIEnv e = *env_ptr;
  216.     jmethodID release = e->GetMethodID(env_ptr, RockboxPCM_class, "release", "()V");
  217.     e->CallVoidMethod(env_ptr, RockboxPCM_instance, release);
  218. }
  219.    
  220. /* Due to limitations of default_event_handler(), parameters gets swallowed when
  221.  * being posted with queue_broadcast(), so workaround this by caching the last
  222.  * value.
  223.  */
  224. static int lastPostedVolume = -1;
  225. int hosted_get_volume(void)
  226. {
  227.     return lastPostedVolume;
  228. }
  229.  
  230. JNIEXPORT void JNICALL
  231. Java_org_rockbox_RockboxPCM_postVolumeChangedEvent(JNIEnv *env,
  232.                                                    jobject this,
  233.                                                    jint volume)
  234. {
  235.     (void) env;
  236.     (void) this;
  237.  
  238.     if (volume != lastPostedVolume)
  239.     {
  240.         lastPostedVolume = volume;
  241.         queue_broadcast(SYS_VOLUME_CHANGED, 0);
  242.     }
  243. }