1
0
mirror of https://github.com/thes3m/XNI synced 2024-12-26 13:26:06 +01:00

Audio support added

git-svn-id: http://xni.googlecode.com/svn/XNI@50 ac433895-eea3-a490-d80a-17149a75e588
This commit is contained in:
Matej Jan 2010-12-15 08:55:39 +00:00
parent 2f0bc9f91a
commit 1ca44f0f04
16 changed files with 636 additions and 0 deletions

View File

@ -0,0 +1,23 @@
#import <OpenAL/al.h>
#import <OpenAL/alc.h>
typedef enum {
AudioChannelsMono = AL_FORMAT_MONO16,
AudioChannelsStereo = AL_FORMAT_STEREO16
} AudioChannels;
typedef enum {
AudioStopOptionsAsAuthored,
AudioStopOptionsImmediate
} AudioStopOptions;
typedef enum {
MicrophoneStateStarted,
MicrophoneStateStopped
} MicrophoneState;
typedef enum {
SoundStatePaused = AL_PAUSED,
SoundStatePlaying = AL_PLAYING,
SoundStateStopped = AL_STOPPED
} SoundState;

View File

@ -0,0 +1,3 @@
#import "AudioEnums.h"
@class SoundEffect, SoundEffectInstance;

View File

@ -0,0 +1,4 @@
#import "AudioEnums.h"
#import "SoundEffect.h"
#import "SoundEffectInstance.h"

View File

@ -0,0 +1,17 @@
//
// SoundEffect+Internal.h
// XNI
//
// Created by Matej Jan on 15.12.10.
// Copyright 2010 Retronator. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "SoundEffect.h"
@interface SoundEffect (Internal)
+ (void) update;
@end

View File

@ -0,0 +1,49 @@
//
// SoundEffect.h
// XNI
//
// Created by Matej Jan on 15.12.10.
// Copyright 2010 Retronator. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <OpenAL/al.h>
#import <OpenAL/alc.h>
#import "Retronator.Xni.Framework.Audio.classes.h"
@interface SoundEffect : NSObject {
@private
NSData *buffer;
int offset;
int count;
int sampleRate;
AudioChannels channels;
int loopStart;
int loopLength;
NSString *name;
NSUInteger bufferID;
}
- (id) initWithBuffer:(NSData*)theBuffer sampleRate:(int)theSampleRate channels:(AudioChannels)theChannels;
- (id) initWithBuffer:(NSData*)theBuffer offset:(int)theOffset count:(int)theCount sampleRate:(int)theSampleRate channels:(AudioChannels)theChannels loopStart:(int)theLoopStart loopLength:(int)theLoopLength;
@property (nonatomic, readonly) NSTimeInterval duration;
@property (nonatomic, retain) NSString *name;
+ (float) speedOfSound;
+ (void) setSpeedOfSound:(float)value;
+ (float) masterVolume;
+ (void) setMasterVolume:(float)value;
+ (SoundEffect*) fromStream:(NSStream*)stream;
+ (NSTimeInterval) getSampleDurationForSizeInBytes:(int)sizeInBytes sampleRate:(int)sampleRate channels:(AudioChannels)channels;
+ (int) getSampleSizeInBytesForDuration:(NSTimeInterval)duration sampleRate:(int)sampleRate channels:(AudioChannels)channels;
- (SoundEffectInstance*) createInstance;
- (BOOL) play;
- (BOOL) playWithVolume:(float)volume pitch:(float)pitch pan:(float)pan;
@end

View File

@ -0,0 +1,151 @@
//
// SoundEffect.m
// XNI
//
// Created by Matej Jan on 15.12.10.
// Copyright 2010 Retronator. All rights reserved.
//
#import "SoundEffect.h"
#import "Retronator.Xni.Framework.Audio.h"
#import "SoundEffectInstance+Internal.h"
@implementation SoundEffect
static ALCdevice *audioDevice = nil;
static ALCcontext *audioContext = nil;
static NSMutableArray *playingInstances;
static float speedOfSound = 343.5;
static float masterVolume = 1;
+ (void) initialize {
if (!audioDevice) {
// Initialization
audioDevice = alcOpenDevice(NULL); // select the "preferred device"
if (audioDevice) {
// use the device to make a context
audioContext=alcCreateContext(audioDevice,NULL);
// set my context to the currently active one
alcMakeContextCurrent(audioContext);
}
playingInstances = [[NSMutableArray alloc] init];
}
}
- (id) initWithBuffer:(NSData*)theBuffer sampleRate:(int)theSampleRate channels:(AudioChannels)theChannels {
return [self initWithBuffer:theBuffer offset:0 count:[theBuffer length] sampleRate:theSampleRate channels:theChannels loopStart:0 loopLength:0];
}
- (id) initWithBuffer:(NSData*)theBuffer
offset:(int)theOffset
count:(int)theCount
sampleRate:(int)theSampleRate
channels:(AudioChannels)theChannels
loopStart:(int)theLoopStart
loopLength:(int)theLoopLength
{
self = [super init];
if (self != nil) {
buffer = theBuffer;
offset = theOffset;
count = theCount;
sampleRate = theSampleRate;
channels = theChannels;
loopStart = theLoopStart;
loopLength = theLoopLength;
// Create an OpenAL buffer.
alGenBuffers(1, &bufferID);
alBufferData(bufferID, channels, [theBuffer bytes], [theBuffer length], theSampleRate);
}
return self;
}
@synthesize duration, name;
+ (float) speedOfSound {
return speedOfSound;
}
+ (void) setSpeedOfSound:(float)value {
speedOfSound = value;
alSpeedOfSound(speedOfSound);
}
+ (float) masterVolume {
return masterVolume;
}
+ (void) setMasterVolume:(float)value {
masterVolume = value;
alListenerf(AL_GAIN, masterVolume);
}
+ (SoundEffect*) fromStream:(NSStream*)stream {
return nil;
}
+ (int) getSecondInBytesForSampleRate:(int)sampleRate channels:(AudioChannels)channels {
int secondInBytes = sampleRate;
// 16 bits per sample = 2 bytes per sample.
secondInBytes *= 2;
// Multiply with number of channels.
if (channels == AudioChannelsStereo) {
secondInBytes *= 2;
}
return secondInBytes;
}
+ (NSTimeInterval) getSampleDurationForSizeInBytes:(int)sizeInBytes sampleRate:(int)sampleRate channels:(AudioChannels)channels {
return (double)sizeInBytes / (double)[SoundEffect getSecondInBytesForSampleRate:sampleRate channels:channels];
}
+ (int) getSampleSizeInBytesForDuration:(NSTimeInterval)duration sampleRate:(int)sampleRate channels:(AudioChannels)channels {
return (int)(duration * [SoundEffect getSecondInBytesForSampleRate:sampleRate channels:channels]);
}
+ (void) update {
int i=0;
while (i<[playingInstances count]) {
SoundEffectInstance *instance = [playingInstances objectAtIndex:i];
if (instance.state == SoundStateStopped) {
[playingInstances removeObjectAtIndex:i];
} else {
i++;
}
}
}
- (SoundEffectInstance *) createInstance {
return [[[SoundEffectInstance alloc] initWithBufferID:bufferID] autorelease];
}
- (BOOL) play {
return [self playWithVolume:1 pitch:0 pan:0];
}
- (BOOL) playWithVolume:(float)volume pitch:(float)pitch pan:(float)pan {
SoundEffectInstance *instance = [self createInstance];
[playingInstances addObject:instance];
instance.volume = volume;
instance.pitch = pitch;
instance.pan = pan;
[instance play];
return YES;
}
- (void) dealloc
{
alDeleteBuffers(1, &bufferID);
[super dealloc];
}
@end

View File

