Created
January 30, 2016 02:26
-
-
Save AlexanderBollbach/d37e164fb829ea2489e4 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// AppDelegate.m | |
// OpenALCocoa | |
// | |
// Created by alexanderbollbach on 1/28/16. | |
// Copyright © 2016 alexanderbollbach. All rights reserved. | |
// | |
#import <AudioToolbox/AudioToolbox.h> | |
#import <OpenAL/al.h> | |
#import <OpenAL/alc.h> | |
#import "AppDelegate.h" | |
#define STREAM_PATH CFSTR ("/Users/alexanderbollbach/Desktop/711.wav") | |
#define ORBIT_SPEED 1 | |
#define BUFFER_DURATION_SECONDS 2 | |
#define BUFFER_COUNT 3 | |
#define RUN_TIME 200.0 | |
typedef struct MyStreamPlayer { | |
AudioStreamBasicDescription dataFormat; | |
UInt32 bufferSizeBytes; | |
SInt64 fileLengthFrames; | |
SInt64 totalFramesRead; | |
ALuint sources[1]; | |
// ALuint buffers[BUFFER_COUNT]; // might need this back for static buffers in ch12/13? | |
ExtAudioFileRef extAudioFile; | |
} MyStreamPlayer; | |
static void CheckError(OSStatus error, const char *operation) | |
{ | |
if (error == noErr) return; | |
char str[20]; | |
// see if it appears to be a 4-char-code | |
*(UInt32 *)(str + 1) = CFSwapInt32HostToBig(error); | |
if (isprint(str[1]) && isprint(str[2]) && isprint(str[3]) && isprint(str[4])) { | |
str[0] = str[5] = '\''; | |
str[6] = '\0'; | |
} else | |
// no, format it as an integer | |
sprintf(str, "%d", (int)error); | |
fprintf(stderr, "Error: %s (%s)\n", operation, str); | |
exit(1); | |
} | |
static void CheckALError (const char *operation) { | |
ALenum alErr = alGetError(); | |
if (alErr == AL_NO_ERROR) return; | |
char *errFormat = NULL; | |
switch (alErr) { | |
case AL_INVALID_NAME: errFormat = "OpenAL Error: %s (AL_INVALID_NAME)"; break; | |
case AL_INVALID_VALUE: errFormat = "OpenAL Error: %s (AL_INVALID_VALUE)"; break; | |
case AL_INVALID_ENUM: errFormat = "OpenAL Error: %s (AL_INVALID_ENUM)"; break; | |
case AL_INVALID_OPERATION: errFormat = "OpenAL Error: %s (AL_INVALID_OPERATION)"; break; | |
case AL_OUT_OF_MEMORY: errFormat = "OpenAL Error: %s (AL_OUT_OF_MEMORY)"; break; | |
default: errFormat = "OpenAL Error: %s (unknown error code)"; break; | |
} | |
fprintf (stderr, errFormat, operation); | |
exit(1); | |
} | |
void updateSourceLocation (MyStreamPlayer player); | |
OSStatus setUpExtAudioFile (MyStreamPlayer* player); | |
void refillALBuffers (MyStreamPlayer* player); | |
void fillALBuffer (MyStreamPlayer* player, ALuint alBuffer); | |
@interface AppDelegate () | |
@end | |
@implementation AppDelegate | |
void updateSourceLocation (MyStreamPlayer player) { | |
// double theta = fmod (CFAbsoluteTimeGetCurrent() * ORBIT_SPEED, M_PI * 2); | |
// printf ("%f\n", theta); | |
ALfloat x = 1; | |
ALfloat y = 1; | |
ALfloat z = 1; | |
printf ("x=%f, y=%f, z=%f\n", x, y, z); | |
alSource3f(player.sources[0], AL_POSITION, x, y, z); | |
} | |
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { | |
MyStreamPlayer __block player; | |
player.totalFramesRead = 0; | |
CheckError(setUpExtAudioFile(&player), "Couldn't open ExtAudioFile") ; | |
ALCdevice *alDevice = alcOpenDevice(NULL); | |
CheckALError ("Couldn't open AL device"); // default device | |
ALCcontext* alContext = alcCreateContext(alDevice, 0); | |
CheckALError ("Couldn't open AL context"); | |
alcMakeContextCurrent(alContext); | |
CheckALError ("Couldn't make AL context current"); | |
ALuint buffers[BUFFER_COUNT]; | |
alGenBuffers(BUFFER_COUNT, buffers); | |
CheckALError ("Couldn't generate buffers"); | |
for (int i=0; i<BUFFER_COUNT; i++) { | |
fillALBuffer(&player, buffers[i]); | |
} | |
// set up streaming source | |
alGenSources(1, player.sources); | |
CheckALError ("Couldn't generate sources"); | |
alSourcef(player.sources[0], AL_GAIN, AL_MAX_GAIN); | |
CheckALError("Couldn't set source gain"); | |
updateSourceLocation(player); | |
CheckALError ("Couldn't set initial source position"); | |
// queue up the buffers on the source | |
alSourceQueueBuffers(player.sources[0], | |
BUFFER_COUNT, | |
buffers); | |
CheckALError("Couldn't queue buffers on source"); | |
// set up listener | |
alListener3f (AL_POSITION, 0.0, 0.0, 0.0); | |
CheckALError("Couldn't set listner position"); | |
// start playing | |
alSourcePlayv (1, player.sources); | |
CheckALError ("Couldn't play"); | |
// and wait | |
printf("Playing...\n"); | |
time_t startTime = time(NULL); | |
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ | |
do { | |
// get next theta | |
updateSourceLocation(player); | |
CheckALError ("Couldn't set source position"); | |
// refill buffers if needed | |
refillALBuffers (&player); | |
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, false); | |
} while (difftime(time(NULL), startTime) < RUN_TIME); | |
}); | |
// cleanup: | |
alSourceStop(player.sources[0]); | |
alDeleteSources(1, player.sources); | |
alDeleteBuffers(BUFFER_COUNT, buffers); | |
alcDestroyContext(alContext); | |
alcCloseDevice(alDevice); | |
printf ("Bottom of main\n"); | |
} | |
void refillALBuffers (MyStreamPlayer* player) { | |
ALint processed; | |
alGetSourcei (player->sources[0], AL_BUFFERS_PROCESSED, &processed); | |
CheckALError ("couldn't get al_buffers_processed"); | |
while (processed > 0) { | |
ALuint freeBuffer; | |
alSourceUnqueueBuffers(player->sources[0], 1, &freeBuffer); | |
CheckALError("couldn't unqueue buffer"); | |
printf ("refilling buffer %d\n", freeBuffer); | |
fillALBuffer(player, freeBuffer); | |
alSourceQueueBuffers(player->sources[0], 1, &freeBuffer); | |
CheckALError ("couldn't queue refilled buffer"); | |
printf ("re-queued buffer %d\n", freeBuffer); | |
processed--; | |
} | |
} | |
void fillALBuffer (MyStreamPlayer* player, ALuint alBuffer) { | |
// abl | |
AudioBufferList *bufferList; | |
UInt32 ablSize = offsetof(AudioBufferList, mBuffers[0]) + (sizeof(AudioBuffer) * 1); | |
bufferList = malloc(ablSize); | |
bufferList->mNumberBuffers = 1; | |
bufferList->mBuffers[0].mNumberChannels = 1; | |
bufferList->mBuffers[0].mDataByteSize = player->bufferSizeBytes; | |
UInt16 *sampleBuffer = malloc(sizeof(UInt16) * player->bufferSizeBytes); | |
bufferList->mBuffers[0].mData = sampleBuffer; | |
// int totalFramesToReadIntoBuffer = 0; | |
UInt32 framesReadIntoBuffer = 0; | |
// do { | |
UInt32 framesRead = (UInt32)player->fileLengthFrames - framesReadIntoBuffer; | |
// int numOfBytesToMoveUp = framesReadIntoBuffer * (sizeof(UInt16)); | |
// bufferList->mBuffers[0].mData = sampleBuffer + numOfBytesToMoveUp; | |
printf("%i \n", *(sampleBuffer + 100000)); | |
CheckError(ExtAudioFileRead(player->extAudioFile, &framesRead, bufferList),"ExtAudioFileRead failed"); | |
printf("%i \n", *(sampleBuffer + 100000)); | |
// framesReadIntoBuffer += framesRead; | |
// player->totalFramesRead += framesRead; | |
// int sizeOfMonoFrame = sizeof(UInt16); | |
// totalFramesToReadIntoBuffer = player->bufferSizeBytes / sizeOfMonoFrame; | |
// } | |
//while (framesReadIntoBuffer < totalFramesToReadIntoBuffer); | |
alBufferData(alBuffer, | |
AL_FORMAT_MONO16, | |
sampleBuffer, | |
player->bufferSizeBytes, | |
player->dataFormat.mSampleRate); | |
free (bufferList); | |
free (sampleBuffer); | |
} | |
OSStatus setUpExtAudioFile (MyStreamPlayer* player) { | |
CFURLRef streamFileURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, | |
STREAM_PATH, | |
kCFURLPOSIXPathStyle, | |
false); | |
// describe the client format - AL needs mono | |
memset(&player->dataFormat, 0, sizeof(player->dataFormat)); | |
player->dataFormat.mFormatID = kAudioFormatLinearPCM; | |
player->dataFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; | |
player->dataFormat.mSampleRate = 44100.0; | |
player->dataFormat.mChannelsPerFrame = 1; | |
player->dataFormat.mFramesPerPacket = 1; | |
player->dataFormat.mBitsPerChannel = 16; | |
player->dataFormat.mBytesPerFrame = 2; | |
player->dataFormat.mBytesPerPacket = 2; | |
CheckError(ExtAudioFileOpenURL(streamFileURL, &player->extAudioFile), "Couldn't open ExtAudioFile for reading"); | |
// tell extAudioFile about our format | |
CheckError(ExtAudioFileSetProperty(player->extAudioFile, | |
kExtAudioFileProperty_ClientDataFormat, | |
sizeof (AudioStreamBasicDescription), | |
&player->dataFormat), "Couldn't set client format on ExtAudioFile"); | |
// figure out how big file is | |
UInt32 propSize = sizeof (player->fileLengthFrames); | |
ExtAudioFileGetProperty(player->extAudioFile, | |
kExtAudioFileProperty_FileLengthFrames, | |
&propSize, | |
&player->fileLengthFrames); | |
//printf ("fileLengthFrames = %lld frames\n", player->fileLengthFrames); | |
player->bufferSizeBytes = BUFFER_DURATION_SECONDS * player->dataFormat.mSampleRate * player->dataFormat.mBytesPerFrame; | |
// printf ("bufferSizeBytes = %d\n", player->bufferSizeBytes); | |
return noErr; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment