Giter VIP home page Giter VIP logo

ocarina's Introduction

Ocarina

Ocarina is a simple and easy to use audio package for Flutter. Its goal is to support audio play from local file (from assets, or filesystem), And to support it across all platforms that Flutter runs.

Right now, we only support mobile (Android and iOS) and will eventually supporting Web and Desktop (Linux, MacOS and Windows).

Discord community

If you have any questions, potential feature requests that you want to discuss with us you can join the Blue Fire Discord server.

How to use

Using a file on your assets

final player = OcarinaPlayer(
  asset: 'assets/Loop-Menu.wav',
  package: 'my_package_name',
  loop: true,
  volume: 0.8,
);

await player.load();

Using a file on the device filesystem

final player = OcarinaPlayer(
  filePath: '/SomeWhere/On/The/Device/Loop-Menu.wav',
  loop: true,
  volume: 0.8,
);

await player.load();

Docs

List of all available methods on the player instance

play

Starts playing

pause

Pauses playback

resume

Resume when playback if it was previously paused

stop

Stops the playback, it can be started again by calling play again.

seek(Duration)

Moves the playback postion to the passed Duration

position

Retrieves playback position in milliseconds.

updateVolume(double)

Updates the volume, must be a value between 0 and 1

dispose

Clears the loaded resources in memory, to use the instance again a subsequent call on the load method is required

iOS Delegation

This feature was developed in concert with the development of the AVAudioEngineDevice in twilio_programmable_video version 0.10.0 to address the issue in iOS in which the operating system gives priority to the VoiceProcessingIO Audio Unit, causing output volume of audio files from ocarina to be significantly diminished when used while a call is underway. While this was the reason for introducing this feature to ocarina, it was designed to be agnostic to the nature of the delegate. It is strongly advised that if you are writing your own delegate for iOS, that you seek to mirror the behaviour of ocarina itself as much as is possible so as to reduce the possibility of inconsistent behaviour across platforms.

If you found your way here because ocarina was recommended by twilio_programmable_video, the following modification to your AppDelegate will setup the AVAudioEngineDevice as the delegate for ocarina:

  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    GeneratedPluginRegistrant.register(with: self)

    let audioDevice = AVAudioEngineDevice.getInstance()
    SwiftTwilioProgrammableVideoPlugin.setCustomAudioDevice(
        audioDevice,
        onConnected: audioDevice.onConnected,
        onDisconnected: audioDevice.onDisconnected)
    SwiftOcarinaPlugin.useDelegate(
        load: audioDevice.addMusicNode,
        dispose: audioDevice.disposeMusicNode,
        play: audioDevice.playMusic,
        pause: audioDevice.pauseMusic,
        resume: audioDevice.resumeMusic,
        stop: audioDevice.stopMusic,
        volume: audioDevice.setMusicVolume,
        seek: audioDevice.seekPosition,
        position: audioDevice.getPosition
    )

    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }

Audio Player State Listeners

This feature was also developed in concert with the ongoing development of the audio system for twilio_programmable_video, though it was designed to be agnostic to what other plugins you are using. As such, you can certainly add custom listeners via this mechanism to receive the same notifications. The only requirement is that they have the following signature:

Android:

(url: String, isPlaying: Boolean) -> Unit

iOS

(_ url: String, _ isPlaying: Bool) -> Void

If you wish to use this feature with twilio_programmable_video, simply add the following to your MainActivity.kt.

    private lateinit var PACKAGE_ID: String

    @RequiresApi(Build.VERSION_CODES.O)
    override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        PACKAGE_ID = applicationContext.packageName
        OcarinaPlugin.addListener(PACKAGE_ID, TwilioProgrammableVideoPlugin.getAudioPlayerEventListener());
    }

    override fun cleanUpFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
        super.cleanUpFlutterEngine(flutterEngine)
        OcarinaPlugin.removeListener(PACKAGE_ID)
    }

The benefit of using this feature (on Android) with the twilio_programmable_video is that it will enable that plugin to update the Audio Focus and usage of Bluetooth Sco based upon whether there are active audio players, in addition to an active call.

Alternatively, you can use it with your own listener for your own purposes.

This feature has not been integrated with twilio_programmable_video on iOS, out of preference for usage of the AVAudioEngineDevice as a delegate for ocarina.

If you wish to use this feature on iOS, add the following to your AppDelegate.swift:

    let bundleID = Bundle.main.bundleIdentifier

    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        SwiftOcarinaPlugin.addListener(bundleID!, ocarinaListener)
    }

    override func applicationWillTerminate(_ application: UIApplication) {
        SwiftOcarinaPlugin.removeListener(bundleID!)
    }

    func ocarinaListener(_ id: String, _ isPlaying: Bool) {
        // do things
    }

ocarina's People

Contributors

dave0921 avatar erickzanardo avatar nightscape avatar nohli avatar orestesgaolin avatar wolfenrain avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

ocarina's Issues

Google Play Pre-Launch-Report warning

API Landroid/media/AudioTrack;->getLatency()I
72 occurrences identified. Only unique stack traces are shown.
StrictMode policy violation: android.os.strictmode.NonSdkApiUsedViolation: Landroid/media/AudioTrack;->getLatency()I
	at android.os.StrictMode.lambda$static$1(StrictMode.java:407)
	at android.os.-$$Lambda$StrictMode$lu9ekkHJ2HMz0jd3F8K8MnhenxQ.accept(Unknown Source:2)
	at java.lang.Class.getDeclaredMethodInternal(Class.java)
	at java.lang.Class.getPublicMethodRecursive(Class.java:2079)
	at java.lang.Class.getMethod(Class.java:2066)
	at java.lang.Class.getMethod(Class.java:1693)
	at com.google.android.exoplayer2.audio.AudioTrackPositionTracker.<init>(AudioTrackPositionTracker.java:21)
	at com.google.android.exoplayer2.audio.DefaultAudioSink.<init>(DefaultAudioSink.java:31)
	at com.google.android.exoplayer2.audio.DefaultAudioSink.<init>(DefaultAudioSink.java:5)
	at com.google.android.exoplayer2.audio.DefaultAudioSink.<init>(DefaultAudioSink.java:1)
	at com.google.android.exoplayer2.DefaultRenderersFactory.a(DefaultRenderersFactory.java:20)
	at com.google.android.exoplayer2.DefaultRenderersFactory.a(DefaultRenderersFactory.java:55)
	at com.google.android.exoplayer2.SimpleExoPlayer.<init>(SimpleExoPlayer.java:81)
	at com.google.android.exoplayer2.SimpleExoPlayer.<init>(SimpleExoPlayer.java:16)
	at com.google.android.exoplayer2.SimpleExoPlayer$Builder.build(SimpleExoPlayer.java:28)
	at xyz.erick.ocarina.OcarinaPlayer.load(OcarinaPlayer.java:7)
	at xyz.erick.ocarina.OcarinaPlugin.onMethodCall(OcarinaPlugin.java:141)
	at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:17)
	at io.flutter.embedding.engine.dart.DartMessenger.handleMessageFromDart(DartMessenger.java:57)
	at io.flutter.embedding.engine.FlutterJNI.handlePlatformMessage(FlutterJNI.java:4)
	at android.os.MessageQueue.nativePollOnce(MessageQueue.java)
	at android.os.MessageQueue.next(MessageQueue.java:336)
	at android.os.Looper.loop(Looper.java:174)
	at android.app.ActivityThread.main(ActivityThread.java:7356)
	at java.lang.reflect.Method.invoke(Method.java)
	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
 StrictMode policy violation: android.os.strictmode.NonSdkApiUsedViolation: Landroid/media/AudioTrack;->getLatency()I
	at android.os.StrictMode.lambda$static$1(StrictMode.java:428)
	at android.os.-$$Lambda$StrictMode$lu9ekkHJ2HMz0jd3F8K8MnhenxQ.accept(Unknown Source:2)
	at java.lang.Class.getDeclaredMethodInternal(Class.java)
	at java.lang.Class.getPublicMethodRecursive(Class.java:2075)
	at java.lang.Class.getMethod(Class.java:2063)
	at java.lang.Class.getMethod(Class.java:1690)
	at com.google.android.exoplayer2.audio.AudioTrackPositionTracker.<init>(AudioTrackPositionTracker.java:21)
	at com.google.android.exoplayer2.audio.DefaultAudioSink.<init>(DefaultAudioSink.java:31)
	at com.google.android.exoplayer2.audio.DefaultAudioSink.<init>(DefaultAudioSink.java:5)
	at com.google.android.exoplayer2.audio.DefaultAudioSink.<init>(DefaultAudioSink.java:1)
	at com.google.android.exoplayer2.DefaultRenderersFactory.a(DefaultRenderersFactory.java:20)
	at com.google.android.exoplayer2.DefaultRenderersFactory.a(DefaultRenderersFactory.java:55)
	at com.google.android.exoplayer2.SimpleExoPlayer.<init>(SimpleExoPlayer.java:81)
	at com.google.android.exoplayer2.SimpleExoPlayer.<init>(SimpleExoPlayer.java:16)
	at com.google.android.exoplayer2.SimpleExoPlayer$Builder.build(SimpleExoPlayer.java:28)
	at xyz.erick.ocarina.OcarinaPlayer.load(OcarinaPlayer.java:7)
	at xyz.erick.ocarina.OcarinaPlugin.onMethodCall(OcarinaPlugin.java:141)
	at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:17)
	at io.flutter.embedding.engine.dart.DartMessenger.handleMessageFromDart(DartMessenger.java:57)
	at io.flutter.embedding.engine.FlutterJNI.handlePlatformMessage(FlutterJNI.java:4)
	at android.os.MessageQueue.nativePollOnce(MessageQueue.java)
	at android.os.MessageQueue.next(MessageQueue.java:326)
	at android.os.Looper.loop(Looper.java:160)
	at android.app.ActivityThread.main(ActivityThread.java:6718)
	at java.lang.reflect.Method.invoke(Method.java)
	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)

No audio on physical iPhone 7

  1. Take example app code from https://github.com/erickzanardo/ocarina/tree/master/example
  2. Run the project on physical iPhone 7, iOS latest (14.4).
  3. Either try to play asset audio or download the file to the device and try to play it.

The sound is not playing. I am successfully able to get sound on iOS simulator, iPad Pro 2 and Android devices. I have completely reset my iPhone 7 just in case, but still no luck. I am also attaching the verbose log even though I see nothing suspicious there.

Is iPhone 7 actually supported at all? Anything else I should try to investigate this?

ocarina.log

Created a version that can provide currentTime/position and allows users to set the number of loops

Excuse my extreme lack of GitHub etiquette, I'm sure this is the wrong way to go about this, but I made an update to the plugin that allows users to call position(), which functions in the same fashion as it does on audioPlayers, and also a "loops" parameter in the constructor that specifies how many loops the audioPlayer should go through.

Is this repo still active? Should I upload the files?

Background Notifications

Hi, first of all ๐Ÿฅ‡ its great about solving the annoying gape in the loop,
Can u plz add background notifications the the player with ability to control what buttons(play,pause,stop) I want to show?
Thank U :)

Issue with the volume parameter (android device)

Looks like the "volume" property doesn't affect the sound output on the device (tested on virtual and physic device).
Even when set to 0, the music keeps playing:

  Future<OcarinaPlayer> play(Audio audio) async {
    OcarinaPlayer op = OcarinaPlayer(
        asset: DEFAULT_DIRECTORY + audio.file,
        loop: audio.loop,
        volume: 0);
    await op.load();
    await op.play;
    return op;
  }

Even calling updateVolume doesn't do the trick

Error when trying to play

Hey,

I have downloaded and used Ocarina as you suggested and I can get local assets to play but when I pass it a local file it always fails with the following error:
NoSuchMethodError (NoSuchMethodError: The method 'play' was called on null.
Receiver: null
Tried calling: play())

Any idea what my issue may be, I am a newbie but I am definitely passing a valid local file path in the apps document directory. The Load file certainly does not complain either just the play command.

Thanks in advance
Alan

Crashes on Android

When using load() method on Android, the app crashes with the following stacktrace:

