달달한 스토리

728x90
반응형

 

참으로 오랜만에 글을 쓴다.

 

오랜만에 글을 쓴다는 말도 자주 사용하는 말이다.

 

TIL은 매일매일 써줘야 하는데 최근에 회사 업무에 집중한 나머지..

 

역시나 아직 나는 부족한가 보다.

 

오늘은 flutter에서 외부 음악(유튜브 음악, 멜론 음악)이 재생될 때,

 

앱 내에서의 재생(음성, 음악)을 실행하면, 외부음악이 잠시 꺼지고,

 

앱 내의 음악이나 음성을 모두 다 재생하고 끝냈을 때, 외부 음악이 다시 켜지는

 

이 복잡한 과정을 컨트롤할 수 있는 패키지를 소개하겠다.

 

첫 번째 패키지는

https://pub.dev/packages/audio_session

 

audio_session | Flutter Package

Sets the iOS audio session category and Android audio attributes for your app, and manages your app's audio focus, mixing and ducking behaviour.

pub.dev

audio_ssesion이다.

 

audio_ssesion은 오디오가 (전화 통화나 외부 음악 등등)

 

꺼질 상황에 따라 callback 메서드를 호출할 수 있는 리스너를 제공해주는 패키지입니다.

 

두 번째 패키지는 

 

위에 패키지와 같이 쓸 

 

just_audio 패키지입니다.

 

https://pub.dev/packages/just_audio

 

just_audio | Flutter Package

A feature-rich audio player for Flutter. Loop, clip and concatenate any sound from any source (asset/file/URL/stream) in a variety of audio formats with gapless playback.

pub.dev

 

just_audio는 말 그대로, 정말 오디오의 기능을 실행해 줍니다.

 

가령 재생, 중지, 일시중지 등등

 

여러 기본적인 오디오 기능을 제공해 줍니다.

 

자 이제 이 두 가지를 이용해 외부 음악에 대한 대처를 해보겠습니다.

 

그전에 저 같은 경우는 녹음 한 제 목소리를 flutter_sound라는 별개의

 

패키지를 통해 url파일을 추출하고 사용했습니다.

 

이 글에서는 아쉽게도 따로 다루지 않고 있습니다.

 

추후에 따로 다뤄보겠습니다.

 

(pubspec.yaml에 적용하는 것도 생략하겠습니다)

 


해결책

 

pubspec.yaml에 두 개의 패키지를 적용시킨 다음에 

 

쓰기 좋게 하나의 클래스의 담아서 사용하는 것을 보여드리겠습니다.

 

import 'package:just_audio/just_audio.dart' as ja;
import 'package:audio_session/audio_session.dart';

우선 두 개의 패키지를 import 해줍니다.

 

just_audio를 사용하기 위에 ja로 타입 캐스팅을 해주어 사용합니다.

 

그리고 아래 클래스를 전체적으로 보여드리겠습니다

 

class AudioPlayerUtil {
  AudioSession audioSessions;
  ja.AudioPlayer player = ja.AudioPlayer(
    handleInterruptions: false,
    androidApplyAudioAttributes: false,
    handleAudioSessionActivation: false,
  );

  static final AudioPlayerUtil _instance = AudioPlayerUtil._internal();

  factory AudioPlayerUtil() => _instance;

  AudioPlayerUtil._internal() {
    _audioSessionConfigure();
  }

  getPlaybackFn(String url) async {
    await player.setUrl(url);
    _handleInterruptions();
  }

  stop() async {
    await player.stop();
    await audioSessions.setActive(false);
  }

  _handleInterruptions() async {
    player.playing ? await player.stop() : await player.play();
    await audioSessions.setActive(player.playing);
  }

  _audioSessionConfigure() => AudioSession.instance.then((audioSession) async => await audioSession
      .configure(AudioSessionConfiguration(
        avAudioSessionCategory: AVAudioSessionCategory.playback,
        avAudioSessionCategoryOptions: AVAudioSessionCategoryOptions.none,
        avAudioSessionMode: AVAudioSessionMode.defaultMode,
        avAudioSessionRouteSharingPolicy: AVAudioSessionRouteSharingPolicy.defaultPolicy,
        avAudioSessionSetActiveOptions: AVAudioSessionSetActiveOptions.notifyOthersOnDeactivation,
        androidAudioAttributes: const AndroidAudioAttributes(
          contentType: AndroidAudioContentType.music,
          flags: AndroidAudioFlags.none,
          usage: AndroidAudioUsage.media,
        ),
        androidAudioFocusGainType: AndroidAudioFocusGainType.gainTransient,
        androidWillPauseWhenDucked: true,
      ))
      .then((_) => audioSessions = audioSession));
}

