Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions PlayEm.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
BB8A100728FF49E400FC6013 /* Scroller.m in Sources */ = {isa = PBXBuildFile; fileRef = BB8A100628FF49E400FC6013 /* Scroller.m */; };
BB8A100C290586E500FC6013 /* CAShapeLayer+Path.m in Sources */ = {isa = PBXBuildFile; fileRef = BB8A100B290586E500FC6013 /* CAShapeLayer+Path.m */; };
BB9401882AD0974900B7B150 /* AcceleratedBiquadFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = BB9401872AD0974900B7B150 /* AcceleratedBiquadFilter.m */; };
BB94D1C92CA970D1001D10D0 /* ReducedVisualSample.m in Sources */ = {isa = PBXBuildFile; fileRef = BB94D1C82CA970D1001D10D0 /* ReducedVisualSample.m */; };
BB9C77E52A8D5F7C00A138BE /* WaveView.m in Sources */ = {isa = PBXBuildFile; fileRef = BB9C77E42A8D5F7C00A138BE /* WaveView.m */; };
BB9C77E72A8D618700A138BE /* CoreImage.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BB9C77E62A8D618700A138BE /* CoreImage.framework */; };
BB9CE9B128D388F3008DFF91 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = BB9CE9B028D388F3008DFF91 /* MainMenu.xib */; };
Expand Down Expand Up @@ -160,6 +161,8 @@
BB8A100B290586E500FC6013 /* CAShapeLayer+Path.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "CAShapeLayer+Path.m"; sourceTree = "<group>"; };
BB9401862AD0974900B7B150 /* AcceleratedBiquadFilter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AcceleratedBiquadFilter.h; sourceTree = "<group>"; };
BB9401872AD0974900B7B150 /* AcceleratedBiquadFilter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AcceleratedBiquadFilter.m; sourceTree = "<group>"; };
BB94D1C72CA970D1001D10D0 /* ReducedVisualSample.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ReducedVisualSample.h; sourceTree = "<group>"; };
BB94D1C82CA970D1001D10D0 /* ReducedVisualSample.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ReducedVisualSample.m; sourceTree = "<group>"; };
BB9C77E32A8D5F3400A138BE /* WaveView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WaveView.h; sourceTree = "<group>"; };
BB9C77E42A8D5F7C00A138BE /* WaveView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WaveView.m; sourceTree = "<group>"; };
BB9C77E62A8D618700A138BE /* CoreImage.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreImage.framework; path = System/Library/Frameworks/CoreImage.framework; sourceTree = SDKROOT; };
Expand Down Expand Up @@ -304,6 +307,8 @@
BBDB1B422B65CC0500A953F7 /* VisualPair.h */,
BB0D527F244CEE22007C909F /* VisualSample.h */,
BB0D5280244CEE22007C909F /* VisualSample.m */,
BB94D1C72CA970D1001D10D0 /* ReducedVisualSample.h */,
BB94D1C82CA970D1001D10D0 /* ReducedVisualSample.m */,
BBDA2FFC2A7FCCB70084E2B6 /* BeatTrackedSample.h */,
BBDA2FFD2A7FCEEC0084E2B6 /* BeatTrackedSample.m */,
BB9401862AD0974900B7B150 /* AcceleratedBiquadFilter.h */,
Expand Down Expand Up @@ -639,6 +644,7 @@
BB2CF8AD259EB66900D6BA03 /* LazySample.m in Sources */,
BB3927022C14C606009C7B32 /* TableCellView.m in Sources */,
BB0C5D9327768D2400856978 /* MatrixUtilities.m in Sources */,
BB94D1C92CA970D1001D10D0 /* ReducedVisualSample.m in Sources */,
BB39270C2C1CD1E3009C7B32 /* MediaMetaData+StateAdditions.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
2 changes: 1 addition & 1 deletion PlayEm/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
<key>CFBundleShortVersionString</key>
<string>1.6</string>
<key>CFBundleVersion</key>
<string>7906</string>
<string>7980</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.music</string>
<key>LSMinimumSystemVersion</key>
Expand Down
19 changes: 19 additions & 0 deletions PlayEm/Sample/ReducedVisualSample.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// ReducedVisualSample.h
// PlayEm
//
// Created by Till Toenshoff on 29.09.24.
// Copyright © 2024 Till Toenshoff. All rights reserved.
//

#import "VisualSample.h"

NS_ASSUME_NONNULL_BEGIN

@interface ReducedVisualSample : VisualSample

- (void)prepareWithCallback:(nonnull void (^)(void))callback;

@end

NS_ASSUME_NONNULL_END
271 changes: 271 additions & 0 deletions PlayEm/Sample/ReducedVisualSample.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,271 @@
//
// ReducedVisualSample.m
// PlayEm
//
// Created by Till Toenshoff on 29.09.24.
// Copyright © 2024 Till Toenshoff. All rights reserved.
//

#import "ReducedVisualSample.h"
#import "LazySample.h"
#import "IndexedBlockOperation.h"
#import "VisualPair.h"
#import "ConcurrentAccessDictionary.h"
#import "ProfilingPointsOfInterest.h"

@interface ReducedVisualSample()
{
// FIXME: losing the error from the previous window
}

@property (strong, nonatomic) NSMutableArray<NSMutableData*>* sampleBuffers;
@property (strong, nonatomic) NSMutableData* precalcBuffer;

@end

const size_t kPrecalcPoolSize = 8192;
const size_t kSampleBufferFrameCount = 4 * 16384;

@implementation ReducedVisualSample
{
dispatch_queue_t _calculations_queue;
}

- (id)initWithSample:(LazySample*)sample pixelPerSecond:(double)pixelPerSecond tileWidth:(size_t)tileWidth
{
self = [super initWithSample:sample pixelPerSecond:pixelPerSecond tileWidth:tileWidth];
if (self) {
_sampleBuffers = [NSMutableArray array];

//unsigned long long framesNeeded = tileWidth * _framesPerPixel;
for (int channel = 0; channel < sample.channels; channel++) {
NSMutableData* buffer = [NSMutableData dataWithCapacity:kSampleBufferFrameCount * sample.frameSize];
[_sampleBuffers addObject:buffer];
}

_precalcBuffer = [NSMutableData dataWithCapacity:kPrecalcPoolSize * sizeof(VisualPair)];

dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INITIATED, 0);
const char* queue_name = [@"ReducedVisualSample" cStringUsingEncoding:NSStringEncodingConversionAllowLossy];
_calculations_queue = dispatch_queue_create(queue_name, attr);
}
return self;
}

- (void)dealloc
{
}

- (void)prepareWithCallback:(nonnull void (^)(void))callback
{
[self runPrecalcWithCallback:callback];
}

- (IndexedBlockOperation*)runPrecalcWithCallback:(nonnull void (^)(void))callback
{
IndexedBlockOperation* blockOperation = [[IndexedBlockOperation alloc] initWithIndex:0];
//NSLog(@"adding %ld", pageIndex);
IndexedBlockOperation* __weak weakOperation = blockOperation;
//ConcurrentAccessDictionary* __weak weakOperations = _operations;
ReducedVisualSample* __weak weakSelf = self;

//[_operations setObject:blockOperation forKey:[NSNumber numberWithLong:pageIndex]];

[blockOperation run:^(void){
if (weakOperation.isCancelled) {
return;
}
assert(!weakOperation.isFinished);

double counter = 0.0;
unsigned long int framesPerEntry = (weakSelf.sample.frames + (kPrecalcPoolSize - 1)) / kPrecalcPoolSize;

unsigned long long framesLeftInBufferCount = 0;

// 2 000 000 / 8000 = 250
// 20 000 000 / 8000 = 2500
// 158 760 000 / 8000 = 19845
//unsigned long int framesNeeded = kSampleBufferFrameCount;

float* data[weakSelf.sample.channels];

for (int channel = 0; channel < weakSelf.sample.channels; channel++) {
data[channel] = (float*)((NSMutableData*)weakSelf.sampleBuffers[channel]).bytes;
}

//NSLog(@"we got room for %ld bytes", width * sizeof(VisualPair));
VisualPair* storage = (VisualPair*)self->_precalcBuffer.mutableBytes;
assert(storage);

const int channels = weakSelf.sample.channels;

unsigned long long displaySampleFrameIndexOffset = 0;

unsigned long int frameIndex = 0;

double negativeSum = 0.0;
double positiveSum = 0.0;
unsigned int positiveCount = 0;
unsigned int negativeCount = 0;
unsigned long long offset = 0;

while(frameIndex < weakSelf.sample.frames) {
//NSLog(@"This block of %lld frames is used to create visuals for %ld pixels", displayFrameCount, width);
negativeSum = 0.0;
positiveSum = 0.0;
positiveCount = 0;
negativeCount = 0;
do {
if (weakOperation.isCancelled) {
break;
}

if (framesLeftInBufferCount == 0) {
unsigned long long framesRequiredCount = MIN(kSampleBufferFrameCount, weakSelf.sample.frames - frameIndex);
// This may block for a loooooong time!
[weakSelf.sample rawSampleFromFrameOffset:displaySampleFrameIndexOffset
frames:framesRequiredCount
outputs:data];

framesLeftInBufferCount = framesRequiredCount;
offset = frameIndex;
}

double s = 0.0;
for (int channel = 0; channel < channels; channel++) {
s += data[channel][frameIndex - offset];
}
s /= channels;

frameIndex++;
counter += 1.0;
framesLeftInBufferCount -= 1.0;

if (s >= 0) {
positiveSum += s;
positiveCount++;
} else {
negativeSum += s;
negativeCount++;
}
} while (counter < framesPerEntry);

if (weakOperation.isCancelled) {
break;
}

counter -= framesPerEntry;
displaySampleFrameIndexOffset += framesPerEntry;

storage->negativeAverage = negativeCount > 0 ? negativeSum / negativeCount : 0.0;
storage->positiveAverage = positiveSum > 0 ? positiveSum / positiveCount : 0.0;

++storage;
};
weakOperation.isFinished = !weakOperation.isCancelled;
callback();
}];

dispatch_async(_calculations_queue, blockOperation.dispatchBlock);

return blockOperation;
}

- (IndexedBlockOperation*)runOperationWithOrigin:(size_t)origin width:(size_t)width callback:(nonnull void (^)(void))callback
{
assert(origin < self.width);

size_t pageIndex = origin / self.tileWidth;

NSNumber* pageNumber = [NSNumber numberWithLong:pageIndex];
IndexedBlockOperation* blockOperation = [self.operations objectForKey:pageNumber];
if (blockOperation != nil) {
NSLog(@"asking for the same operation again on page %ld", pageIndex);
return blockOperation;
}

blockOperation = [[IndexedBlockOperation alloc] initWithIndex:pageIndex];
//NSLog(@"adding %ld", pageIndex);
IndexedBlockOperation* __weak weakOperation = blockOperation;
//ConcurrentAccessDictionary* __weak weakOperations = _operations;
ReducedVisualSample* __weak weakSelf = self;

[self.operations setObject:blockOperation forKey:[NSNumber numberWithLong:pageIndex]];

[blockOperation run:^(void){
if (weakOperation.isCancelled) {
return;
}
assert(!weakOperation.isFinished);

double pairsPerPixel = (double)kPrecalcPoolSize / (double)weakSelf.width;
unsigned long int pairsNeeded = width * pairsPerPixel;

//NSLog(@"we got room for %ld bytes", width * sizeof(VisualPair));
NSMutableData* buffer = [NSMutableData dataWithLength:width * sizeof(VisualPair)];
assert(buffer);
VisualPair* storage = (VisualPair*)buffer.mutableBytes;
assert(storage);

unsigned long long displaySampleFrameIndexOffset = origin * pairsPerPixel;
assert(displaySampleFrameIndexOffset < kPrecalcPoolSize);
unsigned long long displayPairsCount = MIN(pairsNeeded, kPrecalcPoolSize - displaySampleFrameIndexOffset);

VisualPair* source = (VisualPair*)self->_precalcBuffer.bytes + displaySampleFrameIndexOffset;

unsigned long int frameIndex = 0;

double negativeSum = 0.0;
double positiveSum = 0.0;
unsigned int positiveCount = 0;
unsigned int negativeCount = 0;

while(frameIndex < displayPairsCount) {
negativeSum = 0.0;
positiveSum = 0.0;
positiveCount = 0;
negativeCount = 0;
double counter = 0.0;
do {
if (weakOperation.isCancelled) {
break;
}

if (frameIndex >= displayPairsCount) {
break;
}

positiveSum += source->positiveAverage;
positiveCount++;

negativeSum += source->negativeAverage;
negativeCount++;

frameIndex++;
counter += 1.0;

source++;

} while (counter < pairsPerPixel);

if (weakOperation.isCancelled) {
break;
}

storage->negativeAverage = negativeCount > 0 ? negativeSum / negativeCount : 0.0;
storage->positiveAverage = positiveSum > 0 ? positiveSum / positiveCount : 0.0;

++storage;
};
weakOperation.index = pageIndex;
weakOperation.data = buffer;
weakOperation.isFinished = !weakOperation.isCancelled;
callback();
}];

dispatch_async(_calculations_queue, blockOperation.dispatchBlock);

return blockOperation;
}

@end
4 changes: 4 additions & 0 deletions PlayEm/Sample/VisualSample.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,16 @@
NS_ASSUME_NONNULL_BEGIN

@class LazySample;
@class ConcurrentAccessDictionary;

@interface VisualSample : NSObject

@property (strong, nonatomic) LazySample* sample;
@property (readonly, nonatomic) size_t width;
@property (assign, nonatomic) double pixelPerSecond;
@property (assign, nonatomic) size_t tileWidth;
@property (assign, nonatomic) double framesPerPixel;
@property (strong, nonatomic) ConcurrentAccessDictionary* operations;

- (id)initWithSample:(LazySample*)sample pixelPerSecond:(double)pixelPerSecond tileWidth:(size_t)tileWidth;
- (NSData* _Nullable)visualsFromOrigin:(size_t)origin;
Expand Down
6 changes: 1 addition & 5 deletions PlayEm/Sample/VisualSample.m
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,6 @@ @interface VisualSample()
{
// FIXME: losing the error from the previous window
}

@property (assign, nonatomic) size_t tileWidth;
@property (assign, nonatomic) double framesPerPixel;
@property (strong, nonatomic) ConcurrentAccessDictionary* operations;
@property (strong, nonatomic) NSMutableArray<NSMutableData*>* sampleBuffers;

@end
Expand All @@ -41,6 +37,7 @@ - (id)initWithSample:(LazySample*)sample pixelPerSecond:(double)pixelPerSecond t
_framesPerPixel = (double)sample.rate / pixelPerSecond;
_tileWidth = tileWidth;
assert(_framesPerPixel >= 1.0);

_sampleBuffers = [NSMutableArray array];

unsigned long long framesNeeded = tileWidth * _framesPerPixel;
Expand Down Expand Up @@ -273,5 +270,4 @@ - (IndexedBlockOperation*)runOperationWithOrigin:(size_t)origin width:(size_t)wi
return blockOperation;
}


@end
3 changes: 2 additions & 1 deletion PlayEm/Window & ViewControllers/WaveWindowController.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ NS_ASSUME_NONNULL_BEGIN

@class MediaMetaData;
@class VisualSample;
@class ReducedVisualSample;
@class BeatTrackedSample;
@class TotalWaveView;
@class ScrollingTextView;
Expand All @@ -50,7 +51,7 @@ NS_ASSUME_NONNULL_BEGIN

@property (nonatomic, strong) AudioController* audioController;

@property (strong, nonatomic) VisualSample* totalVisual;
@property (strong, nonatomic) ReducedVisualSample* totalVisual;
@property (strong, nonatomic) VisualSample* visualSample;
@property (strong, nonatomic) BeatTrackedSample* beatSample;

Expand Down
Loading