Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(oscillator): Setup IOS Oscillator #27

Merged
merged 22 commits into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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
4 changes: 2 additions & 2 deletions cpp/AudioContext/AudioContextHostObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ namespace audiocontext

public:
explicit AudioContextHostObject(std::shared_ptr<AudioContextWrapper> wrapper) : wrapper_(wrapper) {}

#ifdef ANDROID
static void createAndInstallFromWrapper(const std::shared_ptr<AudioContextWrapper>& wrapper, jlong jsContext) {
auto runtime = reinterpret_cast<jsi::Runtime *>(jsContext);
auto hostObject = std::make_shared<AudioContextHostObject>(wrapper);
auto object = jsi::Object::createFromHostObject(*runtime, hostObject);
runtime->global().setProperty(*runtime, "__AudioContext", std::move(object));
}

#endif
jsi::Value get(jsi::Runtime &runtime, const jsi::PropNameID &name) override;
void set(jsi::Runtime &runtime, const jsi::PropNameID &name, const jsi::Value &value) override;
std::vector<jsi::PropNameID> getPropertyNames(jsi::Runtime &rt) override;
Expand Down
2 changes: 2 additions & 0 deletions cpp/AudioContext/AudioContextWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

#ifdef ANDROID
#include "AudioContext.h"
#else
#include "OscillatorNodeHostObject.h"
#endif