저 같은 경우는 제가 만드는 앱이 다른 곳에서도 소리가 들릴 수 있게 했기 때문에

 

싱글톤으로 클래스를 만들었습니다.

 

다른 분들은 그냥 일반 클래스로 만드시면 됩니다.

 

우선 AudioSession의 객체를 선언해 줍니다.

 

동시에 ja에 AudioPlayer 클래스를 호출하여

 

다음과 같은 파라미터를 넣어 audio player에 설정을 해줍니다.

 

handleInterruptions는 audio를 수동으로 조작할 건지 아닌지를 묻는 파라미터입니다.

이것을 false로 둡니다.

 

나머지 두 파라미터인 

androidApplyAudioAttributes, handleAudioSessionActivation

 

audio가 자동으로 실행되는 것을 원치 않기에 둘 다 false로 설정합니다.

 

 AudioPlayerUtil._internal() {
    _audioSessionConfigure();
  }

싱글톤 생성자를 통해 audioSessionConfigure을 실행해줍니다.

 

audioSessionConfigure은 외부 음악이 들릴 때, 어떤 반응을 하지 설정하는 메서드입니다.

 

_audioSessionConfigure() => AudioSession.instance.then((audioSession) async => await audioSession
      .configure(AudioSessionConfiguration(
        avAudioSessionCategory: AVAudioSessionCategory.playback,
        avAudioSessionCategoryOptions: AVAudioSessionCategoryOptions.none,
        avAudioSessionMode: AVAudioSessionMode.defaultMode,
        avAudioSessionRouteSharingPolicy: AVAudioSessionRouteSharingPolicy.defaultPolicy,
        avAudioSessionSetActiveOptions: AVAudioSessionSetActiveOptions.notifyOthersOnDeactivation,
        androidAudioAttributes: const AndroidAudioAttributes(
          contentType: AndroidAudioContentType.music,
          flags: AndroidAudioFlags.none,
          usage: AndroidAudioUsage.media,
        ),
        androidAudioFocusGainType: AndroidAudioFocusGainType.gainTransient,
        androidWillPauseWhenDucked: true,
      ))
      .then((_) => audioSessions = audioSession));

이런 식으로 설정하시면 됩니다.

 

하나하나 다 설명드리고 싶지만,

 

제가 2주 전에 배우고 바로 정리하지 못한 탓에 많이 잊어버렸습니다.

 

이것저것 여러 파라미터들을 실행하면 여러 가지 기능 있는 것을 알 수 있습니다.

 

위에 기능은 음악을 잠깐 끄는 기능이지만,

 

파라미터를 조작함에 따라

 

외부 음악과 앱 내부에 음악이 동시에 들리게 하거나,

 

잠시 소리만 줄이게 하거나 등등에 구현이 가능합니다.

 

하지만 그 방법을... 하 죄송합니다. 여러 개 바꿔서 다 시도해 보세요 분명 됩니다 하하...

 

그렇게 설정이 완료되면 audioSessions 변수에 담아줍니다.

 

여기까지 설정이 끝난 것입니다.

 

이제 사용은 이 두 메서드를 통해 할 수 있습니다.

 

 getPlaybackFn(String url) async {
    await player.setUrl(url);
    _handleInterruptions();
  }

  stop() async {
    await player.stop();
    await audioSessions.setActive(false);
  }

내가 가지고 있는 음성 url을 getPlaybackFn 메서드에 파라미터로 넣어주면,

 

우리가 설정한 플레이어에 이 url이 저장되고,

 

_handleInterruptions 메서드가 실행됩니다.

 

_handleInterruptions() async {
    player.playing ? await player.stop() : await player.play();
    await audioSessions.setActive(player.playing);
  }

이 것도 제 앱에 상황에 따라 만든 것인데,

 

player가 실행이 되고 있다면, player를 꺼주고 동시에

 

audioSessions를 setActive 해준다.

 

여기서 

setActive(true)는 외부 음악이 실행 됨을 의미하고,

setActive(false)는 외부음악이 꺼지게 하는 것을 의미한다.

 

setActive 메서드를 통해 우리는 어느 타이밍에 음악을 끌지 컨트롤할 수 있다.

 

위에 stop 메서드 또한 녹음도 끄고, 동시에 외부 음악도 끄게 만들었다.

 

더 나아가 패키지 문서를 보면,

 

상황에 따라 리스너를 사용할 수 있는데,

 

긴급 재난 문자 등 특정 상황에 따른 대응을 할 수 있는 함수가 많지만,

 

나는 필요하지 않아 여기서 다루지 않았다.

 

아마 필요하신 분들은 더 찾아보는 게 좋을 듯하다.

 

오디오 컨트롤 끝!

728x90
반응형

공유하기

facebook twitter kakaoTalk kakaostory naver band
loading