
// compile this using './build_example3_tuner_emscripten.sh'

#ifdef __EMSCRIPTEN__

#include "hexfin.c"

#include <emscripten/webaudio.h>

static const float lowest_frequency = 61.735f; // Note B1
static const float highest_frequency = 1318.51f; // Note E6
static const float sample_rate = 48000.0f;

EM_BOOL processAudio(int numInputs, const AudioSampleFrame* inputs,
  int numOutputs, AudioSampleFrame* outputs,
  int numParams, const AudioParamFrame* params,
  void* userData)
{
  struct hexfin_context* ctx = (struct hexfin_context*)userData;
  hexfin_processBlock(ctx, &inputs[0].data[0], 128, sample_rate, lowest_frequency, highest_frequency);

  memcpy(&outputs[0].data[0], &inputs[0].data[0], 128 * sizeof(float));
  memcpy(&outputs[0].data[128], &inputs[0].data[0], 128 * sizeof(float));

  return EM_TRUE;
}

EM_BOOL OnCanvasClick(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData)
{
  EMSCRIPTEN_WEBAUDIO_T audioContext = (EMSCRIPTEN_WEBAUDIO_T)userData;
  if (emscripten_audio_context_state(audioContext) != AUDIO_CONTEXT_STATE_RUNNING) {
    emscripten_resume_audio_context_sync(audioContext);
  }
  return EM_FALSE;
}

void AudioWorkletProcessorCreated(EMSCRIPTEN_WEBAUDIO_T audioContext, EM_BOOL success, void* userData)
{
  int outputChannelCounts[1] = { 2 };
  EmscriptenAudioWorkletNodeCreateOptions options = {
    .numberOfInputs = 1,
    .numberOfOutputs = 1,
    .outputChannelCounts = outputChannelCounts
  };

  // Create node
  EMSCRIPTEN_AUDIO_WORKLET_NODE_T wasmAudioWorklet = emscripten_create_wasm_audio_worklet_node(audioContext, "process-audio", &options, &processAudio, userData);

  EM_ASM({
      navigator.mediaDevices.getUserMedia({ audio: { echoCancellation: false, autoGainControl : false, noiseSuppression : false } }) // disable all processing done by the browser
        .then(function(stream) {
          emscriptenGetAudioObject($1).streamNode = emscriptenGetAudioObject($1).createMediaStreamSource(stream);
          emscriptenGetAudioObject($1).streamNode.connect(emscriptenGetAudioObject($0));
        })
        .catch (function(error) {
      });
      emscriptenGetAudioObject($0).connect(emscriptenGetAudioObject($1).destination);
    }, wasmAudioWorklet, audioContext);

  // Resume context on mouse click
  emscripten_set_click_callback("#startAudioButton", (void*)audioContext, 0, OnCanvasClick);
}

static void AudioThreadInitialized(EMSCRIPTEN_WEBAUDIO_T audioContext, EM_BOOL success, void* userData)
{
  WebAudioWorkletProcessorCreateOptions opts = {
    .name = "process-audio",
  };
  emscripten_create_wasm_audio_worklet_processor_async(audioContext, &opts, &AudioWorkletProcessorCreated, userData);
}

static void initWebaudio(struct hexfin_context* ctx)
{
  static EMSCRIPTEN_WEBAUDIO_T context;
  context = emscripten_create_audio_context(0);
  static unsigned char audioThreadStack[32768];
  emscripten_start_wasm_audio_worklet_thread_async(context, audioThreadStack, sizeof(audioThreadStack), &AudioThreadInitialized, ctx);
}

void setTunerFrequencyJSVariable(void* userData)
{
  struct hexfin_context* ctx = (struct hexfin_context*)userData;

  EM_ASM_({
    tunerFrequency = $0; // Update the global JavaScript variable 'tunerFrequency'
    }, ctx->loudEnough ? ctx->frequency : 0.0f);
}

int main(int argc, char* argv[])
{
  struct hexfin_context ctx = hexfin_create_context(sample_rate, lowest_frequency, highest_frequency);
  initWebaudio(&ctx);
  emscripten_set_main_loop_arg(setTunerFrequencyJSVariable, &ctx, -1, 1);

  return 0;
}

#endif // __EMSCRIPTEN__
