cog/Plugins/WavPack/WavPackDecoder.m

331 lines
7.0 KiB
Objective-C

//
// WavPackFile.m
// Cog
//
// Created by Vincent Spader on 6/6/05.
// Copyright 2005 Vincent Spader All rights reserved.
//
#import "WavPackDecoder.h"
#import "Logging.h"
@implementation WavPackReader
- (id)initWithSource:(id<CogSource>)s {
self = [super init];
if(self) {
source = s;
}
return self;
}
- (void)setSource:(id<CogSource>)s {
source = s;
}
- (id<CogSource>)source {
return source;
}
@end
@implementation WavPackDecoder
int32_t ReadBytesProc(void *ds, void *data, int32_t bcount) {
WavPackReader *wv = (__bridge WavPackReader *)ds;
return (int32_t)[[wv source] read:data amount:bcount];
}
uint32_t GetPosProc(void *ds) {
WavPackReader *wv = (__bridge WavPackReader *)ds;
return (uint32_t)[[wv source] tell];
}
int SetPosAbsProc(void *ds, uint32_t pos) {
WavPackReader *wv = (__bridge WavPackReader *)ds;
return ([[wv source] seek:pos whence:SEEK_SET] ? 0 : -1);
}
int SetPosRelProc(void *ds, int32_t delta, int mode) {
WavPackReader *wv = (__bridge WavPackReader *)ds;
return ([[wv source] seek:delta whence:mode] ? 0 : -1);
}
int PushBackByteProc(void *ds, int c) {
WavPackReader *wv = (__bridge WavPackReader *)ds;
if([[wv source] seekable]) {
[[wv source] seek:-1 whence:SEEK_CUR];
return c;
} else {
return -1;
}
}
uint32_t GetLengthProc(void *ds) {
WavPackReader *wv = (__bridge WavPackReader *)ds;
if([[wv source] seekable]) {
long currentPos = [[wv source] tell];
[[wv source] seek:0 whence:SEEK_END];
long size = [[wv source] tell];
[[wv source] seek:currentPos whence:SEEK_SET];
return (uint32_t)size;
} else {
return 0;
}
}
int CanSeekProc(void *ds) {
WavPackReader *wv = (__bridge WavPackReader *)ds;
return [[wv source] seekable];
}
int32_t WriteBytesProc(void *ds, void *data, int32_t bcount) {
return -1;
}
- (id)init {
self = [super init];
if(self) {
wpc = NULL;
inputBuffer = NULL;
inputBufferSize = 0;
}
return self;
}
- (BOOL)open:(id<CogSource>)s {
int open_flags = 0;
char error[80];
wv = [[WavPackReader alloc] initWithSource:s];
if([s seekable]) {
id audioSourceClass = NSClassFromString(@"AudioSource");
NSURL *wvcurl = [[s url] URLByDeletingPathExtension];
wvcurl = [wvcurl URLByAppendingPathExtension:@"wvc"];
id<CogSource> wvcsrc = [audioSourceClass audioSourceForURL:wvcurl];
if([wvcsrc open:wvcurl]) {
wvc = [[WavPackReader alloc] initWithSource:wvcsrc];
} else {
wvc = nil;
}
} else {
wvc = nil;
}
reader.read_bytes = ReadBytesProc;
reader.get_pos = GetPosProc;
reader.set_pos_abs = SetPosAbsProc;
reader.set_pos_rel = SetPosRelProc;
reader.push_back_byte = PushBackByteProc;
reader.get_length = GetLengthProc;
reader.can_seek = CanSeekProc;
reader.write_bytes = WriteBytesProc;
open_flags |= OPEN_DSD_NATIVE | OPEN_ALT_TYPES;
if(![s seekable])
open_flags |= OPEN_STREAMING;
wpc = WavpackOpenFileInputEx(&reader, (__bridge void *)(wv), (__bridge void *)(wvc), error, open_flags, 0);
if(!wpc) {
DLog(@"Unable to open file..");
return NO;
}
channels = WavpackGetNumChannels(wpc);
channelConfig = WavpackGetChannelMask(wpc);
bitsPerSample = WavpackGetBitsPerSample(wpc);
frequency = WavpackGetSampleRate(wpc);
totalFrames = WavpackGetNumSamples(wpc);
isDSD = NO;
float nativeFrequency = WavpackGetNativeSampleRate(wpc);
if(nativeFrequency != frequency && bitsPerSample == 8) {
isDSD = YES;
frequency = nativeFrequency;
bitsPerSample = 1;
totalFrames *= 8;
}
bitrate = (int)(WavpackGetAverageBitrate(wpc, TRUE) / 1000.0);
floatingPoint = MODE_FLOAT & WavpackGetMode(wpc) && 127 == WavpackGetFloatNormExp(wpc);
isLossy = !(WavpackGetMode(wpc) & MODE_LOSSLESS);
[self willChangeValueForKey:@"properties"];
[self didChangeValueForKey:@"properties"];
return YES;
}
/*
- (int)fillBuffer:(void *)buf ofSize:(UInt32)size
{
int numsamples;
int n;
void *sampleBuf = malloc(size*2);
numsamples = size/(bitsPerSample/8)/channels;
n = WavpackUnpackSamples(wpc, sampleBuf, numsamples);
int i;
for (i = 0; i < n*channels; i++)
{
((UInt16 *)buf)[i] = ((UInt32 *)sampleBuf)[i];
}
n *= (bitsPerSample/8)*channels;
free(sampleBuf);
return n;
}
*/
- (AudioChunk *)readAudio {
int32_t frames = 1024;
id audioChunkClass = NSClassFromString(@"AudioChunk");
AudioChunk *chunk = [[audioChunkClass alloc] initWithProperties:[self properties]];
uint32_t sample;
int32_t audioSample;
uint32_t samplesRead;
int8_t *alias8;
int16_t *alias16;
int32_t *alias32;
const size_t bufferSize = frames * [chunk format].mBytesPerFrame;
uint8_t buffer[bufferSize];
void *buf = (void *)buffer;
size_t newSize = frames * sizeof(int32_t) * channels;
if(!inputBuffer || newSize > inputBufferSize) {
inputBuffer = realloc(inputBuffer, inputBufferSize = newSize);
}
samplesRead = WavpackUnpackSamples(wpc, inputBuffer, frames);
switch(bitsPerSample) {
case 1:
case 8:
// No need for byte swapping
alias8 = buf;
for(sample = 0; sample < samplesRead * channels; ++sample) {
*alias8++ = (int8_t)inputBuffer[sample];
}
break;
case 16:
// Convert to little endian byte order
alias16 = buf;
for(sample = 0; sample < samplesRead * channels; ++sample) {
*alias16++ = OSSwapHostToLittleInt16((int16_t)inputBuffer[sample]);
}
break;
case 24:
// Convert to little endian byte order
alias8 = buf;
for(sample = 0; sample < samplesRead * channels; ++sample) {
audioSample = inputBuffer[sample];
*alias8++ = (int8_t)audioSample;
*alias8++ = (int8_t)(audioSample >> 8);
*alias8++ = (int8_t)(audioSample >> 16);
}
break;
case 32:
// Convert to little endian byte order
alias32 = buf;
for(sample = 0; sample < samplesRead * channels; ++sample) {
*alias32++ = OSSwapHostToLittleInt32(inputBuffer[sample]);
}
break;
default:
ALog(@"Unsupported sample size: %d", bitsPerSample);
}
[chunk assignSamples:buffer frameCount:samplesRead];
return chunk;
}
- (long)seek:(long)frame {
uint32_t trueFrame = (uint32_t)frame;
if(isDSD) {
trueFrame /= 8;
frame = trueFrame * 8;
}
WavpackSeekSample(wpc, trueFrame);
return frame;
}
- (void)close {
if(wpc) {
WavpackCloseFile(wpc);
wpc = NULL;
}
if(inputBuffer) {
free(inputBuffer);
inputBuffer = NULL;
inputBufferSize = 0;
}
wvc = nil;
wv = nil;
}
- (void)dealloc {
[self close];
}
- (NSDictionary *)properties {
return @{ @"channels": @(channels),
@"channelConfig": @(channelConfig),
@"bitsPerSample": @(bitsPerSample),
@"bitrate": @(bitrate),
@"sampleRate": @(frequency),
@"floatingPoint": @(floatingPoint),
@"totalFrames": @(totalFrames),
@"seekable": @([[wv source] seekable]),
@"codec": @"WavPack",
@"endian": @"little",
@"encoding": isLossy ? @"lossy" : @"lossless" };
}
- (NSDictionary *)metadata {
return @{};
}
+ (NSArray *)fileTypes {
return @[@"wv", @"wvp"];
}
+ (NSArray *)mimeTypes {
return @[@"audio/x-wavpack"];
}
+ (float)priority {
return 1.0;
}
+ (NSArray *)fileTypeAssociations {
return @[
@[@"Wavpack File", @"wv.icns", @"wv", @"wvp"]
];
}
@end