अपने Flutter गेम में साउंड और संगीत जोड़ें

1. शुरू करने से पहले

गेम, ऑडियो-विज़ुअल अनुभव देते हैं. Flutter एक शानदार टूल है. इसकी मदद से शानदार विज़ुअल और शानदार यूआई बनाएं, ताकि आप विज़ुअल के ज़रिए चीज़ों को बेहतर तरीके से समझ पाएं. अब सिर्फ़ ऑडियो की ज़रूरत है. इस कोडलैब में, अपने प्रोजेक्ट में वीडियो स्ट्रीम होने और उसके दिखने के समय का अंतर कम करने के बारे में जानने के लिए, flutter_soloud प्लगिन को इस्तेमाल करने का तरीका बताया गया है. आप एक सामान्य मचान से शुरुआत करें, ताकि आप सीधे दिलचस्प हिस्सों पर पहुंच सकें.

हेडफ़ोन का हाथ से बनाया गया इलस्ट्रेशन.

आपने यहां जो कुछ भी सीखा है उसका इस्तेमाल, सिर्फ़ गेम ही नहीं, बल्कि ऐप्लिकेशन में ऑडियो जोड़ने के लिए भी किया जा सकता है. हालांकि, ज़्यादातर गेम में साउंड और संगीत की ज़रूरत होती है, लेकिन ज़्यादातर ऐप्लिकेशन में ऐसा नहीं होता. इसलिए, इस कोडलैब में गेम पर फ़ोकस किया गया है.

ज़रूरी शर्तें

  • Flutter के बारे में बुनियादी जानकारी.
  • Flutter ऐप्लिकेशन चलाने और उन्हें डीबग करने की जानकारी.

आपको ये सब सीखने को मिलेगा

  • एक बार में चलने वाली आवाज़ें चलाने का तरीका.
  • गैपलेस संगीत लूप चलाने और उन्हें पसंद के मुताबिक बनाने का तरीका.
  • आवाज़ों को कम कैसे करें.
  • आवाज़ों पर पर्यावरण से जुड़े इफ़ेक्ट लागू करने का तरीका.
  • अपवादों से कैसे निपटें.
  • इन सभी सुविधाओं को एक ही ऑडियो कंट्रोलर में कैसे शामिल करें.

आपको इन चीज़ों की ज़रूरत पड़ेगी

  • Flutter SDK टूल
  • आपकी पसंद का कोड एडिटर

2. सेट अप करें

  1. नीचे दी गई फ़ाइलें डाउनलोड करें. अगर आपका कनेक्शन धीमा है, तो चिंता न करें. आपको बाद में असल फ़ाइलों की ज़रूरत पड़ेगी. इसलिए, काम करते समय उन्हें डाउनलोड करने दें.
  1. अपनी पसंद के नाम से, Flutter प्रोजेक्ट बनाएं.
  1. प्रोजेक्ट में lib/audio/audio_controller.dart फ़ाइल बनाएं.
  2. फ़ाइल में, यह कोड डालें:

lib/audio/audio_controller.dart

import 'dart:async';

import 'package:logging/logging.dart';

class AudioController {
  static final Logger _log = Logger('AudioController');

  Future<void> initialize() async {
    // TODO
  }

  void dispose() {
    // TODO
  }

  Future<void> playSound(String assetKey) async {
    _log.warning('Not implemented yet.');
  }

  Future<void> startMusic() async {
    _log.warning('Not implemented yet.');
  }

  void fadeOutMusic() {
    _log.warning('Not implemented yet.');
  }

  void applyFilter() {
    // TODO
  }

  void removeFilter() {
    // TODO
  }
}

जैसा कि आप देख सकते हैं, यह आने वाले समय में मिलने वाली सुविधा का सिर्फ़ एक स्केलेटन है. हम इस कोडलैब के दौरान, इन सभी को लागू करेंगे.

  1. इसके बाद, lib/main.dart फ़ाइल खोलें और फिर इसकी सामग्री को इस कोड से बदलें:

lib/main.dart

import 'dart:developer' as dev;

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:logging/logging.dart';

import 'audio/audio_controller.dart';

void main() async {
  // The `flutter_soloud` package logs everything
  // (from severe warnings to fine debug messages)
  // using the standard `package:logging`.
  // You can listen to the logs as shown below.
  Logger.root.level = kDebugMode ? Level.FINE : Level.INFO;
  Logger.root.onRecord.listen((record) {
    dev.log(
      record.message,
      time: record.time,
      level: record.level.value,
      name: record.loggerName,
      zone: record.zone,
      error: record.error,
      stackTrace: record.stackTrace,
    );
  });

  WidgetsFlutterBinding.ensureInitialized();

  final audioController = AudioController();
  await audioController.initialize();

  runApp(
    MyApp(audioController: audioController),
  );
}

