You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
208 lines
4.3 KiB
208 lines
4.3 KiB
/* Extended Module Player |
|
* Copyright (C) 1996-2016 Claudio Matsuoka and Hipolito Carraro Jr |
|
* |
|
* This file is part of the Extended Module Player and is distributed |
|
* under the terms of the GNU General Public License. See the COPYING |
|
* file for more information. |
|
*/ |
|
|
|
#include <Application.h> |
|
#include <SoundPlayer.h> |
|
|
|
#define B_AUDIO_CHAR 1 |
|
#define B_AUDIO_SHORT 2 |
|
|
|
extern "C" { |
|
#include <string.h> |
|
#include <stdlib.h> |
|
#include "xmp.h" |
|
#include "sound.h" |
|
} |
|
|
|
static media_raw_audio_format fmt; |
|
static BSoundPlayer *player; |
|
|
|
static int paused; |
|
static uint8 *buffer; |
|
static int buffer_len; |
|
static int buf_write_pos; |
|
static int buf_read_pos; |
|
static int chunk_size; |
|
static int chunk_num; |
|
/* static int packet_size; */ |
|
|
|
static const char *const help[] = { |
|
"buffer=num,size", "set the number and size of buffer fragments", |
|
NULL |
|
}; |
|
|
|
static int init(struct options *options); |
|
static void deinit(void); |
|
static void play(void *b, int i); |
|
static void flush(void); |
|
static void onpause(void); |
|
static void onresume(void); |
|
|
|
struct sound_driver sound_beos = { |
|
"beos", |
|
"BeOS PCM audio", |
|
help, |
|
init, |
|
deinit, |
|
play, |
|
flush, |
|
onpause, |
|
onresume |
|
}; |
|
|
|
/* return minimum number of free bytes in buffer, value may change between |
|
* two immediately following calls, and the real number of free bytes |
|
* might actually be larger! */ |
|
static int buf_free(void) |
|
{ |
|
int free = buf_read_pos - buf_write_pos - chunk_size; |
|
if (free < 0) |
|
free += buffer_len; |
|
return free; |
|
} |
|
|
|
/* return minimum number of buffered bytes, value may change between |
|
* two immediately following calls, and the real number of buffered bytes |
|
* might actually be larger! */ |
|
static int buf_used(void) |
|
{ |
|
int used = buf_write_pos - buf_read_pos; |
|
if (used < 0) |
|
used += buffer_len; |
|
return used; |
|
} |
|
|
|
/* add data to ringbuffer */ |
|
static int write_buffer(unsigned char *data, int len) |
|
{ |
|
int first_len = buffer_len - buf_write_pos; |
|
int free = buf_free(); |
|
|
|
if (len > free) |
|
len = free; |
|
if (first_len > len) |
|
first_len = len; |
|
|
|
/* till end of buffer */ |
|
memcpy(buffer + buf_write_pos, data, first_len); |
|
if (len > first_len) { /* we have to wrap around */ |
|
/* remaining part from beginning of buffer */ |
|
memcpy(buffer, data + first_len, len - first_len); |
|
} |
|
buf_write_pos = (buf_write_pos + len) % buffer_len; |
|
|
|
return len; |
|
} |
|
|
|
/* remove data from ringbuffer */ |
|
static int read_buffer(unsigned char *data, int len) |
|
{ |
|
int first_len = buffer_len - buf_read_pos; |
|
int buffered = buf_used(); |
|
|
|
if (len > buffered) |
|
len = buffered; |
|
if (first_len > len) |
|
first_len = len; |
|
|
|
/* till end of buffer */ |
|
memcpy(data, buffer + buf_read_pos, first_len); |
|
if (len > first_len) { /* we have to wrap around */ |
|
/* remaining part from beginning of buffer */ |
|
memcpy(data + first_len, buffer, len - first_len); |
|
} |
|
buf_read_pos = (buf_read_pos + len) % buffer_len; |
|
|
|
return len; |
|
} |
|
|
|
static void render_proc(void *theCookie, void *buffer, size_t req, |
|
const media_raw_audio_format &format) |
|
{ |
|
size_t amt; |
|
|
|
while ((amt = buf_used()) < req) |
|
snooze(100000); |
|
|
|
read_buffer((unsigned char *)buffer, req); |
|
} |
|
|
|
static int init(struct options *options) |
|
{ |
|
char **parm = options->driver_parm; |
|
|
|
be_app = new BApplication("application/x-vnd.cm-xmp"); |
|
|
|
chunk_size = 4096; |
|
chunk_num = 20; |
|
|
|
parm_init(parm); |
|
chkparm2("buffer", "%d,%d", &chunk_num, &chunk_size); |
|
parm_end(); |
|
|
|
fmt.frame_rate = options->rate; |
|
fmt.channel_count = options->format & XMP_FORMAT_MONO ? 1 : 2; |
|
fmt.format = options->format & XMP_FORMAT_8BIT ? |
|
B_AUDIO_CHAR : B_AUDIO_SHORT; |
|
fmt.byte_order = B_HOST_IS_LENDIAN ? |
|
B_MEDIA_LITTLE_ENDIAN : B_MEDIA_BIG_ENDIAN; |
|
fmt.buffer_size = chunk_size * chunk_num; |
|
|
|
buffer_len = chunk_size; |
|
buffer = (uint8 *)calloc(1, buffer_len); |
|
buf_read_pos = 0; |
|
buf_write_pos = 0; |
|
paused = 1; |
|
|
|
player = new BSoundPlayer(&fmt, "xmp output", render_proc); |
|
|
|
return 0; |
|
} |
|
|
|
static void play(void *b, int i) |
|
{ |
|
int j = 0; |
|
|
|
/* block until we have enough free space in the buffer */ |
|
while (buf_free() < i) |
|
snooze(100000); |
|
|
|
while (i) { |
|
if ((j = write_buffer((uint8 *)b, i)) > 0) { |
|
i -= j; |
|
b = (uint8 *)b + j; |
|
} else { |
|
break; |
|
} |
|
} |
|
|
|
if (paused) { |
|
player->Start(); |
|
player->SetHasData(true); |
|
paused = 0; |
|
} |
|
} |
|
|
|
static void deinit(void) |
|
{ |
|
player->Stop(); |
|
be_app->Lock(); |
|
be_app->Quit(); |
|
} |
|
|
|
static void flush(void) |
|
{ |
|
} |
|
|
|
static void onpause(void) |
|
{ |
|
} |
|
|
|
static void onresume(void) |
|
{ |
|
}
|
|
|