Browse Source

Adds keys and minute-ticks. Adds fade. Starting to get useful

master
Joshua Moerman 10 years ago
parent
commit
276e840682
  1. 12
      SpectogramPrototype.xcodeproj/project.pbxproj
  2. 6
      SpectogramPrototype/RuledScrollView.h
  3. 56
      SpectogramPrototype/RuledScrollView.m
  4. 24
      SpectogramPrototype/Ruler.h
  5. 114
      SpectogramPrototype/Ruler.m
  6. 15
      SpectogramPrototype/UIScreen+Util.h
  7. 27
      SpectogramPrototype/UIScreen+Util.m
  8. 16
      SpectogramPrototype/ViewController.m

12
SpectogramPrototype.xcodeproj/project.pbxproj

@ -7,6 +7,8 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
424374B818BCD3BC00021EC2 /* Ruler.m in Sources */ = {isa = PBXBuildFile; fileRef = 424374B718BCD3BC00021EC2 /* Ruler.m */; };
424374BB18BD2DD800021EC2 /* UIScreen+Util.m in Sources */ = {isa = PBXBuildFile; fileRef = 424374BA18BD2DD800021EC2 /* UIScreen+Util.m */; };
424F84A918661F8000687D3B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 424F84A818661F8000687D3B /* Foundation.framework */; }; 424F84A918661F8000687D3B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 424F84A818661F8000687D3B /* Foundation.framework */; };
424F84AB18661F8000687D3B /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 424F84AA18661F8000687D3B /* CoreGraphics.framework */; }; 424F84AB18661F8000687D3B /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 424F84AA18661F8000687D3B /* CoreGraphics.framework */; };
424F84AD18661F8000687D3B /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 424F84AC18661F8000687D3B /* UIKit.framework */; }; 424F84AD18661F8000687D3B /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 424F84AC18661F8000687D3B /* UIKit.framework */; };
@ -41,6 +43,10 @@
/* End PBXContainerItemProxy section */ /* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
424374B618BCD3BC00021EC2 /* Ruler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Ruler.h; sourceTree = "<group>"; };
424374B718BCD3BC00021EC2 /* Ruler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Ruler.m; sourceTree = "<group>"; };
424374B918BD2DD800021EC2 /* UIScreen+Util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIScreen+Util.h"; sourceTree = "<group>"; };
424374BA18BD2DD800021EC2 /* UIScreen+Util.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIScreen+Util.m"; sourceTree = "<group>"; };
424F84A518661F8000687D3B /* SpectogramPrototype.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SpectogramPrototype.app; sourceTree = BUILT_PRODUCTS_DIR; }; 424F84A518661F8000687D3B /* SpectogramPrototype.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SpectogramPrototype.app; sourceTree = BUILT_PRODUCTS_DIR; };
424F84A818661F8000687D3B /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 424F84A818661F8000687D3B /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
424F84AA18661F8000687D3B /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 424F84AA18661F8000687D3B /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
@ -143,6 +149,10 @@
42E12A93186F1F4C00866CB3 /* AudioFile.m */, 42E12A93186F1F4C00866CB3 /* AudioFile.m */,
426665B31869A7CC005D62AC /* RuledScrollView.h */, 426665B31869A7CC005D62AC /* RuledScrollView.h */,
426665B41869A7CC005D62AC /* RuledScrollView.m */, 426665B41869A7CC005D62AC /* RuledScrollView.m */,
424374B618BCD3BC00021EC2 /* Ruler.h */,
424374B718BCD3BC00021EC2 /* Ruler.m */,
424374B918BD2DD800021EC2 /* UIScreen+Util.h */,
424374BA18BD2DD800021EC2 /* UIScreen+Util.m */,
426665B61869CEFE005D62AC /* FFTTest.h */, 426665B61869CEFE005D62AC /* FFTTest.h */,
426665B71869CEFE005D62AC /* FFTTest.m */, 426665B71869CEFE005D62AC /* FFTTest.m */,
4261F08718671ECD00AA0EF9 /* ContentView.xib */, 4261F08718671ECD00AA0EF9 /* ContentView.xib */,
@ -282,9 +292,11 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
42E12A94186F1F4C00866CB3 /* AudioFile.m in Sources */, 42E12A94186F1F4C00866CB3 /* AudioFile.m in Sources */,
424374BB18BD2DD800021EC2 /* UIScreen+Util.m in Sources */,
42ABF54B187035F900B20D82 /* Spectographer.m in Sources */, 42ABF54B187035F900B20D82 /* Spectographer.m in Sources */,
426665B81869CEFE005D62AC /* FFTTest.m in Sources */, 426665B81869CEFE005D62AC /* FFTTest.m in Sources */,
424F84C218661F8000687D3B /* ViewController.m in Sources */, 424F84C218661F8000687D3B /* ViewController.m in Sources */,
424374B818BCD3BC00021EC2 /* Ruler.m in Sources */,
424F84B918661F8000687D3B /* AppDelegate.m in Sources */, 424F84B918661F8000687D3B /* AppDelegate.m in Sources */,
426665B51869A7CC005D62AC /* RuledScrollView.m in Sources */, 426665B51869A7CC005D62AC /* RuledScrollView.m in Sources */,
424F84B518661F8000687D3B /* main.m in Sources */, 424F84B518661F8000687D3B /* main.m in Sources */,

