RORK LABEN
TOOLING — Rorkの開発者向けリポジトリが動き続けています。rork-xcodeが7月16日、rork-deviceが7月15日、rork-plistが7月13日に更新されましたOPUS46 — RorkでClaude Opus 4.6が稼働しています。Rork MaxはClaude Codeを土台にアプリを組み立てる設計ですSIM — ブラウザ上で動くクラウドのiOSシミュレータを備え、実機へのインストールは1クリック、App Storeへの提出は2クリックと案内されていますMAX — Rork MaxはReact Nativeではなく純粋なSwiftを出力します。iPhone・iPad・Apple Watch・Apple TV・Vision Pro、そしてiMessageまでが射程ですNATIVE — HealthKit、ARKitとLiDAR、NFC、Dynamic Island、Live Activities、Metalによる3D、Core MLのオンデバイス推論まで扱えますSEED — RorkはLeft Lane Capitalが主導する1,500万ドルのシードラウンドを実施し、Peak XVとa16z Speedrunが参加しましたTOOLING — Rorkの開発者向けリポジトリが動き続けています。rork-xcodeが7月16日、rork-deviceが7月15日、rork-plistが7月13日に更新されましたOPUS46 — RorkでClaude Opus 4.6が稼働しています。Rork MaxはClaude Codeを土台にアプリを組み立てる設計ですSIM — ブラウザ上で動くクラウドのiOSシミュレータを備え、実機へのインストールは1クリック、App Storeへの提出は2クリックと案内されていますMAX — Rork MaxはReact Nativeではなく純粋なSwiftを出力します。iPhone・iPad・Apple Watch・Apple TV・Vision Pro、そしてiMessageまでが射程ですNATIVE — HealthKit、ARKitとLiDAR、NFC、Dynamic Island、Live Activities、Metalによる3D、Core MLのオンデバイス推論まで扱えますSEED — RorkはLeft Lane Capitalが主導する1,500万ドルのシードラウンドを実施し、Peak XVとa16z Speedrunが参加しました
記事一覧/開発ツール
開発ツール/2026-03-28中級

Rork で音楽プレイヤーアプリを作る — expo-av によるオーディオ再生の実装ガイド

Rork と expo-av を使って音楽プレイヤーアプリを構築する方法を解説。再生・一時停止・シークバー・プレイリスト管理まで、実践的なコード例とともにステップバイステップで紹介します。

Rork515expo-av5オーディオ再生音楽プレイヤーReact Native209アプリ開発77

取り組みの背景 — なぜ音楽プレイヤーアプリなのか

音楽プレイヤーは、モバイルアプリ開発において学びの宝庫です。再生・一時停止といった基本操作から、シークバーの実装、プレイリスト管理、バックグラウンド再生まで、アプリ開発に必要なスキルが凝縮されています。

Rork を使えば、AI の力を借りながら React Native ベースのオーディオプレイヤーアプリを効率的に構築できます。Expo のオーディオライブラリ expo-av を活用して、実用的な音楽プレイヤーアプリを一から作る方法を順を追って整理していきます。

この記事で学べること:

  • expo-av を使ったオーディオファイルの読み込みと再生制御
  • シークバー・音量調整などの UI コンポーネントの実装
  • プレイリストの管理と曲の切り替え機能
  • バックグラウンド再生の設定方法

対象読者は、Rork でのアプリ開発に少し慣れてきた方や、React Native の基本的な概念を理解している方です。

環境準備と expo-av のセットアップ

まず、Rork で新しいプロジェクトを作成します。Rork のプロンプトに「音楽プレイヤーアプリを作りたい」と伝えれば、基本的なプロジェクト構造を自動で生成してくれます。

expo-av は Expo SDK に含まれるオーディオ・ビデオ再生ライブラリで、Rork のプロジェクトでそのまま利用できます。手動でインストールする場合は以下のコマンドを実行します。

# expo-av のインストール
npx expo install expo-av

次に、オーディオ再生の初期設定を行います。アプリ起動時に Audio.setAudioModeAsync を呼び出して、再生モードを構成します。

// hooks/useAudioSetup.ts
import { Audio } from 'expo-av';
import { useEffect } from 'react';
 
