//
// AudioStreamer.h
// StreamingAudioPlayer
//
// Created by Matt Gallagher on 27/09/08.
// Copyright 2008 Matt Gallagher. All rights reserved.
//
// Permission is given to use this source code file, free of charge, in any
// project, commercial or otherwise, entirely at your risk, with the condition
// that any redistribution (in part or whole) of source code must retain
// this copyright and permission notice. Attribution in compiled projects is
// appreciated but not required.
//
#ifdef TARGET_OS_IPHONE
#import <UIKit/UIKit.h>
#else
#import <Cocoa/Cocoa.h>
#endif TARGET_OS_IPHONE
#include <pthread.h>
#include <AudioToolbox/AudioToolbox.h>
#define LOG_QUEUED_BUFFERS 0
#define kNumAQBufs 16 // Number of audio queue buffers we allocate.
// Needs to be big enough to keep audio pipeline
// busy (non-zero number of queued buffers) but
// not so big that audio takes too long to begin
// (kNumAQBufs * kAQBufSize of data must be
// loaded before playback will start).
//
// Set LOG_QUEUED_BUFFERS to 1 to log how many
// buffers are queued at any time -- if it drops
// to zero too often, this value may need to
// increase. Min 3, typical 8-24.
#define kAQDefaultBufSize 2048 // Number of bytes in each audio queue buffer
// Needs to be big enough to hold a packet of
// audio from the audio file. If number is too
// large, queuing of audio before playback starts
// will take too long.
// Highly compressed files can use smaller
// numbers (512 or less). 2048 should hold all
// but the largest packets. A buffer size error
// will occur if this number is too small.
#define kAQMaxPacketDescs 512 // Number of packet descriptions in our array
typedef enum
{
AS_INITIALIZED = 0,
AS_STARTING_FILE_THREAD,
AS_WAITING_FOR_DATA,
AS_FLUSHING_EOF,
AS_WAITING_FOR_QUEUE_TO_START,
AS_PLAYING,
AS_BUFFERING,
AS_STOPPING,
AS_STOPPED,
AS_PAUSED
} AudioStreamerState;
typedef enum
{
AS_NO_STOP = 0,
AS_STOPPING_EOF,
AS_STOPPING_USER_ACTION,
AS_STOPPING_ERROR,
AS_STOPPING_TEMPORARILY
} AudioStreamerStopReason;
typedef enum
{
AS_NO_ERROR = 0,
AS_NETWORK_CONNECTION_FAILED,
AS_FILE_STREAM_GET_PROPERTY_FAILED,
AS_FILE_STREAM_SEEK_FAILED,
AS_FILE_STREAM_PARSE_BYTES_FAILED,
AS_FILE_STREAM_OPEN_FAILED,
AS_FILE_STREAM_CLOSE_FAILED,
AS_AUDIO_DATA_NOT_FOUND,
AS_AUDIO_QUEUE_CREATION_FAILED,
AS_AUDIO_QUEUE_BUFFER_ALLOCATION_FAILED,
AS_AUDIO_QUEUE_ENQUEUE_FAILED,
AS_AUDIO_QUEUE_ADD_LISTENER_FAILED,
AS_AUDIO_QUEUE_REMOVE_LISTENER_FAILED,
AS_AUDIO_QUEUE_START_FAILED,
AS_AUDIO_QUEUE_PAUSE_FAILED,
AS_AUDIO_QUEUE_BUFFER_MISMATCH,
AS_AUDIO_QUEUE_DISPOSE_FAILED,
AS_AUDIO_QUEUE_STOP_FAILED,
AS_AUDIO_QUEUE_FLUSH_FAILED,
AS_AUDIO_STREAMER_FAILED,
AS_GET_AUDIO_TIME_FAILED,
AS_AUDIO_BUFFER_TOO_SMALL
} AudioStreamerErrorCode;
extern NSString * const ASStatusChangedNotification;
@interface AudioStreamer : NSObject
{
NSURL *url;
//
// Special threading consideration:
// The audioQueue property should only ever be accessed inside a
// synchronized(self) block and only *after* checking that ![self isFinishing]
//
AudioQueueRef audioQueue;
AudioFileStreamID audioFileStream; // the audio file stream parser
AudioStreamBasicDescription asbd; // description of the audio
NSThread *internalThread; // the thread where the download and
// audio file stream parsing occurs
AudioQueueBufferRef audioQueueBuffer[kNumAQBufs]; // audio queue buffers
AudioStreamPacketDescription packetDescs[kAQMaxPacketDescs]; // packet descriptions for enqueuing audio
unsigned int fillBufferIndex; // the index of the audioQueueBuffer that is being filled
UInt32 packetBufferSize;
size_t bytesFilled; // how many bytes have been filled
size_t packetsFilled; // how many packets have been filled
bool inuse[kNumAQBufs]; // flags to indicate that a buffer is still in use
NSInteger buffersUsed;
NSDictionary *httpHeaders;
AudioStreamerState state;
AudioStreamerStopReason stopReason;
AudioStreamerErrorCode errorCode;
OSStatus err;
bool discontinuous; // flag to indicate middle of the stream
pthread_mutex_t queueBuffersMutex; // a mutex to protect the inuse flags
pthread_cond_t queueBufferReadyCondition; // a condition varable for handling the inuse flags
CFReadStreamRef stream;
NSNotificationCenter *notificationCenter;
UInt32 bitRate; // Bits per second in the file
NSInteger dataOffset; // Offset of the first audio packet in the stream
NSInteger fileLength; // Length of the file in bytes
NSInteger seekByteOffset; // Seek offset within the file in bytes
UInt64 audioDataByteCount; // Used when the actual number of audio bytes in
// the file is known (more accurate than assuming
// the whole file is audio)
UInt64 processedPacketsCount; // number of packets accumulated for bitrate estimation
UInt64 processedPacketsSizeTotal; // byte size of accumulated estimation packets
double seekTime;
BOOL seekWasRequested;
double requestedSeekTime;
double sampleRate; // Sample rate of the file (used to compare with
// samples played by the queue for current playback
// time)
double packetDuration; // sample rate times frames per packet
double lastProgress; // last calculated progress point
}
@property AudioStreamerErrorCode errorCode;
@property (readonly) AudioStreamerState state;
@property (readonly) double progress;
@property (readonly) double duration;
@property (readwrite) UInt32 bitRate;
@property (readonly) NSDictionary *httpHeaders;
- (id)initWithURL:(NSURL *)aURL;
- (void)start;
- (void)stop;
- (void)pause;
- (BOOL)isPlaying;
- (BOOL)isPaused;
- (BOOL)isWaiting;
- (BOOL)isIdle;
- (void)seekToTime:(double)newSeekTime;
- (double)calculatedBitRate;
@end