@ -0,0 +1,16 @@
//
// SoundEffectInstance+Internal.h
// XNI
//
// Created by Matej Jan on 15.12.10.
// Copyright 2010 Retronator. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface SoundEffectInstance (Internal)
- (id) initWithBufferID:(NSUInteger)bufferID;
@end

View File

@ -0,0 +1,32 @@
//
// SoundEffectInstance.h
// XNI
//
// Created by Matej Jan on 15.12.10.
// Copyright 2010 Retronator. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <OpenAL/al.h>
#import <OpenAL/alc.h>
#import "Retronator.Xni.Framework.Audio.classes.h"
@interface SoundEffectInstance : NSObject {
@private
BOOL isLooped;
float pan, pitch, volume;
NSUInteger sourceID;
}
@property (nonatomic) BOOL isLooped;
@property (nonatomic, readonly) SoundState state;
@property (nonatomic) float pan, pitch, volume;
- (void) play;
- (void) pause;
- (void) resume;
- (void) stop;
- (void) stopImmediate:(BOOL)immediate;
@end

View File

@ -0,0 +1,91 @@
//
// SoundEffectInstance.m
// XNI
//
// Created by Matej Jan on 15.12.10.
// Copyright 2010 Retronator. All rights reserved.
//
#import "SoundEffectInstance.h"
#import "SoundEffectInstance+Internal.h"
@implementation SoundEffectInstance
- (id) initWithBufferID:(NSUInteger)bufferID
{
self = [super init];
if (self != nil) {
// grab a source ID from openAL
alGenSources(1, &sourceID);
// attach the buffer to the source
alSourcei(sourceID, AL_BUFFER, bufferID);
// set some basic source prefs
alSourcef(sourceID, AL_PITCH, 1.0f);
alSourcef(sourceID, AL_GAIN, 1.0f);
}
return self;
}
@synthesize isLooped, pan, pitch, volume;
- (void) setIsLooped:(BOOL)value {
isLooped = value;
alSourcei(sourceID, AL_LOOPING, isLooped);
}
- (SoundState) state {
ALenum state;
alGetSourcei(sourceID, AL_SOURCE_STATE, &state);
return state;
}
- (void) setPan:(float)value {
pan = value;
alSource3f(sourceID, AL_POSITION, pan, 0, 0);
}
- (void) setPitch:(float)value {
pitch = value;
float alPitch = pow(2, value);
alSourcef(sourceID, AL_PITCH, alPitch);
}
- (void) setVolume:(float)value {
volume = value;
alSourcef(sourceID, AL_GAIN, volume);
}
- (void) play {
alSourcePlay(sourceID);
}
- (void) pause {
alSourcePause(sourceID);
}
- (void) resume {
alSourcePlay(sourceID);
}
- (void) stop {
[self stopImmediate:YES];
}
- (void) stopImmediate:(BOOL)immediate {
if (immediate) {
alSourceStop(sourceID);
}
}
- (void) dealloc
{
alDeleteSources(1, &sourceID);
[super dealloc];
}
@end

View File

@ -0,0 +1,40 @@
//
// AudioContent.h
// XNI
//
// Created by Matej Jan on 15.12.10.
// Copyright 2010 Retronator. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "Retronator.Xni.Framework.Content.Pipeline.Audio.classes.h"
#import "ContentItem.h"
@interface AudioContent : ContentItem {
NSTimeInterval duration;
NSString *fileName;
AudioFileType fileType;
AudioFormat *format;
int loopLength;
int loopStart;
@private
NSURL *fileUrl;
ExtAudioFileRef extRef;
}
- (id) initWithAudioFileName:(NSString*)theFileName audioFileType:(AudioFileType)theFileType;
@property (nonatomic, readonly) NSData *data;
@property (nonatomic, readonly) NSTimeInterval duration;
@property (nonatomic, readonly) NSString *fileName;
@property (nonatomic, readonly) AudioFileType fileType;
@property (nonatomic, readonly) AudioFormat *format;
@property (nonatomic, readonly) int loopLength;
@property (nonatomic, readonly) int loopStart;
- (void) convertFormatTo:(ConversionFormat)formatType quality:(ConversionQuality)quality targetFileName:(NSString*)targetFileName;
@end

