#include #include #include #include #include #include xas_bank *xas_bank_new(xas_audio_format format, size_t entry_size, size_t entry_count) { xas_bank *bank; size_t entry_size_total = sizeof(xas_bank_entry) + format.sample_size * entry_size; size_t total = sizeof(xas_bank) + entry_count * entry_size_total; if ((bank = malloc(total)) == NULL) { goto error_malloc_bank; } memset(bank, '\0', total); bank->format.channels = XAS_AUDIO_MONO; bank->format.sample_size = format.sample_size; bank->format.sample_rate = format.sample_rate; bank->entry_size = entry_size; bank->entry_count = entry_count; return bank; error_malloc_bank: return NULL; } void xas_bank_destroy(xas_bank *bank) { free(bank); } static inline xas_bank_entry *entry_by_index(xas_bank *bank, size_t index) { size_t size = sizeof(xas_bank_entry) + sizeof(int16_t) * bank->entry_size, offset = size * index; return (xas_bank_entry *)(((uint8_t *)(bank + 1)) + offset); } ssize_t xas_bank_record(xas_bank *bank, xas_audio_stream *source, size_t entry_index, size_t entry_size) { xas_bank_entry *entry = entry_by_index(bank, entry_index); size_t left = entry_size, index_o = 0; if (!xas_audio_format_eq(bank->format, source->format)) { errno = EINVAL; goto error_invalid_format; } if (entry_size > bank->entry_size) { entry_size = bank->entry_size; } while (left) { ssize_t readlen, amount = left > entry_size? entry_size: left; void *buf; if ((readlen = xas_audio_stream_read(source, &buf, amount)) < 0) { goto error_audio_stream_read; } else if (readlen == 0) { break; } xas_audio_copy(bank->format, entry + 1, buf, index_o, 0, readlen); left -= readlen; index_o += readlen; } return entry->duration = index_o; error_audio_stream_read: error_invalid_format: return -1; } ssize_t xas_bank_load_file(xas_bank *bank, size_t entry_index, const char *path) { ssize_t ret; xas_audio_stream *riff; if ((riff = xas_riff_open_file(path, O_RDONLY)) == NULL) { goto error_riff_open_file; } if ((ret = xas_bank_record(bank, riff, entry_index, bank->entry_size)) < 0) { goto error_record; } xas_audio_stream_destroy(riff); return ret; error_record: xas_audio_stream_destroy(riff); error_riff_open_file: return -1; } static int player_start(xas_bank_player *player, size_t index) { if (xas_bank_player_set_entry(player, index) < 0) { goto error_player_set_entry; } return xas_bank_player_start(player); error_player_set_entry: return -1; } static int player_stop(xas_bank_player *player, ...) { return xas_bank_player_stop(player); } static int set_gain(xas_bank_player *player, size_t index, float gain) { xas_bank_player_set_gain(player, gain); return 0; } static int set_entry(xas_bank_player *player, size_t index, size_t entry) { return xas_bank_player_set_entry(player, entry); } static int set_flags(xas_bank_player *player, size_t index, int flags) { return xas_bank_player_set_flags(player, flags); } static xas_object_call_table call_table = { .start = (xas_object_start_callback)player_start, .stop = (xas_object_stop_callback)player_stop, .set_gain = (xas_object_set_gain_callback)set_gain, .set_entry = (xas_object_set_entry_callback)set_entry, .set_flags = (xas_object_set_flags_callback)set_flags, .stream_new = (xas_object_stream_new_callback)xas_bank_player_stream_new, .destroy = (xas_object_destroy_callback)xas_bank_player_destroy }; xas_bank_player *xas_bank_player_new(xas_bank *bank) { xas_bank_player *player; if ((player = malloc(sizeof(*player))) == NULL) { goto error_malloc_player; } player->obj.table = &call_table; player->bank = bank; player->status = XAS_BANK_PLAYER_STOPPED; player->gain = XAS_BANK_PLAYER_DEFAULT_GAIN; player->flags = XAS_BANK_PLAYER_NONE; player->entry = 0; player->index = 0; return player; error_malloc_player: return NULL; } void xas_bank_player_destroy(xas_bank_player *player) { free(player); } void xas_bank_entry_duration(xas_bank *bank, size_t entry_index, struct timeval *tv) { xas_bank_entry *entry = entry_by_index(bank, entry_index); size_t seconds = entry->duration / bank->format.sample_rate, remainder = entry->duration % bank->format.sample_rate; double subseconds = (double)remainder / (double)bank->format.sample_rate; tv->tv_sec = (time_t)seconds; tv->tv_usec = (suseconds_t)(subseconds * 1000000.0); } int xas_bank_player_set_entry(xas_bank_player *player, size_t entry) { if (entry > player->bank->entry_count) { errno = EINVAL; goto error_invalid_entry; } player->entry = entry; return 0; error_invalid_entry: return -1; } void xas_bank_player_set_gain(xas_bank_player *player, float gain) { player->gain = gain; } int xas_bank_player_start(xas_bank_player *player) { player->status = XAS_BANK_PLAYER_PLAYING; player->index = 0; return 0; } int xas_bank_player_stop(xas_bank_player *player) { if (player->status == XAS_BANK_PLAYER_STOPPED) { return 0; } player->status = XAS_BANK_PLAYER_STOPPED; player->index = 0; return 0; } int xas_bank_player_set_flags(xas_bank_player *player, int flags) { player->flags = flags; return 0; } int xas_bank_player_playing(xas_bank_player *player) { return player->status == XAS_BANK_PLAYER_PLAYING; } static ssize_t stream_fill(xas_bank_player *player, int16_t *dest, size_t count) { xas_bank *bank = player->bank; size_t index_o = 0, left = count; while (left) { if (player->status == XAS_BANK_PLAYER_PLAYING) { xas_bank_entry *entry = entry_by_index(bank, player->entry); int16_t *src = (int16_t *)(entry + 1); size_t remaining = entry->duration - player->index, amount = remaining < left? remaining: left; size_t i; for (i=0; igain * src[player->index + i]; } left -= amount; player->index += amount; index_o += amount; if (player->index == entry->duration) { if (player->flags & XAS_BANK_PLAYER_LOOP) { player->index = 0; } else { xas_bank_player_stop(player); } } } else { xas_audio_zero(bank->format, dest, index_o, left); index_o += left; left = 0; } } return count; } void stream_cleanup() { return; } xas_audio_stream *xas_bank_player_stream_new(xas_bank_player *player) { return xas_audio_stream_new_source((xas_audio_fill)stream_fill, (xas_audio_cleanup)stream_cleanup, player->bank->format, player->bank->entry_size, player); }