/*
SndfileSource.m
Load and read sound data using libsndfile.
Copyright (C) 2009 Free Software Foundation, Inc.
Written by: Stefan Bidigaray
Date: Jun 2009
This file is part of the GNUstep GUI Library.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; see the file COPYING.LIB.
If not, see or write to the
Free Software Foundation, 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include
#include
#include
#include
@interface SndfileSource : NSObject
{
NSData *_data;
SNDFILE *_snd;
SF_INFO _info;
NSUInteger _curLoc;
NSTimeInterval _dur;
int _encoding;
}
- (NSData *)data;
- (NSUInteger)currentPosition;
- (void)setCurrentPosition: (NSUInteger)curPos;
@end
static inline sf_count_t dataLength (void *user_data)
{
SndfileSource *snd = (SndfileSource *)user_data;
return (sf_count_t)[[snd data] length];
}
static inline sf_count_t dataSeek (sf_count_t offset, int whence, void *user_data)
{
SndfileSource *snd = (SndfileSource *)user_data;
switch (whence)
{
case SEEK_SET:
[snd setCurrentPosition: (NSUInteger)offset];
break;
case SEEK_END:
offset = (sf_count_t)[[snd data] length] + offset;
[snd setCurrentPosition: (NSUInteger)offset];
break;
case SEEK_CUR:
[snd setCurrentPosition: (NSUInteger)offset];
break;
default:
return 0;
}
return (sf_count_t)[snd currentPosition];
}
static inline sf_count_t dataRead (void *ptr, sf_count_t count, void *user_data)
{
NSRange range;
SndfileSource *snd = (SndfileSource *)user_data;
/* Can't read more data that we have available */
if (([snd currentPosition] + (NSUInteger)count) > [[snd data] length])
{
count = (sf_count_t)([[snd data] length] - [snd currentPosition]);
}
range = NSMakeRange ([snd currentPosition], (NSUInteger)count);
[[snd data] getBytes: ptr range: range];
return count;
}
static inline sf_count_t dataWrite (const void *ptr, sf_count_t count, void *user_data)
{
/* No write support */
return 0;
}
static inline sf_count_t dataTell (void *user_data)
{
SndfileSource *snd = (SndfileSource *)user_data;
return (sf_count_t)[snd currentPosition];
}
/* The libsndfile virtual I/O functions */
static SF_VIRTUAL_IO dataIO = { (sf_vio_get_filelen)dataLength,
(sf_vio_seek)dataSeek,
(sf_vio_read)dataRead,
(sf_vio_write)dataWrite,
(sf_vio_tell)dataTell };
@implementation SndfileSource
+ (NSArray *)soundUnfilteredFileTypes
{
return [NSArray arrayWithObjects: @"wav", @"au", @"snd", @"aif", @"aiff", @"aifc",
@"paf", @"sf", @"voc", @"w64", @"mat", @"mat4", @"mat5", @"pcf", @"xi",
@"caf", @"sd2", @"iff", @"flac", @"ogg", @"oga", nil];
}
+ (NSArray *)soundUnfilteredTypes
{
/* FIXME: I'm not sure what the UTI is for all the types above. */
return [NSArray arrayWithObjects: @"com.microsoft.waveform-audio", @"public.ulaw-audio",
@"public.aiff-audio", @"public.aifc-audio", @"com.apple.coreaudio-format",
@"com.digidesign.sd2-audio",
/* FIXME: are these right? */
@"org.xiph.flac-audio", @"org.xiph.vorbis-audio", nil];
}
+ (BOOL)canInitWithData: (NSData *)data
{
return NO;
}
- (void)dealloc
{
TEST_RELEASE (_data);
sf_close (_snd);
[super dealloc];
}
- (id)initWithData: (NSData *)data
{
[self init];
_data = data;
RETAIN(_data);
/* FIXME: support multiple types */
_encoding = GSSoundFormatPCM16;
_info.format = 0;
_snd = sf_open_virtual (dataIO, SFM_READ, &_info, self);
if (_snd == NULL)
{
DESTROY(self);
return nil;
}
return self;
}
- (NSUInteger)readBytes: (void *)bytes length: (NSUInteger)length
{
return (NSUInteger) (sf_read_short (_snd, bytes, (length>>1)));
}
- (NSTimeInterval)duration
{
return _dur;
}
- (void)setCurrentTime: (NSTimeInterval)currentTime
{
_curPos = (NSUInteger)(currentTime * (NSTimeInterval)[_data length]);
}
- (NSTimeInterval)currentTime
{
return ((NSTimeInterval)_curPos / (NSTimeInterval)[_data length]);
}
- (int)encoding
{
return _encoding;
}
- (NSUInteger)channelCount
{
return (NSUInteger)info.channels;
}
- (NSUInteger)sampleRate;
{
return (NSUInteger)info.samplerate;
}
- (NSByteOrder)byteOrder
{
/* Equivalent to sending native byte order */
return NS_UnknownByteOrder;
}
- (NSData *)data
{
return _data;
}
- (NSUInteger)currentPosition
{
return _curPos;
}
- (void)setCurrentPosition: (NSUInteger)curPos
{
_curPos = curPos;
}
@end