[from now] 2012/10/04 23:24:40

diff --git a/src/sdl_audio.ml b/src/sdl_audio.ml
index 24e2470..345bd79 100644
--- a/src/sdl_audio.ml
+++ b/src/sdl_audio.ml
@@ -1,6 +1,10 @@
 (**
  * this module provide lowlevel SDL bindings for SDL Audio. this don't include
- * high level API for user. these functions are often use only inner library.
+ * high level API for user. these functions are often use only inner
+ library.
+ *
+ * Note: Don't provide with SDL_MixAudio binding. If need to audio
+ * mixing, use and see {!Sdl_mixer}.
  *
  * @author derui
  * @since 0.1
@@ -13,9 +17,11 @@ type audio_format =
   AUDIO_U8                              (** Unsigned 8-bit samples  *)
 | AUDIO_S8                              (** Signed 8-bit samples  *)
 | AUDIO_U16              (** Unsigned 16-bit little-endian samples  *)
+| AUDIO_U16LSB           (** Unsigned 16-bit little-endian samples  *)
 | AUDIO_S16              (** Signed 16-bit little-endian samples  *)
+| AUDIO_S16LSB           (** Signed 16-bit little-endian samples  *)
 | AUDIO_U16MSB           (** Unsigned 16-bit big-endian samples  *)
-| AUDIO_U16MSG           (** Signed 16-bit big-endian samples  *)
+| AUDIO_U16MSB           (** Signed 16-bit big-endian samples  *)
 | AUDIO_U16SYS           (** Either AUDIO_U16LSB or AUDIO_U16MSB
                              depending on you system endianness  *)
 | AUDIO_S16SYS           (** Either AUDIO_S16LSB or AUDIO_S16MSB
@@ -36,8 +42,17 @@ type audio = {
   channels:int;          (** Number of channels: 1 mono, 2 stereo  *)
   silence:int;           (** Audio buffer silence value (calculated) *)
   samples:int;           (** Audio buffer size in samples  *)
-  size:int;              (** Audio buffer size in bytes (calculated) *)
-  callback:
+  size:int;              (** Audio buffer size in bytes
+  (calculated) *)
+  (** callback function. if you need user data to use in this
+      function, you take closure contained user data.
+
+      Caution: this callback function is registered into
+      {b global_root} of OCaml Garbage Collector and have been
+      referenced by C global. When they unregistering, only call
+      {!close}, you are sure to forget call {!close}.
+  *)
+  callback:audio_buffer -> int -> unit;
 }

 (**
@@ -123,3 +138,57 @@ external load_wav : string -> wav_struct = "sdlcaml_load_wav"
    @param wav_struct created by {!load_wav}
 *)
 external free_wav : wav_struct -> unit = "sdlcaml_free_wav"
+
+(**
+   Convert audio data to a desired audio format.
+
+   src_format and dist_format are the source and destination format of
+   the conversion.
+   src_channel and dist_channel are the number of channels in the
+   source and destination formats.
+   src_rate and dist_rate are the frequency of samples-per-second of
+   the source and destination formats.(see {!audio})
+
+   Given audio_buffer contained wav_struct is freed by this function,
+   so you must exchange new wav_struct returned from this to previous
+   wav_struct.
+   Note: If conversion failed, new wav_struct is equal to previous
+   wav_struct, even so you should exchange new to previous.
+
+   @param src_format source format
+   @param dist_format destination format
+   @param src_channel source channels
+   @param dist_channel destination format
+   @param src_rate source frequency of samples-per-second
+   @param dist_rate destination frequency of samples-per-second
+   @param src desired format and audio_buffer contained
+   @return wav_struct contain new audio buffer
+*)
+external convert : src_format:audio_format -> dist_format:audio_format
+  -> src_channel:int -> dist_channel:int -> src_rate:int ->
+    dist_rate:int -> src:wav_struct -> wav_struct = "sdlcaml_audio_convert"
+
+(**
+   Lock out the callback function.
+
+   The lock manipulated by these functions protects the callback
+   function, During a {!lock} period, you can be guaranteed that the
+   callback function is not running. Do not call these from the
+   callback function or you will cause deadlock.
+*)
+external lock: unit -> unit = "sdlcaml_lock_audio"
+
+(**
+   Unlock the callback function.
+
+   Unlocks a previous {!lock} call.
+*)
+external unlock: unit -> unit = "sdlcaml_unlock_audio"
+
+(**
+   Shuts down audio processing and closes the audio device.
+
+   Note: Calling this function dereference to callback of {!audio} for
+         audio processing.
+*)
+external close: unit -> unit = "sdlcaml_close_audio"
diff --git a/src/sdl_audio_stub.c b/src/sdl_audio_stub.c
new file mode 100644
index 0000000..800dedb
--- /dev/null
+++ b/src/sdl_audio_stub.c
@@ -0,0 +1,265 @@
+#include <SDL.h>
+#include <caml/mlvalues.h>
+#include <caml/memory.h>
+#include <caml/alloc.h>
+#include <caml/callback.h>
+
+#include "common.h"
+
+/* global closure for audio callback */
+value __sdlcaml_audio_callback_closure = Val_unit;
+
+
+/* Encapsulation of opaque SDL surface (of type SDL_Joystick *)
+   as Caml custom blocks
+ */
+static struct custom_operations sdl_audio_buffer_ops = {
+  "sdlcaml.sdl_audio_buffer",
+  custom_finalize_default,
+  custom_compare_default,
+  custom_hash_default,
+  custom_serialize_default,
+  custom_deserialize_default
+};
+
+/* Accessing the SDL_Joystick * part of a Caml custom block */
+#define Audio_Buffer_val(v) (*((Uint8**)Data_custom_val(v)))
+
+/* Allocating a Caml custom block to hold the given SDL_Joystick *  */
+static value alloc_audio_buffer(Uint8* audio_buffer) {
+  value v = alloc_custom(&sdl_audio_buffer_ops, sizeof(Uint8*), 0, 1);
+  Audio_Buffer_val(v) = audio_buffer;
+  return v;
+}
+
+/*
+  Callback function is called from SDL_OpenAudio.
+  This function just only call function that given from OCaml's side.
+ */
+static void ml_sdlcaml_audio_callback(void* user, Uint8* buffer, int len) {
+
+}
+
+value ml_convert_audio_format_from_c(Uint16 format) {
+  CAMLparam0();
+
+  switch (format) {
+    case AUDIO_U8: CAMLreturn(Val_int(0));
+    case AUDIO_S8: CAMLreturn(Val_int(1));
+    case AUDIO_U16: CAMLreturn(Val_int(2));
+    case AUDIO_U16LSB: CAMLreturn(Val_int(3));
+    case AUDIO_S16: CAMLreturn(Val_int(4));
+    case AUDIO_S16LSB: CAMLreturn(Val_int(5));
+    case AUDIO_U16MSB: CAMLreturn(Val_int(6));
+    case AUDIO_S16MSB: CAMLreturn(Val_int(7));
+    case AUDIO_U16SYS: CAMLreturn(Val_int(8));
+    case AUDIO_S16SYS: CAMLreturn(Val_int(9));
+  }
+}
+
+Uint16 ml_convert_audio_format_to_c(value format) {
+  CAMLparam1(format);
+
+  switch (Int_val(format)) {
+    case 0: CAMLreturnT(Uint16, AUDIO_U8);
+    case 1: CAMLreturnT(Uint16, AUDIO_S8);
+    case 2: CAMLreturnT(Uint16, AUDIO_U16);
+    case 3: CAMLreturnT(Uint16, AUDIO_U16LSB);
+    case 4: CAMLreturnT(Uint16, AUDIO_S16);
+    case 5: CAMLreturnT(Uint16, AUDIO_S16LSB);
+    case 6: CAMLreturnT(Uint16, AUDIO_U16MSB);
+    case 7: CAMLreturnT(Uint16, AUDIO_S16MSB);
+    case 8: CAMLreturnT(Uint16, AUDIO_U16SYS);
+    case 9: CAMLreturnT(Uint16, AUDIO_S16SYS);
+  }
+}
+
+SDL_AudioSpec* ml_convert_caml_audio_spec_to_c(value desire) {
+  CAMLparam1(desire);
+
+  SDL_AudioSpec *desired = NULL;
+  desired = malloc(sizeof(SDL_AudioSpec));
+  desired->freq = Int_val(Field(desire, 0));
+  desired->format = ml_convert_audio_format_to_c(Field(desire, 1));
+  desired->channels = Int_val(Field(desire, 1));
+  desired->samples = Int_val(Field(desire, 2));
+
+  desired->callback = ml_sdlcaml_audio_callback;
+  desired->userdata = NULL;
+
+  /* register closure to global_root and set reference to global... */
+  __sdlcaml_audio_callback_closure = Field(desire, 6);
+  caml_register_global_root(&__sdlcaml_audio_callback_closure);
+
+  CAMLreturnT(SDL_AudioSpec*, desired);
+}
+
+value ml_convert_caml_audio_spec_from_c(SDL_AudioSpec* obtain) {
+  CAMLparam0();
+  CAMLlocal1(obtained);
+  obtained = caml_alloc(7, 0);
+
+  Store_field(obtained, 0, Val_int(obtain->freq));
+  Store_field(obtained, 1, ml_convert_audio_format_from_c(obtain->format));
+  Store_field(obtained, 2, Val_int(obtain->channels));
+  Store_field(obtained, 3, Val_int(obtain->silence));
+  Store_field(obtained, 4, Val_int(obtain->samples));
+  Store_field(obtained, 5, Val_int(obtain->size));
+  Store_field(obtained, 6, __sdlcaml_audio_callback_closure);
+
+  CAMLreturn(obtained);
+}
+
+CAMLprim value sdlcaml_open_audio(value auto_convert, value desire) {
+  CAMLparam2(auto_convert, desire);
+  CAMLlocal1(obtain_converted);
+
+  SDL_AudioSpec *desired = NULL, *obtained = NULL;
+  if (auto_convert != Val_none) {
+    desired = ml_convert_caml_audio_spec_to_c(desire);
+  }
+
+  obtained = malloc(sizeof(SDL_AudioSpec));
+
+  if (SDL_OpenAudio(desired, obtained) < 0) {
+    if (desired != NULL) {
+      free(desired);
+    }
+    free(obtained);
+    caml_raise_with_string(*caml_named_value("Sdl_audio_exception"),
+                           SDL_GetError());
+  }
+
+  obtain_converted = ml_convert_caml_audio_spec_from_c(obtained);
+
+  if (desired != NULL) {
+    free(desired);
+  }
+  free(obtained);
+
+  CAMLreturn(obtain_converted);
+}
+
+CAMLprim value sdlcaml_audio_pause(value unit) {
+  CAMLparam1(unit);
+
+  SDL_PauseAudio(0);
+
+  CAMLreturn(Val_unit);
+}
+
+CAMLprim value sdlcaml_audio_unpause(value unit) {
+  CAMLparam1(unit);
+
+  SDL_PauseAudio(1);
+
+  CAMLreturn(Val_unit);
+}
+
+CAMLprim value sdlcaml_audio_get_status(value unit) {
+  CAMLparam1(unit);
+
+  SDL_audiostatus state = SDL_GetAudioStatus();
+
+  switch (state) {
+    case SDL_AUDIO_STOPPED: CAMLreturn(Val_int(0));
+    case SDL_AUDIO_PAUSED: CAMLreturn(Val_int(1));
+    case SDL_AUDIO_PLAYING: CAMLreturn(Val_int(2));
+  }
+}
+
+CAMLprim value sdlcaml_load_wav(value file) {
+  CAMLparam1(file);
+  CAMLlocal1(wav_struct);
+
+  SDL_AudioSpec spec;
+  Uint32 wav_length = 0;
+  Uint8 *wav_buffer = NULL;
+
+  if (SDL_LoadWAV(String_val(file), &spec, &wav_buffer, &wav_length) == NULL) {
+    caml_raise_with_string(*caml_named_value("Sdl_audio_exception"),
+                           SDL_GetError());
+  }
+
+  /* construct Caml WAV Struct */
+  wav_struct = caml_alloc(3, 0);
+  Store_field(wav_struct, 0, alloc_audio_buffer(wav_buffer));
+  Store_field(wav_struct, 1, Val_int(wav_length));
+  Store_field(wav_struct, 2, ml_convert_audio_format_from_c(spec));
+
+  CAMLreturn(wav_struct);
+}
+
+CAMLprim value sdlcaml_free_wav(value wavstruct) {
+  CAMLparam1(wavstruct);
+
+  value buf = Field(wavstruct, 0);
+  SDL_FreeWAV(Audio_Buffer_val(buf));
+
+  CAMLreturn(Val_unit);
+}
+
+CAMLprim value sdlcaml_audio_convert_native(
+    value src_format, value dist_format,
+    value src_channel, value dist_channel,
+    value src_rate, value dist_rate, value src) {
+  CAMLparam5(src_format, dist_format, src_channel, dist_channel, src_rate);
+  CAMLxparam2(src_rate, src);
+  CAMLlocal1(converter);
+  converter = Val_none;
+
+  SDL_AudioCVT wav_cvt;
+  int ret = SDL_BuildAudioCVT(&wav_cvt,
+                              ml_convert_audio_format_to_c(src_format),
+                              Int_val(src_channel),
+                              Int_val(src_rate),
+                              ml_convert_audio_format_to_c(dist_format),
+                              Int_val(dist_channel),
+                              Int_val(dist_rate));
+
+  if (ret != -1) {
+    wav_cvt.buf = malloc(Int_val(Field(src, 1)) * wav_cvt.len_mult);
+    wav_cvt.len = Int_val(Field(src, 1));
+    memcpy(wav_cvt.buf, Audio_Buffer_val(Field(src, 0)),
+           Int_val(Field(src, 1)));
+
+    SDL_FreeWAV(Audio_Buffer_val(Field(src, 0)));
+
+    SDL_ConvertAudio(&wav_cvt);
+
+    /* modify buffer and length of given src */
+    Store_field(src, 0, alloc_audio_buffer(wav_cvt.buf));
+    Store_field(src, 1, Val_int(wav_cvt.len * wav_cvt.len_ratio));
+  }
+
+  CAMLreturn(src);
+}
+
+
+CAMLprim value sdlcaml_convert_bytecode(value *argv, int argn) {
+  CAMLreturn(sdlcaml_audio_convert_native(argv[0], argv[1], argv[2], argv[3],
+                                                argv[4], argv[5], argv[6]));
+}
+
+CAMLprim value sdlcaml_lock_audio(value unit) {
+  CAMLparam1(unit);
+  SDL_LockAudio();
+  CAMLreturn(Val_unit);
+}
+
+CAMLprim value sdlcaml_unlock_audio(value unit) {
+  CAMLparam1(unit);
+  SDL_UnlockAudio();
+  CAMLreturn(Val_unit);
+}
+
+CAMLprim value sdlcaml_close_audio(value unit) {
+  CAMLparam1(unit);
+
+  SDL_CloseAudio();
+
+  caml_unregister_global_root(&__sdlcaml_audio_callback_closure);
+  __sdlcaml_audio_callback_closure = Val_unit;
+
+  CAMLreturn(Val_unit);
+}
[from now] 2012/10/05 23:19:41

diff --git a/src/sdl_audio.ml b/src/sdl_audio.ml
index 345bd79..5e9c3d2 100644
--- a/src/sdl_audio.ml
+++ b/src/sdl_audio.ml
@@ -17,15 +17,9 @@ type audio_format =
   AUDIO_U8                              (** Unsigned 8-bit samples  *)
 | AUDIO_S8                              (** Signed 8-bit samples  *)
 | AUDIO_U16              (** Unsigned 16-bit little-endian samples  *)
-| AUDIO_U16LSB           (** Unsigned 16-bit little-endian samples  *)
 | AUDIO_S16              (** Signed 16-bit little-endian samples  *)
-| AUDIO_S16LSB           (** Signed 16-bit little-endian samples  *)
 | AUDIO_U16MSB           (** Unsigned 16-bit big-endian samples  *)
-| AUDIO_U16MSB           (** Signed 16-bit big-endian samples  *)
-| AUDIO_U16SYS           (** Either AUDIO_U16LSB or AUDIO_U16MSB
-                             depending on you system endianness  *)
-| AUDIO_S16SYS           (** Either AUDIO_S16LSB or AUDIO_S16MSB
-                             depending on you system endianness  *)
+| AUDIO_S16MSB           (** Signed 16-bit big-endian samples  *)

 (** audio state used by {!get_status}  *)
 type audio_status =
@@ -34,6 +28,12 @@ type audio_status =
 | PLAYING

 (**
+   malloc'd buffer in C containing the audio data.
+   This data type return from {!load_wav}, and have to be freed by {!free_wav}.
+*)
+type audio_buffer
+
+(**
    Binding for Audio Spec Structure.
 *)
 type audio = {
@@ -44,24 +44,9 @@ type audio = {
   samples:int;           (** Audio buffer size in samples  *)
   size:int;              (** Audio buffer size in bytes
   (calculated) *)
-  (** callback function. if you need user data to use in this
-      function, you take closure contained user data.
-
-      Caution: this callback function is registered into
-      {b global_root} of OCaml Garbage Collector and have been
-      referenced by C global. When they unregistering, only call
-      {!close}, you are sure to forget call {!close}.
-  *)
-  callback:audio_buffer -> int -> unit;
 }

 (**
-   malloc'd buffer in C containing the audio data.
-   This data type return from {!load_wav}, and have to be freed by {!free_wav}.
-*)
-type audio_buffer
-
-(**
    filled with the audio data format of the wave data.
    loaded_spec contained this struct is mostly equal to given {!audio}
    for {!free_wav}. Please see {!free_wav}.
@@ -83,12 +68,14 @@ type wav_struct = {
    {!audio}, and the audio data will be guaranteed to be in the
    requested format.

-   @param auto_convert true if you want to pass to be desired format,
-   without current hardware spec.
    @param audio desired parameters
-   @return Some with hardware spec if successful and {!auto_convert} is false.
+   @param callback audio callback function
+   @return Some with hardware spec if successful and {!auto_convert}
+   is false.
+   @raise Sdl_audio_exception if failed
 *)
-external open_audio: ?auto_convert:bool -> audio -> audio option = "sdlcaml_open_audio"
+external open_audio: audio ->
+  (audio_buffer -> int -> unit) -> audio option = "sdlcaml_open_audio"

 (**
    Pause the audio callback processing.
@@ -127,7 +114,7 @@ external get_status : unit -> audio_status = "sdlcaml_get_audio_status"
    @return struct of infomations of WAVE file
    @raise Sdl_audio_exception when failure
 *)
-external load_wav : string -> wav_struct = "sdlcaml_load_wav"
+external load_wav : file:string -> spec:audio -> wav_struct = "sdlcaml_load_wav"

 (**
    Frees previously opened WAV data.
@@ -166,7 +153,9 @@ external free_wav : wav_struct -> unit = "sdlcaml_free_wav"
 *)
 external convert : src_format:audio_format -> dist_format:audio_format
   -> src_channel:int -> dist_channel:int -> src_rate:int ->
-    dist_rate:int -> src:wav_struct -> wav_struct = "sdlcaml_audio_convert"
+    dist_rate:int -> src:wav_struct -> wav_struct =
+  "sdlcaml_audio_convert_native"
+  "sdlcaml_audio_convert_bytecode"

 (**
    Lock out the callback function.
diff --git a/src/sdl_audio_stub.c b/src/sdl_audio_stub.c
index 800dedb..adac6dd 100644
--- a/src/sdl_audio_stub.c
+++ b/src/sdl_audio_stub.c
@@ -1,14 +1,25 @@
+#define CAML_NAME_SPACE
+#include <unistd.h>
+
 #include <SDL.h>
 #include <caml/mlvalues.h>
+#include <caml/fail.h>
 #include <caml/memory.h>
 #include <caml/alloc.h>
 #include <caml/callback.h>
+#include <caml/custom.h>
+#include <caml/threads.h>

 #include "common.h"

 /* global closure for audio callback */
-value __sdlcaml_audio_callback_closure = Val_unit;
+static value __sdlcaml_audio_callback_closure = Val_unit;

+/* currently audio thread register/unregister state.
+   0 if thread registred yet, 1 if thread registered, 2 if thread prepare being shutdown,
+   and 3 if thread ready shutdown.
+ */
+static int __sdlcaml_audio_thread_state = 0;

 /* Encapsulation of opaque SDL surface (of type SDL_Joystick *)
    as Caml custom blocks
@@ -27,7 +38,7 @@ static struct custom_operations sdl_audio_buffer_ops = {

 /* Allocating a Caml custom block to hold the given SDL_Joystick *  */
 static value alloc_audio_buffer(Uint8* audio_buffer) {
-  value v = alloc_custom(&sdl_audio_buffer_ops, sizeof(Uint8*), 0, 1);
+  value v = caml_alloc_custom(&sdl_audio_buffer_ops, sizeof(Uint8*), 0, 1);
   Audio_Buffer_val(v) = audio_buffer;
   return v;
 }
@@ -36,8 +47,30 @@ static value alloc_audio_buffer(Uint8* audio_buffer) {
   Callback function is called from SDL_OpenAudio.
   This function just only call function that given from OCaml's side.
  */
-static void ml_sdlcaml_audio_callback(void* user, Uint8* buffer, int len) {
+void ml_sdlcaml_audio_callback(void* user, Uint8* buffer, int len) {
+  if (__sdlcaml_audio_thread_state == 3) {
+    return;
+  }
+
+  if (__sdlcaml_audio_thread_state == 0) {
+    caml_c_thread_register();
+    __sdlcaml_audio_thread_state = 1;
+    return;
+  }
+
+  if (__sdlcaml_audio_thread_state == 2) {
+    caml_c_thread_unregister();
+    __sdlcaml_audio_thread_state = 3;
+    return;
+  }

+  if (__sdlcaml_audio_thread_state == 1 &&
+      __sdlcaml_audio_callback_closure != Val_unit) {
+    caml_acquire_runtime_system();
+    caml_callback2(__sdlcaml_audio_callback_closure,
+                   alloc_audio_buffer(buffer), Val_int(len));
+    caml_release_runtime_system();
+  }
 }

 value ml_convert_audio_format_from_c(Uint16 format) {
@@ -47,13 +80,9 @@ value ml_convert_audio_format_from_c(Uint16 format) {
     case AUDIO_U8: CAMLreturn(Val_int(0));
     case AUDIO_S8: CAMLreturn(Val_int(1));
     case AUDIO_U16: CAMLreturn(Val_int(2));
-    case AUDIO_U16LSB: CAMLreturn(Val_int(3));
-    case AUDIO_S16: CAMLreturn(Val_int(4));
-    case AUDIO_S16LSB: CAMLreturn(Val_int(5));
-    case AUDIO_U16MSB: CAMLreturn(Val_int(6));
-    case AUDIO_S16MSB: CAMLreturn(Val_int(7));
-    case AUDIO_U16SYS: CAMLreturn(Val_int(8));
-    case AUDIO_S16SYS: CAMLreturn(Val_int(9));
+    case AUDIO_S16: CAMLreturn(Val_int(3));
+    case AUDIO_U16MSB: CAMLreturn(Val_int(4));
+    case AUDIO_S16MSB: CAMLreturn(Val_int(5));
   }
 }

@@ -81,23 +110,20 @@ SDL_AudioSpec* ml_convert_caml_audio_spec_to_c(value desire) {
   desired = malloc(sizeof(SDL_AudioSpec));
   desired->freq = Int_val(Field(desire, 0));
   desired->format = ml_convert_audio_format_to_c(Field(desire, 1));
-  desired->channels = Int_val(Field(desire, 1));
-  desired->samples = Int_val(Field(desire, 2));
-
+  desired->channels = Int_val(Field(desire, 2));
+  desired->silence = Int_val(Field(desire, 3));
+  desired->samples = Int_val(Field(desire, 4));
+  desired->size = Int_val(Field(desire, 5));
   desired->callback = ml_sdlcaml_audio_callback;
   desired->userdata = NULL;

-  /* register closure to global_root and set reference to global... */
-  __sdlcaml_audio_callback_closure = Field(desire, 6);
-  caml_register_global_root(&__sdlcaml_audio_callback_closure);
-
   CAMLreturnT(SDL_AudioSpec*, desired);
 }

 value ml_convert_caml_audio_spec_from_c(SDL_AudioSpec* obtain) {
   CAMLparam0();
   CAMLlocal1(obtained);
-  obtained = caml_alloc(7, 0);
+  obtained = caml_alloc(6, 0);

   Store_field(obtained, 0, Val_int(obtain->freq));
   Store_field(obtained, 1, ml_convert_audio_format_from_c(obtain->format));
@@ -105,23 +131,21 @@ value ml_convert_caml_audio_spec_from_c(SDL_AudioSpec* obtain) {
   Store_field(obtained, 3, Val_int(obtain->silence));
   Store_field(obtained, 4, Val_int(obtain->samples));
   Store_field(obtained, 5, Val_int(obtain->size));
-  Store_field(obtained, 6, __sdlcaml_audio_callback_closure);

   CAMLreturn(obtained);
 }

-CAMLprim value sdlcaml_open_audio(value auto_convert, value desire) {
-  CAMLparam2(auto_convert, desire);
+CAMLprim value sdlcaml_open_audio(value desire, value callback) {
+  CAMLparam1(desire);
   CAMLlocal1(obtain_converted);
+  obtain_converted = Val_none;

   SDL_AudioSpec *desired = NULL, *obtained = NULL;
-  if (auto_convert != Val_none) {
-    desired = ml_convert_caml_audio_spec_to_c(desire);
-  }
-
   obtained = malloc(sizeof(SDL_AudioSpec));
+  desired = ml_convert_caml_audio_spec_to_c(desire);

   if (SDL_OpenAudio(desired, obtained) < 0) {
+
     if (desired != NULL) {
       free(desired);
     }
@@ -130,6 +154,13 @@ CAMLprim value sdlcaml_open_audio(value auto_convert, value desire) {
                            SDL_GetError());
   }

+  /* register closure to global_root and set reference to global... */
+  if (__sdlcaml_audio_callback_closure == Val_none) {
+    __sdlcaml_audio_callback_closure = callback;
+    caml_register_global_root(&__sdlcaml_audio_callback_closure);
+  }
+  __sdlcaml_audio_thread_state = 0;
+
   obtain_converted = ml_convert_caml_audio_spec_from_c(obtained);

   if (desired != NULL) {
@@ -137,13 +168,13 @@ CAMLprim value sdlcaml_open_audio(value auto_convert, value desire) {
   }
   free(obtained);

-  CAMLreturn(obtain_converted);
+  CAMLreturn(Val_some(obtain_converted));
 }

 CAMLprim value sdlcaml_audio_pause(value unit) {
   CAMLparam1(unit);

-  SDL_PauseAudio(0);
+  SDL_PauseAudio(1);

   CAMLreturn(Val_unit);
 }
@@ -151,12 +182,12 @@ CAMLprim value sdlcaml_audio_pause(value unit) {
 CAMLprim value sdlcaml_audio_unpause(value unit) {
   CAMLparam1(unit);

-  SDL_PauseAudio(1);
+  SDL_PauseAudio(0);

   CAMLreturn(Val_unit);
 }

-CAMLprim value sdlcaml_audio_get_status(value unit) {
+CAMLprim value sdlcaml_get_audio_status(value unit) {
   CAMLparam1(unit);

   SDL_audiostatus state = SDL_GetAudioStatus();
@@ -168,15 +199,18 @@ CAMLprim value sdlcaml_audio_get_status(value unit) {
   }
 }

-CAMLprim value sdlcaml_load_wav(value file) {
-  CAMLparam1(file);
+CAMLprim value sdlcaml_load_wav(value file, value spec) {
+  CAMLparam2(file, spec);
   CAMLlocal1(wav_struct);

-  SDL_AudioSpec spec;
+  SDL_AudioSpec* spec_c = ml_convert_caml_audio_spec_to_c(spec);
   Uint32 wav_length = 0;
   Uint8 *wav_buffer = NULL;

-  if (SDL_LoadWAV(String_val(file), &spec, &wav_buffer, &wav_length) == NULL) {
+  if (SDL_LoadWAV(String_val(file), spec_c, &wav_buffer, &wav_length) == NULL) {
+    if (spec_c != NULL) {
+      free(spec_c);
+    }
     caml_raise_with_string(*caml_named_value("Sdl_audio_exception"),
                            SDL_GetError());
   }
@@ -185,8 +219,11 @@ CAMLprim value sdlcaml_load_wav(value file) {
   wav_struct = caml_alloc(3, 0);
   Store_field(wav_struct, 0, alloc_audio_buffer(wav_buffer));
   Store_field(wav_struct, 1, Val_int(wav_length));
-  Store_field(wav_struct, 2, ml_convert_audio_format_from_c(spec));
+  Store_field(wav_struct, 2, ml_convert_caml_audio_spec_from_c(spec_c));

+  if (spec_c != NULL) {
+    free(spec_c);
+  }
   CAMLreturn(wav_struct);
 }

@@ -236,7 +273,8 @@ CAMLprim value sdlcaml_audio_convert_native(
 }

-CAMLprim value sdlcaml_convert_bytecode(value *argv, int argn) {
+CAMLprim value sdlcaml_audio_convert_bytecode(value *argv, int argn) {
+  CAMLparam0();
   CAMLreturn(sdlcaml_audio_convert_native(argv[0], argv[1], argv[2], argv[3],
                                                 argv[4], argv[5], argv[6]));
 }
@@ -255,10 +293,23 @@ CAMLprim value sdlcaml_unlock_audio(value unit) {

 CAMLprim value sdlcaml_close_audio(value unit) {
   CAMLparam1(unit);
+  /* preparing thread shutdown */
+  if (__sdlcaml_audio_thread_state > 0) {
+    SDL_LockAudio();
+    __sdlcaml_audio_thread_state = 2;
+    SDL_UnlockAudio();
+    while (__sdlcaml_audio_thread_state != 3) {
+
+      usleep(10);
+    }
+    SDL_LockAudio();
+    __sdlcaml_audio_thread_state = 0;
+  }

   SDL_CloseAudio();
+  SDL_UnlockAudio();

-  caml_unregister_global_root(&__sdlcaml_audio_callback_closure);
+  caml_remove_global_root(&__sdlcaml_audio_callback_closure);
   __sdlcaml_audio_callback_closure = Val_unit;

   CAMLreturn(Val_unit);
diff --git a/src/sdl_joystick.ml b/src/sdl_joystick.ml
index d62a0a1..f442174 100644
--- a/src/sdl_joystick.ml
+++ b/src/sdl_joystick.ml
@@ -30,7 +30,6 @@ type hat =
 | HAT_LEFTUP
 | HAT_LEFTDOWN

-
 (**
    Wrapped to raw {b SDL_Joystick}.
 *)
diff --git a/test/test_sdl_audio.ml b/test/test_sdl_audio.ml
new file mode 100644
index 0000000..b30f61e
--- /dev/null
+++ b/test/test_sdl_audio.ml
@@ -0,0 +1,62 @@
+open Sdlcaml
+open OUnit
+
+let test_set_up _ =
+  Sdl.init [`AUDIO;`TIMER]
+
+let test_tear_down _ =
+  Sdl.quit ()
+
+let test_audio_open_close _ =
+  let open Sdl_audio in
+  begin
+    let desire = {freq = 44100; format = AUDIO_U8; channels = 1;
+                  samples = 8;silence = 0;
+                  size = 0;
+                 }
+    in
+    begin
+      match open_audio desire (fun _ _ -> ()) with
+          Some _ -> ()
+        | None -> assert_failure "audio opening failed"
+    end;
+    assert_equal PAUSED (get_status ());
+    close ();
+  end
+
+let test_audio_load_wav _ =
+  let open Sdl_audio in
+  begin
+    let desire = {freq = 44100; format = AUDIO_U8; channels = 1;
+                  samples = 8;silence = 0;
+                  size = 0;
+                 }
+    in
+    begin
+      match open_audio desire (fun _ _ -> Printf.printf "huga\n%!") with
+          Some obtain ->
+            begin
+              let wav = load_wav ~file:"Once.wav" ~spec:obtain in
+              begin
+                assert_equal PAUSED (get_status ()) ~msg:"paused";
+                unpause ();
+                Sdl_timer.delay 1000;
+                assert_equal PLAYING (get_status ()) ~msg:"playing";
+                free_wav wav;
+              end
+            end
+        | None -> assert_failure "audio opening failed"
+    end;
+    close ();
+  end
+
+
+let tmp_bracket f = bracket test_set_up f test_tear_down
+
+let suite = "SDL audio functions specs" >::: [
+  "open and close" >:: tmp_bracket test_audio_open_close;
+  "load and play" >:: tmp_bracket test_audio_load_wav;
+]
+
+let _ =
+  run_test_tt_main suite
[from now] 2012/10/06 20:22:13

diff --git a/src/sdl_audio.ml b/src/sdl_audio.ml
index 5e9c3d2..9de387d 100644
--- a/src/sdl_audio.ml
+++ b/src/sdl_audio.ml
@@ -42,8 +42,7 @@ type audio = {
   channels:int;          (** Number of channels: 1 mono, 2 stereo  *)
   silence:int;           (** Audio buffer silence value (calculated) *)
   samples:int;           (** Audio buffer size in samples  *)
-  size:int;              (** Audio buffer size in bytes
-  (calculated) *)
+  size:int;              (** Audio buffer size in bytes (calculated) *)
 }

 (**