2005-06-02 18:16:43 +00:00
|
|
|
//
|
|
|
|
// MusepackFile.m
|
|
|
|
// zyVorbis
|
|
|
|
//
|
|
|
|
// Created by Vincent Spader on 1/23/05.
|
2005-07-02 21:02:06 +00:00
|
|
|
// Copyright 2005 Vincent Spader All rights reserved.
|
2005-06-02 18:16:43 +00:00
|
|
|
//
|
|
|
|
|
2007-02-24 20:36:27 +00:00
|
|
|
#import "MusepackDecoder.h"
|
2005-06-02 18:16:43 +00:00
|
|
|
|
2013-10-11 12:03:55 +00:00
|
|
|
#import "Logging.h"
|
|
|
|
|
2007-02-24 20:36:27 +00:00
|
|
|
@implementation MusepackDecoder
|
2005-06-02 18:16:43 +00:00
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
mpc_int32_t ReadProc(mpc_reader *p_reader, void *ptr, mpc_int32_t size) {
|
|
|
|
MusepackDecoder *decoder = (__bridge MusepackDecoder *)p_reader->data;
|
|
|
|
|
2021-04-30 01:16:24 +00:00
|
|
|
return (mpc_int32_t)[[decoder source] read:ptr amount:size];
|
2007-03-04 18:41:43 +00:00
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
mpc_bool_t SeekProc(mpc_reader *p_reader, mpc_int32_t offset) {
|
|
|
|
MusepackDecoder *decoder = (__bridge MusepackDecoder *)p_reader->data;
|
2007-07-11 01:20:32 +00:00
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
return [[decoder source] seek:offset whence:SEEK_SET];
|
2007-03-04 18:41:43 +00:00
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
mpc_int32_t TellProc(mpc_reader *p_reader) {
|
|
|
|
MusepackDecoder *decoder = (__bridge MusepackDecoder *)p_reader->data;
|
|
|
|
|
|
|
|
return (mpc_int32_t)[[decoder source] tell];
|
2007-03-04 18:41:43 +00:00
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
mpc_int32_t GetSizeProc(mpc_reader *p_reader) {
|
|
|
|
MusepackDecoder *decoder = (__bridge MusepackDecoder *)p_reader->data;
|
|
|
|
|
|
|
|
if([[decoder source] seekable]) {
|
2007-03-04 18:41:43 +00:00
|
|
|
long currentPos = [[decoder source] tell];
|
2022-02-07 05:49:27 +00:00
|
|
|
|
2007-03-04 18:41:43 +00:00
|
|
|
[[decoder source] seek:0 whence:SEEK_END];
|
|
|
|
long size = [[decoder source] tell];
|
2022-02-07 05:49:27 +00:00
|
|
|
|
2007-03-04 18:41:43 +00:00
|
|
|
[[decoder source] seek:currentPos whence:SEEK_SET];
|
2022-02-07 05:49:27 +00:00
|
|
|
|
|
|
|
if(size > INT32_MAX)
|
|
|
|
size = INT32_MAX;
|
|
|
|
|
2021-04-30 01:16:24 +00:00
|
|
|
return (mpc_int32_t)size;
|
2022-02-07 05:49:27 +00:00
|
|
|
} else {
|
2007-03-04 18:41:43 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
2005-06-02 18:16:43 +00:00
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
mpc_bool_t CanSeekProc(mpc_reader *p_reader) {
|
|
|
|
MusepackDecoder *decoder = (__bridge MusepackDecoder *)p_reader->data;
|
|
|
|
|
2007-03-04 18:41:43 +00:00
|
|
|
return [[decoder source] seekable];
|
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
- (BOOL)open:(id<CogSource>)s {
|
|
|
|
[self setSource:s];
|
|
|
|
|
2007-03-04 20:47:49 +00:00
|
|
|
reader.read = ReadProc;
|
|
|
|
reader.seek = SeekProc;
|
|
|
|
reader.tell = TellProc;
|
|
|
|
reader.get_size = GetSizeProc;
|
|
|
|
reader.canseek = CanSeekProc;
|
2016-05-05 20:05:39 +00:00
|
|
|
reader.data = (__bridge void *)(self);
|
2022-02-07 05:49:27 +00:00
|
|
|
|
2014-10-20 00:42:04 +00:00
|
|
|
/* instantiate a demuxer with our reader */
|
2022-02-07 05:49:27 +00:00
|
|
|
demux = mpc_demux_init(&reader);
|
|
|
|
if(!demux) {
|
2013-10-11 12:03:55 +00:00
|
|
|
DLog(@"Error initializing decoder.");
|
2007-02-24 20:36:27 +00:00
|
|
|
return NO;
|
|
|
|
}
|
2022-02-07 05:49:27 +00:00
|
|
|
mpc_demux_get_info(demux, &info);
|
2005-06-02 18:16:43 +00:00
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
bitrate = (int)(info.average_bitrate / 1000.0);
|
2005-06-02 18:16:43 +00:00
|
|
|
frequency = info.sample_freq;
|
2022-02-07 05:49:27 +00:00
|
|
|
|
|
|
|
totalFrames = mpc_streaminfo_get_length_samples(&info);
|
2005-06-29 15:28:20 +00:00
|
|
|
|
2007-03-04 18:41:43 +00:00
|
|
|
[self willChangeValueForKey:@"properties"];
|
|
|
|
[self didChangeValueForKey:@"properties"];
|
2022-02-07 05:49:27 +00:00
|
|
|
|
2005-06-02 18:16:43 +00:00
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
- (BOOL)writeToBuffer:(float *)sample_buffer fromBuffer:(const MPC_SAMPLE_FORMAT *)p_buffer frames:(unsigned)frames {
|
2007-02-24 20:36:27 +00:00
|
|
|
unsigned n;
|
2022-02-07 05:49:27 +00:00
|
|
|
|
|
|
|
unsigned p_size = frames * 2; // 2 = stereo
|
2013-10-05 21:15:09 +00:00
|
|
|
#ifdef MPC_FIXED_POINT
|
2022-02-07 05:49:27 +00:00
|
|
|
const float float_scale = 1.0 / MPC_FIXED_POINT_SCALE;
|
2013-10-05 21:15:09 +00:00
|
|
|
#endif
|
2022-02-07 05:49:27 +00:00
|
|
|
|
|
|
|
for(n = 0; n < p_size; n++) {
|
2013-10-05 21:15:09 +00:00
|
|
|
float val;
|
2005-06-02 18:16:43 +00:00
|
|
|
#ifdef MPC_FIXED_POINT
|
2013-10-05 21:15:09 +00:00
|
|
|
val = p_buffer[n] * float_scale;
|
2005-06-02 18:16:43 +00:00
|
|
|
#else
|
2013-10-05 21:15:09 +00:00
|
|
|
val = p_buffer[n];
|
2005-06-02 18:16:43 +00:00
|
|
|
#endif
|
2022-02-07 05:49:27 +00:00
|
|
|
|
|
|
|
// sample_buffer[n] = CFSwapInt16LittleToHost(val);
|
2005-06-02 18:16:43 +00:00
|
|
|
sample_buffer[n] = val;
|
2007-02-24 20:36:27 +00:00
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
// m_data_bytes_written += p_size * (m_bps >> 3);
|
2007-02-24 20:36:27 +00:00
|
|
|
return YES;
|
2005-06-02 18:16:43 +00:00
|
|
|
}
|
|
|
|
|
2022-07-10 22:14:47 +00:00
|
|
|
- (AudioChunk *)readAudio {
|
|
|
|
int frames = 1024;
|
2022-07-25 01:31:08 +00:00
|
|
|
void *buf = (void *)floatBuffer;
|
2022-07-10 22:14:47 +00:00
|
|
|
|
2008-02-10 16:18:23 +00:00
|
|
|
int framesRead = 0;
|
2022-07-10 22:14:47 +00:00
|
|
|
int bytesPerFrame = sizeof(float) * 2; // bitsPerSample == 32, channels == 2
|
2022-02-07 05:49:27 +00:00
|
|
|
while(framesRead < frames) {
|
|
|
|
// Fill from buffer, going by bufferFrames
|
|
|
|
// if still needs more, decode and repeat
|
|
|
|
if(bufferFrames == 0) {
|
2008-02-10 16:18:23 +00:00
|
|
|
/* returns the length of the samples*/
|
2022-02-07 05:49:27 +00:00
|
|
|
mpc_frame_info frame;
|
|
|
|
frame.buffer = sampleBuffer;
|
|
|
|
mpc_status err = mpc_demux_decode(demux, &frame);
|
|
|
|
if(frame.bits == -1) {
|
|
|
|
// decode error
|
|
|
|
if(err != MPC_STATUS_OK)
|
|
|
|
DLog(@"Decode error");
|
2008-02-10 16:18:23 +00:00
|
|
|
return 0;
|
2022-02-07 05:49:27 +00:00
|
|
|
} else // status>0 /* status == MPC_FRAME_LENGTH */
|
2008-02-10 16:18:23 +00:00
|
|
|
{
|
|
|
|
}
|
2022-02-07 05:49:27 +00:00
|
|
|
|
2014-10-20 00:42:04 +00:00
|
|
|
bufferFrames = frame.samples;
|
2005-06-02 18:16:43 +00:00
|
|
|
}
|
2008-02-10 16:18:23 +00:00
|
|
|
|
|
|
|
int framesToRead = bufferFrames;
|
2022-02-07 05:49:27 +00:00
|
|
|
if(bufferFrames > frames) {
|
2008-02-10 16:18:23 +00:00
|
|
|
framesToRead = frames;
|
2005-06-02 18:16:43 +00:00
|
|
|
}
|
2008-02-10 16:18:23 +00:00
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
[self writeToBuffer:((float *)(buf + (framesRead * bytesPerFrame))) fromBuffer:sampleBuffer frames:framesToRead];
|
2008-02-10 16:18:23 +00:00
|
|
|
|
|
|
|
frames -= framesToRead;
|
|
|
|
framesRead += framesToRead;
|
|
|
|
bufferFrames -= framesToRead;
|
2022-02-07 05:49:27 +00:00
|
|
|
|
|
|
|
if(bufferFrames > 0) {
|
2008-02-10 16:18:23 +00:00
|
|
|
memmove((uint8_t *)sampleBuffer, ((uint8_t *)sampleBuffer) + (framesToRead * bytesPerFrame), bufferFrames * bytesPerFrame);
|
2005-06-02 18:16:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-10 22:14:47 +00:00
|
|
|
id audioChunkClass = NSClassFromString(@"AudioChunk");
|
|
|
|
AudioChunk *chunk = [[audioChunkClass alloc] initWithProperties:[self properties]];
|
2022-07-25 01:31:08 +00:00
|
|
|
[chunk assignSamples:floatBuffer frameCount:framesRead];
|
2022-07-10 22:14:47 +00:00
|
|
|
|
|
|
|
return chunk;
|
2005-06-02 18:16:43 +00:00
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
- (void)close {
|
|
|
|
if(demux) {
|
|
|
|
mpc_demux_exit(demux);
|
|
|
|
demux = NULL;
|
|
|
|
}
|
2016-06-19 19:57:18 +00:00
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
- (void)dealloc {
|
|
|
|
[self close];
|
2005-06-02 18:16:43 +00:00
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
- (long)seek:(long)sample {
|
2015-02-08 06:24:42 +00:00
|
|
|
mpc_demux_seek_sample(demux, sample);
|
2022-02-07 05:49:27 +00:00
|
|
|
|
2008-02-10 16:18:23 +00:00
|
|
|
return sample;
|
2005-06-02 18:16:43 +00:00
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
- (void)setSource:(id<CogSource>)s {
|
2007-03-04 18:41:43 +00:00
|
|
|
source = s;
|
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
- (id<CogSource>)source {
|
2007-03-04 18:41:43 +00:00
|
|
|
return source;
|
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
- (NSDictionary *)properties {
|
2022-06-17 13:39:02 +00:00
|
|
|
return @{ @"bitrate": @(bitrate),
|
|
|
|
@"sampleRate": @(frequency),
|
|
|
|
@"totalFrames": @(totalFrames),
|
|
|
|
@"bitsPerSample": @(32),
|
|
|
|
@"floatingPoint": @(YES),
|
|
|
|
@"channels": @(2),
|
|
|
|
@"seekable": @([source seekable]),
|
|
|
|
@"codec": @"Musepack",
|
|
|
|
@"endian": @"host",
|
|
|
|
@"encoding": @"lossy" };
|
2022-02-07 05:49:27 +00:00
|
|
|
}
|
|
|
|
|
2022-02-09 03:56:39 +00:00
|
|
|
- (NSDictionary *)metadata {
|
|
|
|
return @{};
|
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
+ (NSArray *)fileTypes {
|
2022-01-19 02:12:57 +00:00
|
|
|
return @[@"mpc"];
|
2005-06-02 18:16:43 +00:00
|
|
|
}
|
2007-02-24 20:36:27 +00:00
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
+ (NSArray *)mimeTypes {
|
2022-01-19 02:12:57 +00:00
|
|
|
return @[@"audio/x-musepack"];
|
2007-10-14 18:39:58 +00:00
|
|
|
}
|
2007-02-24 20:36:27 +00:00
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
+ (float)priority {
|
|
|
|
return 1.0;
|
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
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
+ (NSArray *)fileTypeAssociations {
|
|
|
|
return @[
|
|
|
|
@[@"Musepack Audio File", @"mpc.icns", @"mpc"]
|
|
|
|
];
|
2022-01-18 11:06:03 +00:00
|
|
|
}
|
|
|
|
|
2005-06-02 18:16:43 +00:00
|
|
|
@end
|