On Fri, Jun 19, 2009 at 2:05 AM, Fred Kiefer <[email protected]> wrote:
> I don't understand the general concept here, so just a few detail > comments on the code itself. > I'll try to explain this the best I can, it might be a little long, though... As I explained on my previous e-mails, I'm streaming the data to the sink. In order to do so without blocking the main thread I'm using a separate thread (spawned using -detachNewThreadSelector:toTarget:withObject:)... this will happen in -play and the selector is -_stream. The stream uses 2 objects, one conforms to <GSSoundSource> and the other to <GSSoundSink>, the former outputs the data in a standard format so that the latter can send it to the audio device. To pause and resume the stream I use an NSConditionLock, which is what _private[2] is, if the condition is SOUND_SHOULD_PAUSE the while loop waits until it's SOUND_SHOULD_PLAY. The hardest part, at least for me, is finding out if the thread is already running in -play and -stop and taking the appropiate action. The -pause and -resume methods are pretty straight forward. > Hiding the implementation details in the header is a good thing, but it > makes the code in the implementation harder to read. This could be > improved by using local variables (or even Macros). For example Sounds good! When releasing the conditional lock, you should rather destroy it, as it > could otherwise just be reused, although it has been freed. Wouldn't this cause a bit of a problem in -_stream since I keep checking the NSConditionLock's condition? My plan was to initialize it in -play and free/destroy it in -stop and use it as the way to find out if the audio data is being streamed. Shouldn't the condition of the while loop read something like this? > while ((!_shouldExit) && (bytesRead > 0) && success) Yes! Oops... I've attached a copy of GSSoundSource.h and GSSoundSink.h, these two header files define the protocols and give you an idea of what exactly I'm going for. I've also gone ahead and added documention as well. I'll also go ahead and attach the first two classes that implement these protocols. As before, I haven't had time to test them, so I'm sure there are typos everywhere. I do plan on testing them later today, make sure they actually do what they're supposed to before finishing the NSSound implentation. Thanks Stefan
/* GSSoundSink.h Sink audio data. Copyright (C) 2009 Free Software Foundation, Inc. Written by: Stefan Bidigaray <[email protected]> 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 <http://www.gnu.org/licenses/> or write to the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _GNUstep_H_GSSoundSink #define _GNUstep_H_GSSoundSink @protocol GSSoundSink <NSObject> /** <init/> * Initializes the receiver for output using the defined parameters. * <p><b>WARNING:</b> This method does not open the device, see -open.</p> */ - (id)initWithEncoding: (int)encoding channels: (NSUInteger)channelCount sampleRate: (NSUInteger)sampleRate byteOrder: (NSByteOrder)byteOrder; /** Opens the device for output, called by [NSSound-play]. */ - (BOOL)open; /** Closes the device, called by [NSSound-stop]. */ - (void)close; /** Writes/Plays the data in bytes to the device. Data <i>must</i> be in * the same format as specified in * -initWithEncoding:channels:sampleRate:byteOrder:. */ - (BOOL)writeBytes: (void *)bytes length: (NSUInteger)length; /** Called by [NSSound-setVolume:], and corresponds to it. Parameter volume * is between the values 0.0 and 1.0. */ - (void)setVolume: (float)volume; /** Called by [NSSound-volume]. */ - (float)volume; /** Called by [NSSound-setPlaybackDeviceIdentifier:]. */ - (void)setPlaybackDeviceIdentifier: (NSString *)playbackDeviceIdentifier; /** Called by [NSSound-playbackDeviceIdentifier]. */ - (NSString *)playbackDeviceIdentifier; /** Called by [NSSound-setChannelMapping:]. */ - (void)setChannelMapping: (NSArray *)channelMapping; /** Called by [NSSound-channelMapping]. */ - (NSArray *)channelMapping; @end #endif // _GNUstep_H_GSSoundSink
/* GSSoundSource.h Load and read sound data. Copyright (C) 2009 Free Software Foundation, Inc. Written by: Stefan Bidigaray <[email protected]> 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 <http://www.gnu.org/licenses/> or write to the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _GNUstep_H_GSSoundSource #define _GNUstep_H_GSSoundSource @class NSArray; @protocol GSSoundSource <NSObject> /** Returns an array of the file types supported by the class. */ + (NSArray *)soundUnfilteredFileTypes; /** Returns an array of UTIs identifying the file types the class understands. */ + (NSArray *)soundUnfilteredTypes; /** Returns YES if the class can understand data and NO otherwise. */ + (BOOL)canInitWithData: (NSData *)data; /** <init /> * Initilizes the reciever for output. */ - (id)initWithData: (NSData *)data; /** Reads data provided in -initWithData:. Parameter bytes must be big enough * to hold length bytes. */ - (NSUInteger)readBytes: (void *)bytes length: (NSUInteger)length; /** Returns the duration, in seconds. Equivalent to [NSSound-duration]. */ - (NSTimeInterval)duration; /** Called by [NSSound-setCurrentTime:]. */ - (void)setCurrentTime: (NSTimeInterval)currentTime; /** Called by [NSSound-currentTime]. */ - (NSTimeInterval)currentTime; /** Returns encoding of the audio data. */ - (int)encoding; /** Returns the number of channels. */ - (NSUInteger)channelCount; /** Returns the receiver's sample rate (ie 44100, 8000, etc). */ - (NSUInteger)sampleRate; /** Returns the byte order of the audio data. */ - (NSByteOrder)byteOrder; @end #endif // _GNUstep_H_GSSoundSource
/* AudioOutputSink.m Sink audio data to libao. Copyright (C) 2009 Free Software Foundation, Inc. Written by: Stefan Bidigaray <[email protected]> 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 <http://www.gnu.org/licenses/> or write to the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include <Foundation/Foundation.h> #include <AppKit/NSSound.h> #include <GNUstepGUI/GSSoundInput.h> #include <ao/ao.h> @interface AudioOutputSink : NSObject <GSSoundSink> { ao_device *_dev; int _driver; ao_sample_format _format; } @end @implementation AudioOutputSink - (void)dealloc { ao_shutdown(); [super dealloc]; } - (id)initWithEncoding: (int)encoding channels: (NSUInteger)channelCount sampleRate: (NSUInteger)sampleRate byteOrder: (NSByteOrder)byteOrder { self = [super init]; ao_initialize(); _driver = ao_default_driver_id(); _format.channels = (int)channelCount; _format.rate = (int)sampleRate; switch (encoding) { case GSSoundFormatPCMS8: _format.bits = 8; break; case GSSoundFormatPCM16: _format.bits = 16; break; case GSSoundFormatPCM24: _format.bits = 24; break; case GSSoundFormatPCM32: _format.bits = 32; break; case GSSoundFormatFloat32: // Float and double not supported by libao. case GSSoundFormatFloat64: default: DESTROY(self); return nil; } if (byteOrder == NS_LittleEndian) { _format.byte_format = AO_FMT_LITTLE; } else if (byteOrder == NS_BigEndian) { _format.byte_format = AO_FMT_BIG; } else { _format.byte_format = AO_FMT_NATIVE; } return self } - (BOOL)open { dev = ao_open_live(default_driver, &format, NULL); return ((dev == NULL) ? NO : YES); } - (void)close { ao_close(_dev); } - (BOOL)writeBytes: (void *)bytes length: (NSUInteger)length { return ((ao_play(_dev, bytes, (uint_32)length) == 0) ? NO : YES); } /* Functionality not supported by libao */ - (void)setVolume: (float)volume { return; } - (float)volume { return 1.0; } - (void)setPlaybackDeviceIdentifier: (NSString *)playbackDeviceIdentifier { return; } - (NSString *)playbackDeviceIdentifier { return nil; } - (void)setChannelMapping: (NSArray *)channelMapping { return; } - (NSArray *)channelMapping { return nil; } @end
/* SndfileSource.m Load and read sound data using libsndfile. Copyright (C) 2009 Free Software Foundation, Inc. Written by: Stefan Bidigaray <[email protected]> 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 <http://www.gnu.org/licenses/> or write to the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include <Foundation/Foundation.h> #include <AppKit/NSSound.h> #include <GNUstepGUI/GSSoundSource.h> #include <sndfile.h> @interface SndfileSource : NSObject <GSSoundSource> { 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
_______________________________________________ Gnustep-dev mailing list [email protected] http://lists.gnu.org/mailman/listinfo/gnustep-dev
