Asm/C++/Delphi/Py/PHP/VBA
 Аватар для Jin X
6812 / 2052 / 238
Регистрация: 14.12.2014
Сообщений: 4,305
Записей в блоге: 12

Нестандартные ситуации при записи звука

10.02.2016, 19:30. Показов 842. Ответов 0
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
1. Имеется вот такой проект (см. аттач), ниже приведён код.
Всё работает чётко. Но есть пара нюансов:
а) Если выбран WAVE_MAPPER и происходит смена аудиоустройства по умолчанию (в настройках "Звука" Windows), прога виснет.
б) Если выбран конкретный девайс и происходит его отключение, прога виснет.
Зависание заключается в том, что при очередном вызове callback-функции зависает функция waveInAddBuffer, т.е. после её вызова код не выполняется (соответственно, выхода из callback-функции нет).
Функции waveInStop/Reset (и даже Close) тоже зависают при нажатии на кнопку "Стоп".
Что делать, как это исправить? Может, какую-то проверку нужно делать перед waveInAddBuffer? И если да, то какую и что делать после (в частности, при изменении аудиоустройства по умолчанию... я думаю, не совсем корректно будет закрывать текущее устройство и открывать новое прямо из callback-функции)?
Стандартная программа "Звукозапись" спокойно переживает изменение аудиоустройства по умолчанию и даже переключает устройство на новое (т.е. продолжает запись с нового устройства). Моя же прога при изменении устройства (если убрать функцию waveInAddBuffer из callback) продолжает запись с того же устройства, что было в самом начале. Кстати, если waveInAddBuffer убрать, то waveInStop/Reset/Close работают как положено и ничего не виснет.

2. И второй вопрос (чтобы новую тему не создавать): каким образом можно отследить изменение состава аудиоустройств (например, что-то отключилось или подключилось) и аудиоустройства по умолчанию (в системных настройках)?
Может, какое-то сообщение посылается всем окнам? Чтобы не проверять каждый раз вручную (например, раз в секунду).
И как, кстати, определить какое из аудиоустройств установлено по умолчанию?

А вот, собственно, и код, касающийся первого вопроса:
Code
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
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
unit Unit1;
 
interface
 
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, MMSystem, ComCtrls, ExtCtrls;
 
type
  TForm1 = class(TForm)
    Label1: TLabel;
    cmbDevice: TComboBox;
    btnStart: TButton;
    btnStop: TButton;
    btnReset: TButton;
    pbNow: TProgressBar;
    pbMax: TProgressBar;
    tmrBars: TTimer;
    lblNow: TLabel;
    lblMax: TLabel;
    lblNowText: TLabel;
    lblMaxText: TLabel;
    btnGetDevList: TButton;
    lblBufsText: TLabel;
    edtBufs: TEdit;
    function GetDevList: Integer;
    procedure FormCreate(Sender: TObject);
    procedure btnStartClick(Sender: TObject);
    procedure btnStopClick(Sender: TObject);
    procedure tmrBarsTimer(Sender: TObject);
    procedure btnResetClick(Sender: TObject);
    procedure btnGetDevListClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
 
  TWAVEINCAPS2 = record
    Caps: TWAVEINCAPS;
    ManufacturerGuid, ProductGuid, NameGuid: TGUID
  end;
 
var
  Form1: TForm1;
 
implementation
 
{$R *.dfm}
 
type
  TBuf = array [0..2205] of SmallInt;
  PBuf = ^TBuf;
const
  BufN = 10;
var
  WaveHdr: array [0..BufN-1] of TWAVEHDR;
  WaveH: HWAVEIN;
  Buf: array [0..BufN-1] of TBuf;
  ErrMsg: array [0..255] of Char;
  WaveNow, WaveMax, Bufs: Integer;
  Stop: Boolean;
 
procedure waveInProc(hwi: HWAVEIN; uMsg, dwInstance, dwParam1, dwParam2: DWord); stdcall;
var i, N, wMin, wMax: Integer;
begin
  if uMsg = MM_WIM_DATA then
  begin
    Inc(Bufs);
    with PWAVEHDR(dwParam1)^ do
    begin
      WaveNow := 0;
      wMin := 0;
      wMax := 0;
      for i := 0 to dwBytesRecorded div 2-1 do
      begin
        N := PBuf(lpData)^[i];
        if N < wMin then wMin := N;
        if N > wMax then wMax := N
      end;
      N := (wMax - wMin) div 2;
      WaveNow := N;
      if N > WaveMax then WaveMax := N;
      if not Stop then
      begin
        dwFlags := dwFlags and (not WHDR_DONE);
        dwBytesRecorded := 0;
        waveInAddBuffer(WaveH, PWAVEHDR(dwParam1), SizeOf(WaveHdr[0]));
      end
    end
  end
end;
 
//  Обновляет список устройств и возвращает номер выбранного устройства или -2, если устройство не найдено
function TForm1.GetDevList: Integer;
var
  i, N: Integer;
  DevIn: TWAVEINCAPS;
  S: String;
