piano.dart 4.13 KB
Newer Older
1 2 3 4
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
import 'dart:async';

John McCutchan's avatar
John McCutchan committed
7
import 'package:mojo/mojo/url_response.mojom.dart';
8
import 'package:sky_services/media/media.mojom.dart';
9 10 11
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
12 13 14 15 16 17 18 19 20

// All of these sounds are marked as public domain at soundbible.
const String chimes = "http://soundbible.com/grab.php?id=2030&type=wav";
const String chainsaw = "http://soundbible.com/grab.php?id=1391&type=wav";
const String stag = "http://soundbible.com/grab.php?id=2073&type=wav";
const String frogs = "http://soundbible.com/grab.php?id=2033&type=wav";
const String rattle = "http://soundbible.com/grab.php?id=2037&type=wav";
const String iLoveYou = "http://soundbible.com/grab.php?id=2045&type=wav";

Hixie's avatar
Hixie committed
21 22
class PianoKey {
  PianoKey(this.color, this.soundUrl);
23 24 25

  final Color color;
  final String soundUrl;
Hixie's avatar
Hixie committed
26

27 28 29
  final MediaPlayerProxy player = new MediaPlayerProxy.unbound();

  bool get isPlayerOpen => player.impl.isOpen;
30

Adam Barth's avatar
Adam Barth committed
31
  void down() {
32
    if (!isPlayerOpen) return;
33 34 35 36
    player.ptr.seekTo(0);
    player.ptr.start();
  }

Adam Barth's avatar
Adam Barth committed
37
  void up() {
38
    if (!isPlayerOpen) return;
39 40
    player.ptr.pause();
  }
41 42 43 44 45 46 47 48 49 50 51

  Future load(MediaServiceProxy mediaService) async {
    try {
      mediaService.ptr.createPlayer(player);
      UrlResponse response = await fetchUrl(soundUrl);
      await player.ptr.prepare(response.body);
    } catch (e) {
      print("Error: failed to load sound file $soundUrl");
      player.close();
    }
  }
52 53
}

54
class PianoApp extends StatelessComponent {
Hixie's avatar
Hixie committed
55 56 57 58 59 60 61
  final List<PianoKey> keys = <PianoKey>[
    new PianoKey(Colors.red[500], chimes),
    new PianoKey(Colors.orange[500], chainsaw),
    new PianoKey(Colors.yellow[500], stag),
    new PianoKey(Colors.green[500], frogs),
    new PianoKey(Colors.blue[500], rattle),
    new PianoKey(Colors.purple[500], iLoveYou),
62 63
  ];

Hixie's avatar
Hixie committed
64
  Future loadSounds() async {
65
    MediaServiceProxy mediaService = new MediaServiceProxy.unbound();
66
    try {
67
      shell.connectToService("mojo:media_service", mediaService);
Hixie's avatar
Hixie committed
68 69
      List<Future<MediaPlayerPrepareResponseParams>> pending = <Future<MediaPlayerPrepareResponseParams>>[];
      for (PianoKey key in keys)
70 71 72 73
        pending.add(key.load(mediaService));
      await Future.wait(pending);
    } finally {
      mediaService.close();
74 75 76
    }
  }

77
  Widget build(BuildContext context) {
Hixie's avatar
Hixie committed
78 79
    List<Widget> children = <Widget>[];
    for (PianoKey key in keys) {
80
      children.add(new Flexible(
Hixie's avatar
Hixie committed
81 82 83 84 85 86 87 88 89
        child: new Listener(
          child: new Container(
            decoration: new BoxDecoration(backgroundColor: key.color)
          ),
          onPointerCancel: (_) => key.up(),
          onPointerDown: (_) => key.down(),
          onPointerUp: (_) => key.up()
        )
      ));
90
    }
91
    return new Column(children: children);
92 93 94
  }
}

95 96 97 98
Widget statusBox(Widget child) {
  const mediumGray = const Color(0xff555555);
  const darkGray = const Color(0xff222222);
  return new Center(
Hixie's avatar
Hixie committed
99 100 101 102
    child: new Container(
      decoration: const BoxDecoration(
        boxShadow: const <BoxShadow>[
          const BoxShadow(
Hans Muller's avatar
Hans Muller committed
103
            color: mediumGray, offset: const Offset(6.0, 6.0), blurRadius: 5.0)
Hixie's avatar
Hixie committed
104 105 106 107 108 109 110 111 112
        ],
        backgroundColor: darkGray
      ),
      height: 90.0,
      padding: const EdgeDims.all(8.0),
      margin: const EdgeDims.symmetric(horizontal: 50.0),
      child: new Center(child: child)
    )
  );
113 114 115 116
}

Widget splashScreen() {
  return statusBox(
Hixie's avatar
Hixie committed
117 118 119 120 121
    new Text(
      'Loading sound files!',
      style: new TextStyle(fontSize: 18.0)
    )
  );
122 123 124 125 126 127 128
}

main() async {
  runApp(splashScreen());

  PianoApp app = new PianoApp();
  // use "await" to make sure the sound files are loaded before we show the ui.
Hixie's avatar
Hixie committed
129
  await app.loadSounds();
130 131 132 133 134 135
  runApp(app);
  // runApp() returns immediately so you can't put application cleanup code
  // here.  Android apps can be killed at any time, so there's also no way to
  // catch a close event to do cleanup. Therefore, although we appear to be
  // leaking the "player" handles, this is working as intended and the operating
  // system will clean up when the activity is killed.
136
}