Skip to content

Commit ab5dfd1

Browse files
committed
feat: device switching & QoL enhancments
Implement device switching and enumeration, refactor sound player. - Improve native library path resolution for runtimes directory. - Add context init, uninit, and device enumeration to miniaudio. - Implement device switching in MiniAudioEngine. - Introduce DeviceType enum and DeviceInfo struct. - Refactor SoundPlayer to SoundPlayerBase for shared logic. - Add SwitchDevices sample to demonstrate device switching. - Update samples namespace to SoundFlow.Samples. - Add SetLoopPoints(TimeSpan) to ISoundPlayer.
1 parent a26f46c commit ab5dfd1

File tree

41 files changed

+1877
-1346
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1877
-1346
lines changed

Native/Submodules/miniaudio

Submodule miniaudio updated 135 files

Native/library.cpp

Lines changed: 138 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,13 @@
1-
#define DR_FLAC_IMPLEMENTATION
2-
#define DR_MP3_IMPLEMENTATION
3-
#define DR_WAV_IMPLEMENTATION
4-
#define MINIAUDIO_IMPLEMENTATION
1+
#define MINIAUDIO_IMPLEMENTATION
2+
3+
#include "library.h"
54

6-
#include "Submodules/miniaudio/miniaudio.h"
75

86
#define LIBRARY_H
97

108
// Helper macro for memory allocation.
119
#define sf_create(t) (t*) ma_malloc(sizeof(t), NULL)
1210

