cog/Plugins/Dumb/DumbDecoder.m

336 lines
7.2 KiB
Matlab
Raw Normal View History

//
// DumbFile.m
// Cog
//
// Created by Vincent Spader on 5/29/06.
// Copyright 2006 Vincent Spader. All rights reserved.
//
#import "DumbDecoder.h"
2013-10-05 02:25:45 +00:00
#import "umx.h"
#import "j2b.h"
#import "Logging.h"
@implementation DumbDecoder
2013-10-05 02:25:45 +00:00
struct MEMANDFREEFILE
{
2013-10-05 02:25:45 +00:00
char *ptr, *ptr_begin;
long left, size;
};
static int dumb_maffile_skip(void *f, long n)
{
struct MEMANDFREEFILE *m = f;
if (n > m->left) return -1;
m->ptr += n;
m->left -= n;
return 0;
}
2013-10-05 02:25:45 +00:00
static int dumb_maffile_getc(void *f)
{
2013-10-05 02:25:45 +00:00
struct MEMANDFREEFILE *m = f;
if (m->left <= 0) return -1;
m->left--;
return *(const unsigned char *)m->ptr++;
}
2013-10-05 02:25:45 +00:00
static long dumb_maffile_getnc(char *ptr, long n, void *f)
{
struct MEMANDFREEFILE *m = f;
if (n > m->left) n = m->left;
memcpy(ptr, m->ptr, n);
m->ptr += n;
m->left -= n;
return n;
}
2013-10-05 02:25:45 +00:00
static void dumb_maffile_close(void *f)
{
2013-10-05 02:25:45 +00:00
struct MEMANDFREEFILE *m = f;
free(m->ptr_begin);
free(f);
}
2013-10-05 02:25:45 +00:00
static int dumb_maffile_seek(void *f, long n)
2013-09-28 03:24:23 +00:00
{
2013-10-05 02:25:45 +00:00
struct MEMANDFREEFILE *m = f;
2013-09-28 03:24:23 +00:00
2013-10-05 02:25:45 +00:00
m->ptr = m->ptr_begin + n;
m->left = m->size - n;
2013-09-28 03:24:23 +00:00
2013-10-05 02:25:45 +00:00
return 0;
2013-09-28 03:24:23 +00:00
}
2013-10-05 02:25:45 +00:00
static long dumb_maffile_get_size(void *f)
2013-09-28 03:24:23 +00:00
{
2013-10-05 02:25:45 +00:00
struct MEMANDFREEFILE *m = f;
return m->size;
}
static const DUMBFILE_SYSTEM maffile_dfs = {
NULL,
&dumb_maffile_skip,
&dumb_maffile_getc,
&dumb_maffile_getnc,
&dumb_maffile_close,
&dumb_maffile_seek,
&dumb_maffile_get_size
};
DUMBFILE *dumbfile_open_memory_and_free(char *data, long size)
{
char * try_data = unpackUmx( data, &size );
if ( try_data ) {
free( data );
data = try_data;
}
else {
try_data = unpackJ2b( data, &size );
if ( try_data ) {
free( data );
data = try_data;
}
}
2013-09-28 03:24:23 +00:00
2013-10-05 02:25:45 +00:00
struct MEMANDFREEFILE *m = malloc(sizeof(*m));
if (!m) return NULL;
2013-09-28 03:24:23 +00:00
2013-10-05 02:25:45 +00:00
m->ptr_begin = data;
m->ptr = data;
m->left = size;
m->size = size;
2013-09-28 03:24:23 +00:00
2013-10-05 02:25:45 +00:00
return dumbfile_open_ex(m, &maffile_dfs);
2013-09-28 03:24:23 +00:00
}
void oneTimeInit()
{
static bool initialized = false;
if (!initialized)
{
}
}
int callbackLoop(void *data)
{
long * loops = (long *) data;
++ *loops;
return 0;
}
- (BOOL)open:(id<CogSource>)s
{
[self setSource:s];
2013-10-05 02:25:45 +00:00
[source seek:0 whence:SEEK_END];
long size = [source tell];
[source seek:0 whence:SEEK_SET];
void * data = malloc(size);
[source read:data amount:size];
DUMBFILE * df = dumbfile_open_memory_and_free( data, size );
if (!df)
{
ALog(@"Open failed for file: %@", [[s url] absoluteString]);
return NO;
}
2013-10-04 23:34:29 +00:00
int subsong = 0;
int startOrder = 0;
NSURL * url = [s url];
int track_num;
if ([[url fragment] length] == 0)
track_num = 0;
else
track_num = [[url fragment] intValue];
if ( dumb_get_psm_subsong_count( df ) )
subsong = track_num;
else
startOrder = track_num;
dumbfile_seek( df, 0, SEEK_SET );
NSString *ext = [[[[s url] path] pathExtension] lowercaseString];
2013-10-04 23:34:29 +00:00
duh = dumb_read_any(df, [ext isEqualToString:@"mod"] ? 0 : 1, subsong);
if (!duh)
{
ALog(@"Failed to create duh");
dumbfile_close(df);
return NO;
}
dumbfile_close(df);
2013-10-04 23:34:29 +00:00
length = dumb_it_build_checkpoints( duh_get_it_sigdata( duh ), startOrder );
2013-10-04 23:34:29 +00:00
dsr = duh_start_sigrenderer(duh, 0, 2 /* stereo */, startOrder);
if (!dsr)
{
ALog(@"Failed to create dsr");
return NO;
}
2013-09-28 03:24:23 +00:00
DUMB_IT_SIGRENDERER * itsr = duh_get_it_sigrenderer( dsr );
dumb_it_set_ramp_style( itsr, 2 );
dumb_it_set_loop_callback( itsr, callbackLoop, &loops);
dumb_it_set_xm_speed_zero_callback( itsr, dumb_it_callback_terminate, 0);
dumb_it_set_global_volume_zero_callback( itsr, dumb_it_callback_terminate, 0);
loops = 0;
fadeTotal = fadeRemain = 44100 * 8;
sampptr = allocate_sample_buffer(2, 1024);
2013-09-28 03:24:23 +00:00
[self willChangeValueForKey:@"properties"];
[self didChangeValueForKey:@"properties"];
return YES;
}
- (NSDictionary *)properties
{
return [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:0], @"bitrate",
[NSNumber numberWithFloat:44100], @"sampleRate",
[NSNumber numberWithDouble:((length / 65.536f)*44.1000)], @"totalFrames",
[NSNumber numberWithInt:32], @"bitsPerSample", //Samples are short
[NSNumber numberWithBool:YES], @"floatingPoint",
[NSNumber numberWithInt:2], @"channels", //output from gme_play is in stereo
[NSNumber numberWithBool:[source seekable]], @"seekable",
@"host", @"endian",
nil];
}
- (int)readAudio:(void *)buf frames:(UInt32)frames
{
int total = 0;
while ( total < frames ) {
int framesToRender = 1024;
if ( framesToRender > frames )
framesToRender = frames;
dumb_silence( sampptr[0], framesToRender * 2 );
int rendered = duh_sigrenderer_generate_samples( dsr, 1.0, 65536.0f / 44100.0f, framesToRender, sampptr );
if (rendered <= 0)
break;
for ( int i = 0; i < rendered * 2; i++ ) {
const float scale = 1.0 / 0x800000;
((float *)buf)[(total * 2) + i] = (float)sampptr[0][i] * scale;
}
if ( loops >= 2 ) {
2013-10-05 22:47:35 +00:00
float * sampleBuf = ( float * ) buf + total * 2;
long fadeEnd = fadeRemain - rendered;
if ( fadeEnd < 0 )
fadeEnd = 0;
float fadePosf = (float)fadeRemain / fadeTotal;
const float fadeStep = 1.0 / fadeTotal;
for ( long fadePos = fadeRemain; fadePos > fadeEnd; --fadePos, fadePosf -= fadeStep ) {
long offset = (fadeRemain - fadePos) * 2;
float sampleLeft = sampleBuf[ offset + 0 ];
float sampleRight = sampleBuf[ offset + 1 ];
sampleLeft *= fadePosf;
sampleRight *= fadePosf;
sampleBuf[ offset + 0 ] = sampleLeft;
sampleBuf[ offset + 1 ] = sampleRight;
}
rendered = fadeRemain - fadeEnd;
fadeRemain = fadeEnd;
}
total += rendered;
if ( rendered < framesToRender )
break;
}
return total;
}
- (long)seek:(long)frame
{
double pos = (double)duh_sigrenderer_get_position(dsr) / 65.536f;
double seekPos = frame/44.100;
if (seekPos < pos) {
//Reset. Dumb cannot seek backwards. It's dumb.
[self cleanUp];
2007-10-12 01:49:36 +00:00
[source seek:0 whence:SEEK_SET];
[self open:source];
pos = 0.0;
}
int numSamples = (seekPos - pos)/1000 * 44100;
duh_sigrenderer_generate_samples(dsr, 1.0f, 65536.0f / 44100.0f, numSamples, NULL);
return frame;
}
- (void)cleanUp
{
if (sampptr) {
destroy_sample_buffer(sampptr);
sampptr = NULL;
}
if (dsr) {
duh_end_sigrenderer(dsr);
dsr = NULL;
}
if (duh) {
unload_duh(duh);
duh = NULL;
}
}
- (void)close
{
[self cleanUp];
if (source) {
[source close];
[self setSource:nil];
}
}
- (void)setSource:(id<CogSource>)s
{
[s retain];
[source release];
source = s;
}
- (id<CogSource>)source
{
return source;
}
2007-10-14 18:39:58 +00:00
+ (NSArray *)fileTypes
{
2013-10-05 02:25:45 +00:00
return [NSArray arrayWithObjects:@"it", @"itz", @"xm", @"xmz", @"s3m", @"s3z", @"mod", @"mdz", @"stm", @"stz", @"ptm", @"mtm", @"669", @"psm", @"am", @"j2b", @"dsm", @"amf", @"okt", @"okta", @"umx", nil];
2007-10-14 18:39:58 +00:00
}
+ (NSArray *)mimeTypes
{
return [NSArray arrayWithObjects:@"audio/x-it", @"audio/x-xm", @"audio/x-s3m", @"audio/x-mod", nil];
}
+ (float)priority
{
return 1.0;
}
@end