View File

@ -0,0 +1,119 @@
//
// AudioContent.m
// XNI
//
// Created by Matej Jan on 15.12.10.
// Copyright 2010 Retronator. All rights reserved.
//
#import "AudioContent.h"
#import "Retronator.Xni.Framework.Content.Pipeline.Audio.h"
@implementation AudioContent
- (id) initWithAudioFileName:(NSString *)theFileName audioFileType:(AudioFileType)theFileType
{
self = [super init];
if (self != nil) {
fileName = theFileName;
fileType = theFileType;
// Load file.
OSStatus err = noErr;
AudioStreamBasicDescription theFileFormat;
UInt32 thePropertySize = sizeof(theFileFormat);
// Open a file with ExtAudioFileOpen().
fileUrl = [[NSURL fileURLWithPath:theFileName] retain];
extRef = nil;
err = ExtAudioFileOpenURL((CFURLRef)fileUrl, &extRef);
if (err) {
[NSException raise:@"NotSupportedException" format:@"ExtAudioFileOpenURL FAILED, Error = %ld\n", err];
}
// Get the audio data format.
err = ExtAudioFileGetProperty(extRef, kExtAudioFileProperty_FileDataFormat, &thePropertySize, &theFileFormat);
if (err) {
[NSException raise:@"NotSupportedException" format:@"ExtAudioFileGetProperty(kExtAudioFileProperty_FileDataFormat) FAILED, Error = %ld\n", err];
}
format = [[AudioFormat alloc] initWithDescription:theFileFormat];
}
return self;
}
@synthesize duration, fileName, fileType, format, loopLength, loopStart;
- (NSData *) data {
// Get the total frame count
void* theData = NULL;
SInt64 theFileLengthInFrames = 0;
UInt32 thePropertySize = sizeof(theFileLengthInFrames);
OSStatus err = noErr;
err = ExtAudioFileGetProperty(extRef, kExtAudioFileProperty_FileLengthFrames, &thePropertySize, &theFileLengthInFrames);
if (err) {
[NSException raise:@"NotSupportedException" format:@"MyGetOpenALAudioData: ExtAudioFileGetProperty(kExtAudioFileProperty_FileLengthFrames) FAILED, Error = %ld\n", err];
}
duration = (double)theFileLengthInFrames / (double)format.sampleRate;
// Read all the data into memory
UInt32 dataSize = theFileLengthInFrames * format.nativeWaveFormat.mBytesPerFrame;
theData = malloc(dataSize);
if (theData)
{
AudioBufferList theDataBuffer;
theDataBuffer.mNumberBuffers = 1;
theDataBuffer.mBuffers[0].mDataByteSize = dataSize;
theDataBuffer.mBuffers[0].mNumberChannels = format.nativeWaveFormat.mChannelsPerFrame;
theDataBuffer.mBuffers[0].mData = theData;
// Read the data into an AudioBufferList
err = ExtAudioFileRead(extRef, (UInt32*)&theFileLengthInFrames, &theDataBuffer);
if (err) {
// failure
[NSException raise:@"NotSupportedException" format:@"MyGetOpenALAudioData: ExtAudioFileRead FAILED, Error = %ld\n", err];
}
}
NSData *result = [NSData dataWithBytes:theData length:dataSize];
free(theData);
return result;
}
- (void) convertFormatTo:(ConversionFormat)formatType quality:(ConversionQuality)quality targetFileName:(NSString *)targetFileName {
AudioStreamBasicDescription theOutputFormat;
// Set the client format to 16 bit signed integer (native-endian) data
// Maintain the channel count and sample rate of the original source format
theOutputFormat.mSampleRate = format.nativeWaveFormat.mSampleRate;
theOutputFormat.mChannelsPerFrame = format.nativeWaveFormat.mChannelsPerFrame;
theOutputFormat.mFormatID = formatType;
theOutputFormat.mBytesPerPacket = 2 * theOutputFormat.mChannelsPerFrame;
theOutputFormat.mFramesPerPacket = 1;
theOutputFormat.mBytesPerFrame = 2 * theOutputFormat.mChannelsPerFrame;
theOutputFormat.mBitsPerChannel = 16;
theOutputFormat.mFormatFlags = kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked | kAudioFormatFlagIsSignedInteger;
// Set the desired client (output) data format
OSStatus err = noErr;
err = ExtAudioFileSetProperty(extRef, kExtAudioFileProperty_ClientDataFormat, sizeof(theOutputFormat), &theOutputFormat);
if (err) {
[NSException raise:@"NotSupportedException" format:@"MyGetOpenALAudioData: ExtAudioFileSetProperty(kExtAudioFileProperty_ClientDataFormat) FAILED, Error = %ld\n", err];
}
[format release];
format = [[AudioFormat alloc] initWithDescription:theOutputFormat];
}
- (void) dealloc
{
//if (extRef) ExtAudioFileDispose(extRef);
[fileUrl release];
[super dealloc];
}
@end

