Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
zobkiw committed Mar 11, 2014
1 parent f6324db commit e927286
Show file tree
Hide file tree
Showing 7 changed files with 387 additions and 5 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "TheAmazingAudioEngine"]
path = TheAmazingAudioEngine
url = git@github.com:TheAmazingAudioEngine/TheAmazingAudioEngine.git
170 changes: 170 additions & 0 deletions ABetterMetronome.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<Bucket
type = "1"
version = "2.0">
<Breakpoints>
<BreakpointProxy
BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
<BreakpointContent
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "ABetterMetronome/ZBCDViewController.m"
timestampString = "416262078.642398"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "109"
endingLineNumber = "109"
landmarkName = "-sliderValueChanged:"
landmarkType = "5">
</BreakpointContent>
</BreakpointProxy>
</Breakpoints>
</Bucket>
4 changes: 2 additions & 2 deletions ABetterMetronome/ABetterMetronome-Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@
<array>
<string>armv7</string>
</array>
<key>UIStatusBarStyle</key>
<string>UIStatusBarStyleDefault</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>
95 changes: 93 additions & 2 deletions ABetterMetronome/Base.lproj/Main.storyboard
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="5023" systemVersion="13A603" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" initialViewController="vXZ-lx-hvc">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="5053" systemVersion="13C64" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" initialViewController="vXZ-lx-hvc">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="3733"/>
</dependencies>
<scenes>
<!--class Prefix:identifier View Controller-->
<!--View Controller-->
<scene sceneID="ufC-wZ-h7g">
<objects>
<viewController id="vXZ-lx-hvc" customClass="ZBCDViewController" sceneMemberID="viewController">
Expand All @@ -15,8 +15,99 @@
<view key="view" contentMode="scaleToFill" id="kh9-bI-dsS">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="BcL-Bg-L6A">
<rect key="frame" x="0.0" y="0.0" width="320" height="96"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" usesAttributedText="YES" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Xfb-RD-rdh">
<rect key="frame" x="20" y="28" width="280" height="48"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<attributedString key="attributedText">
<fragment content="A">
<attributes>
<color key="NSColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<font key="NSFont" size="28" name="Avenir-Roman"/>
<paragraphStyle key="NSParagraphStyle" alignment="center" lineBreakMode="wordWrapping" baseWritingDirection="natural"/>
</attributes>
</fragment>
<fragment content="Better">
<attributes>
<color key="NSColor" red="0.0" green="0.50196081399917603" blue="1" alpha="1" colorSpace="calibratedRGB"/>
<font key="NSFont" size="28" name="Avenir-Roman"/>
<paragraphStyle key="NSParagraphStyle" alignment="center" lineBreakMode="wordWrapping" baseWritingDirection="natural"/>
</attributes>
</fragment>
<fragment content="Metronome">
<attributes>
<color key="NSColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<font key="NSFont" size="28" name="Avenir-Roman"/>
<paragraphStyle key="NSParagraphStyle" alignment="center" lineBreakMode="wordWrapping" baseWritingDirection="natural"/>
</attributes>
</fragment>
</attributedString>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
</view>
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="1uk-w2-Pd7">
<rect key="frame" x="0.0" y="94" width="320" height="207"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="42" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="voo-uw-P7k">
<rect key="frame" x="20" y="166" width="280" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" name="Avenir-Roman" family="Avenir" pointSize="14"/>
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="100" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Fsc-oc-Qpn">
<rect key="frame" x="20" y="20" width="280" height="100"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" name="Avenir-Heavy" family="Avenir" pointSize="72"/>
<color key="textColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
<slider opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="0.5" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="2ww-BO-tM6">
<rect key="frame" x="18" y="128" width="284" height="31"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<connections>
<action selector="sliderValueChanged:" destination="vXZ-lx-hvc" eventType="valueChanged" id="Xdc-EM-aYz"/>
</connections>
</slider>
</subviews>
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
</view>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="http://zobkiw.com @zobskewed" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="z9y-Aw-MkM">
<rect key="frame" x="14" y="514" width="280" height="40"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" name="Avenir-Roman" family="Avenir" pointSize="12"/>
<color key="textColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="YUv-9y-Nf5">
<rect key="frame" x="0.0" y="301" width="320" height="200"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" userInteractionEnabled="NO" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="vts-pT-UQ8">
<rect key="frame" x="20" y="10" width="280" height="246"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<string key="text">This is an example of a better metronome. Alot of people use NSTimer or some other mechanism that is simply not as accurate as is needed. This example counts frames to determine when the next beat should play and injects it immediately into the audio buffer. It is therefore accurate to the frame - you can't get much better than that. Use this however you please with credit as appropriate. This example makes use of TheAmazingAudioEngine by A Tasty Pixel.</string>
<fontDescription key="fontDescription" name="Avenir-Roman" family="Avenir" pointSize="12"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
</textView>
</subviews>
<color key="backgroundColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
</view>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
<connections>
<outlet property="bpmLabel" destination="Fsc-oc-Qpn" id="YDS-XJ-abF"/>
<outlet property="bpmSlider" destination="2ww-BO-tM6" id="3TP-jz-abP"/>
<outlet property="statusLabel" destination="voo-uw-P7k" id="egW-c2-5da"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="x5A-6p-PRh" sceneMemberID="firstResponder"/>
</objects>
Expand Down
96 changes: 95 additions & 1 deletion ABetterMetronome/ZBCDViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,111 @@
//

