Start work on OpenAL support

(Really doesn't work as of this commit...)
master
cim 2013-10-31 14:00:18 +00:00
parent bddec79afe
commit f00d3bd2fe
20 changed files with 2017 additions and 10 deletions

View File

@ -41,7 +41,7 @@ else
LIBJS = js_static
ADDITIONAL_INCLUDE_DIRS = -I$(LIBJS_INC_DIR) -Isrc/SDL -Isrc/Core -Isrc/BSDCompat -Isrc/Core/Scripting -Isrc/Core/Materials -Isrc/Core/Entities -Isrc/Core/OXPVerifier -Isrc/Core/Debug -Isrc/Core/Tables -Isrc/Core/MiniZip
ADDITIONAL_OBJC_LIBS = -lGLU -lGL -lX11 -lSDL -lSDL_mixer -lgnustep-base -l$(LIBJS) `nspr-config --libs` -lstdc++
ADDITIONAL_OBJC_LIBS = -lGLU -lGL -lX11 -lSDL -lSDL_mixer -lgnustep-base -l$(LIBJS) `nspr-config --libs` -lstdc++ -lopenal
ADDITIONAL_CFLAGS = -Wall -DLINUX -DNEED_STRLCPY `sdl-config --cflags` `nspr-config --cflags`
ADDITIONAL_OBJCFLAGS = -Wall -std=c99 -DLOADSAVEGUI -DLINUX -DXP_UNIX -Wno-import `sdl-config --cflags` `nspr-config --cflags`
oolite_LIB_DIRS += -L/usr/X11R6/lib/ -L$(LIBJS_LIB_DIR)
@ -313,16 +313,29 @@ OOLITE_SCRIPTING_FILES = \
OOJSFrameCallbacks.m \
OOJSFont.m
#OOLITE_SOUND_FILES = \
# OOBasicSoundReferencePoint.m \
#
# OOSDLConcreteSound.m \
# OOSDLSound.m \
# OOSDLSoundChannel.m \
# OOSDLSoundMixer.m \
# OOSoundSource.m \
# OOSoundSourcePool.m \
# SDLMusic.m
OOLITE_SOUND_FILES = \
OOBasicSoundReferencePoint.m \
OOMusicController.m \
OOSDLConcreteSound.m \
OOSDLSound.m \
OOSDLSoundChannel.m \
OOSDLSoundMixer.m \
OOSoundSource.m \
OOOpenALController.m \
OOMusicController.m \
OOSoundSource.m \
OOSoundSourcePool.m \
SDLMusic.m
OOALMusic.m \
OOALSound.m \
OOALSoundChannel.m \
OOALSoundMixer.m \
OOALSoundDecoder.m \
OOALBufferedSound.m
OOLITE_UI_FILES = \
GuiDisplayGen.m \

View File

@ -371,6 +371,7 @@
sound.customSounds = $soundDebug;
sound.customSounds.recursion = $soundError; // Circular dependency in customsounds.plist, e.g. [foo] = [bar], [bar] = [foo].
sound.load.success = $soundDebug;
sound.initialization.error = $soundError;
// Mac-specific sound messages
sound.channel.cleanup.success = $soundDebugVerbose;
@ -378,7 +379,6 @@
sound.channel.cleanup.failed.broken = inherit;
sound.channel.cleanup.failed.badState = inherit;
sound.channel.machPortError = $soundError;
sound.initialization.error = $soundError;
sound.mixer.outOfChannels = $soundError;
sound.mixer.inspector.loadFailed = $soundError;
sound.mixer.replacingBrokenChannel = $soundDebug;

View File

@ -0,0 +1,43 @@
/*
OOALBufferedSound.h
OOALBufferedSound - OpenAL sound implementation for Oolite.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#import "OOSound.h"
#import "OOALSoundDecoder.h"
@interface OOALBufferedSound: OOSound
{
@private
float *_buffer;
size_t _size;
double _sampleRate;
NSString *_name;
BOOL _stereo;
}
- (id)initWithDecoder:(OOALSoundDecoder *)inDecoder;
@end

View File

@ -0,0 +1,115 @@
/*
OOALBufferedSound.m
OOALBufferedSound - OpenAL sound implementation for Oolite.
Copyright (C) 2005-2013 Jens Ayton
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#import "OOALBufferedSound.h"
#import "OOALSoundDecoder.h"
NSString * const kOOLogSoundLoadError = @"sound.load.error";
@implementation OOALBufferedSound
- (void)dealloc
{
free(_buffer);
_buffer = NULL;
[super dealloc];
}
- (NSString *)name
{
return _name;
}
- (id)initWithDecoder:(OOALSoundDecoder *)inDecoder
{
BOOL OK = YES;
[OOSound setUp];
if (![OOSound isSoundOK] || nil == inDecoder) OK = NO;
if (OK)
{
self = [super init];
if (nil == self) OK = NO;
}
if (OK)
{
_name = [[inDecoder name] copy];
_sampleRate = [inDecoder sampleRate];
if ([inDecoder isStereo])
{
OK = [inDecoder readMonoCreatingBuffer:&_buffer withFrameCount:&_size];
/* TODO: implement stereo - needs to be interleaved for OpenAL, not
* separate buffers */
// OK = [inDecoder readStereoCreatingLeftBuffer:&_bufferL rightBuffer:&_bufferR withFrameCount:&_size];
// _stereo = YES;
}
else
{
OK = [inDecoder readMonoCreatingBuffer:&_buffer withFrameCount:&_size];
}
}
if (!OK)
{
[self release];
self = nil;
}
return self;
}
- (ALuint) soundBuffer
{
ALuint buffer;
ALint error;
OOAL(alGenBuffers(1,&buffer));
if ((error = alGetError()) != AL_NO_ERROR)
{
OOLog(kOOLogSoundLoadError,@"Could not create OpenAL buffer");
return 0;
}
else
{
if (!_stereo)
{
alBufferData(buffer,AL_FORMAT_MONO16,_buffer,_size,_sampleRate);
}
else
{
alBufferData(buffer,AL_FORMAT_STEREO16,_buffer,_size,_sampleRate);
}
return buffer;
}
}
@end