begin
  Result := -2;
  S := cmbDevice.Text;
  cmbDevice.Clear;
  N := 0;
  for i := -1 to waveInGetNumDevs-1 do
  begin
    FillChar(DevIn, SizeOf(DevIn), 0);
    if waveInGetDevCaps(i, @DevIn, SizeOf(DevIn)) = MMSYSERR_NOERROR then cmbDevice.Items.Add(DevIn.szPname);
    if DevIn.szPname = S then
    begin
      N := cmbDevice.Items.Count-1;
      Result := i
    end
  end;
  cmbDevice.ItemIndex := N
end;
 
procedure TForm1.FormCreate(Sender: TObject);
begin
  GetDevList
end;
 
procedure TForm1.btnStartClick(Sender: TObject);
var
  WaveFmt: TWAVEFORMATEX;
  i, DevID: Integer;
 
 function ProcessError(Err: Integer): Boolean;
 var i: Integer;
 begin
   Result := (Err <> MMSYSERR_NOERROR);
   if not Result then Exit;
   if WaveH <> 0 then
   begin
     for i := 0 to BufN-1 do
       if WaveHdr[i].dwFlags and WHDR_PREPARED > 0 then waveInUnprepareHeader(WaveH, @WaveHdr[i], SizeOf(WaveHdr[i]));
     waveInClose(WaveH)
   end;
   waveInGetErrorText(Err, @ErrMsg, SizeOf(ErrMsg));
   MessageBox(0, ErrMsg, PChar('Ошибка '+IntToStr(Err)+'!'), MB_OK or MB_ICONERROR or MB_TASKMODAL);
   btnStart.Enabled := True;
   cmbDevice.Enabled := True;
   btnGetDevList.Enabled := True;
 end;
 
begin
  edtBufs.Text := '0';
  DevID := GetDevList;
  if DevID = -2 then
    if MessageBox(0, PChar('Выбранное устройство было отключено.'+Chr(13)+'Продолжить запись с устройства по умолчанию?'), 'Предупреждение', MB_YESNO or MB_ICONWARNING or MB_TASKMODAL) = idNo then Exit;
  btnStart.Enabled := False;
  cmbDevice.Enabled := False;
  btnGetDevList.Enabled := False;
 
  with WaveFmt do
  begin
    wFormatTag := WAVE_FORMAT_PCM;
    nChannels := 1;
    nSamplesPerSec := 44100;
    wBitsPerSample := 16;
    nBlockAlign := nChannels*wBitsPerSample shr 3;
    nAvgBytesPerSec := nSamplesPerSec*nBlockAlign;
    cbSize := 0
  end;
  WaveH := 0;
  if ProcessError(waveInOpen(@WaveH, DevID, @WaveFmt, DWord(@waveInProc), 0, CALLBACK_FUNCTION)) then Exit;
 
  FillChar(WaveHdr, SizeOf(WaveHdr), 0);
  for i := 0 to BufN-1 do
  begin
    with WaveHdr[i] do
    begin
      lpData := @Buf[i];
      dwBufferLength := SizeOf(Buf[i])
    end;
    if ProcessError(waveInPrepareHeader(WaveH, @WaveHdr[i], SizeOf(WaveHdr[i]))) then Exit;
    if ProcessError(waveInAddBuffer(WaveH, @WaveHdr[i], SizeOf(WaveHdr[i]))) then Exit
  end;
 
  WaveNow := 0;
  WaveMax := 0;
  Bufs := 0;
  Stop := False;
  if ProcessError(waveInStart(WaveH)) then Exit;
 
  btnStop.Enabled := True;
  tmrBars.Enabled := True
end;
 
procedure TForm1.btnStopClick(Sender: TObject);
var
  i, Err: Integer;
//  Done: Boolean;
begin
  btnStop.Enabled := False;
 
  Stop := True;
  waveInReset(WaveH);
 
{ Этот код я убираю, т.к. смысла в неё нет, поскольку waveInReset завершится только тогда, когда все буферы обработаются
  repeat
    Done := True;
    Application.ProcessMessages;
    for i := 0 to BufN-1 do
      if WaveHdr[i].dwFlags and WHDR_DONE = 0 then Done := False
  until Done;
}
  for i := 0 to BufN-1 do
    waveInUnprepareHeader(WaveH, @WaveHdr[i], SizeOf(WaveHdr[i]));
 
  Err := waveInClose(WaveH);
  if Err <> MMSYSERR_NOERROR then
  begin
    waveInGetErrorText(Err, @ErrMsg, SizeOf(ErrMsg));
    MessageBox(0, ErrMsg, PChar('Ошибка '+IntToStr(Err)+'!'), MB_OK or MB_ICONERROR or MB_TASKMODAL);
  end;
 
  tmrBarsTimer(nil);
  tmrBars.Enabled := False;
  GetDevList;
  cmbDevice.Enabled := True;
  btnGetDevList.Enabled := True;
  btnStart.Enabled := True
end;
 
procedure TForm1.btnResetClick(Sender: TObject);
begin
  WaveNow := 0;
  WaveMax := 0;
  tmrBarsTimer(nil)
end;
 
