Gemcook Tech Blog
🎧

Expo AVで音を鳴らしますよ!

2024/06/25に公開

はじめに

こんにちは!

今回社内プロダクトで、音を鳴らす仕様を僕が担当することになったので、ライブラリの選定基準や実装方法についてまとめました。 (今後も新しくReact Nativeで学んだことは記事にしていければなと思っています👀)

まずコード見たい!!って方はこちら

使用するライブラリを決めよう!

「音の鳴らし方ってどうすんねやろ??」 って思った時に、最初はライブラリを使わなくても、React Native標準で音を鳴らすための機能があるのではないかと思っていましたが、標準では機能がなかったのでライブラリを探すことにしました!

次に、ライブラリを選ぶ基準として以下のポイントを押さえました。

使ってる人が多そうか?

まず最初に、githubのスターやフォークの数などをチェックします。これらの指標は、多くのユーザーがそのリポジトリを使用しているかどうかの目安になります。スター数が多いリポジトリは人気が高く、信頼性もあります。

メンテナンスが続いているか?

メンテナンスが続いているかも大事です。定期的に更新されているプロジェクトは、バグ修正やセキュリティアップデートがしっかり行われています。

以上の選定基準からreact-native-soundは半年以上更新がないため、使用を断念しました。他の二つはわりと最近の更新があり、スターやフォークの数もある程度あったのでどちらかが良いかなと思いました。

最終的に選んだライブラリは?

実際どちら(expo-avかreact-native-track-player)も良さそうでしたが、Expoの方が今後もメンテナンスを続けてくれる信頼感や使い勝手が変わらなそうな所があったのでExpo AVを選択しました。

実装方法

基本的な設計

  • まず最初にapp.config.jsを設定します!
app.config.js
{
  "expo": {
    "plugins": [
        ["expo-av",...etc]
    ]
  }
}

そして、音を鳴らせる画面です!
ボタンを押して音が鳴るようにします。

App.tsx

import testSound from "@assets/sounds/sound.mp3";
import { Audio } from "expo-av";
import { useEffect, useState } from "react";
import { Button, StyleSheet, View } from "react-native";

export default function App() {
  const [audio, setAudio] = useState<Audio.Sound | null>(null);

  useEffect(() => {
    // ここでsoundを作成!!
    const initAudio = async () => {
      try {
        const { sound } = await Audio.Sound.createAsync(testSound);
        setAudio(sound);
      } catch (error) {
        console.error("音声の初期化中にエラーが発生しました:", error);
      }
    };
    initAudio();
    return () => {
      // クリーンアップ関数で画面を離れる時はunloadAsyncでaudioをunloadします!
      audio?.unloadAsync();
    };
  }, []);

  async function playAudio() {
    await audio?.playAsync();
  }
  async function playAudio() {
    await audio?.stopAsync();
  }

  return (
    <View style={styles.container}>
      <Button title="Play Audio" onPress={playAudio} />
      <Button title="Stop Audio" onPress={playAudio} />
    </View>
  );
}
const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: "center",
    backgroundColor: "#ecf0f1",
    padding: 10,
  },
});

オプションで様々なことも調整可能に!

  • ループができたり♻️
const { sound } = await Audio.Sound.createAsync(testSound, { isLooping: true });
  • 音量の調整も 🔉
const { sound } = await Audio.Sound.createAsync(testSound, { volume: 0.2 });

参考記事
https://docs.expo.dev/versions/latest/sdk/audio/

詰まったポイント

そもそもTypescriptがjs jsx ts tsx以外の拡張子をモジュールだと判断しません。
なのでmp3をimportしてもエラーが出ます。

そこでsrc配下に@typesフォルダを作成してtsファイルを作成します。
今回はuntyped.tsという名前で作成します。
untyped.ts内でmp3を宣言することで認識するようになります!

@types/untyped.ts
declare module "*.mp3";

すると、以下の画像のようにエラーが出なくなります!

また、mp3ファイルのインポート時にES6のimportではなくES5のrequireでも可能です!
公式もrequireで書かれてます。

const { sound } = await Audio.Sound.createAsync(require(@assets/sounds/sound.mp3))

ですが今回音を鳴らす実装をするにあたって、音声ファイルだけを引数として渡し共通関数的なものにしたかったので動的に音声ファイルを渡す必要がありました。

React Nativeではrequireは静的な文字列リテラルでのみ使用できます。
(以下参照記事)

https://qiita.com/acro5piano/items/68314e30c565278ae4b4

なので、今回は音声ファイルをimportしたので、typeを宣言する必要がありました!

今後Expo AVについて

https://expo.dev/changelog/2024/05-07-sdk-51

終わりに

今回の実装は、思っていたほど難しくありませんでした。今回Expo AVを始めて触った僕にはすごく良い経験になりました。触ったことないライブラリは新しいことが学べるので最高ですね!

最後に、今回の実装が同じような悩みを持つ誰かの役に立てばと思います!React Nativeで音を鳴らしたいと思っている方、ぜひExpo AVを試してみてください!

Gemcook Tech Blog
Gemcook Tech Blog

Discussion