cog/Plugins/modplay/modplay/modDecoder.m

343 lines
7.9 KiB
Objective-C

//
// modDecoder.m
// modplay
//
// Created by Christopher Snowhill on 03/17/14.
// Copyright 2014 __NoWork, Inc__. All rights reserved.
//
#import "modDecoder.h"
#import "PlaylistController.h"
@implementation modDecoder
BOOL s3m_probe_length(unsigned long *intro_length, unsigned long *loop_length, const void *src, unsigned long size, unsigned int subsong) {
void *st3play = st3play_Alloc(44100, 0, 0);
if(!st3play) return NO;
if(!st3play_LoadModule(st3play, src, size)) {
st3play_Free(st3play);
return NO;
}
st3play_PlaySong(st3play, subsong);
unsigned long length_total = 0;
unsigned long length_saved;
const long length_safety = 44100 * 60 * 30;
while(st3play_GetLoopCount(st3play) < 1 && length_total < length_safety) {
st3play_RenderFloat(st3play, NULL, 512);
length_total += 512;
}
if(st3play_GetLoopCount(st3play) < 1) {
*loop_length = 0;
*intro_length = 44100 * 60 * 3;
st3play_Free(st3play);
return YES;
}
length_saved = length_total;
while(st3play_GetLoopCount(st3play) < 2) {
st3play_RenderFloat(st3play, NULL, 512);
length_total += 512;
}
st3play_Free(st3play);
*loop_length = length_total - length_saved;
if(length_saved > *loop_length)
*intro_length = length_saved - *loop_length;
else
*intro_length = 0;
return YES;
}
BOOL xm_probe_length(unsigned long *intro_length, unsigned long *loop_length, const void *src, unsigned long size, unsigned int subsong) {
void *ft2play = ft2play_Alloc(44100, 0, 0);
if(!ft2play) return NO;
if(!ft2play_LoadModule(ft2play, src, size)) {
ft2play_Free(ft2play);
return NO;
}
ft2play_PlaySong(ft2play, subsong);
unsigned long length_total = 0;
unsigned long length_saved;
const long length_safety = 44100 * 60 * 30;
while(ft2play_GetLoopCount(ft2play) < 1 && length_total < length_safety) {
ft2play_RenderFloat(ft2play, NULL, 512);
length_total += 512;
}
if(ft2play_GetLoopCount(ft2play) < 1) {
*loop_length = 0;
*intro_length = 44100 * 60 * 3;
ft2play_Free(ft2play);
return YES;
}
length_saved = length_total;
while(ft2play_GetLoopCount(ft2play) < 2) {
ft2play_RenderFloat(ft2play, NULL, 512);
length_total += 512;
}
ft2play_Free(ft2play);
*loop_length = length_total - length_saved;
if(length_saved > *loop_length)
*intro_length = length_saved - *loop_length;
else
*intro_length = 0;
return YES;
}
- (id)init {
self = [super init];
if(self) {
player = NULL;
data = NULL;
}
return self;
}
- (BOOL)open:(id<CogSource>)s {
[s seek:0 whence:SEEK_END];
size = [s tell];
[s seek:0 whence:SEEK_SET];
data = malloc(size);
[s read:data amount:size];
type = TYPE_UNKNOWN;
dataWasMo3 = 0;
if(size >= 3 && memcmp(data, "MO3", 3) == 0) {
void *in_data = data;
unsigned usize = (unsigned)size;
if(UNMO3_Decode(&in_data, &usize, 0) == 0) {
free(data);
data = in_data;
size = usize;
dataWasMo3 = 1;
}
} else if(size >= 4 && memcmp(data, "\xC1\x83\x2A\x9E", 4) == 0) {
long out_size = size;
void *out_data = unpackUmx(data, &out_size);
if(out_data) {
free(data);
data = out_data;
size = out_size;
}
}
if(size >= (0x2C + 4) && memcmp(data + 0x2C, "SCRM", 4) == 0)
type = TYPE_S3M;
else if(size >= 17 && memcmp(data, "Extended Module: ", 17) == 0)
type = TYPE_XM;
else
return NO;
if([[[s url] fragment] length] == 0)
track_num = 0;
else
track_num = [[[s url] fragment] intValue];
unsigned long intro_length = 0, loop_length = 0;
if(type == TYPE_S3M && !s3m_probe_length(&intro_length, &loop_length, data, size, track_num))
return NO;
else if(type == TYPE_XM && !xm_probe_length(&intro_length, &loop_length, data, size, track_num))
return NO;
framesLength = intro_length + loop_length * 2;
totalFrames = framesLength + 44100 * 8;
[self willChangeValueForKey:@"properties"];
[self didChangeValueForKey:@"properties"];
return YES;
}
- (BOOL)decoderInitialize {
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;
if(type == TYPE_S3M) {
player = st3play_Alloc(44100, resampling_int, 2);
if(!player)
return NO;
if(!st3play_LoadModule(player, data, size))
return NO;
st3play_PlaySong(player, track_num);
} else if(type == TYPE_XM) {
player = ft2play_Alloc(44100, resampling_int, 2);
if(!player)
return NO;
if(!ft2play_LoadModule(player, data, size))
return NO;
ft2play_PlaySong(player, track_num);
}
framesRead = 0;
return YES;
}
- (void)decoderShutdown {
if(player) {
if(type == TYPE_S3M)
st3play_Free(player);
else if(type == TYPE_XM)
ft2play_Free(player);
player = NULL;
}
}
- (NSDictionary *)properties {
return [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:0], @"bitrate",
[NSNumber numberWithFloat:44100], @"sampleRate",
[NSNumber numberWithDouble:totalFrames], @"totalFrames",
[NSNumber numberWithInt:32], @"bitsPerSample",
[NSNumber numberWithBool:YES], @"floatingPoint",
[NSNumber numberWithInt:2], @"channels",
[NSNumber numberWithBool:YES], @"seekable",
@"host", @"endian",
nil];
}
- (int)readAudio:(void *)buf frames:(UInt32)frames {
BOOL repeat_one = IsRepeatOneSet();
if(!repeat_one && framesRead >= totalFrames)
return 0;
if(!player) {
if(![self decoderInitialize])
return 0;
}
int total = 0;
while(total < frames) {
int framesToRender = 512;
if(!repeat_one && framesToRender > totalFrames - framesRead)
framesToRender = (int)(totalFrames - framesRead);
if(framesToRender > frames - total)
framesToRender = frames - total;
float *sampleBuf = (float *)buf + total * 2;
if(type == TYPE_S3M)
st3play_RenderFloat(player, sampleBuf, framesToRender);
else if(type == TYPE_XM)
ft2play_RenderFloat(player, sampleBuf, framesToRender);
if(!repeat_one && framesRead + framesToRender > framesLength) {
long fadeStart = (framesLength > framesRead) ? framesLength : framesRead;
long fadeEnd = (framesRead + framesToRender < totalFrames) ? framesRead + framesToRender : totalFrames;
const long fadeTotal = totalFrames - framesLength;
for(long fadePos = fadeStart; fadePos < fadeEnd; ++fadePos) {
const double scale = (double)(fadeTotal - (fadePos - framesLength)) / (double)fadeTotal;
const long offset = fadePos - framesRead;
float *samples = sampleBuf + offset * 2;
samples[0] = samples[0] * scale;
samples[1] = samples[1] * scale;
}
framesToRender = (int)(fadeEnd - framesRead);
}
if(!framesToRender)
break;
total += framesToRender;
framesRead += framesToRender;
}
return total;
}
- (long)seek:(long)frame {
if(frame < framesRead || !player) {
[self decoderShutdown];
if(![self decoderInitialize])
return 0;
}
while(framesRead < frame) {
int frames_todo = INT_MAX;
if(frames_todo > frame - framesRead)
frames_todo = (int)(frame - framesRead);
if(type == TYPE_S3M)
st3play_RenderFloat(player, NULL, frames_todo);
else if(type == TYPE_XM)
ft2play_RenderFloat(player, NULL, frames_todo);
framesRead += frames_todo;
}
framesRead = frame;
return frame;
}
- (void)close {
[self decoderShutdown];
if(data) {
if(dataWasMo3)
UNMO3_Free(data);
else
free(data);
data = NULL;
}
}
- (void)dealloc {
[self close];
}
+ (NSArray *)fileTypes {
return [NSArray arrayWithObjects:@"s3m", @"s3z", @"xm", @"xmz", @"mo3", @"umx", nil];
}
+ (NSArray *)mimeTypes {
return [NSArray arrayWithObjects:@"audio/x-s3m", @"audio/x-xm", nil];
}
+ (float)priority {
return 1.5;
}
@end