View File

@ -0,0 +1,43 @@
/*
OOALConcreteSound.h
OOALSound - OpenAL sound implementation for Oolite.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#import "OOALSound.h"
#import "OOOpenALController.h"
@interface OOALConcreteSound: OOSound
{
ALuint *_buffer;
NSString *_name;
}
- (ALuint) buffer;
@end

View File

@ -0,0 +1,131 @@
/*
OOALConcreteSound.m
OOALSound - OpenAL sound implementation for Oolite.
Copyright (C) 2006-2013 Jens Ayton
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDEDAS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <ogg/ogg.h>
#include <vorbis/vorbisfile.h>
#include <vorbis/codec.h>
#import "OOALConcreteSound.h"
#import "OOLogging.h"
#import "NSDataOOExtensions.h"
size_t OOReadVorbis (void *ptr, size_t size, size_t nmemb, void *datasource);
typedef struct {
NSData *file;
size_t seek;
} OOOggBytes;
@implementation OOALConcreteSound
- (id) initWithContentsOfFile:(NSString *)path
{
if ((self = [super init]))
{
NSData *fileData = [NSData oo_dataWithOXZFile:path];
OggVorbis_File _vf;
ov_callbacks _callbacks = {
OOReadVorbis, // read sequentially
NULL, // no seek
NULL, // no close
NULL, // no tell
};
OOOggBytes bitData = {
fileData,
0
};
ov_open_callbacks(bitData, _vf, NULL, 0, _callbacks);
SDL_RWops *soundData = SDL_RWFromConstMem([fileData bytes],[fileData length]);
_chunk = Mix_LoadWAV_RW(soundData, 0);
SDL_RWclose(soundData);
if (_chunk != NULL)
{
#ifndef NDEBUG
OOLog(@"sound.load.success", @"Loaded a sound from \"%@\".", path);
#endif
_name = [[path lastPathComponent] copy];
}
else
{
OOLog(@"sound.load.failed", @"Could not load a sound from \"%@\".", path);
[self release];
self = nil;
}
}
return self;
}
- (void) dealloc
{
if (_chunk != NULL) Mix_FreeChunk(_chunk);
[_name autorelease];
[super dealloc];
}
- (NSString *) descriptionComponents
{
return [NSString stringWithFormat:@"\"%@\"", _name];
}
- (NSString *)name
{
return _name;
}
- (ALuint) buffer
{
return _buffer;
}
@end
// wants size*nmemb bytes from datasource transferred to ptr
size_t OOReadVorbis (void *ptr, size_t size, size_t nmemb, void *datasource)
{
size_t toRead = size*nmemb;
NSData *file = (OOOggBytes)datasource.file;
size_t seek = (OOOggBytes)datasource.seek;
if (seek+toRead > [file length])
{
toRead = [file length] - seek;
}
[file getBytes:ptr range:((NSRange){seek, toRead})];
(OOOggBytes)datasource.seek += toRead;
return toRead;
}

46
src/Core/OOALMusic.h Normal file
View File

@ -0,0 +1,46 @@
/*
OOALMusic.h
Subclass of OOSound with additional controls specific to music playback. Only
one instance of OOMusic may be playing at a time.
OOALSound - OpenAL sound implementation for Oolite.
Copyright (C) 2005-2013 Jens Ayton
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#import <Foundation/Foundation.h>
#import "OOALSound.h"
@interface OOMusic: OOSound
{
@private
OOSound *sound;
}
- (void) playLooped:(BOOL)looped;
- (void) stop;
- (BOOL) isPlaying;
@end

108
src/Core/OOALMusic.m Normal file
View File

@ -0,0 +1,108 @@
/*
OOALMusic.m
OOALSound - OpenAL sound implementation for Oolite.
Copyright (C) 2005-2013 Jens Ayton
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#import "OOALMusic.h"
#import "OOSoundSource.h"
static OOMusic *sPlayingMusic = nil;
static OOSoundSource *sMusicSource = nil;
@implementation OOMusic
+ (id)allocWithZone:(NSZone *)inZone
{
return NSAllocateObject([OOMusic class], 0, inZone);
}
- (void)dealloc
{
if (sPlayingMusic == self) [self stop];
[sound release];
[super dealloc];
}
- (id)initWithContentsOfFile:(NSString *)inPath
{
self = [super init];
if (nil != self)
{
sound = [[OOSound alloc] initWithContentsOfFile:inPath];
if (nil == sound)
{
[self release];
self = nil;
}
}
return self;
}
- (NSString *)name
{
return [sound name];
}
- (void)playLooped:(BOOL)inLoop
{
if (sPlayingMusic != self)
{
if (nil == sMusicSource)
{
sMusicSource = [[OOSoundSource alloc] init];
}
[sMusicSource stop];
[sMusicSource setLoop:inLoop];
[sMusicSource setSound:sound];
[sMusicSource play];
sPlayingMusic = self;
}
}
- (BOOL)isPlaying
{
return sPlayingMusic == self && [sMusicSource isPlaying];
}
- (void)stop
{
if (sPlayingMusic == self)
{
sPlayingMusic = nil;
[sMusicSource stop];
}
}
@end

46
src/Core/OOALSound.h Normal file
View File

@ -0,0 +1,46 @@
/*
OOALSound.h
OOALSound - OpenAL sound implementation for Oolite.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#import <Foundation/Foundation.h>
#import "OOOpenALController.h"
@interface OOSound: NSObject
+ (BOOL) setUp;
+ (void) update;
+ (void) setMasterVolume:(float) fraction;
+ (float) masterVolume;
- (id) initWithContentsOfFile:(NSString *)path;
- (NSString *)name;
+ (BOOL) isSoundOK;
- (ALuint) soundBuffer;
@end

165
src/Core/OOALSound.m Normal file
View File

@ -0,0 +1,165 @@
/*
OOALSound.m
OOALSound - OpenAL sound implementation for Oolite.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDEDAS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#import "OOALSound.h"
#import "OOLogging.h"
#import "OOCollectionExtractors.h"
#import "OOMaths.h"
#import "OOALSoundDecoder.h"
#import "OOOpenALController.h"
#import "OOALBufferedSound.h"
#import "OOALSoundMixer.h"
#define KEY_VOLUME_CONTROL @"volume_control"
static int sMaxBufferedSoundSize = 1 << 20; // 1 MB
static BOOL sIsSetUp = NO;
static BOOL sIsSoundOK = NO;
@implementation OOSound
+ (BOOL) setUp
{
if (!sIsSetUp)
{
sIsSetUp = YES;
OOOpenALController* controller = [OOOpenALController sharedController];
if (controller != nil)
{
sIsSoundOK = YES;
}
}
return sIsSoundOK;
}
+ (void) setMasterVolume:(float) fraction
{
if (!sIsSetUp && ![self setUp])
return;
fraction = OOClamp_0_1_f(fraction);
OOOpenALController *controller = [OOOpenALController sharedController];
if (fraction != [controller masterVolume])
{
[controller setMasterVolume:fraction];
[[NSUserDefaults standardUserDefaults] setFloat:[controller masterVolume] forKey:KEY_VOLUME_CONTROL];
}
}
+ (float) masterVolume
{
if (!sIsSetUp && ![self setUp] )
return 0.0;
OOOpenALController *controller = [OOOpenALController sharedController];
return [controller masterVolume];
}
- (id) init
{
if (!sIsSetUp) [OOSound setUp];
return [super init];
}
- (id) initWithContentsOfFile:(NSString *)path
{
[self release];
if (!sIsSetUp && ![OOSound setUp]) return nil;
OOALSoundDecoder *decoder;
decoder = [[OOALSoundDecoder alloc] initWithPath:path];
if (nil == decoder) return nil;
if ([decoder sizeAsBuffer] <= (size_t)sMaxBufferedSoundSize)
{
self = [[OOALBufferedSound alloc] initWithDecoder:decoder];
}
else
{
self = nil;
// not yet implemented
// self = [[OOALStreamingSound alloc] initWithDecoder:decoder];
}
[decoder release];
if (nil != self)
{
#ifndef NDEBUG
OOLog(kOOLogSoundLoadingSuccess, @"Loaded sound %@", self);
#endif
}
else
{
OOLog(kOOLogSoundLoadingError, @"Failed to load sound \"%@\"", path);
}
return self;
}
- (id)initWithDecoder:(OOALSoundDecoder *)inDecoder
{
[self release];
return nil;
}
- (NSString *)name
{
OOLogGenericSubclassResponsibility();
return @"";
}
+ (void) update
{
OOSoundMixer * mixer = [OOSoundMixer sharedMixer];
if( sIsSoundOK && mixer)
[mixer update];
}
+ (BOOL) isSoundOK
{
return sIsSoundOK;
}
- (ALuint) soundBuffer
{
OOLogGenericSubclassResponsibility();
return 0;
}
@end

View File

@ -0,0 +1,68 @@
/*
OOALSoundChannel.h
A channel for audio playback.
This class is an implementation detail. Do not use it directly; use an
OOSoundSource to play an OOSound.
OOALSound - OpenAL sound implementation for Oolite.
Copyright (C) 2006-2013 Jens Ayton
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#import <Foundation/Foundation.h>
#import "OOOpenALController.h"
@class OOSound;
@interface OOSoundChannel: NSObject
{
OOSoundChannel *_next;
id _delegate;
OOSound *_sound;
ALuint _source;
BOOL _playing;
}
- (void) update;
- (void) setDelegate:(id)delegate;
// Unretained pointer used to maintain simple stack
- (OOSoundChannel *) next;
- (void) setNext:(OOSoundChannel *)next;
- (BOOL) playSound:(OOSound *)sound looped:(BOOL)loop;
- (void)stop;
- (OOSound *)sound;
@end
@interface NSObject(OOSoundChannelDelegate)
- (void)channel:(OOSoundChannel *)inChannel didFinishPlayingSound:(OOSound *)inSound;
@end

144
src/Core/OOALSoundChannel.m Normal file
View File

@ -0,0 +1,144 @@
/*
OOALSoundChannel.m
OOALSound - OpenAL sound implementation for Oolite.
Copyright (C) 2006-2013 Jens Ayton
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDEDAS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#import "OOALSoundChannel.h"
#import "OOALSound.h"
#import "OOLogging.h"
@interface OOSoundChannel (Private)
- (void) hasStopped;
@end
@implementation OOSoundChannel
- (id) init
{
if ((self = [super init]))
{
ALuint error;
OOAL(alGenSources(1,&_source));
if ((error = alGetError()) != AL_NO_ERROR)
{
OOLog(kOOLogSoundInitError,@"Could not create OpenAL source");
[self release];
self = nil;
}
}
return self;
}
- (void) update
{
// Check if we've reached the end of a sound.
if (_sound != nil)
{
ALint check;
OOAL(alGetSourcei(_source,AL_SOURCE_STATE,&check));
if (check == AL_STOPPED)
{
[self hasStopped];
}
}
}
- (void) setDelegate:(id)delegate
{
_delegate = delegate;
}
- (OOSoundChannel *) next
{
return _next;
}
- (void) setNext:(OOSoundChannel *)next
{
_next = next;
}
- (BOOL) playSound:(OOSound *)sound looped:(BOOL)loop
{
if (sound == nil) return NO;
if (_sound != nil) [self stop];
// get sound data
ALuint buffer = [sound soundBuffer];
// bind sound data to buffer
OOAL(alSourcei(_source, AL_BUFFER, buffer));
ALuint error;
if ((error = alGetError()) != AL_NO_ERROR)
{
return NO;
}
OOAL(alSourcePlay(_source));
if ((error = alGetError()) != AL_NO_ERROR)
{
return NO;
}
_sound = [sound retain];
return YES;
}
- (void) stop
{
if (_sound != nil)
{
OOAL(alSourceStop(_source));
[self hasStopped];
}
}
- (void) hasStopped
{
OOSound *sound = _sound;
_sound = nil;
if (nil != _delegate && [_delegate respondsToSelector:@selector(channel:didFinishPlayingSound:)])
{
[_delegate channel:self didFinishPlayingSound:sound];
}
[sound release];
}
- (OOSound *)sound
{
return _sound;
}
@end

View File

@ -0,0 +1,62 @@
/*
OOALSoundDecoder.h
Class responsible for converting a sound to a PCM buffer for playback. This
class is an implementation detail. Do not use it directly; use OOSound to
load sounds.
OOALSound - OpenAL sound implementation for Oolite.
Copyright (C) 2005-2013 Jens Ayton
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#import <Foundation/Foundation.h>
@interface OOALSoundDecoder: NSObject
- (id)initWithPath:(NSString *)inPath;
+ (OOALSoundDecoder *)codecWithPath:(NSString *)inPath;
// Full-buffer reading.
- (BOOL)readMonoCreatingBuffer:(float **)outBuffer withFrameCount:(size_t *)outSize;
- (BOOL)readStereoCreatingLeftBuffer:(float **)outLeftBuffer rightBuffer:(float **)outRightBuffer withFrameCount:(size_t *)outSize;
// Stream reading. This will always provide two channels (as non-interleaved PCM), discarding extra channels or doubling mono as necessary.
- (size_t)streamStereoToBufferL:(float *)ioBufferL bufferR:(float *)ioBufferR maxFrames:(size_t)inMax;
// Returns the size of the data -readMonoCreatingBuffer:withFrameCount: will create.
- (size_t)sizeAsBuffer;
- (BOOL)isStereo;
- (long)sampleRate;
// For streaming
- (BOOL)atEnd;
- (BOOL)scanToOffset:(uint64_t)inOffset;
- (void)rewindToBeginning;
- (NSString *)name;
@end

615
src/Core/OOALSoundDecoder.m Normal file
View File

@ -0,0 +1,615 @@
/*
OOALSoundDecoder.m
OOALSound - OpenAL sound implementation for Oolite.
Copyright (C) 2005-2013 Jens Ayton
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#import "OOALSoundDecoder.h"
#import "NSDataOOExtensions.h"
#import <vorbis/vorbisfile.h>
#import "OOLogging.h"
#import "unzip.h"
#define ZIP_BUFFER_SIZE 8192
enum
{
kMaxDecodeSize = 1 << 20 // 2^20 frames = 4 MB
};
static void MixDown(float *inChan1, float *inChan2, float *outMix, size_t inCount);
static size_t OOReadOXZVorbis (void *ptr, size_t size, size_t nmemb, void *datasource);
static int OOCloseOXZVorbis (void *datasource);
@interface OOALSoundVorbisCodec: OOALSoundDecoder
{
OggVorbis_File _vf;
NSString *_name;
BOOL _atEnd;
@public
unzFile uf;
size_t oxzPointer;
}
- (NSDictionary *)comments;
@end
@implementation OOALSoundDecoder
- (id)initWithPath:(NSString *)inPath
{
[self release];
self = nil;
if ([[inPath pathExtension] isEqual:@"ogg"])
{
self = [[OOALSoundVorbisCodec alloc] initWithPath:inPath];
}
return self;
}
+ (OOALSoundDecoder *)codecWithPath:(NSString *)inPath
{
if ([[inPath pathExtension] isEqual:@"ogg"])
{
return [[[OOALSoundVorbisCodec alloc] initWithPath:inPath] autorelease];
}
return nil;
}
- (size_t)streamStereoToBufferL:(float *)ioBufferL bufferR:(float *)ioBufferR maxFrames:(size_t)inMax
{
return 0;
}
- (BOOL)readMonoCreatingBuffer:(float **)outBuffer withFrameCount:(size_t *)outSize
{
if (NULL != outBuffer) *outBuffer = NULL;
if (NULL != outSize) *outSize = 0;
return NO;
}
- (BOOL)readStereoCreatingLeftBuffer:(float **)outLeftBuffer rightBuffer:(float **)outRightBuffer withFrameCount:(size_t *)outSize
{
if (NULL != outLeftBuffer) *outLeftBuffer = NULL;
if (NULL != outRightBuffer) *outRightBuffer = NULL;
if (NULL != outSize) *outSize = 0;
return NO;
}
- (size_t)sizeAsBuffer
{
return 0;
}
- (BOOL)isStereo
{
return NO;
}
- (long)sampleRate
{
return 0;
}
- (BOOL)atEnd
{
return YES;
}
- (void)rewindToBeginning
{
}
- (BOOL)scanToOffset:(uint64_t)inOffset
{
return NO;
}
- (NSString *)name
{
return @"";
}
@end
@implementation OOALSoundVorbisCodec
- (id)initWithPath:(NSString *)path
{
BOOL OK = NO;
unsigned i, cl;
NSArray *components = [path pathComponents];
cl = [components count];
for (i = 0 ; i < cl ; i++)
{
NSString *component = [components objectAtIndex:i];
if ([[[component pathExtension] lowercaseString] isEqualToString:@"oxz"])
{
break;
}
}
// if i == cl then the path is entirely uncompressed
if (i == cl)
{
/* Get vorbis data from a standard file stream */
int err;
FILE *file;
if ((self = [super init]))
{
_name = [[path lastPathComponent] retain];
if (nil != path)
{
file = fopen([path UTF8String], "rb");
if (NULL != file)
{
err = ov_open_callbacks(file, &_vf, NULL, 0, OV_CALLBACKS_DEFAULT);
if (0 == err)
{
OK = YES;
}
}
}
if (!OK)
{
[self release];
self = nil;
}
}
}
else
{
NSRange range;
range.location = 0; range.length = i+1;
NSString *zipFile = [NSString pathWithComponents:[components subarrayWithRange:range]];
range.location = i+1; range.length = cl-(i+1);
NSString *containedFile = [NSString pathWithComponents:[components subarrayWithRange:range]];
const char* zipname = [zipFile cStringUsingEncoding:NSUTF8StringEncoding];
if (zipname != NULL)
{
uf = unzOpen64(zipname);
oxzPointer = 0;
}
if (uf == NULL)
{
OOLog(kOOLogFileNotFound, @"Could not unzip OXZ at %@", zipFile);
[self release];
self = nil;
}
else
{
const char* filename = [containedFile cStringUsingEncoding:NSUTF8StringEncoding];
// unzLocateFile(*, *, 1) = case-sensitive extract
if (unzLocateFile(uf, filename, 1) != UNZ_OK)
{
unzClose(uf);
[self release];
self = nil;
}
else
{
int err = UNZ_OK;
unz_file_info64 file_info = {0};
err = unzGetCurrentFileInfo64(uf, &file_info, NULL, 0, NULL, 0, NULL, 0);
if (err != UNZ_OK)
{
unzClose(uf);
OOLog(kOOLogFileNotFound, @"Could not get properties of %@ within OXZ at %@", containedFile, zipFile);
[self release];
self = nil;
}
else
{
err = unzOpenCurrentFile(uf);
if (err != UNZ_OK)
{
unzClose(uf);
OOLog(kOOLogFileNotFound, @"Could not read %@ within OXZ at %@", containedFile, zipFile);
[self release];
self = nil;
}
else
{
ov_callbacks _callbacks = {
OOReadOXZVorbis, // read sequentially
NULL, // no seek
OOCloseOXZVorbis, // close file
NULL, // no tell
};
err = ov_open_callbacks(self, &_vf, NULL, 0, _callbacks);
if (0 == err)
{
OK = YES;
}
if (!OK)
{
unzClose(uf);
[self release];
self = nil;
}
}
}
}
}
}
return self;
}
- (void)dealloc
{
[_name release];
ov_clear(&_vf);
[super dealloc];
}
- (NSDictionary *)comments
{
vorbis_comment *comments;
unsigned i, count;
NSMutableDictionary *result = nil;
NSString *comment, *key, *value;
NSRange range;
comments = ov_comment(&_vf, -1);
if (NULL != comments)
{
count = comments->comments;
if (0 != count)
{
result = [NSMutableDictionary dictionaryWithCapacity:count];
for (i = 0; i != count; ++i)
{
comment = [[NSString alloc] initWithBytesNoCopy:comments->user_comments[i] length:comments->comment_lengths[i] encoding:NSUTF8StringEncoding freeWhenDone:NO];
range = [comment rangeOfString:@"="];
if (0 != range.length)
{
key = [comment substringToIndex:range.location];
value = [comment substringFromIndex:range.location + 1];
}
else
{
key = comment;
value = @"";
}
[result setObject:value forKey:key];
[comment release];
}
}
}
return result;
}
- (BOOL)readMonoCreatingBuffer:(float **)outBuffer withFrameCount:(size_t *)outSize
{
float *buffer = NULL, *dst, **src;
size_t sizeInFrames = 0;
int remaining;
unsigned chanCount;
long framesRead;
ogg_int64_t totalSizeInFrames;
BOOL OK = YES;
if (NULL != outBuffer) *outBuffer = NULL;
if (NULL != outSize) *outSize = 0;
if (NULL == outBuffer || NULL == outSize) OK = NO;
if (OK)
{
totalSizeInFrames = ov_pcm_total(&_vf, -1);
assert ((uint64_t)totalSizeInFrames < (uint64_t)SIZE_MAX); // Should have been checked by caller
sizeInFrames = (size_t)totalSizeInFrames;
}
if (OK)
{
buffer = malloc(sizeof (float) * sizeInFrames);
if (!buffer) OK = NO;
}
if (OK && sizeInFrames)
{
remaining = (int)MIN(sizeInFrames, (size_t)INT_MAX);
dst = buffer;
do
{
chanCount = ov_info(&_vf, -1)->channels;
framesRead = ov_read_float(&_vf, &src, remaining, NULL);
if (framesRead <= 0)
{
if (OV_HOLE == framesRead) continue;
//else:
break;
}
if (1 == chanCount) bcopy(src[0], dst, sizeof (float) * framesRead);
else MixDown(src[0], src[1], dst, framesRead);
remaining -= framesRead;
dst += framesRead;
} while (0 != remaining);
sizeInFrames -= remaining; // In case we stopped at an error
}
if (OK)
{
*outBuffer = buffer;
*outSize = sizeInFrames;
}
else
{
if (buffer) free(buffer);
}
return OK;
}
- (BOOL)readStereoCreatingLeftBuffer:(float **)outLeftBuffer rightBuffer:(float **)outRightBuffer withFrameCount:(size_t *)outSize
{
float *bufferL = NULL, *bufferR = NULL, *dstL, *dstR, **src;
size_t sizeInFrames = 0;
int remaining;
unsigned chanCount;
long framesRead;
ogg_int64_t totalSizeInFrames;
BOOL OK = YES;
if (NULL != outLeftBuffer) *outLeftBuffer = NULL;
if (NULL != outRightBuffer) *outRightBuffer = NULL;
if (NULL != outSize) *outSize = 0;
if (NULL == outLeftBuffer || NULL == outRightBuffer || NULL == outSize) OK = NO;
if (OK)
{
totalSizeInFrames = ov_pcm_total(&_vf, -1);
assert ((uint64_t)totalSizeInFrames < (uint64_t)SIZE_MAX); // Should have been checked by caller
sizeInFrames = (size_t)totalSizeInFrames;
}
if (OK)
{
bufferL = malloc(sizeof (float) * sizeInFrames);
if (!bufferL) OK = NO;
}
if (OK)
{
bufferR = malloc(sizeof (float) * sizeInFrames);
if (!bufferR) OK = NO;
}
if (OK && sizeInFrames)
{
remaining = (int)MIN(sizeInFrames, (size_t)INT_MAX);
dstL = bufferL;
dstR = bufferR;
do
{
chanCount = ov_info(&_vf, -1)->channels;
framesRead = ov_read_float(&_vf, &src, remaining, NULL);
if (framesRead <= 0)
{
if (OV_HOLE == framesRead) continue;
//else:
break;
}
bcopy(src[0], dstL, sizeof (float) * framesRead);
if (1 == chanCount) bcopy(src[0], dstR, sizeof (float) * framesRead);
else bcopy(src[1], dstR, sizeof (float) * framesRead);
remaining -= framesRead;
dstL += framesRead;
dstR += framesRead;
} while (0 != remaining);
sizeInFrames -= remaining; // In case we stopped at an error
}
if (OK)
{
*outLeftBuffer = bufferL;
*outRightBuffer = bufferR;
*outSize = sizeInFrames;
}
else
{
if (bufferL) free(bufferL);
if (bufferR) free(bufferR);
}
return OK;
}
- (size_t)streamStereoToBufferL:(float *)ioBufferL bufferR:(float *)ioBufferR maxFrames:(size_t)inMax
{
float **src;
unsigned chanCount;
long framesRead;
size_t size;
int remaining;
unsigned rightChan;
// Note: for our purposes, a frame is a set of one sample for each channel.
if (NULL == ioBufferL || NULL == ioBufferR || 0 == inMax) return 0;
if (_atEnd) return inMax;
remaining = (int)MIN(inMax, (size_t)INT_MAX);
do
{
chanCount = ov_info(&_vf, -1)->channels;
framesRead = ov_read_float(&_vf, &src, remaining, NULL);
if (framesRead <= 0)
{
if (OV_HOLE == framesRead) continue;
//else:
_atEnd = YES;
break;
}
size = sizeof (float) * framesRead;
rightChan = (1 == chanCount) ? 0 : 1;
bcopy(src[0], ioBufferL, size);
bcopy(src[rightChan], ioBufferR, size);
remaining -= framesRead;
ioBufferL += framesRead;
ioBufferR += framesRead;
} while (0 != remaining);
return inMax - remaining;
}
- (size_t)sizeAsBuffer
{
ogg_int64_t size;
size = ov_pcm_total(&_vf, -1);
size *= sizeof(float) * ([self isStereo] ? 2 : 1);
if ((uint64_t)SIZE_MAX < (uint64_t)size) size = (ogg_int64_t)SIZE_MAX;
return (size_t)size;
}
- (BOOL)isStereo
{
return 1 < ov_info(&_vf, -1)->channels;
}
- (NSString *)description
{
return [NSString stringWithFormat:@"<%@ %p>{\"%@\", comments=%@}", [self className], self, _name, [self comments]];
}
- (long)sampleRate
{
return ov_info(&_vf, -1)->rate;
}
- (BOOL)atEnd
{
return _atEnd;
}
- (void)rewindToBeginning
{
if (!ov_pcm_seek(&_vf, 0)) _atEnd = NO;
}
- (BOOL)scanToOffset:(uint64_t)inOffset
{
if (!ov_pcm_seek(&_vf, inOffset))
{
_atEnd = NO;
return YES;
}
else return NO;
}
- (NSString *)name
{
return [[_name retain] autorelease];
}
@end
// TODO: optimise, vectorise
static void MixDown(float *inChan1, float *inChan2, float *outMix, size_t inCount)
{
while (inCount--)
{
*outMix++ = (*inChan1++ + *inChan2++) * 0.5f;
}
}
static size_t OOReadOXZVorbis (void *ptr, size_t size, size_t nmemb, void *datasource)
{
OOALSoundVorbisCodec *src = (OOALSoundVorbisCodec *)datasource;
size_t toRead = size*nmemb;
void *buf = (void*)malloc(toRead);
int err = UNZ_OK;
err = unzReadCurrentFile(src->uf, buf, toRead);
if (err > 0)
{
memcpy(ptr, buf, err);
}
if (err < 0)
{
return OV_EREAD;
}
return err;
}
static int OOCloseOXZVorbis (void *datasource)
{
OOALSoundVorbisCodec *src = (OOALSoundVorbisCodec *)datasource;
unzClose(src->uf);
return 0;
}
// TODO: implement seek/tell functions for OXZ datastream

