Cog/Plugins/Hively/Hively/HVLDecoder.m
Christopher Snowhill b025a21e13 [Synthesizers] Implement default overrides
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>
2022-07-02 01:25:24 -07:00

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