版本记录
版本号 | 时间 |
---|---|
V1.0 | 2017.12.29 |
前言
ios系统中有很多方式可以播放音频文件,这里我们就详细的说明下播放音乐文件的原理和实例。感兴趣的可以看我写的上面几篇。
1. 几种播放音频文件的方式(一) —— 播放本地音乐
2. 几种播放音频文件的方式(二) —— 音效播放
3. 几种播放音频文件的方式(三) —— 网络音乐播放
4. 几种播放音频文件的方式(四) —— 音频队列服务(Audio Queue Services)(一)
5. 几种播放音频文件的方式(五) —— 音频队列服务(Audio Queue Services)简介(二)
6. 几种播放音频文件的方式(六) —— 音频队列服务(Audio Queue Services)之关于音频队列(三)
7. 几种播放音频文件的方式(七) —— 音频队列服务(Audio Queue Services)之录制音频(四)
8. 几种播放音频文件的方式(八) —— 音频队列服务(Audio Queue Services)之播放音频(五)
9. 几种播放音频文件的方式(九) —— Media Player框架之基本概览(一)
10. 几种播放音频文件的方式(十) —— Media Player框架之简单播放音频示例(二)
11. 几种播放音频文件的方式(十一) —— AudioUnit框架之基本概览(一)
12. 几种播放音频文件的方式(十二) —— OpenAL框架之基本概览(一)
13. 几种播放音频文件的方式(十三) —— OpenAL框架之分步解析(二)
14. 几种播放音频文件的方式(十四) —— OpenAL框架之简单示例一(三)
GLAirplay示例
下面我们看一下示例。
演示AirPlay
启用时如何独立使用第二台显示器。
下面我们看一下代码。
1. GLAirPlay/main.m
/*
Copyright (C) 2016 Apple Inc. All Rights Reserved.
See LICENSE.txt for this sample’s licensing information
Abstract:
The main file.
*/
#import
#import "AppDelegate.h"
int main(int argc, char *argv[])
{
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
2.GLAirPlay/UserControlDelegate.h
/*
Copyright (C) 2016 Apple Inc. All Rights Reserved.
See LICENSE.txt for this sample’s licensing information
Abstract:
The object that conforms to this UserControlDelegate protocol is responsible for setting the GL cube's rotating radius.
*/
#import
@protocol UserControlDelegate
-(float)rotatingRadius;
@end
3.GLAirPlay/GLViewController.h
/*
Copyright (C) 2016 Apple Inc. All Rights Reserved.
See LICENSE.txt for this sample’s licensing information
Abstract:
This UIViewController configures the OpenGL ES view and its UI when an external display is connected/disconnected.
*/
#import
#import "UserControlDelegate.h"
@interface GLViewController : UIViewController
@property (nonatomic, strong) UIViewController *userInterfaceOnTop;
@property (nonatomic, strong) UIViewController *userInterfaceFullscreen;
- (void)startAnimation;
- (void)stopAnimation;
- (void)screenDidConnect:(UIViewController *)userInterface;
- (void)screenDidDisconnect:(UIViewController *)userInterface;
- (void)setTargetScreen:(UIScreen *)targetScreen;
@end
4.GLAirPlay/GLView.m
/*
Copyright (C) 2016 Apple Inc. All Rights Reserved.
See LICENSE.txt for this sample’s licensing information
Abstract:
The OpenGL ES view which renders a rotating cube. Responsible for creating a CADisplayLink for the new target display when a connection/disconnection occurs.
*/
#import
#import
#import
#import "GLView.h"
#import "CubePlayback.h"
#define BUFFER_OFFSET(i) ((char *)NULL + (i))
GLfloat gCubeVertexData[216] =
{
// Data layout for each line below is:
// positionX, positionY, positionZ, normalX, normalY, normalZ,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f,
0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f,
-0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f,
0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f,
0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f,
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f,
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f
};
static double GetTimeMS()
{
return (CACurrentMediaTime()*1000.0);
}
@interface GLView ()
{
NSInteger _animationFrameInterval;
CADisplayLink *_displayLink;
UIScreen *_targetScreen;
EAGLContext *_context;
// The pixel dimensions of the CAEAGLLayer
GLint _backingWidth;
GLint _backingHeight;
// The OpenGL names for the framebuffer and renderbuffer used to render to this view
GLuint _defaultFramebuffer, _colorRenderbuffer;
// The OpenGL frame for the depth buffer
GLuint _depthRenderbuffer;
GLuint _vertexArray;
GLuint _vertexBuffer;
float _rotation;
float _radius;
double _renderTime;
BOOL _zeroDeltaTime;
}
@property (nonatomic, strong) GLKBaseEffect *effect;
// OpenAL playback is wired up in the storyboard scene
@property (nonatomic, strong) IBOutlet CubePlayback *playback;
@end
@implementation GLView
+ (Class)layerClass
{
return [CAEAGLLayer class];
}
// The GL view is stored in the nib file. When it's unarchived it's sent -initWithCoder:
- (id)initWithCoder:(NSCoder*)coder
{
if ((self = [super initWithCoder:coder]))
{
// Get the layer
CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
eaglLayer.opaque = TRUE;
eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:FALSE], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];
_context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
if (!_context || ![EAGLContext setCurrentContext:_context])
{
return nil;
}
[self setupGL];
_animating = FALSE;
_animationFrameInterval = 1;
_displayLink = nil;
_zeroDeltaTime = TRUE;
}
return self;
}
- (void)setupGL
{
// Create default framebuffer object. The backing will be allocated for the current layer in -resizeFromLayer
glGenFramebuffers(1, &_defaultFramebuffer);
glGenRenderbuffers(1, &_colorRenderbuffer);
glBindFramebuffer(GL_FRAMEBUFFER, _defaultFramebuffer);
glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderbuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _colorRenderbuffer);
// Create a depth buffer as we want to enalbe GL_DEPTH_TEST in this sample
glGenRenderbuffers(1, &_depthRenderbuffer);
// Create a GLKBaseEffect to render the object
self.effect = [[GLKBaseEffect alloc] init];
self.effect.light0.enabled = GL_TRUE;
self.effect.light0.diffuseColor = GLKVector4Make(1.0f, 0.4f, 0.4f, 1.0f);
glEnable(GL_DEPTH_TEST);
// Create a VAO that stores the cube vertex and normal data
glGenVertexArraysOES(1, &_vertexArray);
glBindVertexArrayOES(_vertexArray);
glGenBuffers(1, &_vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(gCubeVertexData), gCubeVertexData, GL_STATIC_DRAW);
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 24, BUFFER_OFFSET(0));
glEnableVertexAttribArray(GLKVertexAttribNormal);
glVertexAttribPointer(GLKVertexAttribNormal, 3, GL_FLOAT, GL_FALSE, 24, BUFFER_OFFSET(12));
glBindVertexArrayOES(0);
}
- (void)drawView:(id)sender
{
double currentTime = GetTimeMS();
double deltaTime = _zeroDeltaTime ? 0.0 : currentTime - _renderTime;
_renderTime = currentTime;
if (_zeroDeltaTime)
_zeroDeltaTime = FALSE;
// Update animation states
if (self.userControlDelegate)
_radius = [self.userControlDelegate rotatingRadius];
_rotation += deltaTime * 0.05 * M_PI / 180.0;
// Update OpenGL
[EAGLContext setCurrentContext:_context];
glBindFramebuffer(GL_FRAMEBUFFER, _defaultFramebuffer);
glViewport(0, 0, _backingWidth, _backingHeight);
glClearColor(0.65f, 0.65f, 0.65f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBindVertexArrayOES(_vertexArray);
// Compute the projection matrix
float aspect = (GLfloat)_backingWidth / (GLfloat)_backingHeight;
GLKMatrix4 projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(65.0f), aspect, 0.1f, 100.0f);
self.effect.transform.projectionMatrix = projectionMatrix;
// Compute the model view matrix
GLKMatrix4 baseModelViewMatrix = GLKMatrix4MakeTranslation(0.0f, 0.0f, -4.0f);
baseModelViewMatrix = GLKMatrix4Rotate(baseModelViewMatrix, _rotation, 0.0f, 1.0f, 0.0f);
GLKMatrix4 modelViewMatrix = GLKMatrix4MakeTranslation(0.0f, 0.0f, -fabs(_radius));
modelViewMatrix = GLKMatrix4Rotate(modelViewMatrix, _rotation, 1.0f, 1.0f, 1.0f);
modelViewMatrix = GLKMatrix4Multiply(baseModelViewMatrix, modelViewMatrix);
self.effect.transform.modelviewMatrix = modelViewMatrix;
// Render the object with GLKit
[self.effect prepareToDraw];
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderbuffer);
[_context presentRenderbuffer:GL_RENDERBUFFER];
// Update OpenAL playback
// cube's current position
GLKVector4 pos = GLKVector4Make(0.0f, 0.0f, 0.0f, 1.0f);
pos = GLKMatrix4MultiplyVector4(modelViewMatrix, pos);
// source
float *s = pos.v;
[self.playback setSourcePos:s];
// listener
float l[] = { 1.0f, 0.0f, -4.0f };
[self.playback setListenerPos:l];
[self.playback setListenerRotation:(float)-M_PI_2]; //points inwards to the screen
}
- (BOOL)resizeFromLayer
{
CAEAGLLayer *layer = (CAEAGLLayer*)self.layer;
// Allocate color buffer backing based on the current layer size
glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderbuffer);
[_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:layer];
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &_backingWidth);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &_backingHeight);
// Allocate storage for the depth buffer, and attach it to the framebuffer’s depth attachment point
glBindRenderbuffer(GL_RENDERBUFFER, _depthRenderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, _backingWidth, _backingHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depthRenderbuffer);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
{
NSLog(@"Failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
return NO;
}
return YES;
}
- (void)layoutSubviews
{
if ([self resizeFromLayer])
{
// An external display might just have been connected/disconnected. We do not want to
// consider time spent in the connection/disconnection in the animation.
_zeroDeltaTime = TRUE;
[self drawView:nil];
}
}
#pragma Display Link
- (NSInteger)animationFrameInterval
{
return _animationFrameInterval;
}
- (void)setAnimationFrameInterval:(NSInteger)frameInterval
{
// Frame interval defines how many display frames must pass between each time the
// display link fires. The display link will only fire 30 times a second when the
// frame internal is two on a display that refreshes 60 times a second. The default
// frame interval setting of one will fire 60 times a second when the display refreshes
// at 60 times a second. A frame interval setting of less than one results in undefined
// behavior.
if (frameInterval >= 1)
{
_animationFrameInterval = frameInterval;
if (_animating)
{
[self stopAnimation];
[self startAnimation];
}
}
}
- (UIScreen *)targetScreen
{
return _targetScreen;
}
- (void)setTargetScreen:(UIScreen *)screen
{
if (_targetScreen != screen)
{
_targetScreen = screen;
if (_animating)
{
[self stopAnimation];
[self startAnimation];
}
}
}
- (void)startAnimation
{
if (!_animating)
{
if (self.targetScreen) {
// Create a CADisplayLink for the target display.
// This will result in the native fps for whatever display you create it from.
_displayLink = [self.targetScreen displayLinkWithTarget:self selector:@selector(drawView:)];
}
else {
// Fall back to use CADislayLink's class method.
// A CADisplayLink created using the class method is always bound to the internal display.
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(drawView:)];
}
[_displayLink setFrameInterval:self.animationFrameInterval];
[_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
// Start playing sound if not already
if (!self.playback.isPlaying)
[self.playback startSound];
// An external display might just have been connected/disconnected. We do not want to
// consider time spent in the connection/disconnection in the animation.
_zeroDeltaTime = TRUE;
_animating = TRUE;
}
}
- (void)stopAnimation
{
if (_animating)
{
[_displayLink invalidate];
_displayLink = nil;
// Stop playing sound if currently playing
if (self.playback.isPlaying)
[self.playback stopSound];
_animating = FALSE;
}
}
- (void)dealloc
{
// tear down OpenGL
if (_defaultFramebuffer)
{
glDeleteFramebuffers(1, &_defaultFramebuffer);
_defaultFramebuffer = 0;
}
if (_colorRenderbuffer)
{
glDeleteRenderbuffers(1, &_colorRenderbuffer);
_colorRenderbuffer = 0;
}
glDeleteBuffers(1, &_vertexBuffer);
glDeleteVertexArraysOES(1, &_vertexArray);
// tear down context
if ([EAGLContext currentContext] == _context)
[EAGLContext setCurrentContext:nil];
}
@end
5.GLAirPlay/CubePlayback.h
/*
Copyright (C) 2016 Apple Inc. All Rights Reserved.
See LICENSE.txt for this sample’s licensing information
Abstract:
An Obj-C class which wraps an OpenAL playback environment
*/
#import
#import
#import
@interface CubePlayback : NSObject
@property BOOL isPlaying; // Whether the sound is playing or stopped
@property BOOL wasInterrupted; // Whether playback was interrupted by the system
@property float* sourcePos; // The coordinates of the sound source
@property float* listenerPos; // The coordinates of the listener
@property float listenerRotation; // The rotation angle of the listener in radians
- (void)initOpenAL;
- (void)teardownOpenAL;
- (void)startSound;
- (void)stopSound;
@end
6.GLAirPlay/MainViewController.h
/*
Copyright (C) 2016 Apple Inc. All Rights Reserved.
See LICENSE.txt for this sample’s licensing information
Abstract:
The root view controller. Demonstrates detailed steps on how to show content on an external display.
*/
#import
@interface MainViewController : UIViewController
@property (nonatomic, strong) UIViewController *userInterfaceController;
@property (nonatomic, strong) UIViewController *glController;
@end
7.GLAirPlay/CubePlayback.m
/*
Copyright (C) 2016 Apple Inc. All Rights Reserved.
See LICENSE.txt for this sample’s licensing information
Abstract:
An Obj-C class which wraps an OpenAL playback environment
*/
#import "CubePlayback.h"
#import "MyOpenALSupport.h"
@interface CubePlayback ()
{
ALuint _source;
ALuint _buffer;
ALCcontext* context;
ALCdevice* device;
void *_data;
ALfloat _sourceVolume;
float _sourcePos[3];
float _listenerPos[3];
float _listenerRotation;
}
@end
@implementation CubePlayback
#pragma mark Object Init / Maintenance
void interruptionListener( void * inClientData,
UInt32 inInterruptionState)
{
CubePlayback *THIS = (__bridge CubePlayback*)inClientData;
if (inInterruptionState == kAudioSessionBeginInterruption)
{
// do nothing
[THIS teardownOpenAL];
if ([THIS isPlaying]) {
THIS->_wasInterrupted = YES;
THIS->_isPlaying = NO;
}
}
else if (inInterruptionState == kAudioSessionEndInterruption)
{
NSError *error;
BOOL success = [[AVAudioSession sharedInstance] setActive:true error:&error];
if (!success) {
NSLog(@"Error setting audio session active! %@", error.localizedDescription);
}
[THIS initOpenAL];
if (THIS->_wasInterrupted)
{
[THIS startSound];
THIS->_wasInterrupted = NO;
}
}
}
// handle[Notification Name]Notification: handleAVAudioSessionInterruptionNotification
#pragma mark AVAudioSession
- (void)handleAVAudioSessionInterruptionNotification:(NSNotification *)notification
{
NSUInteger theInterruptionType = [[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey] intValue];
NSLog(@"Session interrupted > --- %s ---\n", theInterruptionType == AVAudioSessionInterruptionTypeBegan ? "Begin Interruption" : "End Interruption");
if (theInterruptionType == AVAudioSessionInterruptionTypeBegan) {
alcMakeContextCurrent(NULL);
if (self.isPlaying) {
self.wasInterrupted = YES;
}
} else if (theInterruptionType == AVAudioSessionInterruptionTypeEnded) {
// make sure to activate the session
NSError *error;
bool success = [[AVAudioSession sharedInstance] setActive:YES error:&error];
if (!success) NSLog(@"Error setting session active! %@\n", [error localizedDescription]);
alcMakeContextCurrent(self.context);
if (self.wasInterrupted)
{
[self startSound];
self.wasInterrupted = NO;
}
}
}
- (void)initAVAudioSession
{
// Configure the audio session
AVAudioSession *sessionInstance = [AVAudioSession sharedInstance];
NSError *error;
// set the session category
bool success = [sessionInstance setCategory:AVAudioSessionCategoryAmbient error:&error];
if (!success) NSLog(@"Error setting AVAudioSession category! %@\n", [error localizedDescription]);
double hwSampleRate = 44100.0;
success = [sessionInstance setPreferredSampleRate:hwSampleRate error:&error];
if (!success) NSLog(@"Error setting preferred sample rate! %@\n", [error localizedDescription]);
// add interruption handler
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleAVAudioSessionInterruptionNotification:)
name:AVAudioSessionInterruptionNotification
object:sessionInstance];
// activate the audio session
success = [sessionInstance setActive:YES error:&error];
if (!success) NSLog(@"Error setting session active! %@\n", [error localizedDescription]);
}
- (id)init
{
if (self = [super init]) {
// initial position of the sound source and
// initial position and rotation of the listener
// will be set by the view
// Setup AVAudioSession
[self initAVAudioSession];
_wasInterrupted = NO;
// Initialize our OpenAL environment
[self initOpenAL];
}
return self;
}
- (void)dealloc
{
if (_data) free(_data);
[self teardownOpenAL];
}
#pragma mark OpenAL
- (void) initBuffer
{
ALenum error = AL_NO_ERROR;
ALenum format = 0;
ALsizei size = 0;
ALsizei freq = 0;
NSBundle* bundle = [NSBundle mainBundle];
// get some audio data from a wave file
CFURLRef fileURL = CFBridgingRetain([NSURL fileURLWithPath:[bundle pathForResource:@"sound" ofType:@"wav"]]);
if (fileURL)
{
_data = MyGetOpenALAudioData(fileURL, &size, &format, &freq);
CFRelease(fileURL);
if((error = alGetError()) != AL_NO_ERROR) {
printf("error loading sound: %x\n", error);
exit(1);
}
// use the static buffer data API
alBufferDataStaticProc(_buffer, format, _data, size, freq);
if((error = alGetError()) != AL_NO_ERROR) {
printf("error attaching audio to buffer: %x\n", error);
}
}
else
{
printf("Could not find file!\n");
_data = NULL;
}
}
- (void) initSource
{
ALenum error = AL_NO_ERROR;
alGetError(); // Clear the error
// Turn Looping ON
alSourcei(_source, AL_LOOPING, AL_TRUE);
// Set Source Position
alSourcefv(_source, AL_POSITION, _sourcePos);
// Set Source Reference Distance
alSourcef(_source, AL_REFERENCE_DISTANCE, 0.15f);
// attach OpenAL Buffer to OpenAL Source
alSourcei(_source, AL_BUFFER, _buffer);
if((error = alGetError()) != AL_NO_ERROR) {
printf("Error attaching buffer to source: %x\n", error);
exit(1);
}
}
- (void)initOpenAL
{
ALenum error = AL_NO_ERROR;
// Create a new OpenAL Device
// Pass NULL to specify the system’s default output device
device = alcOpenDevice(NULL);
if (device != NULL)
{
// Create a new OpenAL Context
// The new context will render to the OpenAL Device just created
context = alcCreateContext(device, 0);
if (context != NULL)
{
// Make the new context the Current OpenAL Context
alcMakeContextCurrent(context);
// Create some OpenAL Buffer Objects
alGenBuffers(1, &_buffer);
if((error = alGetError()) != AL_NO_ERROR) {
printf("Error Generating Buffers: %x", error);
exit(1);
}
// Create some OpenAL Source Objects
alGenSources(1, &_source);
if(alGetError() != AL_NO_ERROR)
{
printf("Error generating sources! %x\n", error);
exit(1);
}
}
}
// clear any errors
alGetError();
[self initBuffer];
[self initSource];
}
- (void)teardownOpenAL
{
// Delete the Sources
alDeleteSources(1, &_source);
// Delete the Buffers
alDeleteBuffers(1, &_buffer);
//Release context
alcDestroyContext(context);
//Close device
alcCloseDevice(device);
}
#pragma mark Play / Pause
- (void)startSound
{
ALenum error;
// Begin playing our source file
alSourcePlay(_source);
if((error = alGetError()) != AL_NO_ERROR) {
printf("error starting source: %x\n", error);
} else {
// Mark our state as playing
self.isPlaying = YES;
}
}
- (void)stopSound
{
ALenum error;
// Stop playing our source file
alSourceStop(_source);
if((error = alGetError()) != AL_NO_ERROR) {
printf("error stopping source: %x\n", error);
} else {
// Mark our state as not playing
self.isPlaying = NO;
}
}
#pragma mark Setters / Getters
- (ALCcontext *)context
{
return context;
}
- (float*)sourcePos
{
return _sourcePos;
}
- (void)setSourcePos:(float*)pos
{
int i;
for (i=0; i<3; i++)
_sourcePos[i] = pos[i];
// Move our audio source coordinates
alSourcefv(_source, AL_POSITION, _sourcePos);
}
- (float*)listenerPos
{
return _listenerPos;
}
- (void)setListenerPos:(float*)pos
{
int i;
for (i=0; i<3; i++)
_listenerPos[i] = pos[i];
// Move our listener coordinates
alListenerfv(AL_POSITION, _listenerPos);
}
- (float)listenerRotation
{
return _listenerRotation;
}
- (void)setListenerRotation:(float)radians
{
_listenerRotation = radians;
float ori[] = {cosf(radians), 0., sinf(radians), 0., 1., 0.}; //forward & up
// Set our listener orientation (rotation)
alListenerfv(AL_ORIENTATION, ori);
}
@end
8.GLAirPlay/MyOpenALSupport.h
/*
Copyright (C) 2016 Apple Inc. All Rights Reserved.
See LICENSE.txt for this sample’s licensing information
Abstract:
OpenAL-related support functions
*/
#import
#import
#import
#import
typedef ALvoid AL_APIENTRY (*alBufferDataStaticProcPtr) (const ALint bid, ALenum format, ALvoid* data, ALsizei size, ALsizei freq);
ALvoid alBufferDataStaticProc(const ALint bid, ALenum format, ALvoid* data, ALsizei size, ALsizei freq)
{
static alBufferDataStaticProcPtr proc = NULL;
if (proc == NULL) {
proc = (alBufferDataStaticProcPtr) alcGetProcAddress(NULL, (const ALCchar*) "alBufferDataStatic");
}
if (proc)
proc(bid, format, data, size, freq);
return;
}
void* MyGetOpenALAudioData(CFURLRef inFileURL, ALsizei *outDataSize, ALenum *outDataFormat, ALsizei* outSampleRate)
{
OSStatus err = noErr;
UInt64 fileDataSize = 0;
AudioStreamBasicDescription theFileFormat;
UInt32 thePropertySize = sizeof(theFileFormat);
AudioFileID afid = 0;
void* theData = NULL;
// Open a file with ExtAudioFileOpen()
err = AudioFileOpenURL(inFileURL, kAudioFileReadPermission, 0, &afid);
if(err) { printf("MyGetOpenALAudioData: AudioFileOpenURL FAILED, Error = %d\n", (int)err); goto Exit; }
// Get the audio data format
err = AudioFileGetProperty(afid, kAudioFilePropertyDataFormat, &thePropertySize, &theFileFormat);
if(err) { printf("MyGetOpenALAudioData: AudioFileGetProperty(kAudioFileProperty_DataFormat) FAILED, Error = %d\n", (int)err); goto Exit; }
if (theFileFormat.mChannelsPerFrame > 2) {
printf("MyGetOpenALAudioData - Unsupported Format, channel count is greater than stereo\n"); goto Exit;
}
if ((theFileFormat.mFormatID != kAudioFormatLinearPCM) || (!TestAudioFormatNativeEndian(theFileFormat))) {
printf("MyGetOpenALAudioData - Unsupported Format, must be little-endian PCM\n"); goto Exit;
}
if ((theFileFormat.mBitsPerChannel != 8) && (theFileFormat.mBitsPerChannel != 16)) {
printf("MyGetOpenALAudioData - Unsupported Format, must be 8 or 16 bit PCM\n"); goto Exit;
}
thePropertySize = sizeof(fileDataSize);
err = AudioFileGetProperty(afid, kAudioFilePropertyAudioDataByteCount, &thePropertySize, &fileDataSize);
if(err) { printf("MyGetOpenALAudioData: AudioFileGetProperty(kAudioFilePropertyAudioDataByteCount) FAILED, Error = %d\n", (int)err); goto Exit; }
// Read all the data into memory
UInt32 dataSize = (UInt32)fileDataSize;
theData = malloc(dataSize);
if (theData)
{
AudioFileReadBytes(afid, false, 0, &dataSize, theData);
if(err == noErr)
{
// success
*outDataSize = (ALsizei)dataSize;
*outDataFormat = (theFileFormat.mChannelsPerFrame > 1) ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16;
*outSampleRate = (ALsizei)theFileFormat.mSampleRate;
}
else
{
// failure
free (theData);
theData = NULL; // make sure to return NULL
printf("MyGetOpenALAudioData: ExtAudioFileRead FAILED, Error = %d\n", (int)err); goto Exit;
}
}
Exit:
// Dispose the ExtAudioFileRef, it is no longer needed
if (afid) AudioFileClose(afid);
return theData;
}
void TeardownOpenAL()
{
ALCcontext *context = NULL;
ALCdevice *device = NULL;
ALuint returnedName;
// Delete the Sources
alDeleteSources(1, &returnedName);
// Delete the Buffers
alDeleteBuffers(1, &returnedName);
//Get active context
context = alcGetCurrentContext();
//Get device for active context
device = alcGetContextsDevice(context);
//Release context
alcDestroyContext(context);
//Close device
alcCloseDevice(device);
}
9.GLAirPlay/UserInterfaceViewController.m
/*
Copyright (C) 2016 Apple Inc. All Rights Reserved.
See LICENSE.txt for this sample’s licensing information
Abstract:
This UIViewController configures the appearances of the UI when an external display is connected/disconnected.
*/
#import "UserInterfaceViewController.h"
@interface UserInterfaceViewController () {
CGRect _fsFrame;
}
@end
@implementation UserInterfaceViewController
- (float)rotatingRadius
{
return self.slider.value * 2.0f;
}
- (void)screenDidConnect
{
self.view.backgroundColor = [UIColor whiteColor];
self.view.frame = _fsFrame; //fullscreen
self.view.autoresizingMask =
UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin |
UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin |
UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
}
- (void)screenDidDisconnect
{
self.view.backgroundColor = [UIColor clearColor];
self.view.frame = CGRectMake(0, _fsFrame.size.height - 80, _fsFrame.size.width, 50);
self.view.autoresizingMask =
UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin |
UIViewAutoresizingFlexibleTopMargin | /*fixed bottom*/
UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
}
- (void)viewDidLoad
{
[super viewDidLoad];
_fsFrame = self.view.frame;
}
- (BOOL)shouldAutorotate {
return YES;
}
@end
10.GLAirPlay/GLView.h
/*
Copyright (C) 2016 Apple Inc. All Rights Reserved.
See LICENSE.txt for this sample’s licensing information
Abstract:
The OpenGL ES view which renders a rotating cube. Responsible for creating a CADisplayLink for the new target display when a connection/disconnection occurs.
*/
#import
#import
#import "UserControlDelegate.h"
@interface GLView : UIView
@property (readonly, nonatomic, getter=isAnimating) BOOL animating;
@property (nonatomic) NSInteger animationFrameInterval;
@property (nonatomic, strong) id userControlDelegate;
@property (nonatomic, weak) UIScreen *targetScreen;
- (void)startAnimation;
- (void)stopAnimation;
- (void)drawView:(id)sender;
@end
11.GLAirPlay/AppDelegate.m
/*
Copyright (C) 2016 Apple Inc. All Rights Reserved.
See LICENSE.txt for this sample’s licensing information
Abstract:
The application's delegate.
*/
#import "AppDelegate.h"
#import "MainViewController.h"
#import "GLViewController.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
MainViewController *mainController = (MainViewController *)(self.window.rootViewController);
[(GLViewController *)(mainController.glController) startAnimation];
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application
{
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
MainViewController *mainController = (MainViewController *)(self.window.rootViewController);
[(GLViewController *)(mainController.glController) stopAnimation];
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
MainViewController *mainController = (MainViewController *)(self.window.rootViewController);
[(GLViewController *)(mainController.glController) stopAnimation];
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
MainViewController *mainController = (MainViewController *)(self.window.rootViewController);
[(GLViewController *)(mainController.glController) startAnimation];
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
MainViewController *mainController = (MainViewController *)(self.window.rootViewController);
[(GLViewController *)(mainController.glController) startAnimation];
}
- (void)applicationWillTerminate:(UIApplication *)application
{
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
MainViewController *mainController = (MainViewController *)(self.window.rootViewController);
[(GLViewController *)(mainController.glController) stopAnimation];
}
@end
12.GLAirPlay/GLViewController.m
/*
Copyright (C) 2016 Apple Inc. All Rights Reserved.
See LICENSE.txt for this sample’s licensing information
Abstract:
This UIViewController configures the OpenGL ES view and its UI when an external display is connected/disconnected.
*/
#import "GLViewController.h"
#import "UserInterfaceViewController.h"
#import "GLView.h"
@implementation GLViewController
- (void)screenDidConnect:(UIViewController *)userInterface
{
// Remove UI previously added atop the GL view
for (UIView* v in [self.view subviews])
[v removeFromSuperview];
// One of these userInterface view controllers is visible at a time,
// so release the other one to minimize memory usage
self.userInterfaceOnTop = nil;
self.userInterfaceFullscreen = userInterface;
if (self.userInterfaceFullscreen)
{
// Setup UI (When an external display is connected, it will be added to mainViewController's view
// in MainViewController/-screenDidChange:)
[(UserInterfaceViewController *)self.userInterfaceFullscreen screenDidConnect];
// Set the userControlDelegte, which is responsible for setting the cube's rotating radius
[(GLView *)self.view setUserControlDelegate:(id)self.userInterfaceFullscreen];
}
}
- (void)screenDidDisconnect:(UIViewController *)userInterface
{
// One of these userInterface view controllers is visible at a time,
// so release the other one to minimize memory usage
self.userInterfaceFullscreen = nil;
self.userInterfaceOnTop = userInterface;
if (self.userInterfaceOnTop)
{
// Setup UI
[(UserInterfaceViewController *)self.userInterfaceOnTop screenDidDisconnect];
// Add UI on top
[(GLView *)self.view addSubview:self.userInterfaceOnTop.view];
// Set the userControlDelegte, which is responsible for setting the cube's rotating radius
[(GLView *)self.view setUserControlDelegate:(id)self.userInterfaceOnTop];
}
}
- (void)setTargetScreen:(UIScreen *)targetScreen
{
// Delegate to the GL view to create a CADisplayLink for the target display (UIScreen/-displayLinkWithTarget:selector:)
// This will result in the native fps for whatever display you create it from.
[(GLView *)self.view setTargetScreen:targetScreen];
}
- (void)startAnimation
{
[(GLView *)self.view startAnimation];
}
- (void)stopAnimation
{
[(GLView *)self.view stopAnimation];
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.view.autoresizingMask =
UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin |
UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin |
UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (BOOL)shouldAutorotate {
return YES;
}
@end
13.GLAirPlay/UserInterfaceViewController.h
/*
Copyright (C) 2016 Apple Inc. All Rights Reserved.
See LICENSE.txt for this sample’s licensing information
Abstract:
This UIViewController configures the appearances of the UI when an external display is connected/disconnected.
*/
#import
#import
#import "UserControlDelegate.h"
@interface UserInterfaceViewController : UIViewController
@property (nonatomic, weak) IBOutlet UISlider *slider;
- (void)screenDidConnect;
- (void)screenDidDisconnect;
@end
14.GLAirPlay/AppDelegate.h
/*
Copyright (C) 2016 Apple Inc. All Rights Reserved.
See LICENSE.txt for this sample’s licensing information
Abstract:
The application's delegate.
*/
#import
@interface AppDelegate : UIResponder
@property (nonatomic, strong) UIWindow *window;
@end
15.GLAirPlay/MainViewController.m
/*
Copyright (C) 2016 Apple Inc. All Rights Reserved.
See LICENSE.txt for this sample’s licensing information
Abstract:
The root view controller. Demonstrates detailed steps on how to show content on an external display.
*/
#import "MainViewController.h"
#import "GLViewController.h"
@interface MainViewController ()
@property (nonatomic, strong) UIWindow *extWindow;
@property (nonatomic, strong) UIScreen *extScreen;
@end
@implementation MainViewController
- (void)screenDidChange:(NSNotification *)notification
{
// To display content on an external display, do the following:
// 1. Use the screens class method of the UIScreen class to determine if an external display is available.
NSArray *screens = [UIScreen screens];
NSUInteger screenCount = [screens count];
if (screenCount > 1)
{
// 2. If an external screen is available, get the screen object and look at the values in its availableModes
// property. This property contains the configurations supported by the screen.
// Select first external screen
self.extScreen = [screens objectAtIndex:1]; //index 0 is your iPhone/iPad
NSArray *availableModes = [self.extScreen availableModes];
// 3. Select the UIScreenMode object corresponding to the desired resolution and assign it to the currentMode
// property of the screen object.
// Select the highest resolution in this sample
NSInteger selectedRow = [availableModes count] - 1;
self.extScreen.currentMode = [availableModes objectAtIndex:selectedRow];
// Set a proper overscanCompensation mode
self.extScreen.overscanCompensation = UIScreenOverscanCompensationInsetApplicationFrame;
if (self.extWindow == nil) {
// 4. Create a new window object (UIWindow) to display your content.
UIWindow *extWindow = [[UIWindow alloc] initWithFrame:[self.extScreen bounds]];
self.extWindow = extWindow;
self.extWindow.rootViewController = self.glController;
}
// 5. Assign the screen object to the screen property of your new window.
self.extWindow.screen = self.extScreen;
// 6. Configure the window (by adding views or setting up your OpenGL ES rendering context).
// Resize the GL view to fit the external screen
self.glController.view.frame = self.extWindow.frame;
// Set the target screen to the external screen
// This will let the GL view create a CADisplayLink that fires at the native fps of the target display.
[(GLViewController *)self.glController setTargetScreen:self.extScreen];
// Configure user interface
// In this sample, we use the same UI layout when an external display is connected or not.
// In your real application, you probably want to provide distinct UI layouts for best user experience.
[(GLViewController *)self.glController screenDidConnect:self.userInterfaceController];
// Add the GL view
[self.extWindow addSubview:self.glController.view];
// 7. Show the window.
[self.extWindow makeKeyAndVisible];
// On the iPhone/iPad screen
// Remove the GL view (it is displayed on the external screen)
for (UIView* v in [self.view subviews])
[v removeFromSuperview];
// Display the fullscreen UI on the iPhone/iPad screen
[self.view addSubview:self.userInterfaceController.view];
}
else //handles disconnection of the external display
{
// Release external screen and window
self.extScreen = nil;
self.extWindow = nil;
// On the iPhone/iPad screen
// Remove the fullscreen UI (a window version will be displayed atop the GL view)
for (UIView* v in [self.view subviews])
[v removeFromSuperview];
// Resize the GL view to fit the iPhone/iPad screen
self.glController.view.frame = self.view.frame;
// Set the target screen to the main screen
// This will let the GL view create a CADisplayLink that fires at the native fps of the target display.
[(GLViewController *)self.glController setTargetScreen:[UIScreen mainScreen]];
// Configure user interface
// In this sample, we use the same UI layout when an external display is connected or not.
// In your real application, you probably want to provide distinct UI layouts for best user experience.
[(GLViewController *)self.glController screenDidDisconnect:self.userInterfaceController];
// Display the GL view on the iPhone/iPad screen
[self.view addSubview:self.glController.view];
}
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIScreenDidConnectNotification
object:nil];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:UIScreenDidDisconnectNotification
object:nil];
}
- (void)viewDidLoad
{
[super viewDidLoad];
UIViewController *userInterfaceController = [[UIStoryboard storyboardWithName:@"MainStoryboard" bundle:NULL] instantiateViewControllerWithIdentifier:@"UserInterfaceViewController"];
self.userInterfaceController = userInterfaceController;
UIViewController *glController = [[UIStoryboard storyboardWithName:@"MainStoryboard" bundle:NULL] instantiateViewControllerWithIdentifier:@"GLViewController"];
self.glController = glController;
// No notifications are sent for screens that are present when the app is launched.
[self screenDidChange:nil];
// Register for screen connect and disconnect notifications.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(screenDidChange:)
name:UIScreenDidConnectNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(screenDidChange:)
name:UIScreenDidDisconnectNotification
object:nil];
}
- (BOOL)shouldAutorotate {
return YES;
}
@end
后记
未完,待续~~~