61
src/Core/OOALSoundMixer.h Normal file
View File

@ -0,0 +1,61 @@
/*
OOALSoundMixer.h
Class responsible for managing and mixing sound channels. This class is an
implementation detail. Do not use it directly; use an OOSoundSource to play an
OOSound.
OOALSound - OpenAL sound implementation for Oolite.
Copyright (C) 2006-2013 Jens Ayton
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#import <Foundation/Foundation.h>
@class OOSoundChannel;
enum
{
kMixerGeneralChannels = 32
};
@interface OOSoundMixer: NSObject
{
OOSoundChannel *_channels[kMixerGeneralChannels];
OOSoundChannel *_freeList;
uint32_t _maxChannels;
uint32_t _playMask;
}
// Singleton accessor
+ (id) sharedMixer;
- (void) update;
- (OOSoundChannel *) popChannel;
- (void) pushChannel:(OOSoundChannel *)channel;
@end

166
src/Core/OOALSoundMixer.m Normal file
View File

@ -0,0 +1,166 @@
/*
OOALSoundMixer.m
OOALSound - OpenAL sound implementation for Oolite.
Copyright (C) 2006-2013 Jens Ayton
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDEDAS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <assert.h>
#import "OOALSoundMixer.h"
#import "OOCocoa.h"
#import "OOALSound.h"
#import "OOALSoundChannel.h"
static OOSoundMixer *sSingleton = nil;
@implementation OOSoundMixer
+ (id) sharedMixer
{
if (nil == sSingleton)
{
[[self alloc] init];
}
return sSingleton;
}
- (id) init
{
BOOL OK = YES;
uint32_t idx = 0, count = kMixerGeneralChannels;
OOSoundChannel *channel;
if (!(self = [super init])) return nil;
if (![OOSound setUp]) OK = NO;
if (OK)
{
// Allocate channels
do
{
channel = [[OOSoundChannel alloc] init];
if (nil != channel)
{
_channels[idx++] = channel;
[self pushChannel:channel];
}
} while (--count);
}
if (!OK)
{
[super release];
// static analyser complains about this next line; probably nothing - CIM
self = nil;
}
else
{
sSingleton = self;
}
return sSingleton;
}
- (void) update
{
uint32_t i;
for (i = 0; i < kMixerGeneralChannels; ++i)
{
[_channels[i] update];
}
}
- (OOSoundChannel *) popChannel
{
OOSoundChannel *channel = _freeList;
_freeList = [channel next];
[channel setNext:nil];
return channel;
}
- (void) pushChannel:(OOSoundChannel *)channel
{
assert(channel != nil);
[channel setNext:_freeList];
_freeList = channel;
}
@end
@implementation OOSoundMixer (Singleton)
/* Canonical singleton boilerplate.
See Cocoa Fundamentals Guide: Creating a Singleton Instance.
See also +sharedMixer above.
NOTE: assumes single-threaded access.
*/
+ (id)allocWithZone:(NSZone *)inZone
{
if (sSingleton == nil)
{
sSingleton = [super allocWithZone:inZone];
return sSingleton;
}
return nil;
}
- (id)copyWithZone:(NSZone *)inZone
{
return self;
}
- (id)retain
{
return self;
}
- (NSUInteger)retainCount
{
return UINT_MAX;
}
- (void)release
{}
- (id)autorelease
{
return self;
}
@end