#import "ZBCDViewController.h"
#import "TheAmazingAudioEngine.h"
#include <mach/mach_time.h>

@interface ZBCDViewController ()

@property (nonatomic, strong) AEAudioController *audioController;
@property (nonatomic, strong) AEBlockChannel *blockChannel;
@property (nonatomic, assign) float bpm;
@property (nonatomic, strong) IBOutlet UISlider *bpmSlider;
@property (nonatomic, strong) IBOutlet UILabel *bpmLabel;
@property (nonatomic, strong) IBOutlet UILabel *statusLabel;
@end

#define kMinTempo 30
#define kMaxTempo 240 // try setting this to 100000, watch your ears!

@implementation ZBCDViewController

- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.

// Create the audio controller
self.audioController = [[AEAudioController alloc]
initWithAudioDescription:[AEAudioController nonInterleaved16BitStereoAudioDescription]
inputEnabled:NO]; // don't forget to autorelease if you don't use ARC!

// Start the audio controller
NSError *error = NULL;
BOOL result = [_audioController start:&error];
if (!result) {
NSLog(@"_audioController start error: %@", [error localizedDescription]);
return;
}

// Set the metronome (via the slider) to a default value half way between our min and max
[self.bpmSlider setValue:.5];
[self sliderValueChanged:_bpmSlider]; // This will initialize self.bpm

// The total frames that have passed
static UInt64 total_frames = 0;

// The next frame that the beat will play on
static UInt64 next_beat_frame = 0;

// YES if we are currently sounding a beat
static BOOL making_beat = NO;

// Oscillator specifics - instead you can easily load the samples from cowbell.aif or somesuch
float oscillatorRate = 440./44100.0;
__block float oscillatorPosition = 0; // this is outside the block since beats can span calls to the block

// The block that is our metronome
self.blockChannel = [AEBlockChannel channelWithBlock:^(const AudioTimeStamp *time, UInt32 frames, AudioBufferList *audio) {

// How many frames pass between the start of each beat
UInt64 frames_between_beats = 44100/(_bpm/60.);

// For each frame, count and if we reach the frame that should start a beat, start the beat
for (int i=0; i<frames; i++) { // frame...by frame...

// Set a flag that triggers code below to start a beat
if (next_beat_frame == total_frames) {
//NSLog(@"THUD %llu %llu %llu", next_beat_frame, total_frames, frames_between_beats);
making_beat = YES;
oscillatorPosition = 0; // reset the osc position to make them all sound the same
next_beat_frame += frames_between_beats;
}

// We are making the beat, play a sine-like click (from TheAmazingAudioEngine sample project)
if (making_beat) {
float x = oscillatorPosition;
x *= x; x -= 1.0; x *= x; // x now in the range 0...1
x *= INT16_MAX;
x -= INT16_MAX / 2;
oscillatorPosition += oscillatorRate;
if (oscillatorPosition > 1.0) { /* oscillatorPosition -= 2.0; */ making_beat = NO; } // turn off the beat, just a quick tick!
((SInt16*)audio->mBuffers[0].mData)[i] = x;
((SInt16*)audio->mBuffers[1].mData)[i] = x;

// NOTE: We should always make sure we play a minimal number of frames here that prevent overlap
// If we the next_beat_frame was only 100 away and we played 200 frames of the metronome sound,
// the metronome would never stop and likely create some interesting artifacts. Try setting the
// the kMaxTempo to 100000.
}

// Increment the count
total_frames++;
}
}];

// Add the block channel to the audio controller
[_audioController addChannels:[NSArray arrayWithObject:_blockChannel]];
}

- (IBAction)sliderValueChanged:(UISlider *)sender {

// Calculate and display the new bpm
float new_bpm = ((kMaxTempo-kMinTempo) * sender.value) + kMinTempo;
_bpm = roundf(new_bpm); // you don't have to round
[self.bpmLabel setText:[NSString stringWithFormat:@"%.2f", _bpm]];

// Calculate and display the number of frames between beats
UInt64 frames_between_beats = 44100/(_bpm/60.);
[self.statusLabel setText:[NSString stringWithFormat:@"%llu frames between beats", frames_between_beats]];
}

- (void)didReceiveMemoryWarning
Expand Down
1 change: 1 addition & 0 deletions TheAmazingAudioEngine
Submodule TheAmazingAudioEngine added at 311620

0 comments on commit e927286

Please sign in to comment.