আপনার ফ্লটার গেমে শব্দ এবং সঙ্গীত যোগ করুন

1. আপনি শুরু করার আগে

গেম হল অডিওভিজ্যুয়াল অভিজ্ঞতা। সুন্দর ভিজ্যুয়াল এবং কঠিন UI তৈরি করার জন্য ফ্লটার একটি দুর্দান্ত সরঞ্জাম, তাই এটি আপনাকে জিনিসগুলির ভিজ্যুয়াল দিক থেকে অনেক দূরে নিয়ে যায়। বাকি যে অনুপস্থিত উপাদান অডিও. এই কোডল্যাবে, আপনি শিখবেন কীভাবে আপনার প্রোজেক্টে লো-লেটেন্সি সাউন্ড এবং মিউজিক চালু করতে flutter_soloud প্লাগইন ব্যবহার করতে হয়। আপনি একটি মৌলিক স্ক্যাফোল্ড দিয়ে শুরু করুন যাতে আপনি সরাসরি আকর্ষণীয় অংশে যেতে পারেন।

হেডফোনের একটি হাতে আঁকা চিত্র।

আপনি অবশ্যই এখানে যা শিখছেন তা ব্যবহার করতে পারেন আপনার অ্যাপে অডিও যোগ করতে, শুধু গেম নয়। কিন্তু প্রায় সব গেমের জন্য সাউন্ড এবং মিউজিকের প্রয়োজন হয়, বেশিরভাগ অ্যাপই তা করে না, তাই এই কোডল্যাব গেমগুলিতে ফোকাস করে।

পূর্বশর্ত

  • ফ্লটারের সাথে প্রাথমিক পরিচিতি।
  • Flutter অ্যাপগুলি কীভাবে চালাতে এবং ডিবাগ করতে হয় সে সম্পর্কে জ্ঞান।

আপনি কি শিখুন

  • কিভাবে এক-শট শব্দ বাজাবেন।
  • গ্যাপলেস মিউজিক লুপগুলি কীভাবে খেলবেন এবং কাস্টমাইজ করবেন।
  • কিভাবে ভিতরে এবং বাইরে শব্দ বিবর্ণ.
  • শব্দে পরিবেশগত প্রভাব কীভাবে প্রয়োগ করবেন।
  • কিভাবে ব্যতিক্রম মোকাবেলা করতে.
  • কীভাবে এই সমস্ত বৈশিষ্ট্যগুলিকে একটি একক অডিও কন্ট্রোলারে এনক্যাপসুলেট করবেন।

আপনার যা প্রয়োজন

  • ফ্লাটার SDK
  • আপনার পছন্দের একটি কোড সম্পাদক