export function useAudioSetup() {
  useEffect(() => {
    async function configureAudio() {
      await Audio.setAudioModeAsync({
        allowsRecordingIOS: false,
        staysActiveInBackground: true,  // バックグラウンド再生を有効化
        playsInSilentModeIOS: true,     // サイレントモードでも再生
        shouldDuckAndroid: true,         // 他のアプリの音声を一時的に下げる
      });
    }
    configureAudio();
  }, []);
}
 
// 期待される動作:
// - アプリ起動時にオーディオモードが自動設定される
// - バックグラウンドでも音楽が途切れない
// - iOS のサイレントスイッチをオンにしても音が出る

この設定は一度だけ実行すれば十分です。staysActiveInBackground: true を指定することで、ユーザーがアプリを閉じても音楽が再生され続けます。

オーディオ再生の基本 — 読み込みから再生・停止まで

expo-av の Audio.Sound オブジェクトを使って、オーディオファイルの読み込みと再生を制御します。ここでは、カスタムフックとして再利用しやすい形にまとめます。

// hooks/useAudioPlayer.ts
import { useState, useCallback, useRef } from 'react';
import { Audio, AVPlaybackStatus } from 'expo-av';
 
interface Track {
  id: string;
  title: string;
  artist: string;
  uri: string;       // ローカルまたはリモートの音声ファイルURL
  artwork?: string;   // アルバムアートのURL
}
 
interface PlayerState {
  isPlaying: boolean;
  isLoaded: boolean;
  positionMillis: number;
  durationMillis: number;
}
 
export function useAudioPlayer() {
  const soundRef = useRef<Audio.Sound | null>(null);
  const [playerState, setPlayerState] = useState<PlayerState>({
    isPlaying: false,
    isLoaded: false,
    positionMillis: 0,
    durationMillis: 0,
  });
 
  // 再生状態の更新コールバック
  const onPlaybackStatusUpdate = useCallback((status: AVPlaybackStatus) => {
    if (status.isLoaded) {
      setPlayerState({
        isPlaying: status.isPlaying,
        isLoaded: true,
        positionMillis: status.positionMillis,
        durationMillis: status.durationMillis ?? 0,
      });
    }
  }, []);
 
  // トラックの読み込み
  const loadTrack = useCallback(async (track: Track) => {
    // 既存のサウンドオブジェクトを解放
    if (soundRef.current) {
      await soundRef.current.unloadAsync();
    }
 
    const { sound } = await Audio.Sound.createAsync(
      { uri: track.uri },
      { shouldPlay: false },
      onPlaybackStatusUpdate
    );
    soundRef.current = sound;
  }, [onPlaybackStatusUpdate]);
 
  // 再生 / 一時停止の切り替え
  const togglePlayPause = useCallback(async () => {
    if (!soundRef.current) return;
    if (playerState.isPlaying) {
      await soundRef.current.pauseAsync();
    } else {
      await soundRef.current.playAsync();
    }
  }, [playerState.isPlaying]);
 
  // 指定位置へシーク
  const seekTo = useCallback(async (positionMillis: number) => {
    if (!soundRef.current) return;
    await soundRef.current.setPositionAsync(positionMillis);
  }, []);
 
  return { playerState, loadTrack, togglePlayPause, seekTo };
}

ポイントは onPlaybackStatusUpdate コールバックです。expo-av は再生位置が変わるたびにこのコールバックを呼び出すため、UI のリアルタイム更新が可能になります。

シークバーと再生コントロール UI の実装

音楽プレイヤーの操作性を左右するのが、シークバーと再生コントロールの UI です。ユーザーが直感的に操作できるインターフェースを実装しましょう。

// components/PlayerControls.tsx
import React from 'react';
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
import Slider from '@react-native-community/slider';
import { Ionicons } from '@expo/vector-icons';
 
interface PlayerControlsProps {
  isPlaying: boolean;
  positionMillis: number;
  durationMillis: number;
  onTogglePlayPause: () => void;
  onSeek: (position: number) => void;
  onPrevious: () => void;
  onNext: () => void;
}
 
// ミリ秒を "MM:SS" 形式にフォーマットする関数
function formatTime(millis: number): string {
  const totalSeconds = Math.floor(millis / 1000);
  const minutes = Math.floor(totalSeconds / 60);
  const seconds = totalSeconds % 60;
  return `${minutes}:${seconds.toString().padStart(2, '0')}`;
}
 
