chirp / sdl_stubs.c

#include <string.h>
#include <caml/mlvalues.h>
#include <caml/memory.h>
#include <caml/alloc.h>
#include <caml/custom.h>
#include <caml/fail.h>

#include <SDL.h>
#include <SDL_mixer.h>

// raise an OCaml exception with the current SDL_mixer error
void sdl_failwith_current_error(char *caller)
{
  char *mix_err = Mix_GetError();
  char final_str[strlen(mix_err) + strlen(caller) + 4];

  sprintf(final_str, "%s - %s", caller, Mix_GetError());
  caml_failwith(final_str);
}

// initialize SDL for audio and the SDL_mixer library
value sdl_init(value unit)
{
  CAMLparam1(unit);

  // start SDL with audio support
  if(SDL_Init(SDL_INIT_AUDIO) < 0) {
    char *sdl_err = SDL_GetError();
    char err_str[19 + strlen(sdl_err)];
    sprintf(err_str, "SDL_Init failed - %s", sdl_err);
    caml_failwith(err_str);
  }
  
  // open 44.1KHz, signed 16bit, system byte order,
  // stereo audio, using 1024 byte chunks
  if(Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 1024) < 0) {
    sdl_failwith_current_error("sdl_init");
  }
  CAMLreturn(Val_unit);
}

// free a sample allocated with sdl_load_wav. 
// This function does not use CAMLparam or CAMLreturn because it 
// does not allocate (allocation is forbidden in finalizers called
// from C Custom_blocks)
void sdl_free_wav(value caml_wav)
{
  Mix_Chunk *sample;

  sample = (Mix_Chunk *)Field(caml_wav, 0);
  Mix_FreeChunk(sample);
  sample = NULL;
}

static struct custom_operations wav_custom_ops = {
  "sdl_sound",
  sdl_free_wav,
  custom_compare_default,
  custom_hash_default,
  custom_serialize_default,
  custom_deserialize_default
};

// CR: really don't understand this nest of pointers...
#define Val_wav(v) (*(Mix_Chunk **) Data_custom_val(v))

value sdl_load_wav(value caml_filename)
{
  CAMLparam1(caml_filename);
  char *filename = String_val(caml_filename);
  Mix_Chunk *sample = Mix_LoadWAV(filename);

  if(!sample) sdl_failwith_current_error("sdl_load_wav");

  CAMLlocal1(v);
  v = caml_alloc_custom(&wav_custom_ops, sizeof(sample), 0, 1);
  Val_wav(v) = sample;

  CAMLreturn(v);
}

value sdl_set_wav_volume(value wav, value caml_volume)
{
  CAMLparam2(wav, caml_volume);
  Mix_Chunk *sample = Val_wav(wav);
  int volume = Val_int(caml_volume);

  Mix_VolumeChunk(sample, volume);

  CAMLreturn(Val_unit);
}

value sdl_allocate_channels(value caml_num_channels)
{
  CAMLparam1(caml_num_channels);
  Mix_AllocateChannels(Val_int(caml_num_channels));
  CAMLreturn(Val_unit);
}

value sdl_set_channel_volume(value caml_channel, value caml_volume)
{
  CAMLparam2(caml_channel, caml_volume);
  // documentation doesn't say that this will ever return < 0, but since
  // it returns the current volume of the channel, it should never be < 0
  if(Mix_Volume(Val_int(caml_channel), Val_int(caml_volume)) < 0)
    sdl_failwith_current_error("sdl_set_channel_volume");
  CAMLreturn(Val_unit);
}

value sdl_play_channel(value caml_channel, value wav, value caml_repeat)
{
  CAMLparam3(caml_channel, wav, caml_repeat);
  int channel = Val_int(caml_channel);
  Mix_Chunk *sample = Val_wav(wav);
  int repeat = Val_int(caml_repeat);

  if(Mix_PlayChannel(channel, sample, repeat) < 0)
    sdl_failwith_current_error("sdl_play_channel");

  CAMLreturn(Val_unit);
}

value sdl_play_channel_fade_in(
    value caml_channel, 
    value wav, 
    value caml_repeat,
    value caml_fade_in)
{
  CAMLparam4(caml_channel, wav, caml_repeat, caml_fade_in);
  int channel = Val_int(caml_channel);
  Mix_Chunk *sample = Val_wav(wav);
  int repeat = Val_int(caml_repeat);
  int fade_in = Val_int(caml_fade_in);

  if(Mix_FadeInChannel(channel, sample, repeat, fade_in) < 0)
    sdl_failwith_current_error("sdl_play_channel_fade_in");

  CAMLreturn(Val_unit);
}
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.