// // DumbFile.m // Cog // // Created by Vincent Spader on 5/29/06. // Copyright 2006 Vincent Spader. All rights reserved. // #define _USE_SSE 1 #import "DumbDecoder.h" #import "j2b.h" #import "mo3.h" #import "umx.h" #import "Logging.h" #import "PlaylistController.h" @implementation DumbDecoder struct MEMANDFREEFILE { char *ptr, *ptr_begin; long left, size; int is_mo3; }; 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; } static int dumb_maffile_getc(void *f) { struct MEMANDFREEFILE *m = f; if(m->left <= 0) return -1; m->left--; return *(const unsigned char *)m->ptr++; } 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; } static void dumb_maffile_close(void *f) { struct MEMANDFREEFILE *m = f; if(m->is_mo3) freeMo3(m->ptr_begin); else free(m->ptr_begin); free(f); } static int dumb_maffile_seek(void *f, long n) { struct MEMANDFREEFILE *m = f; m->ptr = m->ptr_begin + n; m->left = m->size - n; return 0; } static long dumb_maffile_get_size(void *f) { 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) { int is_mo3 = 0; char *try_data = unpackMo3(data, &size); if(try_data) { free(data); data = try_data; is_mo3 = 1; } else { 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; } } } struct MEMANDFREEFILE *m = malloc(sizeof(*m)); if(!m) return NULL; m->ptr_begin = data; m->ptr = data; m->left = size; m->size = size; m->is_mo3 = is_mo3; return dumbfile_open_ex(m, &maffile_dfs); } + (void)initialize { if(self == [DumbDecoder class]) { // do this here so we don't have to wait on it later _dumb_init_cubic(); _dumb_init_sse(); } } - (id)init { self = [super init]; if(self) { sampptr = NULL; dsr = NULL; duh = NULL; } return self; } int callbackLoop(void *data) { long *loops = (long *)data; ++*loops; return 0; } - (BOOL)open:(id)s { [self setSource:s]; [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; } 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] pathExtension] lowercaseString]; 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); length = dumb_it_build_checkpoints(duh_get_it_sigdata(duh), startOrder); dsr = duh_start_sigrenderer(duh, 0, 2 /* stereo */, startOrder); if(!dsr) { ALog(@"Failed to create dsr"); return NO; } DUMB_IT_SIGRENDERER *itsr = duh_get_it_sigrenderer(dsr); int resampling_int = -1; NSString *resampling = [[NSUserDefaults standardUserDefaults] stringForKey:@"resampling"]; if([resampling isEqualToString:@"zoh"]) resampling_int = 0; else if([resampling isEqualToString:@"blep"]) resampling_int = 1; else if([resampling isEqualToString:@"linear"]) resampling_int = 2; else if([resampling isEqualToString:@"blam"]) resampling_int = 3; else if([resampling isEqualToString:@"cubic"]) resampling_int = 4; else if([resampling isEqualToString:@"sinc"]) resampling_int = 5; dumb_it_set_resampling_quality(itsr, resampling_int); 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); [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 = (int)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(!IsRepeatOneSet() && loops >= 2) { 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 = (int)(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]; [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)dealloc { [self close]; } - (void)setSource:(id)s { source = s; } - (id)source { return source; } + (NSArray *)fileTypes { return [NSArray arrayWithObjects:@"it", @"itz", @"xm", @"xmz", @"s3m", @"s3z", @"mod", @"mdz", @"stm", @"stz", @"ptm", @"mtm", @"669", @"psm", @"am", @"j2b", @"dsm", @"amf", @"okt", @"okta", @"umx", @"mo3", nil]; } + (NSArray *)mimeTypes { return [NSArray arrayWithObjects:@"audio/x-it", @"audio/x-xm", @"audio/x-s3m", @"audio/x-mod", nil]; } + (float)priority { return 1.0; } @end