Cog/Frameworks/Shorten/Files/src/libinputshorten.c

250 lines
8 KiB
C

/*
* lamip input plugin - Shorten decoder
*
*
* well... first version is full of memory leaks i guess :)
*/
/* General includes */
#include <plug_in.h>
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <errno.h>
#include <string.h>
/* General includes for shorten */
#include "decode.h"
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
/* we declare the functions we use in the plugin here */
static int shorten_init(const lamipPluginHandle *);
static void shorten_cleanup(void);
static void shorten_decode(lamipURL *, int);
static void shorten_songinfo(lamipURL *,lamipSonginfo *);
/* We set the functions in the InputPlugin struct... */
static InputPlugin shorten_functions;
InputPlugin *lamip_input_info(void)
{
shorten_functions.common.name = "inputSHORTEN";
shorten_functions.common.description = "plays *.shn - Shorten";
shorten_functions.common.init = shorten_init;
shorten_functions.common.cleanup = shorten_cleanup;
shorten_functions.decode = shorten_decode;
shorten_functions.set_song_info = shorten_songinfo;
return(&shorten_functions);
}
/* we set the module PCM format in this struct... like every input plugin should do by now */
static lamipPCMInfo pcmi;
/* some functions */
/* all things for shorten decoder */
shn_file *shnfile;
shn_config shn_cfg;
static uchar *real_buffer = (uchar *)NULL;
#define CONFIG_ERROR_OUTPUT_METHOD "error_output_method"
#define CONFIG_SEEK_TABLES_PATH "seek_tables_path"
#define CONFIG_RELATIVE_SEEK_TABLES_PATH "relative_seek_tables_path"
#define CONFIG_VERBOSE "verbose"
#define CONFIG_SWAP_BYTES "swap_bytes"
#define NUM_BUFFER_BLOCKS 4096L
static int shorten_init(const lamipPluginHandle *handle)
{
/* Setting when shorten decoder should get active */
lamipPluginHandle *config = (lamipPluginHandle *)handle;
lamip_set_mime_type(config, ".shn", NULL);
/* Initializing the shn_cfg struct, we config it later anyway */
shn_cfg.error_output_method = ERROR_OUTPUT_DEVNULL;
shn_cfg.seek_tables_path = NULL;
shn_cfg.relative_seek_tables_path = NULL;
shn_cfg.verbose = 0;
shn_cfg.swap_bytes = 0;
/* Necessary config variables */
char *val_error_output_method;
char *val_seek_tables_path;
char *val_relative_seek_tables_path;
int val_verbose;
int val_swap_bytes;
/* config: error output */
if(!lamip_cfg_getExist(handle, CONFIG_ERROR_OUTPUT_METHOD)) {
lamip_send_message("SHORTEN: shorten_init: no config value for %s found, resetting to default DEVNULL...\n", CONFIG_ERROR_OUTPUT_METHOD);
lamip_send_message("SHORTEN: shorten_init: possible values for %s are \"DEVNULL\", \"STDERR\"\n", CONFIG_ERROR_OUTPUT_METHOD);
val_error_output_method = strdup("DEVNULL");
lamip_cfg_set(config, CONFIG_ERROR_OUTPUT_METHOD, val_error_output_method);
} else {
val_error_output_method = lamip_cfg_get(handle, CONFIG_ERROR_OUTPUT_METHOD);
}
if(strcasecmp(val_error_output_method, "DEVNULL") == 0)
shn_cfg.error_output_method = ERROR_OUTPUT_DEVNULL;
else if(strcasecmp(val_error_output_method, "STDERR") == 0)
shn_cfg.error_output_method = ERROR_OUTPUT_STDERR;
else {
lamip_send_message("SHORTEN: shorten_init: Wrong value for %s found! Resetting to default DEVNULL...\n");
lamip_send_message("SHORTEN: shorten_init: possible values for %s are \"DEVNULL\", \"STDERR\", \"WINDOW\"\n", CONFIG_ERROR_OUTPUT_METHOD);
val_error_output_method = strdup("DEVNULL");
lamip_cfg_set(config, CONFIG_ERROR_OUTPUT_METHOD, val_error_output_method);
shn_cfg.error_output_method = ERROR_OUTPUT_DEVNULL;
}
/* config: absolute seek tables path */
if(!lamip_cfg_getExist(handle, CONFIG_SEEK_TABLES_PATH)) {
lamip_send_message("SHORTEN: shorten_init: no config value for %s found, resetting to default...\n", CONFIG_SEEK_TABLES_PATH);
val_seek_tables_path = strdup("/tmp");
lamip_cfg_set(config, CONFIG_SEEK_TABLES_PATH, val_seek_tables_path);
} else {
val_seek_tables_path = lamip_cfg_get(handle, CONFIG_SEEK_TABLES_PATH);
}
shn_cfg.seek_tables_path = strdup(val_seek_tables_path);
/* config: relative seek tables path */
if(!lamip_cfg_getExist(handle, CONFIG_RELATIVE_SEEK_TABLES_PATH)) {
lamip_send_message("SHORTEN: shorten_init: no config value for %s found, resetting to default...\n", CONFIG_RELATIVE_SEEK_TABLES_PATH);
val_relative_seek_tables_path = strdup("");
lamip_cfg_set(config, CONFIG_RELATIVE_SEEK_TABLES_PATH, val_relative_seek_tables_path);
} else {
val_relative_seek_tables_path = lamip_cfg_get(handle, CONFIG_RELATIVE_SEEK_TABLES_PATH);
}
shn_cfg.relative_seek_tables_path = strdup(val_relative_seek_tables_path);
/* config: verbose */
if(!lamip_cfg_getExist(handle, CONFIG_VERBOSE)) {
lamip_send_message("SHORTEN: shorten_init: no config value for %s found, resetting to default...\n", CONFIG_VERBOSE);
val_verbose = 0;
lamip_cfg_setBool(config, CONFIG_VERBOSE, val_verbose);
} else {
val_verbose = lamip_cfg_getBool(handle, CONFIG_VERBOSE);
}
shn_cfg.verbose = val_verbose;
/* config: swap bytes */
if(!lamip_cfg_getExist(handle, CONFIG_SWAP_BYTES)) {
lamip_send_message("SHORTEN: shorten_init: no config value for %s found, resetting to default...\n", CONFIG_SWAP_BYTES);
val_swap_bytes = 0;
lamip_cfg_setBool(config, CONFIG_SWAP_BYTES, val_swap_bytes);
} else {
val_swap_bytes = lamip_cfg_getBool(handle, CONFIG_SWAP_BYTES);
}
shn_cfg.swap_bytes = val_swap_bytes;
/* Config cleanup */
free(val_error_output_method);
free(val_seek_tables_path);
free(val_relative_seek_tables_path);
return 1;
}
static void shorten_cleanup(void)
{
return;
}
static void shorten_decode(lamipURL *url, int subtrack)
{
if(!url) {
lamip_send_message("SHORTEN: shorten_decode: Got no url!\n");
return;
}
char *filename = lamip_url_getURL(url);
if(!filename) {
lamip_send_message("SHORTEN: shorten_decode: Got no filename! We cannot play from stream by now!\n");
return;
}
shnfile = shn_load(filename, shn_cfg);
if(!shnfile) {
lamip_send_message("SHORTEN: shorten_decode: Error in opening file! Give it another try with skipping id3v2...\n");
return;
}
if(!shn_init_decoder(shnfile)) {
lamip_send_message("SHORTEN: shorten_decode: shn_init_decoder() failed! Aborting...\n");
shn_unload(shnfile);
shnfile = (shn_file *)NULL;
return;
}
pcmi.channels = shn_get_channels(shnfile);
pcmi.samplerate = shn_get_samplerate(shnfile);
switch(shn_get_bitspersample(shnfile)) {
case 8:
pcmi.format = PCM_FORMAT_U8;
break;
case 16:
pcmi.format = PCM_FORMAT_S16_LE;
break;
/* Next two bit depths aren't supported by shorten anyway */
case 24:
pcmi.format = PCM_FORMAT_S24_LE;
break;
case 32:
pcmi.format = PCM_FORMAT_S32_LE;
break;
default:
lamip_send_message("SHORTEN: shorten_decode: Not supported bits_per_sample format!");
}
/* Getting a clean buffer */
int buffer_size = shn_get_buffer_block_size(shnfile, NUM_BUFFER_BLOCKS);
if(real_buffer) {
free(real_buffer);
real_buffer = (uchar *)NULL;
}
if(!real_buffer) {
real_buffer = (uchar *)malloc(20000);
if(!real_buffer) {
lamip_send_message("SHORTEN: shorten_decode: malloc for real_buffer failed! Aborting...\n");
shn_unload(shnfile);
shnfile = (shn_file *)NULL;
return;
}
}
int read_buffer;
int seekable = shn_seekable(shnfile);
lamip_open(&pcmi, shn_get_song_length(shnfile));
while(lamip_isContinue()) {
read_buffer = shn_read(shnfile, real_buffer, buffer_size);
if(read_buffer <= 0) {
lamip_drain();
break;
}
lamip_writeData((uchar *)real_buffer, buffer_size);
/* If seeking */
if(lamip_isSeek() && seekable)
if(!(shn_seek(shnfile, (unsigned int)(lamip_isSeekGetAndReset() / 1000))))
lamip_send_message("SHORTEN: shorten_decode: Seeking failed!\n");
}
lamip_close();
if(!shn_cleanup_decoder(shnfile))
lamip_send_message("SHORTEN: shorten_decode: shn_cleanup_decoder() failed!\n");
shn_unload(shnfile);
shnfile = NULL;
if(real_buffer) {
free((uchar *)real_buffer);
real_buffer = (uchar *)NULL;
}
return;
}
static void shorten_songinfo(lamipURL* url, lamipSonginfo* songinfo)
{
/* TODO - perhaps it ain't necessary... due to a changing lamip core :) */
return;
}