Cog/Plugins/BASSMODS/BASSMODS/BASSDecoder.mm
Christopher Snowhill 85c7073649 Reformat my own source code with clang-format
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-02-06 21:49:27 -08:00

251 lines
5.8 KiB
Text

//
// BASSDecoder.m
// Cog
//
// Created by Christopher Snowhill on 7/03/14.
// Copyright 2014 __NoWork, Inc__. All rights reserved.
//
#include <mutex>
#import "BASSDecoder.h"
#import "umx.h"
#import "Logging.h"
#import "PlaylistController.h"
static class Bass_Initializer {
std::mutex lock;
bool initialized;
public:
Bass_Initializer()
: initialized(false) {
}
~Bass_Initializer() {
if(initialized) {
BASS_Free();
}
}
bool check_initialized() {
std::lock_guard<std::mutex> lock(this->lock);
return initialized;
}
bool initialize() {
std::lock_guard<std::mutex> lock(this->lock);
if(!initialized) {
BASS_SetConfig(BASS_CONFIG_UPDATEPERIOD, 0);
initialized = !!BASS_Init(0, 44100, 0, NULL, NULL);
}
return initialized;
}
} g_initializer;
static void SyncProc(HSYNC handle, DWORD channel, DWORD data, void *user) {
BASSDecoder *decoder = (__bridge BASSDecoder *)user;
[decoder sync];
}
@implementation BASSDecoder
+ (void)initialize {
if(self == [BASSDecoder class]) {
g_initializer.initialize();
}
}
- (void)sync {
++loops;
}
- (BOOL)open:(id<CogSource>)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];
void *try_data = unpackUmx(data, &size);
if(try_data) {
free(data);
data = try_data;
}
if(size < 4 || (memcmp(data, "mo3", 3) != 0 && memcmp(data, "IMPM", 4) != 0)) {
ALog(@"BASS was passed a non-IT module");
free(data);
return NO;
}
NSURL *url = [s url];
int track_num;
if([[url fragment] length] == 0)
track_num = 0;
else
track_num = [[url fragment] intValue];
int resampling_int = -1;
NSString *resampling = [[NSUserDefaults standardUserDefaults] stringForKey:@"resampling"];
if([resampling isEqualToString:@"zoh"])
resampling_int = 0;
else if([resampling isEqualToString:@"blep"])
resampling_int = 0;
else if([resampling isEqualToString:@"linear"])
resampling_int = 1;
else if([resampling isEqualToString:@"blam"])
resampling_int = 1;
else if([resampling isEqualToString:@"cubic"])
resampling_int = 1;
else if([resampling isEqualToString:@"sinc"])
resampling_int = 2;
music = BASS_MusicLoad(1, data, 0, (DWORD)size, BASS_SAMPLE_FLOAT | BASS_SAMPLE_LOOP | BASS_MUSIC_STOPBACK | BASS_MUSIC_RAMP | BASS_MUSIC_PRESCAN | BASS_MUSIC_DECODE | (resampling_int == 0 ? BASS_MUSIC_NONINTER : ((resampling_int == 2) ? BASS_MUSIC_SINCINTER : 0)), 44100);
if(!music) {
ALog(@"BASS MusicLoad error: %u", BASS_ErrorGetCode());
free(data);
return NO;
}
BASS_ChannelSetPosition(music, MAKELONG(track_num, 0), BASS_POS_MUSIC_ORDER);
length = BASS_ChannelGetLength(music, BASS_POS_BYTE) / (sizeof(float) * 2);
BASS_ChannelSetSync(music, BASS_SYNC_END, 0, SyncProc, (__bridge void *)self);
loops = 0;
fadeTotal = fadeRemain = 44100 * 8;
[self willChangeValueForKey:@"properties"];
[self didChangeValueForKey:@"properties"];
return YES;
}
- (NSDictionary *)properties {
return [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:0], @"bitrate",
[NSNumber numberWithFloat:44100], @"sampleRate",
[NSNumber numberWithDouble:length], @"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) {
float *fbuf = (float *)buf + total * 2;
int framesToRender = 1024;
if(framesToRender > frames)
framesToRender = frames;
int rendered = BASS_ChannelGetData(music, fbuf, (framesToRender * 2 * sizeof(float)) | BASS_DATA_FLOAT);
if(rendered < 0) {
ALog(@"BASS ChannelGetData error: %u", BASS_ErrorGetCode());
return 0;
}
rendered /= sizeof(float) * 2;
if(rendered <= 0)
break;
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 {
long pos = BASS_ChannelGetPosition(music, BASS_POS_BYTE) / (sizeof(float) * 2);
if(frame < pos) {
// Reset. Dumb cannot seek backwards. It's dumb.
[self cleanUp];
[source seek:0 whence:SEEK_SET];
[self open:source];
pos = 0;
}
BASS_ChannelSetPosition(music, frame * (sizeof(float) * 2), BASS_POS_BYTE | BASS_POS_DECODETO);
return frame;
}
- (void)cleanUp {
if(music) {
BASS_MusicFree(music);
music = 0;
}
}
- (void)close {
[self cleanUp];
if(source) {
[source close];
[self setSource:nil];
}
}
- (void)dealloc {
[self close];
}
- (void)setSource:(id<CogSource>)s {
source = s;
}
- (id<CogSource>)source {
return source;
}
+ (NSArray *)fileTypes {
return [NSArray arrayWithObjects:@"it", @"itz", @"umx", @"mo3", nil];
}
+ (NSArray *)mimeTypes {
return [NSArray arrayWithObjects:@"audio/x-it", nil];
}
+ (float)priority {
return 1.5;
}
@end