Skip to content

Commit

Permalink
feat: add detune property, add pseudo abstract classes
Browse files Browse the repository at this point in the history
  • Loading branch information
hubgan committed Jul 19, 2024
1 parent 4f31546 commit c956ba1
Show file tree
Hide file tree
Showing 10 changed files with 112 additions and 14 deletions.
10 changes: 4 additions & 6 deletions cpp/OscillatorNode/ios/OscillatorNodeWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@ namespace audiocontext {
}

jsi::Value OscillatorNodeWrapper::getDetune(jsi::Runtime &runtime, const jsi::PropNameID &propNameId) {
return jsi::Function::createFromHostFunction(runtime, propNameId, 0, [this](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) -> jsi::Value
{
throw std::runtime_error("[OscillatorNodeHostObject::getDetune] Not yet implemented!");
});
return jsi::Value(oscillator_->getDetune());
}

jsi::Value OscillatorNodeWrapper::getType(jsi::Runtime &runtime, const jsi::PropNameID &propNameId) {
Expand Down Expand Up @@ -46,12 +43,13 @@ namespace audiocontext {
}

void OscillatorNodeWrapper::setDetune(jsi::Runtime &runtime, const jsi::PropNameID &propNameId, const jsi::Value &value) {
throw std::runtime_error("[OscillatorNodeHostObject::setDetune] Not yet implemented!");
double detune = value.getNumber();
oscillator_->changeDetune(detune);
}

void OscillatorNodeWrapper::setType(jsi::Runtime &runtime, const jsi::PropNameID &propNameId, const jsi::Value &value) {
std::string waveType = value.getString(runtime).utf8(runtime);
oscillator_->setType(waveType);
}
}
#endif
#endif
6 changes: 3 additions & 3 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ const App: React.FC = () => {
secondaryOscillatorRef.current.type = 'square';

// Destination is not implemented on IOS yet
const destination = audioContextRef.current.destination();
oscillatorRef.current.connect(destination);
secondaryOscillatorRef.current.connect(destination);
// const destination = audioContextRef.current.destination();
// oscillatorRef.current.connect(destination);
// secondaryOscillatorRef.current.connect(destination);
}

return () => {
Expand Down
14 changes: 14 additions & 0 deletions ios/nodes/AudioNode/AudioNode.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>

@interface AudioNode : NSObject

@property (nonatomic, readonly) NSInteger numberOfInputs;
@property (nonatomic, readonly) NSInteger numberOfOutputs;
@property (nonatomic, strong) NSMutableArray<AudioNode *> *connectedNodes;

- (void)connect:(AudioNode *)node;
- (void)disconnect;
- (void)process;

@end
29 changes: 29 additions & 0 deletions ios/nodes/AudioNode/AudioNode.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#import "AudioNode.h"

@implementation AudioNode

- (instancetype)init {
if (self = [super init]) {
_connectedNodes = [NSMutableArray array];
}

return self;
}

- (void)connect:(AudioNode *)node {
if (self.numberOfOutputs > 0) {
[self.connectedNodes addObject:node];
}
}

- (void)disconnect {
[self.connectedNodes removeAllObjects];
}

- (void)process {
for (AudioNode *node in self.connectedNodes) {
[node process];
}
}

@end
8 changes: 8 additions & 0 deletions ios/nodes/AudioScheduledSourceNode/AudioScheduledSourceNode.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#import "AudioNode.h"

@interface AudioScheduledSourceNode : AudioNode

- (void)start;
- (void)stop;

@end
20 changes: 20 additions & 0 deletions ios/nodes/AudioScheduledSourceNode/AudioScheduledSourceNode.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#import "AudioScheduledSourceNode.h"

@implementation AudioScheduledSourceNode

- (void)start {
NSLog(@"Attempting to call `start` on a base class where it is not implemented. You must override `start` in a subclass.");
@throw [NSException exceptionWithName:NSInternalInconsistencyException
reason:[NSString stringWithFormat:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)]
userInfo:nil];
}