40
src/Core/OOOpenAL.h Normal file
View File

@ -0,0 +1,40 @@
/*
OOOpenAL.h
Singleton controller for Open AL interfaces
Oolite
Copyright (C) 2004-2013 Giles C Williams and contributors
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA.
*/
#include <AL/al.h>
#include <AL/alc.h>
// alGetError should always be called before openAL calls to reset the
// error stack
#define OOAL(cmd) alGetError(); cmd;
@interface OOOpenAL : NSObject
{
@private
ALCdevice *alcDevice;
}

View File

@ -0,0 +1,50 @@
/*
OOOpenALController.h
Singleton controller for Open AL interfaces
Oolite
Copyright (C) 2004-2013 Giles C Williams and contributors
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA.
*/
#include <AL/al.h>
#include <AL/alc.h>
#include <Foundation/Foundation.h>
// alGetError should always be called before openAL calls to reset the
// error stack
#define OOAL(cmd) do { alGetError(); cmd; } while (0)
static NSString * const kOOLogSoundInitError = @"sound.initialization.error";
static NSString * const kOOLogSoundLoadingSuccess = @"sound.load.success";
static NSString * const kOOLogSoundLoadingError = @"sound.load.error";
@interface OOOpenALController : NSObject
{
@private
ALCdevice *device;
ALCcontext *context;
}
+ (OOOpenALController *) sharedController;
- (void) setMasterVolume:(ALfloat) fraction;
- (ALfloat) masterVolume;
@end