namespace audiocontext {
Expand Down
2 changes: 2 additions & 0 deletions cpp/AudioContext/android/AudioContextWrapper.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#ifdef ANDROID
#include "AudioContextWrapper.h"

namespace audiocontext {
Expand All @@ -12,3 +13,4 @@ namespace audiocontext {
return std::make_shared<AudioDestinationNodeWrapper>(destination);
}
} // namespace audiocontext
#endif
Empty file removed cpp/AudioContext/ios/.keep
Empty file.
14 changes: 14 additions & 0 deletions cpp/AudioContext/ios/AudioContextWrapper.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#ifndef ANDROID
#include "AudioContextWrapper.h"

namespace audiocontext {

std::shared_ptr<OscillatorNodeWrapper> AudioContextWrapper::createOscillator() {
return std::make_shared<OscillatorNodeWrapper>();
}

std::shared_ptr<AudioDestinationNodeWrapper> AudioContextWrapper::getDestination() {
throw std::runtime_error("[AudioContextHostObject::getDestination] Not yet implemented!");
}
} // namespace audiocontext
#endif
3 changes: 2 additions & 1 deletion cpp/AudioDestinationNode/AudioDestinationNodeWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@
#include "OscillatorNodeWrapper.h"
#endif

#include "AudioNodeWrapper.h"

namespace audiocontext {
using namespace facebook;
hubgan marked this conversation as resolved.
Show resolved Hide resolved

#ifdef ANDROID
class AudioDestinationNode;
Expand Down
2 changes: 0 additions & 2 deletions cpp/AudioNode/AudioNodeWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
#endif

namespace audiocontext {
using namespace facebook;

class AudioNodeWrapper {
public:
#ifdef ANDROID
Expand Down
3 changes: 3 additions & 0 deletions cpp/AudioNode/android/AudioNodeWrapper.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#ifdef ANDROID
#include "AudioNodeWrapper.h"

namespace audiocontext {
Expand All @@ -10,3 +11,5 @@ namespace audiocontext {
node_->disconnect(node->node_);
}
}

#endif
Empty file removed cpp/AudioNode/ios/.keep
Empty file.
15 changes: 15 additions & 0 deletions cpp/AudioNode/ios/AudioNodeWrapper.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#ifndef ANDROID
#include "AudioNodeWrapper.h"
#include "iostream"

namespace audiocontext {

void AudioNodeWrapper::connect(const std::shared_ptr<AudioNodeWrapper> node) const {
throw std::runtime_error("[AudioNodeWrapper::connect] Not yet implemented!");
}

void AudioNodeWrapper::disconnect(const std::shared_ptr<AudioNodeWrapper> node) const {
throw std::runtime_error("[AudioNodeWrapper::disconnect] Not yet implemented!");
}
}
#endif
9 changes: 5 additions & 4 deletions cpp/OscillatorNode/OscillatorNodeWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,28 @@

#ifdef ANDROID
#include "OscillatorNode.h"
#else
#include "IOSOscillator.h"
#endif

namespace audiocontext {
using namespace facebook;

#ifdef ANDROID
class OscillatorNode;
#endif

class OscillatorNodeWrapper: public AudioNodeWrapper {
#ifdef ANDROID
private:
std::shared_ptr<OscillatorNode> oscillator_;
#else
std::shared_ptr<IOSOscillator> oscillator_;
#endif

public:
#ifdef ANDROID
explicit OscillatorNodeWrapper( std::shared_ptr<OscillatorNode> oscillator) : AudioNodeWrapper(
oscillator), oscillator_(oscillator) {}
#else
explicit OscillatorNodeWrapper() {}
explicit OscillatorNodeWrapper() : oscillator_(std::make_shared<IOSOscillator>()) {}
#endif
double getFrequency();
double getDetune();
Expand Down
2 changes: 2 additions & 0 deletions cpp/OscillatorNode/android/OscillatorNodeWrapper.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#ifdef ANDROID
#include "OscillatorNodeWrapper.h"

namespace audiocontext {
Expand Down Expand Up @@ -34,3 +35,4 @@ namespace audiocontext {
oscillator_->setWaveType(type);
}
}
#endif
Empty file removed cpp/OscillatorNode/ios/.keep
Empty file.
38 changes: 38 additions & 0 deletions cpp/OscillatorNode/ios/OscillatorNodeWrapper.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#ifndef ANDROID
#include "OscillatorNodeWrapper.h"

namespace audiocontext {

double OscillatorNodeWrapper::getFrequency() {
return oscillator_->getFrequency();
}

double OscillatorNodeWrapper::getDetune() {
return oscillator_->getDetune();
}

std::string OscillatorNodeWrapper::getType() {
return oscillator_->getType();
}

void OscillatorNodeWrapper::start(double time) {
oscillator_->start();
}

void OscillatorNodeWrapper::stop(double time) {
oscillator_->stop();
}

void OscillatorNodeWrapper::setFrequency(double frequency) {
oscillator_->changeFrequency(frequency);
}

void OscillatorNodeWrapper::setDetune(double detune) {
oscillator_->changeDetune(detune);
}

void OscillatorNodeWrapper::setType(const std::string& type) {
oscillator_->setType(type);
}
}
#endif
43 changes: 20 additions & 23 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,46 +1,43 @@
/* eslint-disable react/react-in-jsx-scope */
import { Button, StyleSheet, Text, View } from 'react-native';
import React from 'react';
import { Button, Platform, StyleSheet, Text, View } from 'react-native';
import { useRef, useEffect } from 'react';

import { AudioContext, type Oscillator } from 'react-native-audio-context';

const App = () => {
const App: React.FC = () => {
const audioContextRef = useRef<AudioContext | null>(null);
const oscillatorRef = useRef<Oscillator | null>(null);
const secondaryOscillatorRef = useRef<Oscillator | null>(null);

const setUp = () => {
audioContextRef.current = new AudioContext();
oscillatorRef.current = audioContextRef.current.createOscillator();
oscillatorRef.current.frequency = 800;
useEffect(() => {
if (!audioContextRef.current) {
audioContextRef.current = new AudioContext();

secondaryOscillatorRef.current = audioContextRef.current.createOscillator();
secondaryOscillatorRef.current.frequency = 300;
secondaryOscillatorRef.current.type = 'square';
oscillatorRef.current = audioContextRef.current.createOscillator();

const destination = audioContextRef.current.destination;
oscillatorRef.current.connect(destination);
secondaryOscillatorRef.current.connect(destination);
};
secondaryOscillatorRef.current =
audioContextRef.current.createOscillator();
secondaryOscillatorRef.current.frequency = 840;
secondaryOscillatorRef.current.type = 'square';

// Destination is not implemented on IOS yet
if (Platform.OS === 'android') {
const destination = audioContextRef.current.destination;
oscillatorRef.current.connect(destination!);
secondaryOscillatorRef.current.connect(destination!);
}
}

useEffect(() => {
return () => {
//TODO
};
}, []);

const startOscillator = () => {
if (!audioContextRef.current) {
setUp();
}

oscillatorRef.current?.start(1);
oscillatorRef.current?.start(0);
secondaryOscillatorRef.current?.start(0);
};
const stopOscillator = () => {
if (!audioContextRef.current) {
setUp();
}
oscillatorRef.current?.stop(0);
secondaryOscillatorRef.current?.stop(0);
};
Expand Down
5 changes: 5 additions & 0 deletions ios/AudioContextModule.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#import <React/RCTBridgeModule.h>

@interface AudioContextModule : NSObject <RCTBridgeModule>

@end
14 changes: 8 additions & 6 deletions ios/JSIExampleModule.mm → ios/AudioContextModule.mm
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
#import "JSIExampleModule.h"
#import "AudioContextModule.h"

#import <React/RCTBridge+Private.h>
#import <React/RCTUtils.h>
#import <jsi/jsi.h>

#import "../cpp/JSIExampleHostObject.h"
#import "../cpp/AudioContext/AudioContextHostObject.h"
#import "../cpp/AudioContext/AudioContextWrapper.h"

@implementation JSIExampleModule
@implementation AudioContextModule

RCT_EXPORT_MODULE(JSIExample)
RCT_EXPORT_MODULE(AudioContextModule)

RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(install)
{
Expand All @@ -27,9 +28,10 @@ @implementation JSIExampleModule
}
auto& runtime = *jsiRuntime;

auto hostObject = std::make_shared<example::JSIExampleHostObject>();
auto wrapper = std::make_shared<audiocontext::AudioContextWrapper>();
auto hostObject = std::make_shared<audiocontext::AudioContextHostObject>(wrapper);
auto object = jsi::Object::createFromHostObject(runtime, hostObject);
runtime.global().setProperty(runtime, "__JSIExampleProxy", std::move(object));
runtime.global().setProperty(runtime, "__AudioContext", std::move(object));

NSLog(@"Successfully installed JSI bindings for react-native-audio-context!");
return @true;
Expand Down
5 changes: 0 additions & 5 deletions ios/JSIExampleModule.h

This file was deleted.

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

27 changes: 27 additions & 0 deletions ios/nodes/Oscillator/IOSOscillator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#pragma once

#ifdef __OBJC__ // when compiled as Objective-C++
#import <OscillatorNode.h>
#else // when compiled as C++
typedef struct objc_object OscillatorNode;
#endif // __OBJC__

#include <string>

namespace audiocontext {
class IOSOscillator {
public:
explicit IOSOscillator();
void start() const;
void stop() 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;

protected:
OscillatorNode *OscillatorNode_;
};
} // namespace audiocontext
Loading