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