Bonjour,
Voilà ces derniers temps je me suis mis en tête de réaliser un logiciel me permettant de récupérer les données brute d'un des micros branché à mon PC.
PS: Je m'excuse d'avance pour le roman écrit ci-dessous
Avant d'exposé mon problème, je vais vous faire une petite mise en situation.
L'interface est composée d'un bouton "Start capture" qui se change en "Stop capture" si une capture est lancée, d'une listBox me permettant de lister les différents périphériques d'entrées audio et d'une editBox pour affiché les données récupéré lors de la capture.
Pour la gestion des périphérique audio et de la capture, j'ai implémenté une classe nommée "EndPointDevice".
Fichier "EndPointDevice.h"
Voici les méthodes importantes de cette classes.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
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 #ifndef ENDPOINTDEVICE_H #define ENDPOINTDEVICE_H #include <iostream> #include <vector> #include <mmdeviceapi.h> #include <FunctionDiscoveryKeys_devpkey.h> #include <Audioclient.h> using namespace std; typedef void (*OutputStream)(BYTE* &buffer, UINT32 &bufferSize); DWORD WINAPI threadCapture(LPVOID param); class EndPointDevice { private: IMMDevice *device; string id; string name; bool capture; public: EndPointDevice(IMMDevice *device, string id, string name); ~EndPointDevice(); const string getId(); const string getName(); const bool isCapture(); bool startCapture(OutputStream outputStream); void stopCapture(); static void getEndPointDevices(vector<EndPointDevice> &devices); }; #endif
Cette méthode me permet de récupérer l'ensemble des entrées audio et de les placer dans le vector devices.
Code : Sélectionner tout - Visualiser dans une fenêtre à part static bool getEndPointDevices(vector<EndPointDevice> &devices);
Comme son nom l'indique, celle-ci est utilisée pour démarrer la capture audio.
Code : Sélectionner tout - Visualiser dans une fenêtre à part bool startCapture(OutputStream outputStream);
Me permet de stopper la capture audio.
Code : Sélectionner tout - Visualiser dans une fenêtre à part void stopCapture();
Maintenant voici l'implémentation de la classe "EndPointDevice".
Fichier "EndPointDevice.cpp"
Bon ici le plus important à savoir c'est qu'à l'intérieur de la méthode "StartCapture", j'alloue un pointer sur la structure "RefAudioCapture" défini tout en haut du fichier .cpp. Ensuite, cette structure est passée en paramètre pour que je puisse l'utilisée dans un thread.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
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 #include "EndPointDevice.h" typedef struct RefAudioCapture { IAudioClient *pAudioClient; IAudioCaptureClient *pAudioCapture; OutputStream outputStream; bool *capture; RefAudioCapture(IAudioClient *pAudioClient, IAudioCaptureClient *pAudioCapture, OutputStream outputStream, bool *capture) { this->pAudioClient = pAudioClient; this->pAudioCapture = pAudioCapture; this->outputStream = outputStream; this->capture = capture; }; } RefAudioCapture; DWORD WINAPI threadCapture(LPVOID param) { RefAudioCapture audioCapture = *((RefAudioCapture *)param); OutputStream outputStream = *audioCapture.outputStream; UINT32 packetSize; BYTE *data; UINT32 numFramesToRead; DWORD flags; while (*audioCapture.capture) { Sleep(33); audioCapture.pAudioCapture->GetNextPacketSize(&packetSize); while (packetSize > 0) { if (audioCapture.pAudioCapture->GetBuffer(&data, &numFramesToRead, &flags, NULL, NULL) == S_OK) if (*data != NULL) outputStream(data, numFramesToRead); audioCapture.pAudioCapture->ReleaseBuffer(numFramesToRead); audioCapture.pAudioCapture->GetNextPacketSize(&packetSize); } } audioCapture.pAudioCapture->ReleaseBuffer(numFramesToRead); audioCapture.pAudioClient->Stop(); audioCapture.pAudioCapture->Release(); audioCapture.pAudioClient->Release(); return 0; } EndPointDevice::EndPointDevice(IMMDevice *device, string id, string name) { this->device = device; this->id = id; this->name = name; } EndPointDevice::~EndPointDevice() { } const string EndPointDevice::getId() { return id; } const string EndPointDevice::getName() { return name; } bool EndPointDevice::startCapture(OutputStream outputStream) { IAudioClient *pAudioClient; if (device->Activate(_uuidof(IAudioClient), CLSCTX_ALL, NULL, (void **)&pAudioClient) == S_OK) { WAVEFORMATEX *wfe; pAudioClient->GetMixFormat(&wfe); if (pAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, 0, 10000000, 0, wfe, NULL) == S_OK) { IAudioCaptureClient *pAudioCapture; if (pAudioClient->GetService(_uuidof(IAudioCaptureClient), (void **)&pAudioCapture) == S_OK) { RefAudioCapture *refAudioCapture = new RefAudioCapture(pAudioClient, pAudioCapture, outputStream, &capture); capture = true; pAudioClient->Start(); CreateThread(NULL, 0, threadCapture, refAudioCapture, 0, 0); } } } return capture; } void EndPointDevice::stopCapture() { if (capture) capture = false; } void EndPointDevice::getEndPointDevices(vector<EndPointDevice> &devices) { IMMDeviceEnumerator *pEnumerator; CoInitialize(NULL); if (CoCreateInstance(_uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, _uuidof(IMMDeviceEnumerator), (void **)&pEnumerator) == S_OK) { IMMDeviceCollection *pDevices; if (pEnumerator->EnumAudioEndpoints(eCapture, DEVICE_STATE_ACTIVE, &pDevices) == S_OK) { UINT cDevices; if (pDevices->GetCount(&cDevices) == S_OK) { for (UINT i = 0; i < cDevices; i++) { IMMDevice *pDevice; if (pDevices->Item(i, &pDevice) == S_OK) { LPWSTR id; if (pDevice->GetId(&id) == S_OK) { IPropertyStore *pProperties; if (pDevice->OpenPropertyStore(STGM_READ, &pProperties) == S_OK) { PROPVARIANT value; if (pProperties->GetValue(PKEY_Device_FriendlyName, &value) == S_OK) { wstring wsId(id); wstring wsName(value.pwszVal); devices.push_back(EndPointDevice(pDevice, string(wsId.begin(), wsId.end()), string(wsName.begin(), wsName.end()))); } } } } } } } } }
Noté qu'à l'intérieur de ma structure, se trouve une variable de type "OutputStream" (Ce type a également été défini dans le header de la classe), c'est en faite un pointer vers une fonction que je pourrais appeler à l'intérieur du thread pendant la capture.
PS: Si vous en avez marre de lire, vous n'êtes pas obligé de continuer
Je vais juste encore m'attarder sur la méthode "threadCapture" et du fichier "Main.cpp".
Dans la méthode "threadCapture", ma structure "RefAudioCapture" me permet d'avoir un accès aux interfaces suivantes.
C'est ces deux interfaces qui me permettront de récupérer les données à l'aide de la méthode "GetBuffer".
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3 IAudioClient *pAudioClient; IAudioCaptureClient *pAudioCapture;
Une fois les données récupérées j'appelle la méthode outputStream en passant les données que j'ai reçu + sa taille.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 if (audioCapture.pAudioCapture->GetBuffer(&data, &numFramesToRead, &flags, NULL, NULL) == S_OK) if (*data != NULL) outputStream(data, numFramesToRead);
Maintenant, la question que vous devriez vous poser est "D'où vient la méthode outputStream ?".
Alors voilà votre réponse en deux mots.
Fichier "Main.cpp"
Voilà je vous ai tout mis
Code : Sélectionner tout - Visualiser dans une fenêtre à part
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 #include <Windows.h> #include <CommCtrl.h> #include <iostream> #include "EndPointDevice.h" using namespace std; vector<EndPointDevice> devices; unsigned int indexSelected; HWND logCapture; HWND listDevice; HWND btnCapture; char *logs = new char[512]; void outputAudioStream(BYTE* &buffer, UINT32 &bufferSize) { memcpy(logs, buffer, bufferSize); SendMessage(logCapture, WM_SETTEXT, 0, (LPARAM)logs); } void initComponents(const HWND handle, LPCREATESTRUCT windowStruct) { HINSTANCE hInstance = windowStruct->hInstance; DWORD dwStyle = WS_TABSTOP | WS_CHILD | WS_VISIBLE; const UINT widthLog = 200; const UINT heightButton = 100; logCapture = CreateWindowEx(0, WC_EDIT, "Log of capture :\r\n", dwStyle | ES_AUTOVSCROLL | ES_MULTILINE | ES_READONLY, windowStruct->cx - widthLog, 0, widthLog - 20, windowStruct->cy - heightButton, handle, NULL, hInstance, NULL); listDevice = CreateWindowEx(0, WC_LISTBOX, "Endpoint devices", dwStyle | LBS_STANDARD, 0, 0, windowStruct->cx - widthLog, windowStruct->cy - heightButton, handle, NULL, hInstance, NULL); EndPointDevice::getEndPointDevices(devices); for (int i = 0; i < devices.size(); i++) SendMessage(listDevice, LB_ADDSTRING, 0, reinterpret_cast<LPARAM>(devices.at(i).getName().c_str())); btnCapture = CreateWindowEx(0, WC_BUTTON, "Start Capture", dwStyle | BS_DEFPUSHBUTTON, 0, windowStruct->cy - heightButton, windowStruct->cx, heightButton / 2, handle, NULL, hInstance, NULL); } LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_CREATE: { initComponents(hwnd, reinterpret_cast<LPCREATESTRUCT>(lParam)); break; } case WM_COMMAND: { switch (HIWORD(wParam)) { case BN_CLICKED: { int index = (int)SendMessage(listDevice, LB_GETCURSEL, 0, 0); char txtButton[16]; GetWindowText(btnCapture, txtButton, 16); if (strcmp(txtButton, "Start Capture") == 0) { if (index >= 0) { SendMessage(logCapture, WM_CLEAR, 0, 0); SendMessage(btnCapture, WM_SETTEXT, 0, (LPARAM)"Stop Capture"); devices.at(index).startCapture(outputAudioStream); indexSelected = index; } } else { devices.at(indexSelected).stopCapture(); SendMessage(btnCapture, WM_SETTEXT, 0, (LPARAM)"Start Capture"); } break; } case LB_GETSEL: { MessageBox(NULL, "tes", "tesawd", MB_OK); break; } } break; } case WM_DESTROY: { PostQuitMessage(0); break; } default: return DefWindowProc(hwnd, uMsg, wParam, lParam); } return 0; } int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX wnd; wnd.cbSize = sizeof(WNDCLASSEX); wnd.style = CS_HREDRAW | CS_VREDRAW; wnd.lpfnWndProc = WindowProc; wnd.cbClsExtra = 0; wnd.cbWndExtra = 0; wnd.hInstance = hInstance; wnd.hIcon = NULL; wnd.hCursor = NULL; wnd.hbrBackground = (HBRUSH)COLOR_WINDOW; wnd.lpszMenuName = NULL; wnd.lpszClassName = "windowClass"; wnd.hIconSm = NULL; RegisterClassEx(&wnd); HWND handle = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW, "windowClass", "Audio capture", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 480, 320, NULL, NULL, hInstance, 0); ShowWindow(handle, SW_SHOW); UpdateWindow(handle); MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } logCapture = NULL; listDevice = NULL; btnCapture = NULL; devices.clear(); delete[] logs; return 0; }
En faite la méthode est définie tout en haut du fichier.
Puis je l'utilise lors de l'appel à la méthode "startCapture" présent dans ce petit bout de code.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6 void outputAudioStream(BYTE* &buffer, UINT32 &bufferSize) { memcpy(logs, buffer, bufferSize); SendMessage(logCapture, WM_SETTEXT, 0, (LPARAM)logs); }
Voilà je pense que vous savez tous ce qu'il faudrait savoir
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12 if (strcmp(txtButton, "Start Capture") == 0) { if (index >= 0) { SendMessage(logCapture, WM_CLEAR, 0, 0); SendMessage(btnCapture, WM_SETTEXT, 0, (LPARAM)"Stop Capture"); devices.at(index).startCapture(outputAudioStream); indexSelected = index; } }
Faut savoir que j'ai testé toute la partie capture, en mode console avant de m'attaquer à l'interface. Donc il y a pas de blocage à cause des boucle while ou autre.
Il faut aussi dire que je suis un débutant extrême en C++
Maintenant mes questions :aie :
Est-ce que l'appel au code ci-dessous (se trouvant dans la méthode outputAudioStream du Main.cpp) est correcte (pour copier les données du buffer dans les logs) ?
J'ai toujours réussi à récupérer deux, trois données lors de la capture en mode console en parlant dans le micro. Et dès que je les affichais à l'aide de "cout" cela faisait des beep.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3 char *logs = new char[512]; // défini en haut du fichier et non dans la méthode. memcpy(logs, buffer, bufferSize);
Deuxième question :
Est-ce que c'est réellement une des façon de récupérer le flux entrant d'un périphérique audio comme le micro ?
J'ai lu la doc sur le site http://msdn.microsoft.com/en-us/libr...=vs.85%29.aspx mais comme je suis pas vraiment fort en anglais, j'ai peut-être raté quelques informations.
Et dernière question :
Alors là, j'ai essayé de chercher mais j'ai rien trouvé.
Quelles sont les données que l'on récupère à l'aide de la méthode que j'utilise (GetBuffer de l'interface IAudioCaptureClient) ?
J'ai quand même persisté à finir mon programme même sans savoir qu'elle était les données que je récupérais même si j'en ai une vague idée.
J'espère que dans la foulée quelqu'un c'est déjà attardé sur un projet du même type.
J'espère aussi que c'était pas trop ennuyeux à lire :S
En tout cas si vous avez des questions, remarques je me ferais un plaisir de vous répondre.
Je vous remercie d'avance pour vos réponse et bonne journée à tous![]()
Partager