View File

@ -0,0 +1,20 @@
#import <AudioToolbox/AudioToolbox.h>
typedef enum {
AudioFileTypeMp3,
AudioFileTypeWav,
// AudioFileTypeWma,
} AudioFileType;
typedef enum {
ConversionFormatAdpcm = kAudioFormatAppleIMA4,
ConversionFormatPcm = kAudioFormatLinearPCM,
// ConversionFormatWindowsMedia,
// ConversionFormatXma,
} ConversionFormat;
typedef enum {
ConversionQualityLow,
ConversionQualityMedium,
ConversionQualityBest,
} ConversionQuality;

View File

@ -0,0 +1,33 @@
//
// AudioFormat.h
// XNI
//
// Created by Matej Jan on 15.12.10.
// Copyright 2010 Retronator. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>
@interface AudioFormat : NSObject {
int averageBytesPerSecond;
int bitsPerSample;
int blockAlign;
int channelCount;
int format;
AudioStreamBasicDescription nativeWaveFormat;
int sampleRate;
}
- (id) initWithDescription:(AudioStreamBasicDescription)description;
@property (nonatomic, readonly) int averageBytesPerSecond;
@property (nonatomic, readonly) int bitsPerSample;
@property (nonatomic, readonly) int blockAlign;
@property (nonatomic, readonly) int channelCount;
@property (nonatomic, readonly) int format;
@property (nonatomic, readonly) AudioStreamBasicDescription nativeWaveFormat;
@property (nonatomic, readonly) int sampleRate;
@end

View File

@ -0,0 +1,31 @@
//
// AudioFormat.m
// XNI
//
// Created by Matej Jan on 15.12.10.
// Copyright 2010 Retronator. All rights reserved.
//
#import "AudioFormat.h"
@implementation AudioFormat
- (id) initWithDescription:(AudioStreamBasicDescription)description {
self = [super init];
if (self != nil) {
averageBytesPerSecond = description.mSampleRate * description.mBytesPerFrame;
bitsPerSample = description.mBitsPerChannel;
blockAlign = description.mBytesPerFrame;
channelCount = description.mChannelsPerFrame;
format = description.mFormatID;
nativeWaveFormat = description;
sampleRate = description.mSampleRate;
}
return self;
}
@synthesize averageBytesPerSecond, bitsPerSample, blockAlign, channelCount, format, nativeWaveFormat, sampleRate;
@end

View File

@ -0,0 +1,3 @@
#import "AudioContentEnums.h"
@class AudioContent, AudioFormat;

View File

@ -0,0 +1,4 @@
#import "AudioContentEnums.h"
#import "AudioContent.h"
#import "AudioFormat.h"