2. সেট আপ করুন

  1. নিচের ফাইলগুলো ডাউনলোড করুন। আপনার যদি ধীর সংযোগ থাকে তবে চিন্তা করবেন না। আপনার প্রকৃত ফাইলগুলি পরে প্রয়োজন, যাতে আপনি কাজ করার সময় সেগুলি ডাউনলোড করতে দিতে পারেন৷
  1. আপনার পছন্দের একটি নাম দিয়ে একটি ফ্লাটার প্রকল্প তৈরি করুন।
  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` সহ একটি `audio` সাবডিরেক্টরি এবং `pubspec দেখতে পারি। yaml` ফাইল।  তীরগুলি নতুন ডিরেক্টরিগুলির দিকে নির্দেশ করে, এবং আপনি এখন পর্যন্ত যে ফাইলগুলি স্পর্শ করেছেন।

এখন যেহেতু ফাইলগুলি আছে, আপনাকে সেগুলি সম্পর্কে ফ্লাটারকে বলতে হবে৷

  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 প্রজেক্টের উপর ভিত্তি করে তৈরি করা হয়েছে, Nintendo SNES Classic-এর দ্বারা ব্যবহৃত গেমগুলির জন্য একটি C++ অডিও ইঞ্জিন।

7ce23849b6d0d09a.png

সোলাউড অডিও ইঞ্জিন শুরু করতে, এই পদক্ষেপগুলি অনুসরণ করুন:

  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
  }

  ...

অডিও কন্ট্রোলার এই ক্ষেত্রের মাধ্যমে অন্তর্নিহিত সোলাউড ইঞ্জিন পরিচালনা করে এবং এটিতে সমস্ত কল ফরোয়ার্ড করবে।

  1. initialize() পদ্ধতিতে, নিম্নলিখিত কোডটি লিখুন:

lib/audio/audio_controller.dart

...

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

...

এটি _soloud ক্ষেত্রটি পূরণ করে এবং আরম্ভের জন্য অপেক্ষা করে। নিম্নলিখিত নোট করুন:

  • সোলাউড একটি সিঙ্গলটন instance ক্ষেত্র সরবরাহ করে। বেশ কয়েকটি সোলাউড দৃষ্টান্ত স্থাপন করার কোন উপায় নেই। এটি এমন কিছু নয় যা C++ ইঞ্জিন অনুমতি দেয়, তাই এটি ডার্ট প্লাগইন দ্বারাও অনুমোদিত নয়।
  • প্লাগইনের সূচনা অসিঙ্ক্রোনাস এবং init() পদ্ধতি ফিরে না আসা পর্যন্ত শেষ হয় না।
  • এই উদাহরণে সংক্ষিপ্ততার জন্য, আপনি try/catch ব্লকে ত্রুটিগুলি ধরছেন না । প্রোডাকশন কোডে, আপনি তা করতে চান এবং ব্যবহারকারীর কাছে কোনো ত্রুটি রিপোর্ট করতে চান।
  1. dispose() পদ্ধতিতে, নিম্নলিখিত কোড লিখুন:

lib/audio/audio_controller.dart

...

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

...

অ্যাপ থেকে প্রস্থান করার সময় সোলাউড বন্ধ করা ভাল অনুশীলন, যদিও আপনি এটি করতে অবহেলা করলেও সবকিছু ঠিকঠাক কাজ করা উচিত।

  1. লক্ষ্য করুন যে AudioController.initialize() পদ্ধতিটি ইতিমধ্যে main() ফাংশন থেকে কল করা হয়েছে। এর মানে হল যে প্রজেক্টটি হট-রিস্টার্ট করলে ব্যাকগ্রাউন্ডে সোলাউড শুরু হয়, কিন্তু আপনি আসলে কিছু সাউন্ড বাজানোর আগে এটি আপনার কোন উপকার করবে না।

4. এক-শট শব্দ চালান

একটি সম্পদ লোড করুন এবং এটি খেলুন

এখন যেহেতু আপনি জানেন যে সোলাউড স্টার্টআপে আরম্ভ করা হয়েছে, আপনি এটিকে শব্দ বাজানোর জন্য বলতে পারেন।

SoLoud একটি অডিও উত্সের মধ্যে পার্থক্য করে, যা একটি শব্দ বর্ণনা করতে ব্যবহৃত ডেটা এবং মেটাডেটা এবং এর "শব্দ দৃষ্টান্ত", যা আসলে বাজানো শব্দ। একটি অডিও উত্সের একটি উদাহরণ মেমরিতে লোড করা একটি mp3 ফাইল হতে পারে, চালানোর জন্য প্রস্তুত, এবং AudioSource ক্লাসের একটি উদাহরণ দ্বারা উপস্থাপন করা হয়। আপনি যখনই এই অডিও উৎসটি চালান, SoLoud একটি "সাউন্ড ইনস্ট্যান্স" তৈরি করে যা SoundHandle টাইপ দ্বারা উপস্থাপিত হয়।

আপনি এটি লোড করে একটি AudioSource ইনস্ট্যান্স পাবেন। উদাহরণস্বরূপ, যদি আপনার সম্পদে একটি mp3 ফাইল থাকে, তাহলে আপনি একটি AudioSource পেতে এটি লোড করতে পারেন। তারপর, আপনি সোলাউডকে এই 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 — একই স্ট্রিং যা আপনি অন্য যেকোন অ্যাসেট-লোডিং ফ্লাটার এপিআই, যেমন Image.asset() উইজেটকে প্রদান করবেন।
  • SoLoud উদাহরণ একটি loadAsset() পদ্ধতি প্রদান করে যা ফ্লাটার প্রকল্পের সম্পদ থেকে একটি অডিও ফাইলকে অ্যাসিঙ্ক্রোনাসভাবে লোড করে এবং AudioSource ক্লাসের একটি উদাহরণ প্রদান করে। ফাইল সিস্টেম ( loadFile() পদ্ধতি থেকে একটি ফাইল লোড করার জন্য এবং একটি URL ( loadUrl() পদ্ধতি) থেকে নেটওয়ার্কে লোড করার জন্য সমতুল্য পদ্ধতি রয়েছে।
  • নতুন অর্জিত AudioSource ইনস্ট্যান্স তারপর সোলাউডের play() পদ্ধতিতে পাস করা হয়। এই পদ্ধতিটি SoundHandle প্রকারের একটি উদাহরণ প্রদান করে যা নতুন বাজানো শব্দকে উপস্থাপন করে। এই হ্যান্ডেলটি, পরিবর্তে, শব্দের ভলিউম বিরতি, থামানো বা পরিবর্তন করার মতো জিনিসগুলি করতে অন্যান্য সোলাউড পদ্ধতিতে প্রেরণ করা যেতে পারে।
  • যদিও play() একটি অ্যাসিঙ্ক্রোনাস পদ্ধতি, প্লেব্যাক মূলত তাৎক্ষণিকভাবে শুরু হয়। flutter_soloud প্যাকেজটি ডার্টের বিদেশী ফাংশন ইন্টারফেস (FFI) ব্যবহার করে C কোডকে সরাসরি এবং সিঙ্ক্রোনাসভাবে কল করতে। ডার্ট কোড এবং প্ল্যাটফর্ম কোডের মধ্যে সাধারন মেসেজিং যা বেশিরভাগ ফ্লাটার প্লাগইনগুলির জন্য বৈশিষ্ট্যযুক্ত কোথাও খুঁজে পাওয়া যায় না। কিছু পদ্ধতি অ্যাসিঙ্ক্রোনাস হওয়ার একমাত্র কারণ হল যে প্লাগইনের কিছু কোড তার নিজস্ব আইসোলেটে চলে এবং ডার্ট আইসোলেটের মধ্যে যোগাযোগ অ্যাসিঙ্ক্রোনাস।
  • আপনি কেবল জোর দিয়ে বলেছেন যে _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 ব্যতিক্রম। প্রতিটি পদ্ধতির API দস্তাবেজগুলি নিক্ষেপ করা হতে পারে এমন ব্যতিক্রমগুলির তালিকা করে৷

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.

  ...

সঙ্গীত শুরু করুন

সংক্ষেপে, সঙ্গীত বাজানো এক-শট শব্দ বাজানো থেকে আলাদা নয়। আপনাকে এখনও প্রথমে AudioSource ক্লাসের একটি উদাহরণ হিসাবে assets/music/looped-song.ogg ফাইলটি লোড করতে হবে, তারপর এটি চালানোর জন্য SoLoud's 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) লোড করা এবং ডিকম্প্রেস করা আরও বোধগম্য।

যদিও আপনার কয়েকটি সমস্যা আছে। প্রথমত, মিউজিক খুব জোরে, শব্দগুলোকে অপ্রতিরোধ্য করে। বেশিরভাগ গেমে, সঙ্গীত বেশিরভাগ সময় ব্যাকগ্রাউন্ডে থাকে, যা বক্তৃতা এবং সাউন্ড ইফেক্টের মতো আরও তথ্যপূর্ণ অডিওকে কেন্দ্রের মঞ্চ দেয়। প্লে পদ্ধতির ভলিউম প্যারামিটার ব্যবহার করে এটি ঠিক করা সহজ। উদাহরণস্বরূপ, 60% ভলিউমে গানটি চালানোর জন্য আপনি _soloud!.play(musicSource, volume: 0.6) চেষ্টা করতে পারেন। বিকল্পভাবে, আপনি _soloud!.setVolume(_musicHandle, 0.6) এর মতো কিছু দিয়ে পরবর্তী যেকোনো সময়ে ভলিউম সেট করতে পারেন।

দ্বিতীয় সমস্যা হল গানটি হঠাৎ বন্ধ হয়ে যায়। কারণ এটি এমন একটি গান যা একটি লুপে বাজানোর কথা এবং লুপের শুরুর স্থানটি অডিও ফাইলের শুরু নয়

88d2c57ffdfe996.png

এটি গেম মিউজিকের জন্য একটি জনপ্রিয় পছন্দ কারণ এর অর্থ হল গানটি একটি স্বাভাবিক ইন্ট্রো দিয়ে শুরু হয় এবং তারপর একটি সুস্পষ্ট লুপ পয়েন্ট ছাড়াই যতক্ষণ প্রয়োজন ততক্ষণ বাজানো হয়। যখন গেমটিকে বর্তমানে বাজানো গান থেকে স্থানান্তরিত করতে হবে, তখন এটি কেবল গানটিকে বিবর্ণ করে দেয়।

সৌভাগ্যক্রমে, সোলাউড লুপিং অডিও চালানোর উপায় সরবরাহ করে। 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 , এবং ছোট ছোট করে মিউজিকের ভলিউম কমিয়ে দেয়। এটা কাজ করবে, কিন্তু এটা অনেক কাজ.

সৌভাগ্যক্রমে, সোলাউড সুবিধাজনক ফায়ার-এন্ড-ফোরজিট পদ্ধতিগুলি সরবরাহ করে যা আপনার জন্য এটি করে। এখানে আপনি কীভাবে পাঁচ সেকেন্ডের মধ্যে সঙ্গীতকে বিবর্ণ করতে পারেন এবং তারপরে সাউন্ড ইন্সট্যান্স বন্ধ করতে পারেন যাতে এটি অপ্রয়োজনীয়ভাবে CPU সংস্থানগুলি গ্রাস না করে। এই কোড দিয়ে 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 বিভিন্ন অডিও প্রভাব প্রদান করে, যা আপনি অডিওতে প্রয়োগ করতে পারেন।

  • আপনার প্লেয়ার একটি বড় কক্ষে, যেমন একটি ক্যাথিড্রাল বা একটি গুহার মতো শব্দ করতে, FilterType.freeverbFilter enum ব্যবহার করুন:

lib/audio/audio_controller.dart

...

  void applyFilter() {
    _soloud!.addGlobalFilter(FilterType.freeverbFilter);
    _soloud!.setFilterParameter(FilterType.freeverbFilter, 0, 0.2);
    _soloud!.setFilterParameter(FilterType.freeverbFilter, 2, 0.9);
  }

  void removeFilter() {
    _soloud!.removeGlobalFilter(FilterType.freeverbFilter);
  }

...

আপনি দেখতে পাচ্ছেন, ফিল্টারগুলির সাহায্যে আপনি আরও নিম্ন-স্তরের অঞ্চলে প্রবেশ করেন৷ একটি ফিল্টার পরামিতি সেট করা প্যারামিটারের সূচকের সাথে সম্পন্ন করা হয়। উদাহরণস্বরূপ, freeverb-এর ওয়েট প্যারামিটারের সূচক আছে 0 এবং রুম সাইজ প্যারামিটারের সূচক 2 আছে।

পূর্ববর্তী কোডের সাথে, আপনি নিম্নলিখিতগুলি করবেন:

  • শুধুমাত্র একটি শব্দ নয়, বিশ্বব্যাপী ফ্রিভার্ব ফিল্টার বা পুরো অডিও মিক্সে সক্ষম করুন৷
  • ওয়েট প্যারামিটারটি 0.2 এ সেট করুন, যার অর্থ হল যে অডিওটি 80% আসল এবং 20% রিভার্ব প্রভাবের আউটপুট হবে। আপনি যদি এই প্যারামিটারটি 1.0 তে সেট করেন তবে এটি কেবলমাত্র শব্দ তরঙ্গগুলি শোনার মতো হবে যা ঘরের দূরবর্তী দেয়াল থেকে আপনার কাছে ফিরে আসে এবং আসল অডিওগুলির কিছুই নয়৷
  • রুম সাইজ প্যারামিটার 0.9 এ সেট করুন। আপনি এই প্যারামিটারটি আপনার পছন্দ অনুসারে পরিবর্তন করতে পারেন বা এমনকি এটি গতিশীলভাবে পরিবর্তন করতে পারেন। 1.0 হল একটি বিশাল গুহা যেখানে 0.0 হল একটি বাথরুম৷
  • আপনি যদি এটি করতে চান, কোডটি পরিবর্তন করুন এবং নিম্নলিখিত ফিল্টারগুলির একটি বা নিম্নলিখিত ফিল্টারগুলির সংমিশ্রণ প্রয়োগ করুন:
  • FilterType.biquadResonantFilter (নিম্ন পাস ফিল্টার হিসাবে ব্যবহার করা যেতে পারে)
  • FilterType.eqFilter
  • FilterType.echoFilter
  • FilterType.lofiFilter
  • FilterType.flangerFilter
  • FilterType.bassboostFilter
  • FilterType.waveShaperFilter
  • FilterType.robotizeFilter
  • FilterType.freeverbFilter

7. অভিনন্দন

আপনি একটি অডিও কন্ট্রোলার প্রয়োগ করেছেন যা শব্দ বাজায়, সঙ্গীত লুপ করে এবং প্রভাব প্রয়োগ করে।

আরও জানুন

  • স্টার্টআপে সাউন্ড প্রিলোড করা, একটি সিকোয়েন্সে গান বাজানো, বা সময়ের সাথে ধীরে ধীরে ফিল্টার প্রয়োগ করার মতো বৈশিষ্ট্য সহ অডিও কন্ট্রোলারটিকে আরও এগিয়ে নেওয়ার চেষ্টা করুন।
  • flutter_soloud এর প্যাকেজ ডকুমেন্টেশন পড়ুন।
  • অন্তর্নিহিত C++ লাইব্রেরির হোমপেজ পড়ুন।
  • ডার্ট এফএফআই সম্পর্কে আরও পড়ুন, C++ লাইব্রেরির সাথে ইন্টারফেস করতে ব্যবহৃত প্রযুক্তি।
  • অনুপ্রেরণার জন্য গেম অডিও প্রোগ্রামিং সম্পর্কে গাই সোমবার্গের আলোচনা দেখুন। (এছাড়াও একটা লম্বাও আছে।) গাই যখন "মিডলওয়্যার" নিয়ে কথা বলে, তখন তার মানে সোলাউড এবং এফএমওডির মতো লাইব্রেরি। বাকি কোড প্রতিটি গেমের জন্য নির্দিষ্ট হতে থাকে।
  • আপনার গেম তৈরি করুন এবং এটি ছেড়ে দিন।

হেডফোনের একটি চিত্র