- (void)stop {
NSLog(@"Attempting to call `stop` on a base class where it is not implemented. You must override `stop` in a subclass.");
@throw [NSException exceptionWithName:NSInternalInconsistencyException
reason:[NSString stringWithFormat:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)]
userInfo:nil];
}

@end

4 changes: 3 additions & 1 deletion ios/nodes/Oscillator/IOSOscillator.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ namespace audiocontext {
explicit IOSOscillator();
void start() const;
void stop() const;
void changeFrequency(const float frequency) const;
void changeFrequency(const float frequency) const;
float getFrequency() const;
void changeDetune(const float detune) const;
float getDetune() const;
void setType(const std::string &type) const;
std::string getType() const;

Expand Down
8 changes: 8 additions & 0 deletions ios/nodes/Oscillator/IOSOscillator.mm
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@
return [OscillatorNode_ getFrequency];
}

void IOSOscillator::changeDetune(const float detune) const {
[OscillatorNode_ changeDetune:detune];
}

float IOSOscillator::getDetune() const {
return [OscillatorNode_ getDetune];
}

void IOSOscillator::setType(const std::string &type) const {
NSString *nsType = [NSString stringWithUTF8String:type.c_str()];
[OscillatorNode_ setType:nsType];
Expand Down
8 changes: 7 additions & 1 deletion ios/nodes/Oscillator/OscillatorNode.h
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
#import "WaveType.h"
#import "AudioScheduledSourceNode.h"

@interface OscillatorNode : NSObject
@interface OscillatorNode : AudioScheduledSourceNode

@property (nonatomic, strong) AVAudioEngine *audioEngine;
@property (nonatomic, strong) AVAudioPlayerNode *playerNode;
@property (nonatomic, strong) AVAudioPCMBuffer *buffer;
@property (nonatomic, strong) AVAudioFormat *format;
@property (nonatomic, assign) WaveTypeEnum waveType;
@property (nonatomic, assign) float frequency;
@property (nonatomic, assign) float detune;
@property (nonatomic, assign) double sampleRate;

- (instancetype)init;
Expand All @@ -22,6 +24,10 @@

- (float)getFrequency;

- (void)changeDetune:(float)detune;

- (float)getDetune;

- (void)setType:(NSString *)type;

- (NSString *)getType;
Expand Down
19 changes: 16 additions & 3 deletions ios/nodes/Oscillator/OscillatorNode.m
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
#import <OscillatorNode.h>

@implementation OscillatorNode {}
@implementation OscillatorNode

- (instancetype)init {
if (self = [super init]) {
self.frequency = 440;
self.detune = 0;
self.sampleRate = 44100;
self.waveType = WaveTypeSine;

Expand Down Expand Up @@ -41,8 +42,11 @@ - (void)stop {

- (void)setBuffer {
AVAudioFrameCount bufferFrameCapacity = (AVAudioFrameCount)self.sampleRate;

double phaseIncrement = FULL_CIRCLE_RADIANS * self.frequency / self.sampleRate;

// Convert cents to HZ
double detuneRatio = pow(2.0, self.detune / 1200.0);
double detunedFrequency = detuneRatio * self.frequency;
double phaseIncrement = FULL_CIRCLE_RADIANS * detunedFrequency / self.sampleRate;
double phase = 0.0;
float *audioBufferPointer = self.buffer.floatChannelData[0];

Expand All @@ -65,6 +69,15 @@ - (float)getFrequency {
return self.frequency;
}

- (void)changeDetune:(float)detune {
self.detune = detune;
[self setBuffer];
}

- (float)getDetune {
return self.detune;
}

- (void)setType:(NSString *)type {
self.waveType = [WaveType waveTypeFromString:type];
[self setBuffer]; // Update the buffer with the new wave type
Expand Down

0 comments on commit c956ba1

Please sign in to comment.