export function PlayerControls({
  isPlaying,
  positionMillis,
  durationMillis,
  onTogglePlayPause,
  onSeek,
  onPrevious,
  onNext,
}: PlayerControlsProps) {
  return (
    <View style={styles.container}>
      {/* シークバー */}
      <Slider
        style={styles.slider}
        minimumValue={0}
        maximumValue={durationMillis}
        value={positionMillis}
        onSlidingComplete={onSeek}
        minimumTrackTintColor="#1DB954"
        maximumTrackTintColor="#535353"
        thumbTintColor="#1DB954"
      />
      {/* 経過時間 / 残り時間 */}
      <View style={styles.timeRow}>
        <Text style={styles.timeText}>
          {formatTime(positionMillis)}
        </Text>
        <Text style={styles.timeText}>
          -{formatTime(durationMillis - positionMillis)}
        </Text>
      </View>
      {/* 再生コントロールボタン */}
      <View style={styles.controls}>
        <TouchableOpacity onPress={onPrevious}>
          <Ionicons name="play-skip-back" size={32} color="#fff" />
        </TouchableOpacity>
        <TouchableOpacity
          onPress={onTogglePlayPause}
          style={styles.playButton}
        >
          <Ionicons
            name={isPlaying ? 'pause-circle' : 'play-circle'}
            size={64}
            color="#1DB954"
          />
        </TouchableOpacity>
        <TouchableOpacity onPress={onNext}>
          <Ionicons name="play-skip-forward" size={32} color="#fff" />
        </TouchableOpacity>
      </View>
    </View>
  );
}
 
const styles = StyleSheet.create({
  container: { padding: 20 },
  slider: { width: '100%', height: 40 },
  timeRow: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    marginTop: -8,
  },
  timeText: { color: '#b3b3b3', fontSize: 12 },
  controls: {
    flexDirection: 'row',
    justifyContent: 'center',
    alignItems: 'center',
    gap: 32,
    marginTop: 16,
  },
  playButton: { marginHorizontal: 24 },
});
 
// 期待される表示:
// - 緑色のシークバーが曲の進行に合わせて動く
// - 経過時間と残り時間が "M:SS" 形式で表示される
// - 前の曲・再生/停止・次の曲の3つのボタンが並ぶ

@react-native-community/slider はスムーズなドラッグ操作を提供してくれるライブラリです。onSlidingComplete を使うことで、ユーザーがスライダーを離したタイミングでシーク処理を実行できます。

プレイリスト管理と曲の切り替え

複数の曲を順番に再生するプレイリスト機能は、音楽プレイヤーの核となる部分です。現在再生中の曲のインデックスを管理し、次の曲・前の曲への切り替えを実装します。

// hooks/usePlaylist.ts
import { useState, useCallback } from 'react';
 
interface Track {
  id: string;
  title: string;
  artist: string;
  uri: string;
  artwork?: string;
}
 
export function usePlaylist(tracks: Track[]) {
  const [currentIndex, setCurrentIndex] = useState(0);
  const [isShuffled, setIsShuffled] = useState(false);
  const [repeatMode, setRepeatMode] = useState<'off' | 'all' | 'one'>('off');
 
  const currentTrack = tracks[currentIndex] ?? null;
 
  // 次の曲へ進む
  const playNext = useCallback(() => {
    setCurrentIndex((prev) => {
      if (repeatMode === 'one') return prev;  // 1曲リピート
      const next = prev + 1;
      if (next >= tracks.length) {
        return repeatMode === 'all' ? 0 : prev;  // 全曲リピートまたは停止
      }
      return next;
    });
  }, [tracks.length, repeatMode]);
 
  // 前の曲へ戻る
  const playPrevious = useCallback(() => {
    setCurrentIndex((prev) => {
      const prevIndex = prev - 1;
      if (prevIndex < 0) {
        return repeatMode === 'all' ? tracks.length - 1 : 0;
      }
      return prevIndex;
    });
  }, [tracks.length, repeatMode]);
 
  // シャッフル切り替え
  const toggleShuffle = useCallback(() => {
    setIsShuffled((prev) => !prev);
  }, []);
 
  // リピートモード切り替え(off → all → one → off)
  const toggleRepeat = useCallback(() => {
    setRepeatMode((prev) => {
      if (prev === 'off') return 'all';
      if (prev === 'all') return 'one';
      return 'off';
    });
  }, []);
 
  return {
    currentTrack,
    currentIndex,
    isShuffled,
    repeatMode,
    playNext,
    playPrevious,
    toggleShuffle,
    toggleRepeat,
  };
}

