Default time, fade, loop count, and sample rate may now be overridden. Synchronized preferences strings tables. Spanish translation of new options pending, new releases won't be pushed until they're complete. Signed-off-by: Christopher Snowhill <kode54@gmail.com>
212 lines
4.5 KiB
Objective-C
212 lines
4.5 KiB
Objective-C
//
|
|
// HVLDecoder.m
|
|
// Hively
|
|
//
|
|
// Created by Christopher Snowhill on 10/29/13.
|
|
// Copyright 2013 __NoWork, Inc__. All rights reserved.
|
|
//
|
|
|
|
#import "HVLDecoder.h"
|
|
|
|
#import "PlaylistController.h"
|
|
|
|
static void oneTimeInit(void) {
|
|
static BOOL initialized = NO;
|
|
if(!initialized) {
|
|
hvl_InitReplayer();
|
|
initialized = YES;
|
|
}
|
|
}
|
|
|
|
@implementation HVLDecoder
|
|
|
|
+ (void)initialize {
|
|
if([self class] == [HVLDecoder class])
|
|
oneTimeInit();
|
|
}
|
|
|
|
- (BOOL)open:(id<CogSource>)s {
|
|
[s seek:0 whence:SEEK_END];
|
|
long size = [s tell];
|
|
[s seek:0 whence:SEEK_SET];
|
|
|
|
if(size > UINT_MAX)
|
|
return NO;
|
|
|
|
sampleRate = [[[[NSUserDefaultsController sharedUserDefaultsController] defaults] valueForKey:@"synthSampleRate"] doubleValue];
|
|
if(sampleRate < 8000.0) {
|
|
sampleRate = 44100.0;
|
|
} else if(sampleRate > 192000.0) {
|
|
sampleRate = 192000.0;
|
|
}
|
|
|
|
void *data = malloc(size);
|
|
[s read:data amount:size];
|
|
|
|
tune = hvl_LoadTune(data, (uint32_t)size, (int)sampleRate, 2);
|
|
free(data);
|
|
if(!tune)
|
|
return NO;
|
|
|
|
unsigned long safety = 2 * 60 * 60 * 50 * tune->ht_SpeedMultiplier;
|
|
|
|
NSURL *url = [s url];
|
|
if([[url fragment] length] == 0)
|
|
trackNumber = 0;
|
|
else
|
|
trackNumber = [[url fragment] intValue];
|
|
|
|
hvl_InitSubsong(tune, trackNumber);
|
|
|
|
unsigned long loops = 0;
|
|
|
|
while(loops < 2 && safety) {
|
|
while(!tune->ht_SongEndReached && safety) {
|
|
hvl_play_irq(tune);
|
|
--safety;
|
|
}
|
|
tune->ht_SongEndReached = 0;
|
|
++loops;
|
|
}
|
|
|
|
double defaultFade = [[[[NSUserDefaultsController sharedUserDefaultsController] defaults] valueForKey:@"synthDefaultFadeSeconds"] doubleValue];
|
|
if(defaultFade < 0.0) {
|
|
defaultFade = 0.0;
|
|
}
|
|
|
|
framesLength = tune->ht_PlayingTime * sampleRate / (tune->ht_SpeedMultiplier * 50);
|
|
framesFade = (int)ceil(sampleRate * defaultFade);
|
|
totalFrames = framesLength + framesFade;
|
|
|
|
framesRead = 0;
|
|
framesInBuffer = 0;
|
|
|
|
buffer = malloc(sizeof(int32_t) * ((int)ceil(sampleRate) / 50) * 2);
|
|
|
|
hvl_InitSubsong(tune, trackNumber);
|
|
|
|
[self willChangeValueForKey:@"properties"];
|
|
[self didChangeValueForKey:@"properties"];
|
|
|
|
return YES;
|
|
}
|
|
|
|
- (NSDictionary *)properties {
|
|
return @{ @"bitrate": @(0),
|
|
@"sampleRate": @(sampleRate),
|
|
@"totalFrames": @(totalFrames),
|
|
@"bitsPerSample": @(32),
|
|
@"floatingPoint": @(YES),
|
|
@"channels": @(2),
|
|
@"seekable": @(YES),
|
|
@"endian": @"host",
|
|
@"encoding": @"synthesized" };
|
|
}
|
|
|
|
- (NSDictionary *)metadata {
|
|
return @{};
|
|
}
|
|
|
|
- (int)readAudio:(void *)buf frames:(UInt32)frames {
|
|
BOOL repeatone = IsRepeatOneSet();
|
|
|
|
if(!repeatone && framesRead >= totalFrames)
|
|
return 0;
|
|
|
|
int total = 0;
|
|
while(total < frames) {
|
|
if(framesInBuffer) {
|
|
float *outbuffer = ((float *)buf) + total * 2;
|
|
int framesToCopy = frames - total;
|
|
if(framesToCopy > framesInBuffer)
|
|
framesToCopy = (int)framesInBuffer;
|
|
for(int i = 0; i < framesToCopy; ++i) {
|
|
outbuffer[0] = buffer[i * 2 + 0] * (1.0f / 16777216.0f);
|
|
outbuffer[1] = buffer[i * 2 + 1] * (1.0f / 16777216.0f);
|
|
outbuffer += 2;
|
|
}
|
|
framesInBuffer -= framesToCopy;
|
|
total += framesToCopy;
|
|
if(framesInBuffer) {
|
|
memcpy(buffer, buffer + framesToCopy * 2, sizeof(int32_t) * framesInBuffer * 2);
|
|
break;
|
|
}
|
|
}
|
|
hvl_DecodeFrame(tune, (int8_t *)buffer, ((int8_t *)buffer) + 4, 8);
|
|
framesInBuffer = (int)ceil(sampleRate / 50);
|
|
}
|
|
|
|
if(!repeatone && framesRead + total > framesLength) {
|
|
long fadeStart = (framesLength > framesRead) ? framesLength : framesRead;
|
|
long fadeEnd = (framesRead + total) > totalFrames ? totalFrames : (framesRead + total);
|
|
long fadePos;
|
|
|
|
float *buff = (float *)buf;
|
|
|
|
float fadeScale = (float)(totalFrames - fadeStart) / (float)framesFade;
|
|
float fadeStep = 1.0 / (float)framesFade;
|
|
for(fadePos = fadeStart; fadePos < fadeEnd; ++fadePos) {
|
|
buff[0] *= fadeScale;
|
|
buff[1] *= fadeScale;
|
|
buff += 2;
|
|
fadeScale -= fadeStep;
|
|
if(fadeScale <= 0.0) break;
|
|
}
|
|
total = (int)(fadePos - fadeStart);
|
|
}
|
|
|
|
framesRead += total;
|
|
|
|
return total;
|
|
}
|
|
|
|
- (long)seek:(long)frame {
|
|
if(frame < framesRead) {
|
|
hvl_InitSubsong(tune, trackNumber);
|
|
framesRead = 0;
|
|
}
|
|
|
|
while(framesRead < frame) {
|
|
hvl_play_irq(tune);
|
|
framesRead += (int)ceil(sampleRate / 50);
|
|
}
|
|
|
|
return framesRead;
|
|
}
|
|
|
|
- (void)close {
|
|
if(tune) {
|
|
hvl_FreeTune(tune);
|
|
tune = NULL;
|
|
}
|
|
|
|
if(buffer) {
|
|
free(buffer);
|
|
buffer = NULL;
|
|
}
|
|
}
|
|
|
|
- (void)dealloc {
|
|
[self close];
|
|
}
|
|
|
|
+ (NSArray *)fileTypes {
|
|
return @[@"hvl", @"ahx"];
|
|
}
|
|
|
|
+ (NSArray *)fileTypeAssociations {
|
|
return @[
|
|
@[@"Hively Tracker File", @"song.icns", @"hvl"],
|
|
@[@"Abyss' Highest eXperience File", @"song.icns", @"ahx"]
|
|
];
|
|
}
|
|
|
|
+ (NSArray *)mimeTypes {
|
|
return nil;
|
|
}
|
|
|
|
+ (float)priority {
|
|
return 1.0;
|
|
}
|
|
|
|
@end
|