13-
void sf_debug(const char *msg, ...) {
14-
va_list args;
15-
va_start(args, msg);
16-
17-
// SF: Print
18-
freopen("native_output.txt", "a", stdout); // Redirect stdout to a file
19-
vprintf(msg, args);
20-
fclose(stdout);
21-
22-
va_end(args);
23-
}
24-
2511
extern "C" {
2612
// Frees a structure allocated with sf_create().
2713
MA_API void sf_free(void *ptr) {
@@ -43,10 +29,17 @@ MA_API ma_device *sf_allocate_device() {
4329
return sf_create(ma_device);
4430
}
4531

32+
// Allocate memory for a context struct.
33+
MA_API ma_context *sf_allocate_context() {
34+
return sf_create(ma_context);
35+
}
36+
4637
// Allocate memory for a device configuration struct.
4738
MA_API ma_device_config *sf_allocate_device_config(const ma_device_type deviceType, const ma_format format,
4839
const ma_uint32 channels, const ma_uint32 sampleRate,
49-
const ma_device_data_proc dataCallback) {
40+
const ma_device_data_proc dataCallback,
41+
const ma_device_id *playbackDeviceId,
42+
const ma_device_id *captureDeviceId) {
5043
auto *config = sf_create(ma_device_config);
5144
if (config == nullptr) {
5245
return nullptr;
@@ -65,6 +58,9 @@ MA_API ma_device_config *sf_allocate_device_config(const ma_device_type deviceTy
6558
config->capture.format = format;
6659
config->capture.channels = channels;
6760

61+
// Set device IDs
62+
config->playback.pDeviceID = playbackDeviceId;
63+
config->capture.pDeviceID = captureDeviceId;
6864

6965
return config;
7066
}
@@ -80,7 +76,6 @@ MA_API ma_decoder_config *sf_allocate_decoder_config(const ma_format outputForma
8076
MA_ZERO_OBJECT(pConfig);
8177
*pConfig = ma_decoder_config_init(outputFormat, outputChannels, outputSampleRate);
8278

83-
8479
return pConfig;
8580
}
8681

@@ -98,18 +93,133 @@ MA_API ma_encoder_config *sf_allocate_encoder_config(const ma_encoding_format en
9893
return pConfig;
9994
}
10095

101-
// Seek current stream position to a specific offset (necessary to call ma_decoder_seek_to_pcm_frame from native code because of invoking issue).
102-
MA_API ma_result sf_decoder_seek_to_frame(ma_decoder *decoder, const ma_uint64 frameIndex) {
103-
return ma_decoder_seek_to_pcm_frame(decoder, frameIndex);
96+
ma_result sf_get_devices(ma_context *context, sf_device_info **ppPlaybackDeviceInfos,
97+
sf_device_info **ppCaptureDeviceInfos, ma_uint32 *pPlaybackDeviceCount,
98+
ma_uint32 *pCaptureDeviceCount) {
99+
ma_device_info *pPlaybackDevices = nullptr;
100+
ma_device_info *pCaptureDevices = nullptr;
101+
102+
const auto result = ma_context_get_devices(context,
103+
&pPlaybackDevices,
104+
pPlaybackDeviceCount,
105+
&pCaptureDevices,
106+
pCaptureDeviceCount);
107+
108+
if (result != MA_SUCCESS || *pPlaybackDeviceCount == 0 && *pCaptureDeviceCount == 0) {
109+
return result;
110+
}
111+
112+
sf_device_info *playbackDeviceInfos = nullptr; // Local variables, better names
113+
sf_device_info *captureDeviceInfos = nullptr;
114+
115+
if (*pPlaybackDeviceCount > 0) {
116+
playbackDeviceInfos = static_cast<sf_device_info *>(ma_malloc(sizeof(sf_device_info) * *pPlaybackDeviceCount,
117+
nullptr));
118+
if (playbackDeviceInfos == nullptr) {
119+
sf_free(pPlaybackDevices);
120+
sf_free(pCaptureDevices);
121+
return MA_OUT_OF_MEMORY;
122+
}
123+
}
124+
125+
if (*pCaptureDeviceCount > 0) {
126+
captureDeviceInfos = static_cast<sf_device_info *>(ma_malloc(sizeof(sf_device_info) * *pCaptureDeviceCount,
127+
nullptr));
128+
if (captureDeviceInfos == nullptr) {
129+
sf_free(pPlaybackDevices);
130+
sf_free(pCaptureDevices);
131+
sf_free(playbackDeviceInfos);
132+
return MA_OUT_OF_MEMORY;
133+
}
134+
}
135+
136+
for (ma_uint32 iDevice = 0; iDevice < *pPlaybackDeviceCount; ++iDevice) {
137+
auto *pPlaybackDeviceInfo = &pPlaybackDevices[iDevice];
138+
sf_device_info deviceInfo;
139+
deviceInfo.id = &pPlaybackDeviceInfo->id;
140+
strncpy(deviceInfo.name, pPlaybackDeviceInfo->name, MA_MAX_DEVICE_NAME_LENGTH); // Use strncpy
141+
deviceInfo.name[MA_MAX_DEVICE_NAME_LENGTH] = '\0'; // Ensure null termination
142+
deviceInfo.isDefault = pPlaybackDeviceInfo->isDefault;
143+
deviceInfo.nativeDataFormatCount = pPlaybackDeviceInfo->nativeDataFormatCount;
144+
145+
if (deviceInfo.nativeDataFormatCount > 0) {
146+
// Allocate memory for nativeDataFormats
147+
deviceInfo.nativeDataFormats = static_cast<native_data_format *>(ma_malloc(
148+
sizeof(native_data_format) * pPlaybackDeviceInfo->nativeDataFormatCount,
149+
nullptr));
150+
151+
// Copy all nativeDataFormats
152+
for (ma_uint32 iFormat = 0; iFormat < pPlaybackDeviceInfo->nativeDataFormatCount; ++iFormat) {
153+
deviceInfo.nativeDataFormats[iFormat].format = pPlaybackDeviceInfo->nativeDataFormats[iFormat].format;
154+
deviceInfo.nativeDataFormats[iFormat].channels = pPlaybackDeviceInfo->nativeDataFormats[iFormat].
155+
channels;
156+
deviceInfo.nativeDataFormats[iFormat].sampleRate = pPlaybackDeviceInfo->nativeDataFormats[iFormat].
157+
sampleRate;
158+
deviceInfo.nativeDataFormats[iFormat].flags = pPlaybackDeviceInfo->nativeDataFormats[iFormat].flags;
159+
}
160+
}
161+
162+
if (playbackDeviceInfos != nullptr)
163+
playbackDeviceInfos[iDevice] = deviceInfo;
164+
}
165+
166+
for (ma_uint32 iDevice = 0; iDevice < *pCaptureDeviceCount; ++iDevice) {
167+
auto *pCaptureDeviceInfo = &pCaptureDevices[iDevice];
168+
sf_device_info deviceInfo;
169+
deviceInfo.id = &pCaptureDeviceInfo->id;
170+
strncpy(deviceInfo.name, pCaptureDeviceInfo->name, MA_MAX_DEVICE_NAME_LENGTH);
171+
deviceInfo.name[MA_MAX_DEVICE_NAME_LENGTH] = '\0'; // Ensure null termination
172+
deviceInfo.isDefault = pCaptureDeviceInfo->isDefault;
173+
deviceInfo.nativeDataFormatCount = pCaptureDeviceInfo->nativeDataFormatCount;
174+
175+
if (deviceInfo.nativeDataFormatCount > 0) {
176+
// Allocate memory for nativeDataFormats
177+
deviceInfo.nativeDataFormats = static_cast<native_data_format *>(ma_malloc(
178+
sizeof(native_data_format) * pCaptureDeviceInfo->nativeDataFormatCount,
179+
nullptr));
180+
181+
// Copy all nativeDataFormats
182+
for (ma_uint32 iFormat = 0; iFormat < pCaptureDeviceInfo->nativeDataFormatCount; ++iFormat) {
183+
deviceInfo.nativeDataFormats[iFormat].format = pCaptureDeviceInfo->nativeDataFormats[iFormat].format;
184+
deviceInfo.nativeDataFormats[iFormat].channels = pCaptureDeviceInfo->nativeDataFormats[iFormat].
185+
channels;
186+
deviceInfo.nativeDataFormats[iFormat].sampleRate = pCaptureDeviceInfo->nativeDataFormats[iFormat].
187+
sampleRate;
188+
deviceInfo.nativeDataFormats[iFormat].flags = pCaptureDeviceInfo->nativeDataFormats[iFormat].flags;
189+
}
190+
}
191+
192+
if (captureDeviceInfos != nullptr)
193+
captureDeviceInfos[iDevice] = deviceInfo;
194+
}
195+
196+
*ppPlaybackDeviceInfos = playbackDeviceInfos;
197+
*ppCaptureDeviceInfos = captureDeviceInfos;
198+
199+
return result;
104200
}
105201

106-
// Seek current stream position to a specific time in seconds.
107-
MA_API ma_result sf_decoder_seek_to_time(ma_decoder *decoder, const double timeInSec) {
108-
if (timeInSec < 0) {
109-
return MA_INVALID_ARGS;
202+
203+
/*
204+
MA_API ma_result sf_get_devices(ma_context *context, ma_device_info **ppPlaybackDeviceInfos,
205+
const ma_device_info **ppCaptureDeviceInfos, ma_uint32 *pPlaybackDeviceCount,
206+
ma_uint32 *pCaptureDeviceCount) {
207+
ma_device_info *pPlaybackDevices = nullptr;
208+
ma_device_info *pCaptureDevices = nullptr;
209+
210+
const auto result = ma_context_get_devices(context,
211+
&pPlaybackDevices,
212+
pPlaybackDeviceCount,
213+
&pCaptureDevices,
214+
pCaptureDeviceCount);
215+
216+
if (result == MA_SUCCESS) {
217+
*ppPlaybackDeviceInfos = pPlaybackDevices;
218+
*ppCaptureDeviceInfos = pCaptureDevices;
110219
}
111220
112-
auto target_frame = static_cast<ma_uint64>(timeInSec * decoder->outputSampleRate);
113-
return ma_decoder_seek_to_pcm_frame(decoder, target_frame);
221+
return result;
114222
}
223+
224+
*/
115225
} // End of extern "C" block

Native/library.h

Lines changed: 41 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,36 +5,57 @@
55
#ifndef LIBRARY_H
66
#define LIBRARY_H
77

8+
#include <vector>
9+
810
#include "Submodules/miniaudio/miniaudio.h"
911

10-
extern "C" {
12+
struct native_data_format {
13+
ma_format format;
14+
ma_uint32 channels;
15+
ma_uint32 sampleRate;
16+
ma_uint32 flags;
17+
};
18+
19+
struct sf_device_info {
20+
ma_device_id *id;
21+
char name[MA_MAX_DEVICE_NAME_LENGTH + 1]; // MA_MAX_DEVICE_NAME_LENGTH is 255
22+
bool isDefault;
23+
ma_uint32 nativeDataFormatCount;
24+
native_data_format *nativeDataFormats;
25+
};
1126

12-
// Frees a structure allocated with sf_create().
13-
MA_API void sf_free(void* ptr);
27+
extern "C" {
28+
// Frees a structure allocated with sf_create().
29+
MA_API void sf_free(void *ptr);
1430

15-
// Allocate memory for a decoder struct.
16-
MA_API ma_decoder* sf_allocate_decoder();
31+
// Allocate memory for a decoder struct.
32+
MA_API ma_decoder *sf_allocate_decoder();
1733

18-
// Allocate memory for an encoder struct.
19-
MA_API void* sf_allocate_encoder();
34+
// Allocate memory for an encoder struct.
35+
MA_API ma_encoder *sf_allocate_encoder();
2036

21-
// Allocate memory for a device struct.
22-
MA_API void* sf_allocate_device();
37+
// Allocate memory for a device struct.
38+
MA_API ma_device *sf_allocate_device();
2339

24-
// Allocate memory for a device configuration struct.
25-
MA_API ma_device_config* sf_allocate_device_config(ma_device_type deviceType, ma_uint32 sampleRate, ma_device_data_proc dataCallback);
40+
// Allocate memory for a context struct.
41+
MA_API ma_context *sf_allocate_context();
2642

27-
// Allocate memory for a decoder configuration struct.
28-
MA_API ma_decoder_config* sf_allocate_decoder_config(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate);
43+
// Allocate memory for a device configuration struct.
44+
MA_API ma_device_config *sf_allocate_device_config(ma_device_type deviceType, ma_format format, ma_uint32 channels,
45+
ma_uint32 sampleRate, ma_device_data_proc dataCallback,
46+
const ma_device_id *playbackDeviceId, const ma_device_id *captureDeviceId);
2947

30-
// Allocate memory for an encoder configuration struct.
31-
MA_API ma_encoder_config* sf_allocate_encoder_config(ma_encoding_format encodingFormat, ma_format format, ma_uint32 channels, ma_uint32 sampleRate);
48+
// Allocate memory for a decoder configuration struct.
49+
MA_API ma_decoder_config *sf_allocate_decoder_config(ma_format outputFormat, ma_uint32 outputChannels,
50+
ma_uint32 outputSampleRate);
3251

33-
// Seek current stream position to a specific offset.
34-
MA_API ma_result sf_decoder_seek_to_frame(ma_decoder* decoder, ma_uint64 frameIndex);
52+
// Allocate memory for an encoder configuration struct.
53+
MA_API ma_encoder_config *sf_allocate_encoder_config(ma_encoding_format encodingFormat, ma_format format,
54+
ma_uint32 channels, ma_uint32 sampleRate);
3555

36-
// Seek current stream position to a specific time in seconds.
37-
MA_API ma_result sf_decoder_seek_to_time(const ma_decoder* decoder, double timeInSec);
56+
MA_API ma_result sf_get_devices(ma_context *context, sf_device_info **ppPlaybackDeviceInfos,
57+
sf_device_info **ppCaptureDeviceInfos, ma_uint32 *pPlaybackDeviceCount,
58+
ma_uint32 *pCaptureDeviceCount);
3859
}
3960

40-
#endif // LIBRARY_H
61+
#endif // LIBRARY_H

README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,7 @@ We welcome contributions to SoundFlow! If you'd like to contribute, please follo
119119
* Fork the repository.
120120
* Create a new branch for your changes.
121121
* Make your changes, following the project's coding style and conventions.
122-
* Write unit tests for your code.
123-
* Ensure all tests pass.
122+
* Make sure your changes is well tested.
124123
* Submit a pull request to the `master` branch.
125124
4. **Coding Style:**
126125
* Follow the .NET coding conventions.

Samples/SoundFlow.SimplePlayer/ComponentTests.cs renamed to Samples/SoundFlow.Samples.SimplePlayer/ComponentTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
using SoundFlow.Visualization;
99
using VoiceActivityDetector = SoundFlow.Components.VoiceActivityDetector;
1010

11-
namespace SoundFlow.SimplePlayer;
11+
namespace SoundFlow.Samples.SimplePlayer;
1212

1313
internal static class ComponentTests
1414
{

Samples/SoundFlow.SimplePlayer/DataProviderTests.cs renamed to Samples/SoundFlow.Samples.SimplePlayer/DataProviderTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
using SoundFlow.Interfaces;
66
using SoundFlow.Providers;
77

8-
namespace SoundFlow.SimplePlayer;
8+
namespace SoundFlow.Samples.SimplePlayer;
99

1010
internal static class DataProviderTests
1111
{

Samples/SoundFlow.SimplePlayer/EqualizerPresets.cs renamed to Samples/SoundFlow.Samples.SimplePlayer/EqualizerPresets.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using SoundFlow.Modifiers;
22

3-
namespace SoundFlow.SimplePlayer;
3+
namespace SoundFlow.Samples.SimplePlayer;
44

55
public static class EqualizerPresets
66
{

0 commit comments

Comments
 (0)