View File

@ -0,0 +1,83 @@
/*
OOOpenALController.m
Oolite
Copyright (C) 2004-2013 Giles C Williams and contributors
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA.
*/
#import "OOOpenALController.h"
#import "OOLogging.h"
static id sSingleton = nil;
@implementation OOOpenALController
+ (OOOpenALController *) sharedController
{
if (sSingleton == nil)
{
sSingleton = [[self alloc] init];
}
return sSingleton;
}
- (id) init
{
self = [super init];
if (self != nil)
{
ALuint error;
device = alcOpenDevice(NULL); // default device
if (!device)
{
OOLog(kOOLogSoundInitError,@"Failed to open default sound device");
return nil;
}
context = alcCreateContext(device,NULL); // default context
if (!alcMakeContextCurrent(context))
{
OOLog(kOOLogSoundInitError,@"Failed to create default sound context");
return nil;
}
if ((error = alGetError()) != AL_NO_ERROR)
{
OOLog(kOOLogSoundInitError,@"Error %d creating sound context",error);
}
}
return self;
}
- (void) setMasterVolume:(ALfloat) fraction
{
OOAL(alListenerf(AL_GAIN,fraction));
}
- (ALfloat) masterVolume
{
ALfloat fraction = 0.0;
OOAL(alGetListenerf(AL_GAIN,&fraction));
return fraction;
}
@end

View File

@ -43,7 +43,14 @@ MA 02110-1301, USA.
#import "OOCocoa.h"
// currently on SDL platforms only as I can't test Mac OS X
#define OOLITE_OPENAL OOLITE_SDL
#if OOLITE_OPENAL
#import "OOALSound.h"
#import "OOALMusic.h"
#import "OOBasicSoundReferencePoint.h"
#else
#if OOLITE_SDL
#import "OOSDLSound.h"
#import "SDLMusic.h"
@ -53,5 +60,6 @@ MA 02110-1301, USA.
#import "OOCAMusic.h"
#import "OOCASoundReferencePoint.h"
#endif
#endif
#import "OOSoundSource.h"