1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
|
/*
Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely.
*/
#define SDL_MAIN_USE_CALLBACKS 1
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#include <SDL3/SDL_test.h>
static SDL_Window *window = NULL;
static SDL_Renderer *renderer = NULL;
static SDL_AudioStream *stream_in = NULL;
static SDL_AudioStream *stream_out = NULL;
static SDLTest_CommonState *state = NULL;
SDL_AppResult SDL_AppInit(void **appstate, int argc, char **argv)
{
SDL_AudioDeviceID *devices;
SDL_AudioSpec outspec;
SDL_AudioSpec inspec;
SDL_AudioDeviceID device;
SDL_AudioDeviceID want_device = SDL_AUDIO_DEVICE_DEFAULT_RECORDING;
const char *devname = NULL;
int i;
/* this doesn't have to run very much, so give up tons of CPU time between iterations. */
SDL_SetHint(SDL_HINT_MAIN_CALLBACK_RATE, "15");
/* Initialize test framework */
state = SDLTest_CommonCreateState(argv, 0);
if (!state) {
return SDL_APP_SUCCESS;
}
/* Parse commandline */
for (i = 1; i < argc;) {
int consumed;
consumed = SDLTest_CommonArg(state, i);
if (!consumed) {
if (!devname) {
devname = argv[i];
consumed = 1;
}
}
if (consumed <= 0) {
static const char *options[] = { "[device_name]", NULL };
SDLTest_CommonLogUsage(state, argv[0], options);
return SDL_APP_FAILURE;
}
i += consumed;
}
/* Load the SDL library */
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s", SDL_GetError());
return SDL_APP_SUCCESS;
}
if (!SDL_CreateWindowAndRenderer("testaudiorecording", 320, 240, 0, &window, &renderer)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create SDL window and renderer: %s", SDL_GetError());
return SDL_APP_SUCCESS;
}
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);
SDL_RenderPresent(renderer);
SDL_Log("Using audio driver: %s", SDL_GetCurrentAudioDriver());
devices = SDL_GetAudioRecordingDevices(NULL);
for (i = 0; devices[i] != 0; i++) {
const char *name = SDL_GetAudioDeviceName(devices[i]);
SDL_Log(" Recording device #%d: '%s'", i, name);
if (devname && (SDL_strcmp(devname, name) == 0)) {
want_device = devices[i];
}
}
if (devname && (want_device == SDL_AUDIO_DEVICE_DEFAULT_RECORDING)) {
SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Didn't see a recording device named '%s', using the system default instead.", devname);
devname = NULL;
}
/* DirectSound can fail in some instances if you open the same hardware
for both recording and output and didn't open the output end first,
according to the docs, so if you're doing something like this, always
open your recording devices second in case you land in those bizarre
circumstances. */
SDL_Log("Opening default playback device...");
device = SDL_OpenAudioDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, NULL);
if (!device) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't open an audio device for playback: %s!", SDL_GetError());
SDL_free(devices);
return SDL_APP_FAILURE;
}
SDL_PauseAudioDevice(device);
SDL_GetAudioDeviceFormat(device, &outspec, NULL);
stream_out = SDL_CreateAudioStream(&outspec, &outspec);
if (!stream_out) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create an audio stream for playback: %s!", SDL_GetError());
SDL_free(devices);
return SDL_APP_FAILURE;
} else if (!SDL_BindAudioStream(device, stream_out)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't bind an audio stream for playback: %s!", SDL_GetError());
SDL_free(devices);
return SDL_APP_FAILURE;
}
SDL_Log("Opening recording device %s%s%s...",
devname ? "'" : "",
devname ? devname : "[[default]]",
devname ? "'" : "");
device = SDL_OpenAudioDevice(want_device, NULL);
if (!device) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't open an audio device for recording: %s!", SDL_GetError());
SDL_free(devices);
return SDL_APP_FAILURE;
}
SDL_free(devices);
SDL_PauseAudioDevice(device);
SDL_GetAudioDeviceFormat(device, &inspec, NULL);
stream_in = SDL_CreateAudioStream(&inspec, &inspec);
if (!stream_in) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create an audio stream for recording: %s!", SDL_GetError());
return SDL_APP_FAILURE;
} else if (!SDL_BindAudioStream(device, stream_in)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't bind an audio stream for recording: %s!", SDL_GetError());
return SDL_APP_FAILURE;
}
SDL_SetAudioStreamFormat(stream_in, NULL, &outspec); /* make sure we output at the playback format. */
SDL_Log("Ready! Hold down mouse or finger to record!");
return SDL_APP_CONTINUE;
}
SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
{
if (event->type == SDL_EVENT_QUIT) {
return SDL_APP_SUCCESS;
} else if (event->type == SDL_EVENT_KEY_DOWN) {
if (event->key.key == SDLK_ESCAPE) {
return SDL_APP_SUCCESS;
}
} else if (event->type == SDL_EVENT_MOUSE_BUTTON_DOWN) {
if (event->button.button == 1) {
SDL_PauseAudioStreamDevice(stream_out);
SDL_FlushAudioStream(stream_out); /* so no samples are held back for resampling purposes. */
SDL_ResumeAudioStreamDevice(stream_in);
}
} else if (event->type == SDL_EVENT_MOUSE_BUTTON_UP) {
if (event->button.button == 1) {
SDL_PauseAudioStreamDevice(stream_in);
SDL_FlushAudioStream(stream_in); /* so no samples are held back for resampling purposes. */
SDL_ResumeAudioStreamDevice(stream_out);
}
}
return SDL_APP_CONTINUE;
}
SDL_AppResult SDL_AppIterate(void *appstate)
{
if (!SDL_AudioStreamDevicePaused(stream_in)) {
SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255);
} else {
SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
}
SDL_RenderClear(renderer);
SDL_RenderPresent(renderer);
/* Feed any new data we recorded to the output stream. It'll play when we unpause the device. */
while (SDL_GetAudioStreamAvailable(stream_in) > 0) {
Uint8 buf[1024];
const int br = SDL_GetAudioStreamData(stream_in, buf, sizeof(buf));
if (br < 0) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to read from input audio stream: %s", SDL_GetError());
return SDL_APP_FAILURE;
} else if (!SDL_PutAudioStreamData(stream_out, buf, br)) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to write to output audio stream: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
}
return SDL_APP_CONTINUE;
}
void SDL_AppQuit(void *appstate, SDL_AppResult result)
{
SDL_Log("Shutting down.");
const SDL_AudioDeviceID devid_in = SDL_GetAudioStreamDevice(stream_in);
const SDL_AudioDeviceID devid_out = SDL_GetAudioStreamDevice(stream_out);
SDL_CloseAudioDevice(devid_in); /* !!! FIXME: use SDL_OpenAudioDeviceStream instead so we can dump this. */
SDL_CloseAudioDevice(devid_out);
SDL_DestroyAudioStream(stream_in);
SDL_DestroyAudioStream(stream_out);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
SDLTest_CommonDestroyState(state);
}
|