procedure TForm1.tmrBarsTimer(Sender: TObject);
begin
  pbNow.Position := Round(Ln(WaveNow+1)/Ln(32768)*100);
  if WaveNow = 0 then lblNow.Caption := '-Inf db'
  else lblNow.Caption := IntToStr(Round((Ln(WaveNow)-Ln(32767))/Ln(10)*20)) + ' db';
  pbMax.Position := Round(Ln(WaveMax+1)/Ln(32768)*100);
  if WaveMax = 0 then lblMax.Caption := '-Inf db'
  else lblMax.Caption := IntToStr(Round((Ln(WaveMax)-Ln(32767))/Ln(10)*20)) + ' db';
  edtBufs.Text := IntToStr(Bufs)
end;
 
procedure TForm1.btnGetDevListClick(Sender: TObject);
begin
  GetDevList
end;
 
end.
Вложения
Тип файла: zip WaveMeter.zip (5.7 Кб, 2 просмотров)
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
10.02.2016, 19:30
Ответы с готовыми решениями:

Реализация многопоточности при записи звука
Здравствуйте, уважаемые форумчане! Не &quot;закидывайте тухлыми помидорами&quot;, но хочу опять задать вопрос про звук. Есть задача: обрабатывать...

Рассинхрон звука с видео при записи игры
Здравствуйте! Мне очень нужна ваша помощь. Я снимаю игры программой MSI Afterburner. Я знаю, что она для разгона видеокарт, но в при записи...

О качестве звука при записи АУДИО cd с конвертацией из flac
По запросу Mich'а были даны рекомендации, как аудиозапись в формате flac записать на компакт-диск, читаемый обычным плеером. Означает ли...

0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
10.02.2016, 19:30
Помогаю со студенческими работами здесь

Нет звука в Sony Vegas при записи видео в OBS
Всем привет! Сразу извиняюсь если не в тот раздел пишу, или вообще не на том форуме. Но есть такая проблема. Не могу понять что не так...

При записи звука windows sound recorder не совпадает число данных в хедере wav-файла с реальным значением?
При записи с микрофона стандартной windows прогой почему не совпадает число данных в хедере wav файла с реальным значением? Например,...

Нестандартные процедуры при работе со строками
Помогите, пожалуйста с программным кодом. Вот условие задачи: Написать программу, заменяющую в тексте все подсловия вида abc на def....

Нестандартные способы добыть дрова - или нестандартные дрова)
Еще расскажу, как я дрова добываю там, где люди все легкодоступное повытаскали. Или зимой это бывает полезно, если сухостоя найти не...

Исключительные ситуации при работе с числами
Поставлена ​​задача сделать приложение, в котором задаются действительное и целое число. На форму выводится сообщение о возможных ошибках...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
1
Ответ Создать тему
Опции темы

Новые блоги и статьи
SDL3 для Desktop (MinGW): Рисуем цветные прямоугольники с помощью рисовальщика SDL3 на Си и C++
8Observer8 17.03.2026
Содержание блога Финальные проекты на Си и на C++: finish-rectangles-sdl3-c. zip finish-rectangles-sdl3-cpp. zip
Символические и жёсткие ссылки в Linux.
algri14 15.03.2026
Существует два типа ссылок — символические и жёсткие. Ссылка в Linux — это запись в каталоге, которая может указывать либо на inode «файла-ИСТОЧНИКА», тогда это будет «жёсткая ссылка» (hard link),. . .
[Owen Logic] Поддержание уровня воды в резервуаре количеством включённых насосов: моделирование и выбор регулятора
ФедосеевПавел 14.03.2026
Поддержание уровня воды в резервуаре количеством включённых насосов: моделирование и выбор регулятора ВВЕДЕНИЕ Выполняя задание на управление насосной группой заполнения резервуара,. . .
делаю науч статью по влиянию грибов на сукцессию
anaschu 13.03.2026
прикрепляю статью
SDL3 для Desktop (MinGW): Создаём пустое окно с нуля для 2D-графики на SDL3, Си и C++
8Observer8 10.03.2026
Содержание блога Финальные проекты на Си и на C++: hello-sdl3-c. zip hello-sdl3-cpp. zip Результат:
Установка CMake и MinGW 13.1 для сборки С и C++ приложений из консоли и из Qt Creator в EXE
8Observer8 10.03.2026
Содержание блога MinGW - это коллекция инструментов для сборки приложений в EXE. CMake - это система сборки приложений. Здесь описаны базовые шаги для старта программирования с помощью CMake и. . .
Как дизайн сайта влияет на конверсию: 7 решений, которые реально повышают заявки
Neotwalker 08.03.2026
Многие до сих пор воспринимают дизайн сайта как “красивую оболочку”. На практике всё иначе: дизайн напрямую влияет на то, оставит человек заявку или уйдёт через несколько секунд. Даже если у вас. . .
Модульная разработка через nuget packages
DevAlt 07.03.2026
Сложившийся в . Net-среде способ разработки чаще всего предполагает монорепозиторий в котором находятся все исходники. При создании нового решения, мы просто добавляем нужные проекты и имеем. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru