feat!: add audio out-of-band

We'll need to decide on the Enum case for getting the `Type`. Long term it might be better to just not have this to avoid breaking changes.

If we decide to remove that, or how it is currently in the PR with the extended conditions, it's a breaking change.

The changes to `LocalAssetLoader` aren't breaking though, I realise now.

I also removed deprecated things.

Diffs=
1a8b3c7cc feat!: add audio out-of-band (#7037)
99d28e1ac Xxxx randomization updates part 2 (#7097)
a0004fa72 Xxxx support random transitions (#7094)
edac19b06 support randomizing transitions (#7082)

Co-authored-by: Gordon <pggordonhayes@gmail.com>
This commit is contained in:
HayesGordon
2024-04-20 07:12:54 +00:00
parent e34fc4cc41
commit 06265422cc
16 changed files with 154 additions and 43 deletions

View File

@ -1 +1 @@
e64daefffdcd34df6a6cc9f5b54ebc92e0ae89cb
1a8b3c7ccd4bb10f085f2b45bf4685cd23cc0b0f

View File

@ -1,5 +1,8 @@
## 0.13.2
- DEPRECATED: `Extension` and `Type` enum on `FileAsset`. You can create a custom maintained version, see example: https://gist.github.com/HayesGordon/5d37d3fb26f54b2c231760c2c8685963
- BREAKING: Removal of previously deprecated methods `assetResolver` on `RiveFile.network` and class `NetworkAssetResolver`
- Add Audio out-of-band, with examples.
- Support for asset audio volume.
- Fixed an issue with audio decoder in web build.
- Adds `play()`/`pause()` and `isPlaying` to an `Artboard`. This completely stops the artboard from playing (including nested artboards) and stops/starts the animation ticker. Pausing an artboard can be used to reduce resources used for Rive graphics that aren't visible on screen.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
example/assets/lip-sync.riv Normal file

Binary file not shown.

Binary file not shown.

View File

@ -12,6 +12,8 @@ import 'package:rive_example/liquid_download.dart';
import 'package:rive_example/little_machine.dart';
import 'package:rive_example/play_one_shot_animation.dart';
import 'package:rive_example/play_pause_animation.dart';
import 'package:rive_example/rive_audio.dart';
import 'package:rive_example/rive_audio_out_of_band.dart';
import 'package:rive_example/simple_animation.dart';
import 'package:rive_example/simple_animation_network.dart';
import 'package:rive_example/simple_machine_listener.dart';
@ -43,7 +45,7 @@ class RiveExampleApp extends StatefulWidget {
}
class _RiveExampleAppState extends State<RiveExampleApp> {
// Example animations
// Examples
final _pages = [
const _Page('Simple Animation - Asset', SimpleAssetAnimation()),
const _Page('Simple Animation - Network', SimpleNetworkAnimation()),
@ -64,6 +66,8 @@ class _RiveExampleAppState extends State<RiveExampleApp> {
const _Page('Event Open URL Button', EventOpenUrlButton()),
const _Page('Event Sounds', EventSounds()),
const _Page('Event Star Rating', EventStarRating()),
const _Page('Rive Audio', RiveAudioExample()),
const _Page('Rive Audio [Out-of-Band]', RiveAudioOutOfBandExample()),
];
@override

View File

@ -0,0 +1,20 @@
import 'package:flutter/material.dart';
import 'package:rive/rive.dart';
class RiveAudioExample extends StatelessWidget {
const RiveAudioExample({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Rive Audio'),
),
body: const RiveAnimation.asset(
"assets/lip-sync.riv",
artboard: 'Lip_sync_2',
stateMachines: ['State Machine 1'],
),
);
}
}

View File

@ -0,0 +1,48 @@
import 'package:flutter/material.dart';
import 'package:rive/rive.dart';
class RiveAudioOutOfBandExample extends StatefulWidget {
const RiveAudioOutOfBandExample({super.key});
@override
State<RiveAudioOutOfBandExample> createState() =>
_RiveAudioOutOfBandExampleState();
}
class _RiveAudioOutOfBandExampleState extends State<RiveAudioOutOfBandExample> {
@override
void initState() {
super.initState();
_loadRiveFile();
}
RiveFile? _riveAudioAssetFile;
Future<void> _loadRiveFile() async {
final riveFile = await RiveFile.asset(
'assets/ping_pong_audio_demo.riv',
assetLoader: LocalAssetLoader(audioPath: 'assets/audio'),
);
setState(() {
_riveAudioAssetFile = riveFile;
});
}
@override
Widget build(BuildContext context) {
if (_riveAudioAssetFile == null) {
return const Center(child: CircularProgressIndicator());
}
return Scaffold(
appBar: AppBar(
title: const Text('Rive Audio [Out-of-Band]'),
),
body: RiveAnimation.direct(
_riveAudioAssetFile!,
stateMachines: const ['State Machine 1'],
fit: BoxFit.cover,
),
);
}
}

View File

@ -1,12 +1,12 @@
name: rive_example
description: A collection of Rive Flutter examples
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
publish_to: "none" # Remove this line if you wish to publish to pub.dev
version: 1.0.0+1
environment:
sdk: '>=2.17.0 <3.0.0'
sdk: ">=2.17.0 <3.0.0"
dependencies:
flutter:
@ -28,3 +28,4 @@ flutter:
assets:
- assets/
- assets/fonts/
- assets/audio/

View File

@ -12,6 +12,7 @@ export 'package:rive/src/rive_core/animation/linear_animation_instance.dart';
export 'package:rive/src/rive_core/animation/loop.dart';
export 'package:rive/src/rive_core/animation/state_machine.dart';
export 'package:rive/src/rive_core/artboard.dart';
export 'package:rive/src/rive_core/assets/audio_asset.dart';
export 'package:rive/src/rive_core/assets/font_asset.dart';
export 'package:rive/src/rive_core/assets/image_asset.dart';
export 'package:rive/src/rive_core/event.dart';

View File

@ -2,10 +2,27 @@ import 'package:rive/src/rive_core/assets/file_asset.dart';
export 'package:rive/src/generated/artboard_base.dart';
/// TODO: do we prefer this, or do we want to wrap our FileAssets
/// into a custom asset class.
const _deprecationExtensionMessage =
'''This Extension is no longer maintained. Similar behaviour can
be re-created with a custom extension.
Example: https://gist.github.com/HayesGordon/5d37d3fb26f54b2c231760c2c8685963
''';
const _deprecationEnumMessage =
'''This Enum is no longer maintained. Similar behaviour can
be re-created with a custom Enum.
Example: https://gist.github.com/HayesGordon/5d37d3fb26f54b2c231760c2c8685963
''';
@Deprecated(_deprecationExtensionMessage)
extension FileAssetExtension on FileAsset {
@Deprecated(_deprecationExtensionMessage)
Extension get extension => _getExtension(fileExtension);
@Deprecated(_deprecationExtensionMessage)
Type get type => _getType(fileExtension);
}
@ -38,6 +55,7 @@ Type _getType(String ext) {
return Type.unknown;
}
@Deprecated(_deprecationEnumMessage)
enum Extension {
otf,
ttf,
@ -47,6 +65,7 @@ enum Extension {
unknown,
}
@Deprecated(_deprecationEnumMessage)
enum Type {
font,
image,

View File

@ -1,6 +1,6 @@
import 'package:flutter/services.dart';
import 'package:http/http.dart' as http;
import 'package:rive/src/asset.dart';
import 'package:rive/rive.dart';
import 'package:rive/src/debug.dart';
import 'package:rive/src/rive_core/assets/file_asset.dart';
import 'package:rive/src/utilities/utilities.dart';
@ -68,17 +68,37 @@ class CDNAssetLoader extends FileAssetLoader {
/// Convenience class for loading assets from the local file system.
///
/// Specify the [fontPath] and [imagePath] to load assets from the asset bundle.
/// Specify the [fontPath], [imagePath], and [audioPath] to load assets from the
/// asset bundle for a specific asset type. Or use [path] as a general
/// fallback instead. [path] will only be used for an asset type if the
/// corresponding asset path is null.
///
/// For example, to provide an audio asset path:
/// ```dart
/// final riveFile = await RiveFile.asset(
/// 'assets/ping_pong_audio_demo.riv',
/// assetLoader: LocalAssetLoader(
/// audioPath: 'assets/some/path',
/// // path: 'assets/some/path', // or provide fallback/general
/// ),
/// );
/// ```
///
/// Be sure to provide the correct path where the file is located.
///
/// If more control is desired, extend [FileAssetLoader] and override [load].
class LocalAssetLoader extends FileAssetLoader {
final String fontPath;
final String imagePath;
final String? audioPath;
final String? fontPath;
final String? imagePath;
final String? path;
final AssetBundle _assetBundle;
LocalAssetLoader({
required this.fontPath,
required this.imagePath,
this.audioPath,
this.fontPath,
this.imagePath,
this.path,
AssetBundle? assetBundle,
}) : _assetBundle = assetBundle ?? rootBundle;
@ -89,17 +109,35 @@ class LocalAssetLoader extends FileAssetLoader {
return false;
}
String? assetPath;
switch (asset.type) {
case Type.unknown:
String filePath;
switch (asset.runtimeType) {
case AudioAsset:
assert(audioPath != null || path != null,
'''Audio asset not found. Be sure to provide either `audioPath` or `path` in `LocalAssetLoader`.''');
if (audioPath == null && path == null) return false;
filePath = audioPath ?? path!;
break;
case FontAsset:
assert(fontPath != null || path != null,
'''Font asset not found. Be sure to provide either `fontPath` or `path` in `LocalAssetLoader`.''');
if (fontPath == null && path == null) return false;
filePath = fontPath ?? path!;
break;
case ImageAsset:
assert(imagePath != null || path != null,
'''Image asset not found. Be sure to provide either `imagePath` or `path` in `LocalAssetLoader`.''');
if (imagePath == null && path == null) return false;
filePath = imagePath ?? path!;
break;
default:
return false;
case Type.image:
assetPath = imagePath + asset.uniqueFilename;
break;
case Type.font:
assetPath = fontPath + asset.uniqueFilename;
break;
}
filePath = filePath.endsWith("/") ? filePath : "$filePath/";
assetPath = filePath + asset.uniqueFilename;
final bytes = await _assetBundle.load(assetPath);
await asset.decode(Uint8List.view(bytes.buffer));
return true;

View File

@ -4,12 +4,6 @@ import 'package:rive/src/debug.dart';
import 'package:rive/src/rive_core/assets/file_asset.dart';
import 'package:rive/src/rive_core/assets/file_asset_contents.dart';
// TODO: Deprecated. Remove this in the next major version (0.12.0).
// ignore: one_member_abstracts
abstract class FileAssetResolver {
Future<Uint8List> loadContents(FileAsset asset);
}
class FileAssetImporter extends ImportStackObject {
final FileAssetLoader? assetLoader;
final FileAsset fileAsset;

View File

@ -339,7 +339,6 @@ class RiveFile {
/// [RiveUnsupportedVersionException] if the version is not supported.
factory RiveFile.import(
ByteData bytes, {
@Deprecated('Use `assetLoader` instead.') FileAssetResolver? assetResolver,
FileAssetLoader? assetLoader,
ObjectGenerator? objectGenerator,
bool loadCdnAssets = true,
@ -428,7 +427,6 @@ class RiveFile {
static Future<RiveFile> network(
String url, {
Map<String, String>? headers,
@Deprecated('Use `assetLoader` instead.') FileAssetResolver? assetResolver,
FileAssetLoader? assetLoader,
bool loadCdnAssets = true,
ObjectGenerator? objectGenerator,
@ -475,18 +473,3 @@ class RiveFile {
Artboard? artboardByName(String name) =>
_artboards.firstWhereOrNull((a) => a.name == name);
}
// TODO: remove in v0.13.0
/// Resolves a Rive asset from the network provided a [baseUrl].
@Deprecated('Use `CallbackAssetLoader` instead. Will be removed in v0.13.0')
class NetworkAssetResolver extends FileAssetResolver {
final String baseUrl;
NetworkAssetResolver(this.baseUrl);
@override
Future<Uint8List> loadContents(FileAsset asset) async {
final res = await http.get(Uri.parse(baseUrl + asset.uniqueFilename));
return Uint8List.view(res.bodyBytes.buffer);
}
}