class MyApp extends StatelessWidget {
  const MyApp({required this.audioController, super.key});

  final AudioController audioController;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter SoLoud Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.brown),
        useMaterial3: true,
      ),
      home: MyHomePage(audioController: audioController),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.audioController});

  final AudioController audioController;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  static const _gap = SizedBox(height: 16);

  bool filterApplied = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Flutter SoLoud Demo')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            OutlinedButton(
              onPressed: () {
                widget.audioController.playSound('assets/sounds/pew1.mp3');
              },
              child: const Text('Play Sound'),
            ),
            _gap,
            OutlinedButton(
              onPressed: () {
                widget.audioController.startMusic();
              },
              child: const Text('Start Music'),
            ),
            _gap,
            OutlinedButton(
              onPressed: () {
                widget.audioController.fadeOutMusic();
              },
              child: const Text('Fade Out Music'),
            ),
            _gap,
            Row(
              mainAxisSize: MainAxisSize.min,
              children: [
                const Text('Apply Filter'),
                Checkbox(
                  value: filterApplied,
                  onChanged: (value) {
                    setState(() {
                      filterApplied = value!;
                    });
                    if (filterApplied) {
                      widget.audioController.applyFilter();
                    } else {
                      widget.audioController.removeFilter();
                    }
                  },
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}
  1. ऑडियो फ़ाइलें डाउनलोड होने के बाद, अपने प्रोजेक्ट के रूट में assets नाम की डायरेक्ट्री बनाएं.
  2. assets डायरेक्ट्री में, दो सबडायरेक्ट्री बनाएं. एक का नाम music और दूसरी का नाम sounds.
  3. डाउनलोड की गई फ़ाइलों को अपने प्रोजेक्ट में ले जाएं, ताकि गाने की फ़ाइल assets/music/looped-song.ogg फ़ाइल में और प्यू की साउंड, इन फ़ाइलों में हो:
  • assets/sounds/pew1.mp3
  • assets/sounds/pew2.mp3
  • assets/sounds/pew3.mp3

आपके प्रोजेक्ट का स्ट्रक्चर अब कुछ ऐसा दिखेगा:

प्रोजेक्ट का ट्री व्यू, जिसमें `android`, `ios` जैसे फ़ोल्डर, `README.md` और `analysis_options.yaml` जैसी फ़ाइलें शामिल हैं. इनमें से, हम `संगीत` और `साउंड` सबडायरेक्ट्री वाली `ऐसेट` डायरेक्ट्री, `main.dart` के साथ `lib` डायरेक्ट्री, और `audio_controller.dart` के साथ `ऑडियो` सबडायरेक्ट्री, और `pubspec.yaml` फ़ाइल को देखते हैं.  ऐरो, नई डायरेक्ट्री और उन फ़ाइलों पर ले जाते हैं जिन पर आपने अब तक काम किया है.

फ़ाइलें सेव हो जाने के बाद, आपको Flutter को उनके बारे में बताना होगा.

  1. pubspec.yaml फ़ाइल खोलें और फिर फ़ाइल के निचले हिस्से में दिए गए flutter: सेक्शन को नीचे दिए गए विकल्पों से बदलें:

pubspec.yaml

...

flutter:
  uses-material-design: true

  assets:
    - assets/music/
    - assets/sounds/
  1. flutter_soloud पैकेज और logging पैकेज पर डिपेंडेंसी जोड़ें.

pubspec.yaml

...

dependencies:
  flutter:
    sdk: flutter

  flutter_soloud: ^2.0.0
  logging: ^1.2.0

...
  1. प्रोजेक्ट चलाएं. फ़िलहाल, कुछ भी काम नहीं करेगा, क्योंकि आपने इन सेक्शन में फ़ंक्शन जोड़े हैं.

10f0f751c9c47038.png

/flutter_soloud/src/filters/filters.cpp:21:24: warning: implicit conversion loses integer precision: 'decltype(__x.base() - __y.base())' (aka 'long') to 'int' [-Wshorten-64-to-32];

ये गड़बड़ियां, SoLoud C++ लाइब्रेरी से आती हैं. इनसे सुविधाओं पर कोई असर नहीं पड़ता और इन्हें सुरक्षित तरीके से अनदेखा किया जा सकता है.

3. शुरू करना और बंद करना

ऑडियो चलाने के लिए, flutter_soloud प्लग इन का इस्तेमाल किया जाता है. यह प्लग इन, SoLoud प्रोजेक्ट पर आधारित है. यह गेम के लिए C++ ऑडियो इंजन है. इसका इस्तेमाल Nintendo SNES Classic के साथ-साथ अन्य डिवाइसों पर भी किया जाता है.

7ce23849b6d0d09a.png

SoLoud ऑडियो इंजन शुरू करने के लिए, यह तरीका अपनाएं:

  1. audio_controller.dart फ़ाइल में, flutter_soloud पैकेज इंपोर्ट करें और क्लास में निजी _soloud फ़ील्ड जोड़ें.

lib/audio/audio_controller.dart

import 'dart:ui';

import 'package:flutter_soloud/flutter_soloud.dart';  //  Add this...
import 'package:logging/logging.dart';

class AudioController {
  static final Logger _log = Logger('AudioController');

  SoLoud? _soloud;                                    //  ... and this.

  Future<void> initialize() async {
    // TODO
  }

  ...

ऑडियो कंट्रोलर, इस फ़ील्ड की मदद से मौजूद SoLoud इंजन को मैनेज करता है. साथ ही, सभी कॉल उस पर फ़ॉरवर्ड करेगा.

  1. initialize() तरीके में, यह कोड डालें:

lib/audio/audio_controller.dart

...

  Future<void> initialize() async {
    _soloud = SoLoud.instance;
    await _soloud!.init();
  }

...

यह _soloud फ़ील्ड को भरता है और शुरू होने का इंतज़ार करता है. निम्न पर ध्यान दें:

  • SoLoud, सिंगलटन instance फ़ील्ड उपलब्ध कराता है. एक से ज़्यादा SoLoud इंस्टेंस बनाने का कोई तरीका नहीं है. C++ इंजन ऐसा करने की अनुमति नहीं देता. इसलिए, Dart प्लग इन भी ऐसा करने की अनुमति नहीं देता.
  • प्लग इन को शुरू करने की प्रोसेस एसिंक्रोनस होती है. यह प्रोसेस तब तक पूरी नहीं होती, जब तक init() तरीका वापस नहीं आता.
  • इस उदाहरण में कम शब्दों में बताने के लिए, try/catch ब्लॉक में गड़बड़ियों को नहीं पकड़ा जा रहा है. आपको प्रोडक्शन कोड में ऐसा करना होगा और किसी भी गड़बड़ी की शिकायत उपयोगकर्ता से करनी होगी.
  1. dispose() तरीके में, यह कोड डालें:

lib/audio/audio_controller.dart

...

  void dispose() {
    _soloud?.deinit();
  }

...

ऐप्लिकेशन से बाहर निकलते समय, SoLoud को बंद करना अच्छी बात है. हालांकि, ऐसा करना अच्छा नहीं रहेगा और भले ही आप ऐसा न करें.

  1. ध्यान दें कि AudioController.initialize() तरीके को पहले ही main() फ़ंक्शन से कॉल किया जा चुका है. इसका मतलब है कि प्रोजेक्ट को हॉट-रीस्टार्ट करने पर, SoLoud बैकग्राउंड में शुरू हो जाता है. हालांकि, कुछ साउंड चलाने से पहले, आपको इससे कोई फ़ायदा नहीं होगा.

4. एक शॉट में साउंड चलाएं

कोई ऐसेट लोड करें और उसे चलाएं

अब आपको पता है कि SoLoud, डिवाइस के चालू होने पर शुरू हो जाता है. इसलिए, आपके पास इसे आवाज़ें चलाने के लिए कहने का विकल्प है.

SoLoud, ऑडियो सोर्स के बीच अंतर करता है. ऑडियो सोर्स में किसी आवाज़ के बारे में बताने के लिए इस्तेमाल किया गया डेटा और मेटाडेटा होता है. साथ ही, वह "साउंड इंस्टेंस" के बीच भी अंतर करता है. इन साउंड इंस्टेंस, असल में चलाई जाने वाली आवाज़ें होती हैं. ऑडियो सोर्स का एक उदाहरण यह हो सकता है कि कोई mp3 फ़ाइल मेमोरी में लोड की गई हो, जिसे चलाया जा सके, और जिसे AudioSource क्लास के इंस्टेंस के ज़रिए दिखाया जाता हो. जब भी इस ऑडियो सोर्स को चलाया जाता है, तब SoLoud एक "साउंड इंस्टेंस" बनाता है जिसे SoundHandle टाइप से दिखाया जाता है.

इसे लोड करके, आपको AudioSource इंस्टेंस मिलता है. उदाहरण के लिए, अगर आपकी एसेट में कोई mp3 फ़ाइल है, तो AudioSource पाने के लिए उसे लोड किया जा सकता है. इसके बाद, आप SoLoud को यह AudioSource चलाने के लिए कहते हैं. इसे कई बार और एक साथ भी खेला जा सकता है.

किसी ऑडियो सोर्स का इस्तेमाल करने के बाद, उसे SoLoud.disposeSource() तरीके से हटाया जा सकता है.

कोई ऐसेट लोड करने और उसे चलाने के लिए, यह तरीका अपनाएं:

  1. AudioController क्लास के playSound() तरीके में, यह कोड डालें:

lib/audio/audio_controller.dart

  ...

  Future<void> playSound(String assetKey) async {
    final source = await _soloud!.loadAsset(assetKey);
    await _soloud!.play(source);
  }

  ...
  1. फ़ाइल को सेव करें, हॉट रीलोड करें, और फिर साउंड चलाएं को चुनें. आपको एक अजीब प्यू की आवाज़ सुनाई देगी. निम्न पर ध्यान दें:
  • दिया गया assetKey आर्ग्युमेंट, assets/sounds/pew1.mp3 जैसा कुछ है. यह वही स्ट्रिंग है जो किसी अन्य ऐसेट-लोडिंग Flutter API (एपीआई) को उपलब्ध कराई जाती है, जैसे कि Image.asset() विजेट.
  • SoLoud इंस्टेंस, loadAsset() तरीके का इस्तेमाल करता है. यह Flutter प्रोजेक्ट की ऐसेट से एक ऑडियो फ़ाइल एसिंक्रोनस रूप से लोड करता है और AudioSource क्लास का इंस्टेंस दिखाता है. फ़ाइल सिस्टम (loadFile() तरीका) से फ़ाइल लोड करने और यूआरएल से नेटवर्क पर लोड करने के लिए एक जैसे तरीके हैं (loadUrl() तरीका).
  • इसके बाद, हाल ही में हासिल किए गए AudioSource इंस्टेंस को SoLoud के play() तरीके में पास किया जाता है. इस तरीके से, SoundHandle टाइप का इंस्टेंस मिलता है, जो चल रही नई आवाज़ को दिखाता है. इस हैंडल को SoLoud के अन्य तरीकों के साथ इस्तेमाल किया जा सकता है. जैसे, आवाज़ को रोकना, बंद करना या उसकी आवाज़ कम या ज़्यादा करना.
  • हालांकि, play() एसिंक्रोनस तरीका है, लेकिन वीडियो तुरंत चलना शुरू हो जाता है. flutter_soloud पैकेज, सी कोड को सीधे और सिंक्रोनस तरीके से कॉल करने के लिए, Dart के फ़ॉरेन फ़ंक्शन इंटरफ़ेस (एफ़एफ़आई) का इस्तेमाल करता है. Dart कोड और प्लैटफ़ॉर्म कोड के बीच, एक-दूसरे को भेजे जाने वाले सामान्य मैसेज, ज़्यादातर Flutter प्लगिन की खास बात होती है. हालांकि, इस प्लगिन में ऐसा नहीं होता. कुछ मेथड के एसिंक्रोनस होने का एक ही वजह है कि प्लगिन के कुछ कोड, अपने आइसोलेटेड में चलता है और Dart आइसोलेटेड के बीच कम्यूनिकेशन एसिंक्रोनस होता है.
  • आप बस दावा करते/करती हैं कि _soloud! के साथ _soloud फ़ील्ड खाली नहीं होता है. यह भी कम शब्दों में लिखने के लिए है. प्रोडक्शन कोड को इस स्थिति में आसानी से काम करना चाहिए, जब डेवलपर ऑडियो कंट्रोलर के पूरी तरह से शुरू होने से पहले ही कोई साउंड चलाने की कोशिश करता है.

अपवादों के साथ डील करें

आपको पता चल गया होगा कि आपने फिर से संभावित अपवादों को अनदेखा किया है. आइए, सीखने के मकसद से इस खास तरीके को ठीक करते हैं. (कम शब्दों में बताने के लिए, कोडलैब इस सेक्शन के बाद, अपवादों को अनदेखा करने पर वापस आ जाता है.)

  • इस मामले में अपवादों को समझने के लिए, playSound() तरीके की दो लाइनों को try/catch ब्लॉक में रैप करें और सिर्फ़ SoLoudException के इंस्टेंस कैप्चर करें.

lib/audio/audio_controller.dart

  ...

  Future<void> playSound(String assetKey) async {
    try {
      final source = await _soloud!.loadAsset(assetKey);
      await _soloud!.play(source);
    } on SoLoudException catch (e) {
      _log.severe("Cannot play sound '$assetKey'. Ignoring.", e);
    }
  }

  ...

SoLoud में कई अपवाद होते हैं, जैसे कि SoLoudNotInitializedException या SoLoudTemporaryFolderFailedException अपवाद. हर तरीके के एपीआई दस्तावेज़ में थ्रॉ किए जा सकने वाले अपवादों के बारे में बताया जाता है.

SoLoud अपने सभी अपवादों के लिए एक पैरंट क्लास भी उपलब्ध कराता है, जिसे SoLoudException अपवाद कहा जाता है. इससे, ऑडियो इंजन की मुख्य सुविधाओं से जुड़ी सभी गड़बड़ियों का पता लगाया जा सकता है. यह उन मामलों में खास तौर पर मददगार होता है जहां ऑडियो चलाना ज़रूरी नहीं है. उदाहरण के लिए, जब आपको सिर्फ़ इसलिए प्लेयर के गेम सेशन को क्रैश नहीं करना है, क्योंकि कोई एक प्यू-प्यू साउंड लोड नहीं हो सका.

जैसा कि आपको उम्मीद होगी, अगर कोई ऐसी ऐसेट कुंजी दी जाती है जो मौजूद नहीं है, तो loadAsset() तरीका भी FlutterError गड़बड़ी दिखा सकता है. आम तौर पर, आपको उन ऐसेट को लोड करने से बचना चाहिए जो गेम के साथ बंडल नहीं हैं. इसलिए, यह एक गड़बड़ी है.

अलग-अलग आवाज़ें चलाएँ

आपको शायद पता चला हो कि आपने सिर्फ़ pew1.mp3 फ़ाइल को चलाया है, लेकिन ऐसेट डायरेक्ट्री में साउंड के दो अन्य वर्शन मौजूद हैं. जब गेम में एक ही आवाज़ के कई वर्शन होते हैं, तो गेम में खेलने का अनुभव ज़्यादा स्वाभाविक लगता है. ऐसे गेम में, अलग-अलग वर्शन को किसी भी क्रम में या अलग-अलग तरीके से खेला जाता है. उदाहरण के लिए, इस तरह की कार्रवाइयों की वजह से, वीडियो में पैदल चलने वालों की आवाज़ और गोली की आवाज़ एक जैसी नहीं होती. इस वजह से, उनकी आवाज़ नकली होती है.

  • विकल्प के तौर पर, हर बार बटन पर टैप किए जाने पर एक अलग प्यू साउंड चलाने के लिए, कोड में बदलाव करें.

इसका इलस्ट्रेशन

5. लूप में संगीत चलाएं

ज़्यादा देर तक चलने वाली आवाज़ें मैनेज करें

कुछ ऑडियो लंबे समय तक चलने के लिए होते हैं. संगीत इसका सबसे आसान उदाहरण है. हालांकि, कई गेम में बैकग्राउंड साउंड भी चलाया जाता है. जैसे, गलियारों में हवा का गुज़रना, दूर से आने वाले भिक्षुओं के मंत्रोच्चार की आवाज़, सदियों पुरानी धातु की खड़खड़ाहट या मरीजों की खांसी की आवाज़.

ये प्लेटाइम वाले ऑडियो सोर्स होते हैं, जिन्हें मिनट में मापा जा सकता है. आपको उनका ट्रैक रखना होगा, ताकि ज़रूरत पड़ने पर उन्हें रोका या बंद किया जा सके. इनका बैकअप अक्सर बड़ी फ़ाइलों से लिया जाता है और ये ज़्यादा मेमोरी का इस्तेमाल कर सकते हैं. इसलिए, इन्हें ट्रैक करने की एक और वजह यह है कि जब AudioSource इंस्टेंस की ज़रूरत न हो, तब उसे हटाया जा सके.

इस वजह से, आपको AudioController में नया निजी फ़ील्ड शामिल करना होगा. यह अभी चल रहे गाने के लिए एक हैंडल है. यह लाइन जोड़ें:

lib/audio/audio_controller.dart

...

class AudioController {
  static final Logger _log = Logger('AudioController');

  SoLoud? _soloud;

  SoundHandle? _musicHandle;    // ← Add this.

  ...

संगीत शुरू करें

कम शब्दों में कहें, तो संगीत चलाना, वन-शॉट साउंड चलाने से अलग नहीं है. इसके लिए, आपको पहले assets/music/looped-song.ogg फ़ाइल को AudioSource क्लास के इंस्टेंस के तौर पर लोड करना होगा. इसके बाद, उसे चलाने के लिए SoLoud के play() तरीके का इस्तेमाल करना होगा.

हालांकि, इस बार आपको उस साउंड हैंडल को पकड़ना है जो play() मेथड, ऑडियो चलने के दौरान उसमें बदलाव करने के लिए दिखाता है.

  • अगर आप चाहें, तो AudioController.startMusic() तरीका खुद लागू करें. अगर आपको कुछ जानकारी सही नहीं लगती है, तो कोई समस्या नहीं है. अहम बात यह है कि संगीत चलाएं चुनने पर, संगीत शुरू हो जाता है.

यहां रेफ़रंस के तौर पर इसे लागू करने की जानकारी दी गई है:

lib/audio/audio_controller.dart

...

  Future<void> startMusic() async {
    if (_musicHandle != null) {
      if (_soloud!.getIsValidVoiceHandle(_musicHandle!)) {
        _log.info('Music is already playing. Stopping first.');
        await _soloud!.stop(_musicHandle!);
      }
    }
    final musicSource = await _soloud!
        .loadAsset('assets/music/looped-song.ogg', mode: LoadMode.disk);
    _musicHandle = await _soloud!.play(musicSource);
  }

...

ध्यान दें कि संगीत फ़ाइल को डिस्क मोड (LoadMode.disk enum) में लोड किया जाता है. इसका सीधा मतलब यह है कि फ़ाइल को ज़रूरत के हिसाब से ही कई हिस्सों में लोड किया जाता है. लंबे समय तक चलने वाले ऑडियो के लिए, आम तौर पर डिस्क मोड में लोड करना सबसे अच्छा होता है. छोटे साउंड इफ़ेक्ट के लिए, उन्हें लोड करना और कंप्रेस करना, ज़्यादा बेहतर विकल्प होता है. यह डिफ़ॉल्ट LoadMode.memory enum है.

हालांकि, आपके सामने कुछ समस्याएं हैं. पहली बात, संगीत की क्वालिटी बहुत ज़्यादा है और आवाज़ इस पर बहुत ज़्यादा है. ज़्यादातर गेम में, संगीत ज़्यादातर समय बैकग्राउंड में चलता है. इससे, गेम में बोली जाने वाली बातों और साउंड इफ़ेक्ट जैसे ज़्यादा जानकारी देने वाले ऑडियो को मुख्यता मिलती है. इसे ठीक करने के लिए, play तरीके के वॉल्यूम पैरामीटर का इस्तेमाल करें. उदाहरण के लिए, _soloud!.play(musicSource, volume: 0.6) का इस्तेमाल करके, गाने को 60% वॉल्यूम पर चलाया जा सकता है. इसके अलावा, _soloud!.setVolume(_musicHandle, 0.6) जैसी किसी सुविधा का इस्तेमाल करके, बाद में किसी भी समय आवाज़ को सेट किया जा सकता है.

दूसरी समस्या यह है कि गाना अचानक बंद हो जाता है. ऐसा इसलिए होता है, क्योंकि यह एक ऐसा गाना होता है जिसे लूप में चलाया जाना होता है और लूप का शुरुआती पॉइंट, ऑडियो फ़ाइल की शुरुआत से अलग होता है.

88d2c57fffdfe996.png

यह गेम म्यूज़िक के लिए लोकप्रिय पसंद है. ऐसा इसलिए है, क्योंकि इसका मतलब है कि गाना एक सही इंट्रो के साथ शुरू होता है और ज़रूरत के मुताबिक, लूप पॉइंट के बिना चलता रहता है. जब गेम को चल रहे गाने से ट्रांज़िशन करना होता है, तो वह गाने को धीरे-धीरे कम कर देता है.

हालांकि, अच्छी बात यह है कि SoLoud लूप में ऑडियो चलाने के तरीके उपलब्ध कराता है. play() तरीका, looping पैरामीटर के लिए बूलियन वैल्यू लेता है. साथ ही, loopingStartAt पैरामीटर के तौर पर लूप के शुरुआती पॉइंट की वैल्यू भी लेता है. इससे मिलने वाला कोड कुछ ऐसा दिखेगा:

lib/audio/audio_controller.dart

...

_musicHandle = await _soloud!.play(
  musicSource,
  volume: 0.6,
  looping: true,
  //  The exact timestamp of the start of the loop.
  loopingStartAt: const Duration(seconds: 25, milliseconds: 43),
);

...

अगर loopingStartAt पैरामीटर सेट नहीं किया जाता है, तो यह डिफ़ॉल्ट रूप से Duration.zero (दूसरे शब्दों में, ऑडियो फ़ाइल की शुरुआत) पर सेट हो जाता है. अगर आपके पास कोई ऐसा संगीत ट्रैक है जो बिना किसी परिचय के, लूप में चलने वाला शानदार लूप है, तो पेश है.

  • हर ऑडियो सोर्स से मिलने वाली allInstancesFinished स्ट्रीम को सुनें, ताकि यह पक्का किया जा सके कि उसके पूरा चलने के बाद ऑडियो सोर्स सही तरीके से नष्ट हो जाए. लॉग कॉल जोड़ने के बाद, startMusic() का तरीका इस तरह दिखता है:

lib/audio/audio_controller.dart

...

  Future<void> startMusic() async {
    if (_musicHandle != null) {
      if (_soloud!.getIsValidVoiceHandle(_musicHandle!)) {
        _log.info('Music is already playing. Stopping first.');
        await _soloud!.stop(_musicHandle!);
      }
    }
    _log.info('Loading music');
    final musicSource = await _soloud!
        .loadAsset('assets/music/looped-song.ogg', mode: LoadMode.disk);
    musicSource.allInstancesFinished.first.then((_) {
      _soloud!.disposeSource(musicSource);
      _log.info('Music source disposed');
      _musicHandle = null;
    });

    _log.info('Playing music');
    _musicHandle = await _soloud!.play(
      musicSource,
      volume: 0.6,
      looping: true,
      loopingStartAt: const Duration(seconds: 25, milliseconds: 43),
    );
  }

...

साउंड को धीमा करना

आपकी अगली समस्या यह है कि संगीत कभी खत्म नहीं होता. आइए, फ़ेड स्टाइल को लागू करते हैं.

फ़ेड लागू करने का एक तरीका यह है कि आप एक ऐसी सुविधा इस्तेमाल करें जिसे एक सेकंड में कई बार कहा जाए. जैसे, Ticker या Timer.periodic. साथ ही, संगीत की आवाज़ को थोड़ा कम करके कम किया जा सकता है. यह ठीक रहेगा, लेकिन अभी काफ़ी काम करना है.

हालांकि, अच्छी बात यह है कि SoLoud आपके लिए आग जलाने और मिटाने का आसान तरीका मुहैया कराती है. यहां पांच सेकंड के अंदर संगीत को धीरे-धीरे कम करने और फिर साउंड इंस्टेंस को बंद करने का तरीका बताया गया है. इससे सीपीयू के संसाधनों का गलत इस्तेमाल नहीं होता. fadeOutMusic() तरीके को इस कोड से बदलें:

lib/audio/audio_controller.dart

...

  void fadeOutMusic() {
    if (_musicHandle == null) {
      _log.info('Nothing to fade out');
      return;
    }
    const length = Duration(seconds: 5);
    _soloud!.fadeVolume(_musicHandle!, 0, length);
    _soloud!.scheduleStop(_musicHandle!, length);
  }

...

6. इफ़ेक्ट लागू करना

आपके पास सही ऑडियो इंजन होने का एक बड़ा फ़ायदा यह है कि आपके पास ऑडियो प्रोसेस करने का विकल्प होता है. जैसे, कुछ आवाज़ों को रिवरब, इक्वलाइज़र या लो-पास फ़िल्टर के ज़रिए रूट करना.

गेम में, इसका इस्तेमाल जगहों की आवाज़ों में अंतर करने के लिए किया जा सकता है. उदाहरण के लिए, जंगल में ताली बजाने पर उसकी आवाज़, कंक्रीट बंकर में बजाने पर सुनाई देने वाली आवाज़ से अलग होती है. जंगल में आवाज़ कम हो जाती है और उसे सोख लिया जाता है. वहीं, बंकर की दीवारें आवाज़ को वापस भेजती हैं, जिससे रिवरब होता है. इसी तरह, किसी दीवार से सुनाई देने पर लोगों की आवाज़ें अलग तरह की होती हैं. जब वे ठोस माध्यम से गुज़रते हैं, तो उनकी ज़्यादा फ़्रीक्वेंसी का आसानी से पता लगाया जाता है. इस वजह से, लो-पास फ़िल्टर इफ़ेक्ट बनता है.

एक कमरे में बातचीत करते हुए दो लोगों की इमेज. ध्वनि तरंगें न सिर्फ़ एक व्यक्ति से दूसरे व्यक्ति तक सीधे जाती हैं, बल्कि दीवारों और छत से भी टकरा जाती हैं.

SoLoud में अलग-अलग तरह के कई ऑडियो इफ़ेक्ट उपलब्ध होते हैं. इन्हें ऑडियो के साथ इस्तेमाल किया जा सकता है.

  • अगर आपको लगे कि आपका प्लेयर किसी बड़े कमरे, जैसे कि कैथेड्रल या गुफा में है, तो SoLoud.filters फ़ील्ड का इस्तेमाल करें:

lib/audio/audio_controller.dart

...

  void applyFilter() {
    _soloud!.filters.freeverbFilter.activate();
    _soloud!.filters.freeverbFilter.wet.value = 0.2;
    _soloud!.filters.freeverbFilter.roomSize.value = 0.9;
  }

  void removeFilter() {
    _soloud!.filters.freeverbFilter.deactivate();
  }

...

SoLoud.filters फ़ील्ड की मदद से, सभी तरह के फ़िल्टर और उनके पैरामीटर को ऐक्सेस किया जा सकता है. हर पैरामीटर में पहले से मौजूद सुविधाएं भी होती हैं, जैसे कि धीरे-धीरे फ़ेड करना और ऑसिलेशन करना.

ध्यान दें: _soloud!.filters ग्लोबल फ़िल्टर दिखाता है. अगर आपको किसी एक सोर्स पर फ़िल्टर लागू करने हैं, तो कृपया AudioSource.filters का इस्तेमाल करें. यह फ़ंक्शन, की तरह ही काम करता है.

पिछले कोड का इस्तेमाल करके, ये काम किए जा सकते हैं:

  • दुनिया भर में फ़्रीवर्ब फ़िल्टर को चालू करें.
  • Wet पैरामीटर को 0.2 पर सेट करें. इसका मतलब है कि नतीजा 80% ओरिजनल और 20% रीवरब इफ़ेक्ट का आउटपुट होगा. अगर इस पैरामीटर को 1.0 पर सेट किया जाता है, तो आपको सिर्फ़ रूम की दीवारों से आने वाली आवाज़ें सुनाई देंगी. आपको ओरिजनल ऑडियो नहीं सुनाई देगा.
  • कमरे के साइज़ के पैरामीटर को 0.9 पर सेट करें. इस पैरामीटर में अपनी पसंद के हिसाब से बदलाव किया जा सकता है. इसके अलावा, इसे डाइनैमिक तौर पर भी बदला जा सकता है. 1.0 एक बहुत बड़ी गुफा है. वहीं, 0.0 एक बाथरूम है.
  • अगर आपको लगता है कि यह सही है, तो कोड बदलें और इनमें से कोई एक फ़िल्टर या इन फ़िल्टर के कॉम्बिनेशन का इस्तेमाल करें:
  • biquadFilter (इसे लो पास फ़िल्टर के तौर पर इस्तेमाल किया जा सकता है)
  • pitchShiftFilter
  • equalizerFilter
  • echoFilter
  • lofiFilter
  • flangerFilter
  • bassboostFilter
  • waveShaperFilter
  • robotizeFilter

7. बधाई हो

आपने एक ऐसा ऑडियो कंट्रोलर लागू किया है जो साउंड चलाता है, संगीत को लूप करता है, और इफ़ेक्ट लागू करता है.

ज़्यादा जानें

  • ऑडियो कंट्रोलर को बेहतर बनाने के लिए, इन सुविधाओं का इस्तेमाल करें. जैसे, डिवाइस चालू होने पर साउंड को पहले से लोड करना, गाने को क्रम से चलाना या समय के साथ फ़िल्टर लागू करना.
  • flutter_soloud के पैकेज दस्तावेज़ को पढ़ें.
  • इस्तेमाल की जा रही C++ लाइब्रेरी का होम पेज पढ़ें.
  • C++ लाइब्रेरी के साथ इंटरफ़ेस करने के लिए इस्तेमाल की जाने वाली टेक्नोलॉजी, Dart FFI के बारे में और पढ़ें.
  • गेम के ऑडियो प्रोग्रामिंग के बारे में गाय सोमबर्ग की बातचीत देखें. इससे आपको प्रेरणा मिलेगी. (एक बड़ा वीडियो भी है.) जब गे ऑस्टिन "मीडियमवेयर" की बात करते हैं, तो उनका मतलब SoLoud और FMOD जैसी लाइब्रेरी से है. बाकी कोड, हर गेम के हिसाब से अलग-अलग होते हैं.
  • अपना गेम बनाएं और उसे रिलीज़ करें.

हेडफ़ोन की इमेज