Adds keys and minute-ticks. Adds fade. Starting to get useful
This commit is contained in:
parent
4b80ab096e
commit
276e840682
8 changed files with 222 additions and 48 deletions
|
@ -7,6 +7,8 @@
|
|||
objects = {
|
||||
|
||||
/* 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 */; };
|
||||
424F84AB18661F8000687D3B /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 424F84AA18661F8000687D3B /* CoreGraphics.framework */; };
|
||||
424F84AD18661F8000687D3B /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 424F84AC18661F8000687D3B /* UIKit.framework */; };
|
||||
|
@ -41,6 +43,10 @@
|
|||
/* End PBXContainerItemProxy 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; };
|
||||
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; };
|
||||
|
@ -143,6 +149,10 @@
|
|||
42E12A93186F1F4C00866CB3 /* AudioFile.m */,
|
||||
426665B31869A7CC005D62AC /* RuledScrollView.h */,
|
||||
426665B41869A7CC005D62AC /* RuledScrollView.m */,
|
||||
424374B618BCD3BC00021EC2 /* Ruler.h */,
|
||||
424374B718BCD3BC00021EC2 /* Ruler.m */,
|
||||
424374B918BD2DD800021EC2 /* UIScreen+Util.h */,
|
||||
424374BA18BD2DD800021EC2 /* UIScreen+Util.m */,
|
||||
426665B61869CEFE005D62AC /* FFTTest.h */,
|
||||
426665B71869CEFE005D62AC /* FFTTest.m */,
|
||||
4261F08718671ECD00AA0EF9 /* ContentView.xib */,
|
||||
|
@ -282,9 +292,11 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
42E12A94186F1F4C00866CB3 /* AudioFile.m in Sources */,
|
||||
424374BB18BD2DD800021EC2 /* UIScreen+Util.m in Sources */,
|
||||
42ABF54B187035F900B20D82 /* Spectographer.m in Sources */,
|
||||
426665B81869CEFE005D62AC /* FFTTest.m in Sources */,
|
||||
424F84C218661F8000687D3B /* ViewController.m in Sources */,
|
||||
424374B818BCD3BC00021EC2 /* Ruler.m in Sources */,
|
||||
424F84B918661F8000687D3B /* AppDelegate.m in Sources */,
|
||||
426665B51869A7CC005D62AC /* RuledScrollView.m in Sources */,
|
||||
424F84B518661F8000687D3B /* main.m in Sources */,
|
||||
|
|
|
@ -8,8 +8,10 @@
|
|||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@class AbstractRuler;
|
||||
|
||||
@interface RuledScrollView : UIScrollView
|
||||
@property (nonatomic, readonly) UIView * content;
|
||||
@property (nonatomic, readonly) UIView * topRuler;
|
||||
@property (nonatomic, readonly) UIView * leftRuler;
|
||||
@property (nonatomic, readonly) AbstractRuler * topRuler;
|
||||
@property (nonatomic, readonly) AbstractRuler * leftRuler;
|
||||
@end
|
||||
|
|
|
@ -7,13 +7,13 @@
|
|||
//
|
||||
|
||||
#import "RuledScrollView.h"
|
||||
#import "Ruler.h"
|
||||
|
||||
@interface RuledScrollView () <UIScrollViewDelegate> {
|
||||
CGFloat rulerHeight;
|
||||
CGFloat rulerWidth;
|
||||
}
|
||||
- (void) initView;
|
||||
- (void) placeTicks;
|
||||
@end
|
||||
|
||||
@implementation RuledScrollView
|
||||
|
@ -46,62 +46,44 @@
|
|||
|
||||
CGRect frame2 = self.frame;
|
||||
frame2.size.height = rulerHeight;
|
||||
topRuler = [[UIView alloc] initWithFrame:frame2];
|
||||
topRuler = [[HorizontalRuler alloc] initWithFrame:frame2];
|
||||
topRuler.layer.opacity = 0.5;
|
||||
topRuler.backgroundColor = self.backgroundColor;
|
||||
[self addSubview:topRuler];
|
||||
|
||||
CGRect frame3 = self.frame;
|
||||
frame3.size.width = rulerWidth;
|
||||
leftRuler = [[UIView alloc] initWithFrame:frame3];
|
||||
leftRuler = [[VerticalRuler alloc] initWithFrame:frame3];
|
||||
leftRuler.layer.opacity = 0.5;
|
||||
leftRuler.backgroundColor = self.backgroundColor;
|
||||
[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 {
|
||||
[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;
|
||||
frame.origin.y = self.contentOffset.y;
|
||||
self.topRuler.frame = frame;
|
||||
[self placeTicks];
|
||||
CGRect frame2 = frame;
|
||||
frame2.size.height = rulerHeight;
|
||||
self.topRuler.frame = frame2;
|
||||
|
||||
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;
|
||||
frame.origin.x = self.contentOffset.x;
|
||||
self.leftRuler.frame = frame;
|
||||
CGRect frame2 = frame;
|
||||
frame2.size.width = rulerWidth;
|
||||
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{
|
||||
[super setContentSize:CGSizeMake(contentSize.width + rulerWidth, contentSize.height + rulerHeight)];
|
||||
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{
|
||||
|
|
24
SpectogramPrototype/Ruler.h
Normal file
24
SpectogramPrototype/Ruler.h
Normal file
|
@ -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
Normal file
114
SpectogramPrototype/Ruler.m
Normal file
|
@ -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
Normal file
15
SpectogramPrototype/UIScreen+Util.h
Normal file
|
@ -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
Normal file
27
SpectogramPrototype/UIScreen+Util.m
Normal file
|
@ -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
|
|
@ -72,6 +72,7 @@ device get_device(){
|
|||
|
||||
- (void)openAudioFile:(NSURL *)filePath{
|
||||
[self removeAllSlices];
|
||||
[self resetView:nil];
|
||||
if(!spectographer)
|
||||
spectographer = [[Spectographer alloc] init];
|
||||
|
||||
|
@ -87,25 +88,22 @@ device get_device(){
|
|||
[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{
|
||||
if(slices.count){
|
||||
UIImageView * lastSlice = slices.lastObject;
|
||||
float x = CGRectGetMaxX(lastSlice.frame);
|
||||
CGFloat x = CGRectGetMaxX(lastSlice.frame);
|
||||
view.frame = CGRectOffset(view.frame, x, 0);
|
||||
} // else leave the origin at 0
|
||||
|
||||
[contentView addSubview:view];
|
||||
[slices addObject:view];
|
||||
|
||||
view.layer.opacity = 0;
|
||||
[UIView animateWithDuration:0.5 animations:^{
|
||||
view.layer.opacity = 1;
|
||||
}];
|
||||
|
||||
UIImageView * lastSlice = slices.lastObject;
|
||||
scrollView.contentSize = CGSizeMake(CGRectGetMaxX(lastSlice.frame), CGRectGetMaxY(lastSlice.frame));
|
||||
|
|
Reference in a new issue