1// Copyright 2017-present 650 Industries. All rights reserved. 2 3#import <ABI48_0_0EXAV/AudioSampleCallbackWrapper.h> 4 5namespace ABI48_0_0expo { 6namespace av { 7 8std::shared_ptr<LongLivedObjectCollection> AudioSampleCallbackWrapper::callbackCollection = 9 std::make_shared<LongLivedObjectCollection>(); 10 11AudioSampleCallbackWrapper::AudioSampleCallbackWrapper(jsi::Function &&callback, 12 jsi::Runtime &runtime, 13 std::shared_ptr<CallInvoker> jsInvoker) 14: weakWrapper(JsiCallbackWrapper::createWeak(callbackCollection, std::move(callback), runtime, jsInvoker)) {} 15 16AudioSampleCallbackWrapper::~AudioSampleCallbackWrapper() { 17 auto strongWrapper = weakWrapper.lock(); 18 if (strongWrapper) { 19 strongWrapper->destroy(); 20 } 21} 22 23void AudioSampleCallbackWrapper::call(AudioBuffer* buffer, double timestamp) 24{ 25 auto strongWrapper = this->weakWrapper.lock(); 26 if (!strongWrapper) { 27 return; 28 } 29 30 // we want to capture only the wrapper, not the whole `this` object, 31 // because it may no longer exist when the lambda is invoked 32 auto weakWrapper = this->weakWrapper; 33 34 // We need to invoke the callback from the JS thread, otherwise Hermes complains 35 strongWrapper->jsInvoker().invokeAsync([weakWrapper, buffer, timestamp]{ 36 auto jsiCallbackWrapper = weakWrapper.lock(); 37 if (!jsiCallbackWrapper || !buffer) { 38 return; 39 } 40 41 auto &rt = jsiCallbackWrapper->runtime(); 42 43 auto channelsCount = (size_t) buffer->mNumberChannels; 44 auto framesCount = buffer->mDataByteSize / sizeof(float); 45 float *data = reinterpret_cast<float *>(buffer->mData); 46 if (!data) { 47 return; 48 } 49 50 auto channels = jsi::Array(rt, channelsCount); 51 52 // Channels in AudioBuffer are interleaved, so for 2 channels we do steps of 2: 53 // [0, 2, 4, 6, ...] and [1, 3, 5, 7, ...] 54 for (auto channelIndex = 0; channelIndex < channelsCount; channelIndex++) 55 { 56 auto channel = jsi::Object(rt); 57 auto frames = jsi::Array(rt, static_cast<int>(framesCount / channelsCount)); 58 59 for (int frameIndex = channelIndex, arrayIndex = 0; 60 frameIndex < framesCount; 61 frameIndex += channelsCount, arrayIndex++) 62 { 63 double frame = static_cast<double>(data[frameIndex]); 64 frames.setValueAtIndex(rt, arrayIndex, jsi::Value(frame)); 65 } 66 67 channel.setProperty(rt, "frames", std::move(frames)); 68 channels.setValueAtIndex(rt, channelIndex, std::move(channel)); 69 } 70 71 auto sample = jsi::Object(rt); 72 sample.setProperty(rt, "channels", std::move(channels)); 73 sample.setProperty(rt, "timestamp", jsi::Value(timestamp)); 74 75 jsiCallbackWrapper->callback().call(rt, std::move(sample)); 76 }); 77} 78 79} // namespace av 80} // namespace ABI48_0_0expo 81