リピートモードは「オフ → 全曲リピート → 1曲リピート」の3段階で切り替わります。シャッフル機能を本格的に実装する場合は、Fisher-Yates アルゴリズムでトラック配列をランダムに並べ替える処理を追加するとよいでしょう。

Rork アプリでのアニメーション実装について詳しく知りたい方は、Rork アニメーション実装ガイド も参考になります。

バックグラウンド再生と通知コントロール

音楽プレイヤーアプリにとって、バックグラウンド再生は必須機能です。ユーザーがアプリを閉じても音楽が途切れないようにするには、いくつかの追加設定が必要です。

iOS の設定

app.json(または app.config.js)の ios.infoPlist に、バックグラウンドモードの指定を追加します。

{
  "expo": {
    "ios": {
      "infoPlist": {
        "UIBackgroundModes": ["audio"]
      }
    }
  }
}

Android の設定

Android ではフォアグラウンドサービスを使ってバックグラウンド再生を維持します。expo-av の staysActiveInBackground: true 設定に加えて、app.json に以下を追加します。

{
  "expo": {
    "android": {
      "permissions": ["FOREGROUND_SERVICE"]
    }
  }
}

ロック画面の再生コントロール

ロック画面やコントロールセンターに再生情報を表示するには、expo-avAudio.Sound が自動的にシステムのメディアセッションと連携します。ただし、アルバムアートやトラック情報をロック画面に表示したい場合は、expo-media-libraryreact-native-track-player のような専用ライブラリの導入を検討してください。

// より高度なメディアコントロールが必要な場合の参考構成
// react-native-track-player を使う場合:
//
// import TrackPlayer from 'react-native-track-player';
//
// await TrackPlayer.setupPlayer();
// await TrackPlayer.add({
//   id: 'track-1',
//   url: 'https://example.com/song.mp3',
//   title: '曲のタイトル',
//   artist: 'アーティスト名',
//   artwork: 'https://example.com/artwork.jpg',
// });
// await TrackPlayer.play();

react-native-track-player は、ロック画面コントロール・通知バー・Apple CarPlay / Android Auto など、プラットフォーム固有のメディア機能をフルサポートしています。本格的な音楽プレイヤーアプリを目指す場合は、こちらへの移行をおすすめします。

再利用可能なカスタムフックの設計パターンについては、Rork カスタムフック活用ガイド で詳しく解説しています。

まとめ

ここではRork と expo-av を使って音楽プレイヤーアプリを構築する方法を解説しました。オーディオの初期設定から再生制御、シークバー UI の実装、プレイリスト管理、バックグラウンド再生まで、音楽プレイヤーに必要な機能を網羅的にカバーしました。

ここで紹介したカスタムフック(useAudioPlayerusePlaylist)はそのまま再利用できるので、ぜひ自分のプロジェクトに取り入れてみてください。UI のデザインやアニメーションをさらに磨きたい方には、Rork アプリの UX デザインパターン完全ガイド がおすすめです。

シェア

お読みいただきありがとうございます

Rork Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

開発ツール2026-07-15
Rorkの癒しアプリにスリープタイマーを付けたら、フェードアウトが実際の音とズレた話
Rorkが生成するスリープタイマーは、壁時計のsetIntervalで音量を下げます。数値上は30分でも、実際の音はもっと早く消えたり不自然に途切れたりします。フェードを再生位置で駆動し直す実装と、対数カーブ・停止処理・画面ロック時の限界までを実運用の視点でまとめました。
開発ツール2026-06-19
Rork が生成した画面に、壊さないための最小限の自動テストを足す
Rork で新しい画面を1つ足したつもりが、離れた場所の課金判定が静かに壊れていた——そんな事故を防ぐために、生成コードへ最小限の自動テストを後付けする手順をまとめました。Jest と React Native Testing Library で、壊れると痛い3か所だけを守る現実的な設計です。
開発ツール2026-05-13
Rork で癒し系アプリのアンビエントサウンドを実装して詰まった3つのこと
癒し・瞑想系アプリのアンビエントサウンド実装でRorkが苦手な3つのパターンを解説。ループ途切れ・iOSサイレントモード・バッテリー消耗の問題をexpo-avで解決する実践コード付き。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →