Permalink
Browse files

First version.

  • Loading branch information...
stephencelis committed Mar 2, 2009
0 parents commit 2e3a7b9d56ff892270596b2b8261614915e57b04
Showing with 296 additions and 0 deletions.
  1. +6 −0 CHANGELOG.markdown
  2. +67 −0 README.markdown
  3. +32 −0 SCListener.h
  4. +191 −0 SCListener.m
@@ -0,0 +1,6 @@
+Version 1.0
+===========
+
+2 Mar 2009
+
+* Birthday!
@@ -0,0 +1,67 @@
+SCListener 1.0
+==============
+
+A simple class for listening to microphone levels, suitable for the iPhone.
+
+* [Intro](http://stephencelis.com/2009/03/02/now-i-just-need-an-audience.html)
+* [GitHub](http://github.com/stephencelis/sc_listener)
+
+
+Usage
+-----
+
+ #import "SCListener.h" // Remember to link to AudioToolbox.framework.
+
+ // Start listening.
+ [[SCListener sharedListener] listen];
+
+ // Retrieve the average power.
+ [[SCListener sharedListener] averagePower];
+
+ // Retrieve the peak power.
+ [[SCListener sharedListener] peakPower];
+
+ // Hmm...we're using this guy a lot...
+ SCListener *listener = [SCListener sharedListener];
+
+ // We can temporarily stop returning levels
+ [listener pause];
+ [listener listen]; // Quick.
+
+ // Or free up resources when we're not listening for awhile.
+ [listener stop];
+ [listener listen]; // Slower.
+
+ // Advanced!:
+ //
+ // If you're using the average and the peak, fetch both at once.
+ if (![listener isListening]) // If listener has paused or stopped...
+ return; // ...bail.
+
+ AudioQueueLevelMeterState *levels = [listener levels];
+ Float32 peak = levels[0].mPeakPower;
+ Float32 average = levels[0].mAveragePower;
+
+
+License
+-------
+
+(c) 2009-* Stephen Celis, <stephen@stephencelis.com>.
+
+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.
@@ -0,0 +1,32 @@
+//
+// SCListener 1.0
+// http://github.com/stephencelis/sc_listener
+//
+// (c) 2009-* Stephen Celis, <stephen@stephencelis.com>.
+// Released under the MIT License.
+//
+
+#import <Foundation/Foundation.h>
+#import <AudioToolbox/AudioQueue.h>
+#import <AudioToolbox/AudioServices.h>
+
+@interface SCListener : NSObject {
+ AudioQueueLevelMeterState *levels;
+
+ AudioQueueRef queue;
+ AudioStreamBasicDescription format;
+ Float64 sampleRate;
+}
+
++ (SCListener *)sharedListener;
+
+- (void)listen;
+- (BOOL)isListening;
+- (void)pause;
+- (void)stop;
+
+- (Float32)averagePower;
+- (Float32)peakPower;
+- (AudioQueueLevelMeterState *)levels;
+
+@end
@@ -0,0 +1,191 @@
+//
+// SCListener 1.0
+// http://github.com/stephencelis/sc_listener
+//
+// (c) 2009-* Stephen Celis, <stephen@stephencelis.com>.
+// Released under the MIT License.
+//
+
+#import "SCListener.h"
+
+@interface SCListener (Private)
+
+- (void)updateLevels;
+- (void)setupQueue;
+- (void)setupFormat;
+- (void)setupBuffers;
+- (void)setupMetering;
+
+@end
+
+static SCListener *sharedListener = nil;
+
+static void listeningCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer, const AudioTimeStamp *inStartTime, UInt32 inNumberPacketsDescriptions, const AudioStreamPacketDescription *inPacketDescs) {
+ SCListener *listener = (SCListener *)inUserData;
+ if ([listener isListening])
+ AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, NULL);
+}
+
+@implementation SCListener
+
++ (SCListener *)sharedListener {
+ @synchronized(self) {
+ if (sharedListener == nil)
+ [[self alloc] init];
+ }
+
+ return sharedListener;
+}
+
+- (void)dealloc {
+ [sharedListener stop];
+ [super dealloc];
+}
+
+#pragma mark -
+#pragma mark Listening
+
+- (void)listen {
+ if (queue == nil)
+ [self setupQueue];
+
+ AudioQueueStart(queue, NULL);
+}
+
+- (void)pause {
+ if (![self isListening])
+ return;
+
+ AudioQueueStop(queue, true);
+}
+
+- (void)stop {
+ if (queue == nil)
+ return;
+
+ AudioQueueDispose(queue, true);
+}
+
+- (BOOL)isListening {
+ if (queue == nil)
+ return NO;
+
+ UInt32 isListening, ioDataSize = sizeof(UInt32);
+ OSStatus result = AudioQueueGetProperty(queue, kAudioQueueProperty_IsRunning, &isListening, &ioDataSize);
+ return (result != noErr) ? NO : isListening;
+}
+
+#pragma mark -
+#pragma mark Levels getters
+
+- (Float32)averagePower {
+ if (![self isListening])
+ return 0.0;
+
+ return [self levels][0].mAveragePower;
+}
+
+- (Float32)peakPower {
+ if (![self isListening])
+ return 0.0;
+
+ return [self levels][0].mPeakPower;
+}
+
+- (AudioQueueLevelMeterState *)levels {
+ if (![self isListening])
+ return nil;
+
+ [self updateLevels];
+ return levels;
+}
+
+- (void)updateLevels {
+ UInt32 ioDataSize = format.mChannelsPerFrame * sizeof(AudioQueueLevelMeterState);
+ AudioQueueGetProperty(queue, (AudioQueuePropertyID)kAudioQueueProperty_CurrentLevelMeter, levels, &ioDataSize);
+}
+
+#pragma mark -
+#pragma mark Setup
+
+- (void)setupQueue {
+ if (queue)
+ return;
+
+ [self setupFormat];
+ [self setupBuffers];
+ AudioQueueNewInput(&format, listeningCallback, self, NULL, NULL, 0, &queue);
+ [self setupMetering];
+}
+
+- (void)setupFormat {
+#if TARGET_IPHONE_SIMULATOR
+ format.mSampleRate = 44100.0;
+#else
+ UInt32 ioDataSize = sizeof(sampleRate);
+ AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareSampleRate, &ioDataSize, &sampleRate);
+ format.mSampleRate = sampleRate;
+#endif
+ format.mFormatID = kAudioFormatLinearPCM;
+ format.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
+ format.mFramesPerPacket = format.mChannelsPerFrame = 1;
+ format.mBitsPerChannel = 16;
+ format.mBytesPerPacket = format.mBytesPerFrame = 2;
+}
+
+- (void)setupBuffers {
+ AudioQueueBufferRef buffers[3];
+ for (NSInteger i = 0; i < 3; ++i) {
+ AudioQueueAllocateBuffer(queue, 735, &buffers[i]);
+ AudioQueueEnqueueBuffer(queue, buffers[i], 0, NULL);
+ }
+}
+
+- (void)setupMetering {
+ levels = (AudioQueueLevelMeterState *)calloc(sizeof(AudioQueueLevelMeterState), format.mChannelsPerFrame);
+ UInt32 trueValue = true;
+ AudioQueueSetProperty(queue, kAudioQueueProperty_EnableLevelMetering, &trueValue, sizeof(UInt32));
+}
+
+#pragma mark -
+#pragma mark Singleton Pattern
+
++ (id)allocWithZone:(NSZone *)zone {
+ @synchronized(self) {
+ if (sharedListener == nil) {
+ sharedListener = [super allocWithZone:zone];
+ return sharedListener;
+ }
+ }
+
+ return nil;
+}
+
+- (id)copyWithZone:(NSZone *)zone {
+ return self;
+}
+
+- (id)init {
+ if ([super init] == nil)
+ return nil;
+
+ return self;
+}
+
+- (id)retain {
+ return self;
+}
+
+- (unsigned)retainCount {
+ return UINT_MAX;
+}
+
+- (void)release {
+ // Do nothing.
+}
+
+- (id)autorelease {
+ return self;
+}
+
+@end

0 comments on commit 2e3a7b9

Please sign in to comment.