Cog/Plugins/Hively/Hively/HVLDecoder.m
Christopher Snowhill 3c351f6968 [Input API] Change input readAudio method
readAudio now returns an AudioChunk object directly, and all inputs have
been changed to accomodate this. Also, input and converter processing
have been altered to better work with this.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-10 15:14:47 -07:00

220 lines
4.8 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 @{};
}
- (AudioChunk *)readAudio {
int frames = 1024;
float buffer[frames * 2];
void *buf = (void *)buffer;
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;
id audioChunkClass = NSClassFromString(@"AudioChunk");
AudioChunk *chunk = [[audioChunkClass alloc] initWithProperties:[self properties]];
[chunk assignSamples:buffer frameCount:total];
return chunk;
}
- (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