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

Docs/readme api coverage #59

Closed
wants to merge 19 commits into from
Closed
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
152 changes: 152 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,158 @@
npm install react-native-audio-context
```

## API Coverage

#### Android

- [ ] AudioContext

properties:
- [x] destination
- [x] sampleRate
- [x] state

methods:
- [x] createGain()
- [x] createOscillator()
- [x] createStereoPanner()
- [ ] createBiquadFilter()
- [x] getCurrentTime()
- [ ] close()

- [x] AudioNode

properties:
- [x] context
- [x] numberOfInputs
- [x] numberOfOutputs

methods:
- [x] connect()
- [x] disconnect()

- [x] AudioScheduledSourceNode

methods:
- [x] start(time: number)
- [x] stop(time: number)

- [x] AudioDestinationNode

- [x] AudioParam

properties:
- [x] value
- [x] defaultValue
- [x] minValue
- [x] maxValue

methods:
- [x] setValueAtTime(value: number, startTime: number)
- [x] linearRampToValueAtTime(value: number, endTime: number)
- [x] exponentialRampToValueAtTime(value: number, endTime: number)

- [x] OscillatorNode

properties:
- [x] frequency
- [x] detune
- [x] type

- [x] GainNode

properties:
- [x] gain

- [x] StereoPannerNode

properties:
- [x] pan

- [ ] BiquadFilterNode

properties:
- [ ] frequency
- [ ] detune
- [ ] Q
- [ ] gain
- [ ] type

#### IOS

- [ ] AudioContext

properties:
- [ ] destination
- [ ] sampleRate
- [ ] state

methods:
- [ ] createGain()
- [ ] createOscillator()
- [ ] createStereoPanner()
- [ ] createBiquadFilter()
- [ ] getCurrentTime()
- [ ] close()

- [ ] AudioNode

properties:
- [ ] context
- [ ] numberOfInputs
- [ ] numberOfOutputs

methods:
- [ ] connect()
- [ ] disconnect()

- [ ] AudioScheduledSourceNode

methods:
- [ ] start(time: number)
- [ ] stop(time: number)

- [ ] AudioDestinationNode

- [ ] AudioParam

properties:
- [ ] value
- [ ] defaultValue
- [ ] minValue
- [ ] maxValue

methods:
- [ ] setValueAtTime(value: number, startTime: number)
- [ ] linearRampToValueAtTime(value: number, endTime: number)
- [ ] exponentialRampToValueAtTime(value: number, endTime: number)

- [ ] OscillatorNode

properties:
- [ ] frequency
- [ ] detune
- [ ] type

- [ ] GainNode

properties:
- [ ] gain

- [ ] StereoPannerNode

properties:
- [ ] pan

- [ ] BiquadFilterNode

properties:
- [ ] frequency
- [ ] detune
- [ ] Q
- [ ] gain
- [ ] type

## Contributing

See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow.
Expand Down
2 changes: 2 additions & 0 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ dependencies {
implementation "com.facebook.react:react-native:+"
implementation 'androidx.core:core-ktx:1.13.1'
implementation 'com.facebook.fbjni:fbjni:0.6.0'
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2"
}

if (isNewArchitectureEnabled()) {
Expand Down
15 changes: 15 additions & 0 deletions android/src/main/cpp/AudioParam/AudioParam.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,19 @@ namespace audiocontext {
static const auto method = javaClassLocal()->getMethod<double()>("getMaxValue");
return method(javaObject_.get());
}

void AudioParam::setValueAtTime(double value, double startTime) {
static const auto method = javaClassLocal()->getMethod<void(double, double)>("setValueAtTime");
method(javaObject_.get(), value, startTime);
}

void AudioParam::linearRampToValueAtTime(double value, double endTime) {
static const auto method = javaClassLocal()->getMethod<void(double, double)>("linearRampToValueAtTime");
method(javaObject_.get(), value, endTime);
}

void AudioParam::exponentialRampToValueAtTime(double value, double endTime) {
static const auto method = javaClassLocal()->getMethod<void(double, double)>("exponentialRampToValueAtTime");
method(javaObject_.get(), value, endTime);
}
}
3 changes: 3 additions & 0 deletions android/src/main/cpp/AudioParam/AudioParam.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ namespace audiocontext {
double getDefaultValue();
double getMinValue();
double getMaxValue();
void setValueAtTime(double value, double startTime);
void linearRampToValueAtTime(double value, double endTime);
void exponentialRampToValueAtTime(double value, double endTime);

protected:
friend HybridBase;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ class AudioContext() : BaseAudioContext {
external fun initHybrid(): HybridData?
external fun install(jsContext: Long)

fun getCurrentTime(): Double {
return (SystemClock.elapsedRealtimeNanos() - contextStartTime)/1_000_000_000.0
override fun getCurrentTime(): Double {
return (SystemClock.elapsedRealtimeNanos() - contextStartTime) / 1e9
}

fun getState(): String {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ interface BaseAudioContext {
val destination: AudioDestinationNode
var state: ContextState

abstract fun getCurrentTime(): Double
abstract fun createOscillator(): OscillatorNode
abstract fun createGain(): GainNode
abstract fun createStereoPanner(): StereoPannerNode
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,6 @@ class AudioDestinationNode(context: BaseAudioContext): AudioNode(context) {
override val numberOfInputs = 1
override val numberOfOutputs = 0

private val mHybridData: HybridData? = initHybrid();

companion object {
init {
System.loadLibrary("react-native-audio-context")
}
}

private fun setVolumeAndPanning(playbackParameters: PlaybackParameters) {
val leftPan = playbackParameters.gain * playbackParameters.leftPan
val rightPan = playbackParameters.gain * playbackParameters.rightPan
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,78 @@
package com.audiocontext.nodes

import android.media.AudioAttributes
import android.media.AudioFormat
import android.media.AudioManager
import android.media.AudioTrack
import com.audiocontext.context.BaseAudioContext
import com.audiocontext.nodes.parameters.PlaybackParameters
import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock

abstract class AudioScheduledSourceNode(context: BaseAudioContext) : AudioNode(context) {
abstract fun start(time: Double)
abstract fun stop(time: Double)
override val numberOfInputs: Int = 0
override val numberOfOutputs: Int = 1

protected var playbackParameters: PlaybackParameters
@Volatile protected var isPlaying: Boolean = false

private val coroutineScope = CoroutineScope(Dispatchers.Default)
private val mutex = Mutex()
private var audioJob: Job? = null

init {
val bufferSize = AudioTrack.getMinBufferSize(
context.sampleRate,
AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT) / 4

val audioAttributes = AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build()

val audioFormat = AudioFormat.Builder()
.setSampleRate(context.sampleRate)
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
.setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
.build()

val audioTrack = AudioTrack(audioAttributes, audioFormat, bufferSize, AudioTrack.MODE_STREAM, AudioManager.AUDIO_SESSION_ID_GENERATE)
val buffer = ShortArray(bufferSize)

this.playbackParameters = PlaybackParameters(audioTrack, buffer)
}

protected abstract fun generateSound();

fun start(time: Double) {
coroutineScope.launch {
mutex.withLock {
if (audioJob?.isActive == true) return@launch
audioJob = launch {
while (context.getCurrentTime() < time) {
delay(1)
}
isPlaying = true
playbackParameters.audioTrack.play()
generateSound()
}
}
}
}

fun stop(time: Double) {
coroutineScope.launch {
mutex.withLock {
launch {
while (context.getCurrentTime() < time) {
delay(1)
}
isPlaying = false
playbackParameters.audioTrack.stop()
audioJob?.cancel()
}
}
}
}
}
10 changes: 5 additions & 5 deletions android/src/main/java/com/audiocontext/nodes/GainNode.kt
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
package com.audiocontext.nodes

import android.os.Build
import androidx.annotation.RequiresApi
import com.audiocontext.context.BaseAudioContext
import com.audiocontext.nodes.parameters.AudioParam
import com.audiocontext.nodes.parameters.PlaybackParameters
import com.facebook.jni.HybridData

class GainNode(context: BaseAudioContext): AudioNode(context) {
override val numberOfInputs: Int = 1
override val numberOfOutputs: Int = 1
private var gain: AudioParam = AudioParam(1.0, 1.0, 0.0)
private var gain: AudioParam = AudioParam(context,1.0, 1.0, 0.0)
get() = field
set(value) {
field = value
}

private val mHybridData: HybridData? = initHybrid();

@RequiresApi(Build.VERSION_CODES.N)
override fun process(playbackParameters: PlaybackParameters) {
playbackParameters.gain = gain.getValue()
playbackParameters.gain = gain.getValueAtTime(context.getCurrentTime())
super.process(playbackParameters)
}
}
12 changes: 6 additions & 6 deletions android/src/main/java/com/audiocontext/nodes/StereoPannerNode.kt
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
package com.audiocontext.nodes

import android.os.Build
import androidx.annotation.RequiresApi
import com.audiocontext.context.BaseAudioContext
import com.audiocontext.nodes.parameters.AudioParam
import com.audiocontext.nodes.parameters.PlaybackParameters
import com.facebook.jni.HybridData
import kotlin.math.min

class StereoPannerNode(context: BaseAudioContext): AudioNode(context) {
override val numberOfInputs: Int = 1
override val numberOfOutputs: Int = 1
private var pan: AudioParam = AudioParam(0.0, 1.0, -1.0)
private var pan: AudioParam = AudioParam(context,0.0, 1.0, -1.0)
get() = field
set(value) {
field = value
}

private val mHybridData: HybridData? = initHybrid();

@RequiresApi(Build.VERSION_CODES.N)
override fun process(playbackParameters: PlaybackParameters) {
playbackParameters.leftPan = min(1.0 - pan.getValue(), 1.0)
playbackParameters.rightPan = min(1.0 + pan.getValue(), 1.0)
playbackParameters.leftPan = min(1.0 - pan.getValueAtTime(context.getCurrentTime()), 1.0)
playbackParameters.rightPan = min(1.0 + pan.getValueAtTime(context.getCurrentTime()), 1.0)
super.process(playbackParameters)
}
}
Loading