6
SpectogramPrototype/RuledScrollView.h

@ -8,8 +8,10 @@
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
@class AbstractRuler;
@interface RuledScrollView : UIScrollView @interface RuledScrollView : UIScrollView
@property (nonatomic, readonly) UIView * content; @property (nonatomic, readonly) UIView * content;
@property (nonatomic, readonly) UIView * topRuler; @property (nonatomic, readonly) AbstractRuler * topRuler;
@property (nonatomic, readonly) UIView * leftRuler; @property (nonatomic, readonly) AbstractRuler * leftRuler;
@end @end

56
SpectogramPrototype/RuledScrollView.m

@ -7,13 +7,13 @@
// //
#import "RuledScrollView.h" #import "RuledScrollView.h"
#import "Ruler.h"
@interface RuledScrollView () <UIScrollViewDelegate> { @interface RuledScrollView () <UIScrollViewDelegate> {
CGFloat rulerHeight; CGFloat rulerHeight;
CGFloat rulerWidth; CGFloat rulerWidth;
} }
- (void) initView; - (void) initView;
- (void) placeTicks;
@end @end
@implementation RuledScrollView @implementation RuledScrollView
@ -46,62 +46,44 @@
CGRect frame2 = self.frame; CGRect frame2 = self.frame;
frame2.size.height = rulerHeight; frame2.size.height = rulerHeight;
topRuler = [[UIView alloc] initWithFrame:frame2]; topRuler = [[HorizontalRuler alloc] initWithFrame:frame2];
topRuler.layer.opacity = 0.5; topRuler.layer.opacity = 0.5;
topRuler.backgroundColor = self.backgroundColor;
[self addSubview:topRuler]; [self addSubview:topRuler];
CGRect frame3 = self.frame; CGRect frame3 = self.frame;
frame3.size.width = rulerWidth; frame3.size.width = rulerWidth;
leftRuler = [[UIView alloc] initWithFrame:frame3]; leftRuler = [[VerticalRuler alloc] initWithFrame:frame3];
leftRuler.layer.opacity = 0.5; leftRuler.layer.opacity = 0.5;
leftRuler.backgroundColor = self.backgroundColor;
[self addSubview:leftRuler]; [self addSubview:leftRuler];
} }
/* we should draw the ticks in drawRect:
[[UIColor blackColor] setFill];
UIRectFill((CGRect){0,200,rect.size.width,1});
and do tiling for the text
*/
- (void)placeTicks {
[topRuler.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
CGFloat spacing = 600.0f;
for (CGFloat x = rulerWidth + 0.5f*spacing; x < topRuler.frame.size.width; x += spacing){
UIView * line = [[UIView alloc] initWithFrame:CGRectMake(x, 0, 1, rulerHeight)];
line.backgroundColor = [UIColor blackColor];
[topRuler addSubview:line];
CGFloat width = 100;
UILabel * text = [[UILabel alloc] initWithFrame:CGRectMake(x - width/2, 0, width, rulerHeight)];
text.text = [NSString stringWithFormat:@"%.0f", 0.1 * x];
text.textAlignment = NSTextAlignmentCenter;
[topRuler addSubview:text];
}
}
- (void)layoutSubviews { - (void)layoutSubviews {
[super layoutSubviews]; [super layoutSubviews];
const CGRect frame = {.origin = self.contentOffset, .size = self.bounds.size};
const CGRect vis_frame = [self convertRect:self.bounds toView:content];
{ {
CGRect frame = self.topRuler.frame; CGRect frame2 = frame;
frame.origin.y = self.contentOffset.y; frame2.size.height = rulerHeight;
self.topRuler.frame = frame; self.topRuler.frame = frame2;
[self placeTicks];
CGFloat begin = vis_frame.origin.x;
CGFloat end = vis_frame.origin.x + vis_frame.size.width;
topRuler.visibleInterval = (Interval){.begin = begin, .end = end};
} }
{ {
CGRect frame = self.leftRuler.frame; CGRect frame2 = frame;
frame.origin.x = self.contentOffset.x; frame2.size.width = rulerWidth;
self.leftRuler.frame = frame; self.leftRuler.frame = frame2;
CGFloat begin = vis_frame.origin.y;
CGFloat end = vis_frame.origin.y + vis_frame.size.height;
leftRuler.visibleInterval = (Interval){.begin = begin, .end = end};
} }
} }
- (void)setContentSize:(CGSize)contentSize{ - (void)setContentSize:(CGSize)contentSize{
[super setContentSize:CGSizeMake(contentSize.width + rulerWidth, contentSize.height + rulerHeight)]; [super setContentSize:CGSizeMake(contentSize.width + rulerWidth, contentSize.height + rulerHeight)];
content.frame = CGRectMake(rulerWidth, rulerHeight, contentSize.width, contentSize.height); content.frame = CGRectMake(rulerWidth, rulerHeight, contentSize.width, contentSize.height);
topRuler.frame = CGRectMake(0, 0, contentSize.width + rulerWidth, rulerHeight);
leftRuler.frame = CGRectMake(0, 0, rulerWidth, contentSize.height + rulerHeight);
} }
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)__unused scrollView{ - (UIView *)viewForZoomingInScrollView:(UIScrollView *)__unused scrollView{

24
SpectogramPrototype/Ruler.h

@ -0,0 +1,24 @@
//
// Ruler.h
// SpectogramPrototype
//
// Created by Joshua Moerman on 25/02/14.
// Copyright (c) 2014 Joshua Moerman. All rights reserved.
//
#import <UIKit/UIKit.h>
typedef struct Interval {
CGFloat begin;
CGFloat end;
} Interval;
@interface AbstractRuler : UIView
@property (nonatomic) Interval visibleInterval;
@end
@interface HorizontalRuler : AbstractRuler
@end
@interface VerticalRuler : AbstractRuler
@end

114
SpectogramPrototype/Ruler.m

@ -0,0 +1,114 @@
//
// Ruler.m
// SpectogramPrototype
//
// Created by Joshua Moerman on 25/02/14.
// Copyright (c) 2014 Joshua Moerman. All rights reserved.
//
#import "Ruler.h"
#import "UIScreen+Util.h"
@implementation AbstractRuler
@synthesize visibleInterval;
- (void)setVisibleInterval:(Interval)visible_interval_{
if (visibleInterval.begin != visible_interval_.begin || visibleInterval.end != visible_interval_.end) {
[self setNeedsDisplay];
visibleInterval = visible_interval_;
}
}
@end
@implementation HorizontalRuler
- (void)drawRect:(CGRect) __unused rect{
[[UIColor grayColor] setFill];
UIRectFill(self.bounds);
const Interval visibleInterval = self.visibleInterval;
if(visibleInterval.end <= visibleInterval.begin) return;
const CGFloat f = 60 * 44100.0 / 4096.0;
const CGFloat start = f * ceil(visibleInterval.begin / f);
const CGFloat d = visibleInterval.end - visibleInterval.begin;
const int n = (int)floor(d / f) + 1;
for(int i = 0; i < n; ++i){
const CGFloat x = start + i * f;
const CGFloat r = (x - visibleInterval.begin) / d;
const CGFloat gx = r * self.bounds.size.width;
[[UIColor blackColor] setFill];
if([UIScreen isRetinaDisplay]){
UIRectFill(CGRectMake(gx, 0, 0.5, self.bounds.size.height));
} else {
UIRectFill(CGRectMake(gx, 0, 1.0, self.bounds.size.height));
}
NSString* string = [NSString stringWithFormat:@"%.0f:00", x/f];
NSMutableParagraphStyle* style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
style.alignment = NSTextAlignmentCenter;
UIFont* font = [UIFont boldSystemFontOfSize:14];
[string drawInRect:CGRectMake(gx-50, 20, 100, self.bounds.size.height-20)
withAttributes:@{NSParagraphStyleAttributeName: style,
NSFontAttributeName: font}];
}
}
@end
@implementation VerticalRuler
- (UIColor*)colorForKey:(int)j{
switch (j) {
case 0:
return [UIColor redColor];
case 2:
case 3:
case 5:
case 7:
case 8:
case 10:
return [UIColor whiteColor];
default:
return [UIColor blackColor];
}
}
- (void)drawRect:(CGRect) __unused rect{
[[UIColor grayColor] setFill];
UIRectFill(self.bounds);
const Interval visibleInterval = self.visibleInterval;
if(visibleInterval.end <= visibleInterval.begin) return;
const CGFloat d = visibleInterval.end - visibleInterval.begin;
const CGFloat f = 44100;
const CGFloat N = 1 << 12;
const CGFloat height = (1 << 12) >> 3;
for(int i = 220; i < 44100/8; i *= 2){
for(int j = 0; j < 12; ++j){
const CGFloat k = i * pow(2, j/12.0);
const CGFloat y = N*k/f;
const CGFloat y2 = (height - y);
const CGFloat gy = 1024 * (y2 - visibleInterval.begin) / d;
[[self colorForKey:j] setFill];
if([UIScreen isRetinaDisplay]){
UIRectFill(CGRectMake(0, gy-0.5, self.bounds.size.width, 1.0));
} else {
UIRectFill(CGRectMake(0, gy-1, self.bounds.size.width, 2.0));
}
}
}
}
@end

15
SpectogramPrototype/UIScreen+Util.h

@ -0,0 +1,15 @@
//
// UIScreen+Util.h
// SpectogramPrototype
//
// Created by Joshua Moerman on 25/02/14.
// Copyright (c) 2014 Joshua Moerman. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface UIScreen (Util)
+ (BOOL) isRetinaDisplay;
@end

27
SpectogramPrototype/UIScreen+Util.m

@ -0,0 +1,27 @@
//
// UIScreen+Util.m
// SpectogramPrototype
//
// Created by Joshua Moerman on 25/02/14.
// Copyright (c) 2014 Joshua Moerman. All rights reserved.
//
#import "UIScreen+Util.h"
@implementation UIScreen (Util)
// from http://stackoverflow.com/questions/3504173/detect-retina-display
+ (BOOL) isRetinaDisplay {
static BOOL retina = NO;
static BOOL alreadyChecked = NO;
if (!alreadyChecked) {
UIScreen *mainScreen = self.mainScreen;
if (mainScreen) {
retina = mainScreen.scale > 1.0;
alreadyChecked = YES;
}
}
return retina;
}
@end

16
SpectogramPrototype/ViewController.m

@ -72,6 +72,7 @@ device get_device(){
- (void)openAudioFile:(NSURL *)filePath{ - (void)openAudioFile:(NSURL *)filePath{
[self removeAllSlices]; [self removeAllSlices];
[self resetView:nil];
if(!spectographer) if(!spectographer)
spectographer = [[Spectographer alloc] init]; spectographer = [[Spectographer alloc] init];
@ -87,25 +88,22 @@ device get_device(){
[self addSlice:view]; [self addSlice:view];
}); });
}]; }];
// [contentView removeFromSuperview];
// contentView = view;
// scrollView.zoomScale = 1.0;
//
// [scrollView.content addSubview:contentView];
// contentView.autoresizingMask = UIViewAutoresizingNone;
// scrollView.contentSize = contentView.bounds.size;
} }
- (void)addSlice:(UIImageView *)view{ - (void)addSlice:(UIImageView *)view{
if(slices.count){ if(slices.count){
UIImageView * lastSlice = slices.lastObject; UIImageView * lastSlice = slices.lastObject;
float x = CGRectGetMaxX(lastSlice.frame); CGFloat x = CGRectGetMaxX(lastSlice.frame);
view.frame = CGRectOffset(view.frame, x, 0); view.frame = CGRectOffset(view.frame, x, 0);
} // else leave the origin at 0 } // else leave the origin at 0
[contentView addSubview:view]; [contentView addSubview:view];
[slices addObject:view]; [slices addObject:view];
view.layer.opacity = 0;
[UIView animateWithDuration:0.5 animations:^{
view.layer.opacity = 1;
}];
UIImageView * lastSlice = slices.lastObject; UIImageView * lastSlice = slices.lastObject;
scrollView.contentSize = CGSizeMake(CGRectGetMaxX(lastSlice.frame), CGRectGetMaxY(lastSlice.frame)); scrollView.contentSize = CGSizeMake(CGRectGetMaxX(lastSlice.frame), CGRectGetMaxY(lastSlice.frame));