Fatal Exception: java.lang.NoClassDefFoundError
Failed resolution of: Lcom/google/android/exoplayer2/source/ExtractorMediaSource;
xyz.erick.ocarina.AssetOcarinaPlayer.extractMediaSourceFromUri
Caused by java.lang.ClassNotFoundException
Didn't find class "com.google.android.exoplayer2.source.ExtractorMediaSource" on path: DexPathList[[zip file "/data/app/~~oMFEGBoML4tbSPBBEah5RQ==/app.gesso.android-vn5Sl0AXHKbWvevXMSpxQw==/base.apk"],nativeLibraryDirectories=[/data/app/~~oMFEGBoML4tbSPBBEah5RQ==/app.gesso.android-vn5Sl0AXHKbWvevXMSpxQw==/lib/x86, /data/app/~~oMFEGBoML4tbSPBBEah5RQ==/app.gesso.android-vn5Sl0AXHKbWvevXMSpxQw==/base.apk!/lib/x86, /system/lib, /system_ext/lib, /product/lib]]

dalvik.system.BaseDexClassLoader.findClass (BaseDexClassLoader.java:202)
java.lang.ClassLoader.loadClass (ClassLoader.java:312)

xyz.erick.ocarina.AssetOcarinaPlayer.extractMediaSourceFromUri (OcarinaPlugin.kt:131)
xyz.erick.ocarina.OcarinaPlayer.load (OcarinaPlugin.kt:89)
xyz.erick.ocarina.OcarinaPlugin.load (OcarinaPlugin.kt:209)
xyz.erick.ocarina.OcarinaPlugin.onMethodCall (OcarinaPlugin.kt:174)
io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage (MethodChannel.java:233)
io.flutter.embedding.engine.dart.DartMessenger.handleMessageFromDart (DartMessenger.java:84)
io.flutter.embedding.engine.FlutterJNI.handlePlatformMessage (FlutterJNI.java:822)

Seems like it may be caused by deprecation of ExtractorMediaSource

Doesn't play audio from '/storage/emulated/0' and its sub-directories

I have followed ocarina's official example from lib/main.dart:

_ElevatedButton(
child: Text("Play asset audio"),
onPressed: () async {
final player = OcarinaPlayer(
asset: 'storage/emulated/0/Quran.mp3', loop: _loop);
setState(() {
player = player;
});
}),

This snippet fails to load an MP3 file that exists in the described filesystem and can be played by YouTube Music Player locally .

Kindly, have a look .

Updates causing issues

Hi,

All was working fine yesterday until I updated flutter and android studio to latest versions.
Issues with ocarina are now causing app to fail in run for both debug and release modes. I'm using ocarina 0.0.3 for info.

Console is splitting out a bunch of redeclarations and then:
None of the following functions can be called with the arguments supplied:
public constructor AssetOcarinaPlayer(url: String, volume: Double, loop: Boolean, context: Context) defined in xyz.erick.ocarina.AssetOcarinaPlayer
public constructor AssetOcarinaPlayer(url: String, volume: Double, loop: Boolean, context: Context, flutterAssets: FlutterPlugin.FlutterAssets) defined in xyz.erick.ocarina.AssetOcarinaPlayer

What went wrong:
Execution failed for task ':ocarina:compileDebugKotlin'.

I know I've been loading assets and using ocarina methods correctly as been using the package since v0.0.1 with no issues to date, until this update.

Flagging and hopefully an easy fix.

Thanks

Audio loop stopped when different audio starts

I am working on an app that requires me to layer audio files. Specifically: short audio snippets as well as a background music that can be toggled.

For the main audio snippets I use the audioplayers package. As it uses up to 50 different audio snippets and they have to be played in low latency and using the stayAwake (which is lovely btw)
To get the background music as a seamless loop I came to this package, which works really well on my iOS simulator and my android phone. However when I test this on a physical iOS device the background audio stops as soon as the first audio snippet comes up. I can also get it to stop playing audio snippets and just the background music. However layering these 2 is impossible. One audio seems to duck the other one.

As a solution I'm open to using Ocarina for all sounds, but it seems to be targeted at just looping audio, rather than loading all sorts of different sounds.

Linux support

Add support for the Linux platform.

I know that all platforms are planned in the future, so this task issue more as a feature request as well as a way for people to follow later progress on the feature.

isPlaying state is needed

