Covellite++  Version: 2.3.0 Revision: ??? Platform: x64 Build: 23:13 04.01.2025
Кроссплатформенный фреймворк для разработки приложений на С++
Загрузка...
Поиск...
Не найдено
SoundDevice.hpp
1
2#pragma once
3#include <string>
4#include <vector>
5#include <mutex>
6#include <memory>
7#include <algorithm>
8#include <cstdio>
9
10#include <miniaudio/miniaudio.hpp>
11
12class SoundDevice final
13{
14public:
15 class Sound final
16 {
17 public:
18 // cppcheck-suppress functionStatic
19 void Dummy(void) const {};
20
21 public:
22 SoundDevice * m_pDevice;
23 ::std::vector<uint8_t> m_Data;
24 ma_decoder m_Decoder;
25 ma_uint64 m_Lenght = 0;
26 ma_uint64 m_CurrentPosition = 0;
27
28 public:
29 Sound(SoundDevice * _pDevice, const ::std::string & _PathToFile) :
30 m_pDevice(_pDevice)
31 {
32 const auto Result =
33 ma_decoder_init_file(_PathToFile.c_str(), &_pDevice->m_DecoderConfig, &m_Decoder);
34 if (Result != MA_SUCCESS)
35 {
36 throw ::std::runtime_error{ " ma_decoder_init_...() fail." };
37 }
38
39 // Длина композиции в кадрах
40 ma_decoder_get_length_in_pcm_frames(&m_Decoder, &m_Lenght);
41
42 ::std::lock_guard<::std::mutex> lock(m_pDevice->m_Mutex);
43 m_pDevice->m_Decoders.push_back(this);
44 }
45
46 Sound(SoundDevice * _pDevice, const ::std::vector<uint8_t> & _Data) :
47 m_pDevice(_pDevice),
48 m_Data(_Data)
49 {
50 const auto Result = ma_decoder_init_memory(m_Data.data(),
51 m_Data.size(), &_pDevice->m_DecoderConfig, &m_Decoder);
52 if (Result != MA_SUCCESS)
53 {
54 throw ::std::runtime_error{ " ma_decoder_init_...() fail." };
55 }
56
57 // Длина композиции в кадрах
58 ma_decoder_get_length_in_pcm_frames(&m_Decoder, &m_Lenght);
59
60 ::std::lock_guard<::std::mutex> lock(m_pDevice->m_Mutex);
61 m_pDevice->m_Decoders.push_back(this);
62 }
63
64 ~Sound(void)
65 {
66 ::std::lock_guard<::std::mutex> lock(m_pDevice->m_Mutex);
67 const auto itThis = ::std::find(m_pDevice->m_Decoders.cbegin(),
68 m_pDevice->m_Decoders.cend(), this);
69 m_pDevice->m_Decoders.erase(itThis);
70 ma_decoder_uninit(&m_Decoder);
71 }
72 };
73
74public:
75 template<class T>
76 ::std::shared_ptr<Sound> Make(const T & _PathToFile)
77 {
78 return ::std::make_shared<Sound>(this, _PathToFile);
79 }
80
81private:
82 static void cbData(
83 ma_device * _pDevice,
84 void * _pOutput,
85 const void *,
86 ma_uint32 _FrameCount)
87 {
88 auto * pSoundDevice = reinterpret_cast<SoundDevice *>(_pDevice->pUserData);
89 if (pSoundDevice == nullptr) return;
90
91 auto * pOutput = reinterpret_cast<float *>(_pOutput);
92 memset(pOutput, 0x00, sizeof(float) * _FrameCount * CHANNEL_COUNT);
93
94 ::std::vector<float> Buffer(_FrameCount * CHANNEL_COUNT, 0.0f);
95
96 ::std::lock_guard<::std::mutex> lock(pSoundDevice->m_Mutex);
97
98 for (auto * pSound : pSoundDevice->m_Decoders)
99 {
100 ma_uint64 FrameCount = 0;
101
102 ma_decoder_read_pcm_frames(&pSound->m_Decoder, Buffer.data(), _FrameCount, &FrameCount);
103
104 if (FrameCount == 0)
105 {
106 // Повтор воспроизведения
107 pSound->m_CurrentPosition = 0;
108
109 // Зацикливает воспроизведение, начиная с конкретного кадра
110 const auto Result = ma_decoder_seek_to_pcm_frame(&pSound->m_Decoder, 0);
111 if (Result != MA_SUCCESS) continue;
112 }
113 else
114 {
115 pSound->m_CurrentPosition += FrameCount;
116
117 // Смешивание сэмплов нескольких сигналов
118 for (::std::size_t i = 0; i < Buffer.size(); i++)
119 {
120 pOutput[i] += Buffer[i];
121 //* 0.25f; // Громкость звука
122 }
123 }
124 }
125 }
126
127private:
128 static const ma_uint32 CHANNEL_COUNT = 2;
129 const ma_decoder_config m_DecoderConfig;
130 ma_device_config m_DeviceConfig;
131 ma_device m_Device;
132 ::std::vector<Sound *> m_Decoders;
133 ::std::mutex m_Mutex;
134
135public:
136 SoundDevice(void) :
137 m_DecoderConfig{ ma_decoder_config_init(ma_format_f32, CHANNEL_COUNT, 44100) },
138 m_DeviceConfig(ma_device_config_init(ma_device_type_playback))
139 {
140 m_DeviceConfig.playback.format = m_DecoderConfig.format;
141 m_DeviceConfig.playback.channels = m_DecoderConfig.channels;
142 m_DeviceConfig.sampleRate = m_DecoderConfig.sampleRate;
143 m_DeviceConfig.dataCallback = cbData;
144 m_DeviceConfig.pUserData = this;
145
146 auto Result = ma_device_init(NULL, &m_DeviceConfig, &m_Device);
147 if (Result != MA_SUCCESS)
148 {
149 throw ::std::runtime_error{ "ma_device_init() fail." };
150 }
151
152 Result = ma_device_start(&m_Device);
153 if (Result != MA_SUCCESS)
154 {
155 throw ::std::runtime_error{ "ma_device_start() fail." };
156 }
157 }
158 ~SoundDevice(void)
159 {
160 ma_device_uninit(&m_Device);
161 }
162};