2005-06-06 17:47:29 +00:00
|
|
|
//
|
|
|
|
// WavPackFile.m
|
|
|
|
// Cog
|
|
|
|
//
|
2005-07-02 21:02:06 +00:00
|
|
|
// Created by Vincent Spader on 6/6/05.
|
|
|
|
// Copyright 2005 Vincent Spader All rights reserved.
|
2005-06-06 17:47:29 +00:00
|
|
|
//
|
|
|
|
|
2007-02-24 20:36:27 +00:00
|
|
|
#import "WavPackDecoder.h"
|
2005-06-06 17:47:29 +00:00
|
|
|
|
2013-10-11 12:03:55 +00:00
|
|
|
#import "Logging.h"
|
2005-06-06 17:47:29 +00:00
|
|
|
|
2007-02-24 20:36:27 +00:00
|
|
|
@implementation WavPackDecoder
|
2005-06-06 17:47:29 +00:00
|
|
|
|
2007-03-04 20:47:49 +00:00
|
|
|
int32_t ReadBytesProc(void *ds, void *data, int32_t bcount)
|
|
|
|
{
|
2016-05-05 20:05:39 +00:00
|
|
|
WavPackDecoder *decoder = (__bridge WavPackDecoder *)ds;
|
2007-03-04 20:47:49 +00:00
|
|
|
|
2016-06-30 05:10:29 +00:00
|
|
|
return (int32_t) [[decoder source] read:data amount:bcount];
|
2007-03-04 20:47:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t GetPosProc(void *ds)
|
|
|
|
{
|
2016-05-05 20:05:39 +00:00
|
|
|
WavPackDecoder *decoder = (__bridge WavPackDecoder *)ds;
|
2007-03-04 20:47:49 +00:00
|
|
|
|
2016-06-30 05:10:29 +00:00
|
|
|
return (uint32_t) [[decoder source] tell];
|
2007-03-04 20:47:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int SetPosAbsProc(void *ds, uint32_t pos)
|
|
|
|
{
|
2016-05-05 20:05:39 +00:00
|
|
|
WavPackDecoder *decoder = (__bridge WavPackDecoder *)ds;
|
2007-03-04 20:47:49 +00:00
|
|
|
|
|
|
|
return ([[decoder source] seek:pos whence:SEEK_SET] ? 0: -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
int SetPosRelProc(void *ds, int32_t delta, int mode)
|
|
|
|
{
|
2016-05-05 20:05:39 +00:00
|
|
|
WavPackDecoder *decoder = (__bridge WavPackDecoder *)ds;
|
2007-03-04 20:47:49 +00:00
|
|
|
|
|
|
|
return ([[decoder source] seek:delta whence:mode] ? 0: -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
int PushBackByteProc(void *ds, int c)
|
|
|
|
{
|
2016-05-05 20:05:39 +00:00
|
|
|
WavPackDecoder *decoder = (__bridge WavPackDecoder *)ds;
|
2007-03-04 20:47:49 +00:00
|
|
|
|
|
|
|
if ([[decoder source] seekable]) {
|
|
|
|
[[decoder source] seek:-1 whence:SEEK_CUR];
|
|
|
|
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t GetLengthProc(void *ds)
|
|
|
|
{
|
2016-05-05 20:05:39 +00:00
|
|
|
WavPackDecoder *decoder = (__bridge WavPackDecoder *)ds;
|
2007-03-04 20:47:49 +00:00
|
|
|
|
|
|
|
if ([[decoder source] seekable]) {
|
|
|
|
long currentPos = [[decoder source] tell];
|
|
|
|
|
|
|
|
[[decoder source] seek:0 whence:SEEK_END];
|
|
|
|
long size = [[decoder source] tell];
|
|
|
|
|
|
|
|
[[decoder source] seek:currentPos whence:SEEK_SET];
|
|
|
|
|
2016-06-30 05:10:29 +00:00
|
|
|
return (uint32_t) size;
|
2007-03-04 20:47:49 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int CanSeekProc(void *ds)
|
|
|
|
{
|
2016-05-05 20:05:39 +00:00
|
|
|
WavPackDecoder *decoder = (__bridge WavPackDecoder *)ds;
|
2007-03-04 20:47:49 +00:00
|
|
|
|
|
|
|
return [[decoder source] seekable];
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t WriteBytesProc(void *ds, void *data, int32_t bcount)
|
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2016-06-19 19:57:18 +00:00
|
|
|
- (id)init
|
|
|
|
{
|
|
|
|
self = [super init];
|
|
|
|
if (self)
|
|
|
|
{
|
|
|
|
wpc = NULL;
|
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
2007-03-04 20:47:49 +00:00
|
|
|
|
|
|
|
- (BOOL)open:(id<CogSource>)s
|
2005-06-06 17:47:29 +00:00
|
|
|
{
|
|
|
|
int open_flags = 0;
|
|
|
|
char error[80];
|
2007-03-04 20:47:49 +00:00
|
|
|
|
|
|
|
[self setSource:s];
|
2005-06-06 17:47:29 +00:00
|
|
|
|
2007-03-04 20:47:49 +00:00
|
|
|
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;
|
2016-08-28 20:03:54 +00:00
|
|
|
|
|
|
|
open_flags |= OPEN_DSD_AS_PCM | OPEN_ALT_TYPES;
|
2007-03-04 20:47:49 +00:00
|
|
|
|
|
|
|
//No corrections file (WVC) support at the moment.
|
2016-05-05 20:05:39 +00:00
|
|
|
wpc = WavpackOpenFileInputEx(&reader, (__bridge void *)(self), NULL, error, open_flags, 0);
|
2007-03-04 20:47:49 +00:00
|
|
|
if (!wpc) {
|
2013-10-11 12:03:55 +00:00
|
|
|
DLog(@"Unable to open file..");
|
2005-06-06 17:47:29 +00:00
|
|
|
return NO;
|
2007-03-04 20:47:49 +00:00
|
|
|
}
|
2005-06-06 17:47:29 +00:00
|
|
|
|
|
|
|
channels = WavpackGetNumChannels(wpc);
|
2006-04-21 20:43:47 +00:00
|
|
|
bitsPerSample = WavpackGetBitsPerSample(wpc);
|
2005-06-29 15:28:20 +00:00
|
|
|
|
|
|
|
frequency = WavpackGetSampleRate(wpc);
|
2005-06-06 17:47:29 +00:00
|
|
|
|
2007-11-24 20:16:27 +00:00
|
|
|
totalFrames = WavpackGetNumSamples(wpc);
|
2005-06-06 17:47:29 +00:00
|
|
|
|
2006-06-19 00:39:41 +00:00
|
|
|
bitrate = (int)(WavpackGetAverageBitrate(wpc, TRUE)/1000.0);
|
2016-08-28 20:03:54 +00:00
|
|
|
|
|
|
|
floatingPoint = MODE_FLOAT & WavpackGetMode(wpc) && 127 == WavpackGetFloatNormExp(wpc);
|
2005-06-06 17:47:29 +00:00
|
|
|
|
2007-03-04 20:47:49 +00:00
|
|
|
[self willChangeValueForKey:@"properties"];
|
|
|
|
[self didChangeValueForKey:@"properties"];
|
|
|
|
|
2005-06-29 15:28:20 +00:00
|
|
|
return YES;
|
2005-06-06 17:47:29 +00:00
|
|
|
}
|
2007-03-04 20:47:49 +00:00
|
|
|
/*
|
2005-06-06 17:47:29 +00:00
|
|
|
- (int)fillBuffer:(void *)buf ofSize:(UInt32)size
|
|
|
|
{
|
|
|
|
int numsamples;
|
|
|
|
int n;
|
2006-04-21 20:43:47 +00:00
|
|
|
void *sampleBuf = malloc(size*2);
|
2005-06-06 17:47:29 +00:00
|
|
|
|
2006-04-21 20:43:47 +00:00
|
|
|
numsamples = size/(bitsPerSample/8)/channels;
|
|
|
|
n = WavpackUnpackSamples(wpc, sampleBuf, numsamples);
|
2005-06-06 17:47:29 +00:00
|
|
|
|
2005-06-29 15:28:20 +00:00
|
|
|
int i;
|
2006-04-21 20:43:47 +00:00
|
|
|
for (i = 0; i < n*channels; i++)
|
2005-06-29 15:28:20 +00:00
|
|
|
{
|
2006-04-21 20:43:47 +00:00
|
|
|
((UInt16 *)buf)[i] = ((UInt32 *)sampleBuf)[i];
|
2005-06-29 15:28:20 +00:00
|
|
|
}
|
2007-03-04 20:47:49 +00:00
|
|
|
|
2006-04-21 20:43:47 +00:00
|
|
|
n *= (bitsPerSample/8)*channels;
|
2005-06-29 15:28:20 +00:00
|
|
|
|
2006-04-28 23:19:14 +00:00
|
|
|
free(sampleBuf);
|
|
|
|
|
2005-06-06 17:47:29 +00:00
|
|
|
return n;
|
|
|
|
}
|
2007-03-04 20:47:49 +00:00
|
|
|
*/
|
2007-11-24 20:16:27 +00:00
|
|
|
- (int)readAudio:(void *)buf frames:(UInt32)frames
|
2007-03-04 20:47:49 +00:00
|
|
|
{
|
|
|
|
uint32_t sample;
|
|
|
|
int32_t audioSample;
|
|
|
|
uint32_t samplesRead;
|
|
|
|
int8_t *alias8;
|
|
|
|
int16_t *alias16;
|
|
|
|
int32_t *alias32;
|
|
|
|
|
2007-11-24 20:16:27 +00:00
|
|
|
int32_t *inputBuffer = malloc(frames*sizeof(int32_t));
|
2007-03-04 20:47:49 +00:00
|
|
|
|
|
|
|
// Wavpack uses "complete" samples (one sample across all channels), i.e. a Core Audio frame
|
2007-11-24 20:16:27 +00:00
|
|
|
samplesRead = WavpackUnpackSamples(wpc, inputBuffer, frames/channels);
|
2007-03-04 20:47:49 +00:00
|
|
|
|
|
|
|
switch(bitsPerSample) {
|
|
|
|
case 8:
|
|
|
|
// No need for byte swapping
|
|
|
|
alias8 = buf;
|
|
|
|
for(sample = 0; sample < samplesRead * channels; ++sample) {
|
|
|
|
*alias8++ = (int8_t)inputBuffer[sample];
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 16:
|
2009-03-03 16:19:46 +00:00
|
|
|
// Convert to little endian byte order
|
2007-03-04 20:47:49 +00:00
|
|
|
alias16 = buf;
|
|
|
|
for(sample = 0; sample < samplesRead*channels; ++sample) {
|
2009-03-03 16:19:46 +00:00
|
|
|
*alias16++ = OSSwapHostToLittleInt16((int16_t)inputBuffer[sample]);
|
2007-03-04 20:47:49 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 24:
|
2009-03-03 16:19:46 +00:00
|
|
|
// Convert to little endian byte order
|
2007-03-04 20:47:49 +00:00
|
|
|
alias8 = buf;
|
|
|
|
for(sample = 0; sample < samplesRead * channels; ++sample) {
|
2007-03-04 20:52:39 +00:00
|
|
|
audioSample = inputBuffer[sample];
|
2007-03-04 20:47:49 +00:00
|
|
|
*alias8++ = (int8_t)audioSample;
|
2009-03-03 16:19:46 +00:00
|
|
|
*alias8++ = (int8_t)(audioSample >> 8);
|
|
|
|
*alias8++ = (int8_t)(audioSample >> 16);
|
2007-03-04 20:47:49 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 32:
|
2009-03-03 16:19:46 +00:00
|
|
|
// Convert to little endian byte order
|
2007-03-04 20:47:49 +00:00
|
|
|
alias32 = buf;
|
|
|
|
for(sample = 0; sample < samplesRead * channels; ++sample) {
|
2009-03-03 16:19:46 +00:00
|
|
|
*alias32++ = OSSwapHostToLittleInt32(inputBuffer[sample]);
|
2007-03-04 20:47:49 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
2013-10-11 12:03:55 +00:00
|
|
|
ALog(@"Unsupported sample size: %d", bitsPerSample);
|
2007-03-04 20:47:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
free(inputBuffer);
|
|
|
|
|
2007-11-24 20:16:27 +00:00
|
|
|
return samplesRead;
|
2007-03-04 20:47:49 +00:00
|
|
|
}
|
2005-06-06 17:47:29 +00:00
|
|
|
|
2007-11-24 20:16:27 +00:00
|
|
|
- (long)seek:(long)frame
|
2005-06-06 17:47:29 +00:00
|
|
|
{
|
2016-06-30 05:10:29 +00:00
|
|
|
WavpackSeekSample(wpc, (uint32_t) frame);
|
2005-06-06 17:47:29 +00:00
|
|
|
|
2007-11-24 20:16:27 +00:00
|
|
|
return frame;
|
2005-06-06 17:47:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)close
|
|
|
|
{
|
2016-06-19 19:57:18 +00:00
|
|
|
if (wpc) {
|
|
|
|
WavpackCloseFile(wpc);
|
|
|
|
wpc = NULL;
|
|
|
|
}
|
2016-05-05 20:05:39 +00:00
|
|
|
source = nil;
|
2005-06-06 17:47:29 +00:00
|
|
|
}
|
|
|
|
|
2016-06-19 19:57:18 +00:00
|
|
|
- (void)dealloc
|
|
|
|
{
|
|
|
|
[self close];
|
|
|
|
}
|
|
|
|
|
2007-03-04 20:47:49 +00:00
|
|
|
- (void)setSource:(id<CogSource>)s
|
|
|
|
{
|
|
|
|
source = s;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id<CogSource>)source
|
|
|
|
{
|
|
|
|
return source;
|
|
|
|
}
|
|
|
|
|
2007-02-24 20:36:27 +00:00
|
|
|
- (NSDictionary *)properties
|
|
|
|
{
|
|
|
|
return [NSDictionary dictionaryWithObjectsAndKeys:
|
|
|
|
[NSNumber numberWithInt:channels],@"channels",
|
|
|
|
[NSNumber numberWithInt:bitsPerSample],@"bitsPerSample",
|
|
|
|
[NSNumber numberWithInt:bitrate],@"bitrate",
|
|
|
|
[NSNumber numberWithFloat:frequency],@"sampleRate",
|
2016-08-28 20:03:54 +00:00
|
|
|
[NSNumber numberWithBool:floatingPoint],@"floatingPoint",
|
2007-11-24 20:16:27 +00:00
|
|
|
[NSNumber numberWithDouble:totalFrames],@"totalFrames",
|
2007-05-27 15:11:30 +00:00
|
|
|
[NSNumber numberWithBool:[source seekable]], @"seekable",
|
2009-03-03 16:19:46 +00:00
|
|
|
@"little",@"endian",
|
2007-02-24 20:36:27 +00:00
|
|
|
nil];
|
|
|
|
}
|
|
|
|
|
|
|
|
+ (NSArray *)fileTypes
|
|
|
|
{
|
2007-10-14 18:39:58 +00:00
|
|
|
return [NSArray arrayWithObjects:@"wv", @"wvp", nil];
|
|
|
|
}
|
|
|
|
|
|
|
|
+ (NSArray *)mimeTypes
|
|
|
|
{
|
|
|
|
return [NSArray arrayWithObjects:@"audio/x-wavpack", nil];
|
2007-02-24 20:36:27 +00:00
|
|
|
}
|
|
|
|
|
Implemented support for multiple decoders per file name extension, with a floating point priority control per interface. In the event that more than one input is registered to a given extension, and we match that extension, it will be passed off to an instance of the multi-decoder wrapper, which will try opening the file with all of the decoders in order of priority, until either one of them accepts it, or all of them have failed. This paves the way for adding a VGMSTREAM input, so I can give it a very low priority, since it has several formats that are verified by file name extension only. All current inputs have been given a priority of 1.0, except for CoreAudio, which was given a priority of 0.5, because it contains an MP3 and AC3 decoders that I'd rather not use if I don't have to.
2013-10-21 17:54:11 +00:00
|
|
|
+ (float)priority
|
|
|
|
{
|
|
|
|
return 1.0;
|
|
|
|
}
|
|
|
|
|
2007-02-24 20:36:27 +00:00
|
|
|
|
2005-06-06 17:47:29 +00:00
|
|
|
@end
|