[ReasonReact][reason-expo] Audio bindings


#1

I’m having difficulty reading the typings of the audio module exported by reason-expo:

module Sound = {
  class type _sound =
    [@bs]
    {
      pub unloadAsync: unit => Js.Promise.t(unit);
      pub getStatusAsync: unit => Js.Promise.t('a);
      pub setOnPlaybackStatusUpdate: ('a => unit) => unit;
      pub setStatusAsync: 'a => Js.Promise.t(unit);
      pub playAsync: unit => Js.Promise.t(unit);
      pub replayAsync: unit => Js.Promise.t(unit);
      pub pauseAsync: unit => Js.Promise.t(unit);
      pub stopAsync: unit => Js.Promise.t(unit);
      pub setPositionAsync: int => Js.Promise.t(unit);
      pub setRateAsync: (float, bool) => Js.Promise.t(unit);
      pub setVolumeAsync: float => Js.Promise.t(unit);
      pub setIsMutedAsync: bool => Js.Promise.t(unit);
      pub setIsLoopingAsync: bool => Js.Promise.t(unit);
      pub setProgressUpdateIntervalAsync: int => Js.Promise.t(unit);
    };

  type t = Js.t(_sound);

  [@bs.new] [@bs.module "expo-av"] [@bs.scope "Audio"]
  external make: unit => t = "Sound";

  [@bs.module "expo-av"] [@bs.scope ("Audio", "Sound")]
  external _createAsync:
    (Source.rawSourceJS, 'a, 'a => unit, bool) => Js.Promise.t(t) =
    "createAsync";

  let createAsync =
      (source, initialStatus, onPlaybackStatusUpdate, downloadFirst) =>
    _createAsync(
      Source.encodeSource(source),
      initialStatus,
      onPlaybackStatusUpdate,
      downloadFirst,
    );
};

I thought _createAsync returns a promise that resolves with the type Js.t(_sound). But when I try to access the resolved object with

  Expo.Audio.Sound.createAsync(`URI(url))
  |> Js.Promise.then_(playbackObject => playbackObject##playAsync());

The error is a bit difficult to decipher for me:

  /home/rick/dev/app/frontend/src/components/Podcast.re 28:6-50

  26 │ let play = url => {
  27 │   Audio.Sound.createAsync(`URI(url))
  28 │   |> Js.Promise.then_(playbackObject => playbackObject##playAsync());
  29 │ };
  30 │ [@react.component]
  
  This value seems to need to be wrapped in a function that takes extra
  arguments of type: 'a, 'a => unit, bool
  
  Here's the original error message
  This has type:
    Js.Promise.t(Js.t(({.. playAsync: (unit => Js.Promise.t('b)) [@bs.meth]}
                       as 'a))) =>
    Js.Promise.t('b)
  But somewhere wanted:
    (('c, 'c => unit, bool) => Js.Promise.t(Expo.Audio.Sound.t)) => 'd
  
  The incompatible parts:
    Js.Promise.t(Js.t('a)) (defined as Js.Promise.t(Js.t('a)))
    vs
    ('c, 'c => unit, bool) => Js.Promise.t(Expo.Audio.Sound.t)

How can I call the playAsync method on the playbackObject


#2

It looks like the Audio.Sound.createAsync function has 4 arguments (source, initialStatus, onPlaybackStatusUpdate, downloadFirst) but you have provided just the first one.

Try this (customizing the argument values to your requirements):

let initialStatus = Js.undefined;
let onPlaybackStatusUpdate = _ => ();
let downloadFirst = true;
Expo.Audio.Sound.createAsync(
  `URI(url),
  initialStatus,
  onPlaybackStatusUpdate,
  downloadFirst
) |> Js.Promise.then_(playbackObject => playbackObject##playAsync());

#3

perfect! thank you @pewniak747, it makes sense, but I was a bit confused because by the promise error message. Thanks again!