Hi! there, Thank you for this plugin. I tried a lot of plugins but none of them support gapless loop. Finally tried ocarina and it worked. But I'm having a problem where I need whether the audio is playing or not. So is there any way I can get something like isPlaying?

expose a method to check if player is Loaded ...

Hey Erick, thank you for this plugin. I can confirm so far testing in iOS it works perfectly and I have a gapless playback.

I use Ocarina in a custom AudioClass that has other functionality inbuilt. I would supply an asset to this custom class and call Ocarina methods to play/pause/resume accordingly.

To make better - would be great if :

1- You expose a method to check if the player is already loaded :

bool isLoaded(){
 return (_id!=null);
}

This is required because no pause/play/resume etc method in Ocarina can be called unless the Player has been loaded and we need a way to instantiate and prepare Ocarina player if its used inside another class and the asset/filepath is supplied in the constructor of a custom class. ie.


class AudioLoop{
  bool playMusic = false;
  final String assetToPlay;
  final OcarinaPlayer _audioPlayer;
 

  AudioLoop({@required this.assetToPlay})
      : _audioPlayer = OcarinaPlayer(asset: assetToPlay, loop: true) {
    // Created but not loaded and you cannot call _audioPlayer.load() here
  }

  play({bool play: false}) async {
    playMusic = play;
    if (playMusic )
   {
/// Would be better to have :
/// if (!_audioPlayer.isLoaded()){
///await _audioPlayer.load();
///} 
       await _audioPlayer.load(); 
      _audioPlayer.play();
    }
  }
}

2- Unless you want to allow load() to be repeatably called, we optimise Load() so it checks if _id is null and only loads inits if this is the case :

Future<void> load() async {
    if (_id!=null) return;

_id = await  _channel.invokeMethod('load', {
      'url': asset ?? filePath,
      'volume': volume,
      'loop': loop,
      'isAsset': asset != null,
    });
  }

3- Add a dispose so we can cleanup resources

Does this plugin support gapless sound?

Hello, I was using your gapless sound plugin and wanted to know if this plugin also supports gapless looping sound? I had great results with previous plugin :)

Doesnt work in Android

Unfortunately there are issues trying to run even the sample in Android.

E/flutter ( 4436): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: PlatformException(error, lateinit property context has not been initialized, null)

[iOS] Background music is stopped

Expected behaviour
I want to play the asset audio with ocarina library without stoping background music (e.g. Spotify)

Actual behaviour
The background music is currently stoped and even does not restart after playing the audio from ocarina.

How to get a single instance of the player

Hi Erick - I'm trying to use the plugin you have written and cannot find a way to pass a single instance of the player with an asset already loaded to any widget in my tree (via lets say Provider).

If I try and create a simple class such as :

class CarinaPlayer {
 
  final OcarinaPlayer carinaPlayer;

 
  CarinaPlayer.fromCarinaPlayer(OcarinaPlayer player)
      : carinaPlayer = player:

.. etc other methods that act on carinaPlayer
}
 

and use like this :

final player= OcarinaPlayer(asset: 'my asset file', loop: true);
await player.load();
var cp=CarinaPlayer.fromCarinaPlayer(player);

... and then we pass 'cp' to any widget in our tree etc ...

When I perform the lines above the player doesnt play , there are a few errors stating the that player needs to be initialised first and there is another strange error before this ''lateinit property context has not been initialized"

I tried to run the sample and this also doesnt work for me.

Feature request: Add 'respectSilence'

Recently (maybe with iOS 13.6), something has changed...when the phone is muted, Ocarina doesn't play any more. So users either think, the audio is broken, or complain about this change, because they would like audio during mediation, but the rest of the phone muted.
It would be really cool if there could be a variable to play audio anyway.

And thanks for this plugin, it works really well.

Resume after stops replays

Hi there,

I could not see a way to detect whether the player was actually playing or not and need a way to pause and resume sounds. In Ocarina however if you play a sound and the sound ends if you pause and resume it the resume will replay the sound when it should not as it was not playing.

So it would be nice if either resume worked as it should and not restart the sound or a way to detect if the sound is actually still playing is made available.

Thanks
Alan

Support for web

Add ocarina support for flutter web, and support all the current set of features supported on mobile.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.