├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── audioplayer
├── audioplayer.hpp
├── internal
│ ├── audiodevices.hpp
│ └── callbacks.hpp
└── main.cpp
├── example
├── .gitignore
├── .metadata
├── README.md
├── lib
│ └── main.dart
├── linux
│ ├── .gitignore
│ ├── CMakeLists.txt
│ ├── flutter
│ │ ├── CMakeLists.txt
│ │ ├── generated_plugin_registrant.cc
│ │ ├── generated_plugin_registrant.h
│ │ └── generated_plugins.cmake
│ ├── main.cc
│ ├── my_application.cc
│ └── my_application.h
├── pubspec.lock
├── pubspec.yaml
└── windows
│ ├── .gitignore
│ ├── CMakeLists.txt
│ ├── flutter
│ ├── .template_version
│ ├── CMakeLists.txt
│ ├── generated_plugin_registrant.cc
│ ├── generated_plugin_registrant.h
│ └── generated_plugins.cmake
│ └── runner
│ ├── CMakeLists.txt
│ ├── Runner.rc
│ ├── flutter_window.cpp
│ ├── flutter_window.h
│ ├── main.cpp
│ ├── resource.h
│ ├── resources
│ └── app_icon.ico
│ ├── run_loop.cpp
│ ├── run_loop.h
│ ├── runner.exe.manifest
│ ├── utils.cpp
│ ├── utils.h
│ ├── win32_window.cpp
│ └── win32_window.h
├── flutter_audio_desktop.iml
├── lib
├── flutter_audio_desktop.dart
└── source
│ ├── core
│ ├── channel.dart
│ ├── devices.dart
│ └── events.dart
│ ├── main.dart
│ └── types
│ ├── audio.dart
│ └── source.dart
├── linux
├── CMakeLists.txt
├── flutter_audio_desktop_plugin.cc
└── include
│ └── flutter_audio_desktop
│ ├── flutter_audio_desktop_plugin.h
│ └── flutter_types.hpp
├── pubspec.lock
├── pubspec.yaml
└── windows
├── .gitignore
├── CMakeLists.txt
├── flutter_audio_desktop_plugin.cpp
└── include
└── flutter_audio_desktop
├── flutter_audio_desktop_plugin.h
└── flutter_types.hpp
/.gitignore:
--------------------------------------------------------------------------------
1 | # vscode
2 |
3 | .vscode
4 |
5 | # audio
6 |
7 | *.mp3
8 |
9 | # dart
10 |
11 | .dart_tool
12 | .packages
13 |
14 | # flutter
15 |
16 | .flutter-plugins
17 | .flutter-plugins-dependencies
18 |
19 | # miniaudio: https://github.com/mackron/miniaudio
20 | # Get miniaudio.h & miniaudio_engine.h from the link above & place at the following location.
21 | audioplayer/miniaudio/*
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 0.1.0
2 |
3 | - Now you can listen to playback events using `stream` (a broadcast stream) inside `AudioPlayer`. This was a great problem in earlier version as one always needs to trigger UI updates whenever playback progresses, ends etc.
4 | - One of the big problems in earlier version was that there was no way to detect if an audio playback has ended after completion. Resulting in issues like #25 & #26. Now `audio.isCompleted` stores `bool` if an audio has ended, same can be accessed from `stream`.
5 | - Added a new `Queue` class to play audio files sequentially, without having to deal with things like `audio.isCompleted` etc. manually.
6 | - Added methods to deal with `Queue` or repeat playback etc.
7 | - Now you can provide any random `id` while creating new instance of `AudioPlayer`, this was a big problem earlier as new `id` had to be consecutive to earlier one.
8 | - Now you can access same instance of `AudioPlayer` even if you make new constructor, by providing same `id`.
9 | - Now asset files can be played & loaded into `AudioPlayer` using `load` method.
10 | - `AudioSource` class has two static methods
11 | - `AudioSource.fromFile` to load an audio file.
12 | - `AudioSource.fromAsset` to load an audio asset.
13 | - Now audio field stores `Audio` object, inside the AudioPlayer class & contains following fields to get information about current playback.
14 | - `file`: Current loaded `File`.
15 | - `isPlaying` : Whether file is playing.
16 | - `isCompleted`: Whether file is ended playing.
17 | - By default once playback is ended, `stop` method is called & `AudioPlayer` is reverted to initial configuration.
18 | - `isStopped`: Whether file is loaded.
19 | - `position`: Position of current playback in `Duration`.
20 | - `duration`: Duration of current file in `Duration`.
21 | - Now contructor of `AudioPlayer` no longer calls async methods, which could result in false assertions.
22 | - Now `ma_resource_manager` is used from `miniaudio_engine` with `MA_DATA_SOURCE_FLAG_STREAM` flag.
23 | - This will improve general performance during playback, as whole file will not be loaded into memory.
24 | - Structure of code improved & separated into various files & classes.
25 | - Now device handling is present in an entirely separate class `AudioDevices`.
26 | - Improvements to how methods are identified & called in method channel. `flutter_types.hpp` improves code readability.
27 | - Other bugs that randomly caused termination after false assertions are also fixed to an extent.
28 | - Removed wave & noise APIs temporarily. Apologies to everyone & [MichealReed](https://github.com/MichealReed).
29 |
30 | ## 0.0.9
31 |
32 | - Missed
33 |
34 | ## 0.0.8
35 |
36 | **Multiple player instances, wave & noise methods**
37 |
38 | - Now multiple AudioPlayer instances can be made, by providing optional id parameter to the constructor.
39 | - Added methods for playing waves.
40 | - Added methods for playing noise.
41 |
42 | ## 0.0.7
43 |
44 | **Initial Playback Device Changing Support**
45 |
46 | - Added setDevice method to AudioPlayer class.
47 |
48 | ## 0.0.6
49 |
50 | **Microsoft Windows Support**
51 |
52 | - Plugin is now capable of playing audio files on Windows.
53 |
54 | ## 0.0.5
55 |
56 | **A Little Fix**
57 |
58 | - pub package now has miniaudio in it.
59 |
60 | ## 0.0.4
61 |
62 | **Final Improvements**
63 |
64 | - Now plugin uses MethodChannel instead of dart:ffi for calling native methods.
65 | - Any additional setup is not required anymore.
66 |
67 | ## 0.0.3
68 |
69 | **First Public Release**
70 |
71 | - Added docstrings.
72 | - Improved dart usage.
73 | - Fixed wrong sample rate.
74 | - Now Dart code is asynchronous.
75 |
76 |
77 | ## 0.0.2
78 |
79 | **Now Fully Open Source**
80 |
81 | Changed native code to use [miniaudio](https://github.com/mackron/miniaudio)
82 |
83 |
84 | ## 0.0.1
85 |
86 | **Initial Release**
87 |
88 | Supports audio playback on Linux.
89 |
90 | Added mandatory audio playback functions like:
91 | - Loading audio file
92 | - Playing
93 | - Pausing
94 | - Getting duration of an audio file.
95 | - Seeking
96 | - Changing volume
97 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Hitesh Kumar Saini
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ✒ [libwinmedia](https://github.com/harmonoid/libwinmedia) is sequel to this project.
2 | #### It provides network playback, better format support, control & features.
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | #### An audio playback library for Flutter Desktop.
11 |
12 | Feel free to open issue anytime.
13 |
14 |
15 | ## Installing
16 |
17 | Mention in your pubspec.yaml:
18 |
19 | ```yaml
20 | dependencies:
21 | ...
22 | flutter_audio_desktop: ^0.1.0
23 | ```
24 |
25 | ## Using
26 |
27 | ```dart
28 | // Create new instance.
29 | var audioPlayer = new AudioPlayer(id: 0)
30 | ..stream.listen(
31 | (Audio audio) {
32 | // Listen to playback events.
33 | },
34 | );
35 | // Load audio source
36 | audioPlayer.load(
37 | new AudioSource.fromFile(
38 | new File('/home/alexmercerind/music.mp3'),
39 | ),
40 | );
41 | // Start playback.
42 | audioPlayer.play();
43 | // Get audio duration.
44 | audioPlayer.audio.duration;
45 | // Change playback volume.
46 | audioPlayer.setVolume(0.5);
47 | // Change playback position.
48 | audioPlayer.setPosition(Duration(seconds: 10));
49 | // Get playback position.
50 | audioPlayer.audio.position;
51 | Timer(Duration(seconds: 10), () {
52 | // Pause playback.
53 | audioPlayer.pause();
54 | }
55 | // Few other things.
56 | audioPlayer.audio.file;
57 | audioPlayer.audio.isPlaying;
58 | audioPlayer.audio.isCompleted;
59 | audioPlayer.audio.isStopped;
60 |
61 | ```
62 |
63 | Other classes & methods are documented in their docstrings very well.
64 |
65 | See [this](https://github.com/alexmercerind/flutter_audio_desktop/blob/master/example/lib/main.dart) example for a better overview.
66 |
67 | #### Windows
68 |
69 |
70 |
71 | #### Linux
72 |
73 |
74 |
75 | ## Support
76 |
77 | If you want to be kind to me, then consider buying me a coffee.
78 |
79 |
80 |
81 | Thankyou!
82 |
83 |
84 | ## Progress
85 |
86 | |Platform |Status |
87 | |--------------------|----------------------------------------------------------|
88 | |Linux |Working |
89 | |Microsoft Windows |Working |
90 | |MacOS |[Learn More](https://www.youtube.com/watch?v=dQw4w9WgXcQ) |
91 |
92 |
93 | ## License
94 |
95 | I don't want to put any restrictions on how you distribute your Flutter Desktop apps, so this library comes under very permissive software, MIT license.
96 |
97 | Since, other libraries like [libvlcpp](https://github.com/videolan/libvlcpp) or [libvlc](https://www.videolan.org/vlc/libvlc.html) come under GPL & LGPL licenses respectively, so there will be many restrictions if I plan to use them.
98 |
99 | Thus, this project uses [miniaudio](https://github.com/mackron/miniaudio) and [miniaudio_engine](https://github.com/mackron/miniaudio) from [David Reid](https://github.com/mackron) under MIT license.
100 |
101 |
102 | ## Acknowledgments
103 |
104 | - [David Reid](https://github.com/mackron) for his amazing single header libraries [miniaudio](https://github.com/mackron/miniaudio) and [miniaudio_engine](https://github.com/mackron/miniaudio).
105 | - Thanks to [MichealReed](https://github.com/MichealReed) for his support to the project.
106 |
--------------------------------------------------------------------------------
/audioplayer/audioplayer.hpp:
--------------------------------------------------------------------------------
1 | #define MINIAUDIO_IMPLEMENTATION
2 | #include
3 | #include
4 | #include
5 | #include
6 |
7 | #include "miniaudio/miniaudio.h"
8 | #include "miniaudio/miniaudio_engine.h"
9 |
10 | #include "internal/audiodevices.hpp"
11 | #include "internal/callbacks.hpp"
12 |
13 |
14 | const std::string __title__ = "flutter_audio_desktop";
15 | const std::string __author__ = "alexmercerind";
16 | const std::string __license__ = "MIT";
17 | const std::string __version__ = "0.0.9";
18 |
19 |
20 | class AudioPlayerInternal {
21 | protected:
22 | std::vector audioDevices;
23 | AudioDevice* preferredAudioDevice;
24 | AudioDevice* defaultAudioDevice;
25 | ma_device device;
26 | ma_resource_manager resourceManager;
27 | ma_resource_manager_data_source dataSource;
28 | bool isLoaded = false;
29 |
30 | void initialize() {
31 | this->audioDevices = AudioDevices::getAll();
32 | this->defaultAudioDevice = AudioDevices::getDefault();
33 | this->initDevice(
34 | this->preferredAudioDevice == nullptr ? this->defaultAudioDevice: this->preferredAudioDevice
35 | );
36 | this->initResourceManager();
37 | }
38 |
39 | void uninitialize() {
40 | ma_device_uninit(&this->device);
41 | ma_resource_manager_data_source_uninit(&this->dataSource);
42 | ma_resource_manager_uninit(&this->resourceManager);
43 | ma_context_uninit(&deviceContext);
44 | }
45 |
46 | private:
47 | ma_device_config deviceConfig;
48 | ma_resource_manager_config resourceManagerConfig;
49 |
50 | void initDevice(AudioDevice* audioDevice) {
51 | this->deviceConfig = ma_device_config_init(ma_device_type_playback);
52 | this->deviceConfig.dataCallback = dataCallbackStream;
53 | this->deviceConfig.playback.pDeviceID = &audioDevice->info.id;
54 | this->deviceConfig.pUserData = &this->dataSource;
55 | ma_device_init(&deviceContext, &this->deviceConfig, &this->device);
56 | }
57 |
58 | void initResourceManager() {
59 | this->resourceManagerConfig = ma_resource_manager_config_init();
60 | this->resourceManagerConfig.decodedFormat = this->device.playback.format;
61 | this->resourceManagerConfig.decodedChannels = this->device.playback.channels;
62 | this->resourceManagerConfig.decodedSampleRate = this->device.sampleRate;
63 | ma_resource_manager_init(&resourceManagerConfig, &resourceManager);
64 | }
65 | };
66 |
67 |
68 | class AudioPlayer: protected AudioPlayerInternal {
69 | public:
70 | AudioPlayer(AudioDevice* audioDevice) {
71 | this->preferredAudioDevice = audioDevice;
72 | };
73 |
74 | void play(bool isLooping = false) {
75 | if (this->isLoaded) {
76 | ma_resource_manager_data_source_set_looping(&this->dataSource, isLooping);
77 | ma_device_start(&this->device);
78 | }
79 | }
80 |
81 | void pause() {
82 | ma_device_stop(&this->device);
83 | }
84 |
85 | void load(std::string filePath) {
86 | this->isLoaded = true;
87 | this->initialize();
88 | ma_resource_manager_data_source_init(
89 | &this->resourceManager,
90 | filePath.c_str(),
91 | MA_DATA_SOURCE_FLAG_STREAM,
92 | NULL,
93 | &this->dataSource
94 | );
95 | }
96 |
97 | void stop() {
98 | if (this->isLoaded) {
99 | this->uninitialize();
100 | }
101 | this->isLoaded = false;
102 | }
103 |
104 | void setVolume(float volume) {
105 | if (this->isLoaded) {
106 | ma_device_set_master_volume(&this->device, volume);
107 | }
108 | }
109 |
110 | void setPosition(int durationMilliseconds) {
111 | if (this->isLoaded) {
112 | uint32_t durationPCMFrame = ma_calculate_buffer_size_in_frames_from_milliseconds(durationMilliseconds, this->device.sampleRate);
113 | ma_resource_manager_data_source_seek_to_pcm_frame(&this->dataSource, durationPCMFrame);
114 | }
115 | }
116 |
117 | int getDuration() {
118 | if (this->isLoaded) {
119 | unsigned long long durationPCMFrame;
120 | ma_resource_manager_data_source_get_length_in_pcm_frames(
121 | &this->dataSource,
122 | &durationPCMFrame
123 | );
124 | uint32_t duration = ma_calculate_buffer_size_in_milliseconds_from_frames(
125 | static_cast(durationPCMFrame),
126 | this->device.sampleRate
127 | );
128 | return static_cast(duration);
129 | }
130 | else return 0;
131 | }
132 |
133 | int getPosition() {
134 | if (this->isLoaded) {
135 | unsigned long long positionPCMFrame;
136 | ma_resource_manager_data_source_get_cursor_in_pcm_frames(
137 | &this->dataSource,
138 | &positionPCMFrame
139 | );
140 | uint32_t position = ma_calculate_buffer_size_in_milliseconds_from_frames(
141 | static_cast(positionPCMFrame),
142 | this->device.sampleRate
143 | );
144 | return static_cast(position);
145 | }
146 | else return 0;
147 | }
148 | };
149 |
--------------------------------------------------------------------------------
/audioplayer/internal/audiodevices.hpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include