Преобразование WAV файла

Дмитрий Маштаков
  С помощью простых программ на Турбо Бейсике легко преобразовывать звуковые файлы WAV формата. Совмещать звук разных записей, переписывать файлы с одной формы записи на другую, проводить нестандартные исследования звука, записанного в файлы.

  У меня возникла задача сравнения звука, созданного в компьютере с записью этого же звука через колонки на диктофон. И для того, чтобы посмотреть, как звук изменяется в такой процедуре записи, и для того, чтобы понять, как мы звук слышим.
  Подробности этого в предыдущей статье - http://www.proza.ru/2015/11/22/1341
  Компьютер создавал звуковой трек со скоростью раздачи - 32000 семпл/сек, а диктофон - 8000 семпл/сек. Утилитарная задача - совместить звуки в одной записи, решается линейным интерполированием амплитуд записи диктофона. Но как это сделать?
  Разумеется, для этого можно найти редакторы, но можно сделать это и средствами Турбо Бейсика.

  Прежде чем делать что-то с WAV файлом, следует выяснить его структуру.
  Запишите в текущий каталог звуковой файл с именем "r.wav" и запустите из этого каталога программу:

'read of head "r.wav" to "t.txt"
OPEN "b",#1,"r.wav"
OPEN "t.txt" FOR OUTPUT AS #2
SEEK #1,0
FOR I=0 TO 43
  GET$ #1,1,A$
  PRINT #2,I;" "+STR$(ASC(A$))+" ";A$
NEXT I
CLOSE #1,#2 : END

==========
В среде Визуал Бейсик 6 эта программа выглядит так -

 'read of head "P_M.wav" to "t.txt"
Open "P_M.wav" For Binary As #1:
Open "t1.txt" For Output As #2
Seek #1, 1
For I = 1 To 44
  Get #1, , L
  Print #2, I; " " + Str(L) '+ " "; S$
Next I
Close #2, #1:
===========

  В результате работы программы, в текстовом файле "t.txt" Вы получите распечатку шапки WAV файла. Как разобраться в ней, написано тут - http://www.proza.ru/2015/04/11/1943

  Обнаружив, что действительно, в записи диктофона скорость раздачи составляет 8000 семпл/сек, и приходится 2 байта на один семпл, приступаем к работе.
  Переписываем запись диктофона в файл "b8.wav" и запускаем программу:

' b8.wav (8000 semp/sec) => r.wav (32000 semp/sec)
OPEN "B",#1,"r.wav"
OPEN "B",#2,"b8.wav"
 SEEK #2,40 : SEEK #1,44
 GET$ #2,4,S$ : ND=CVL(S$) : PRINT ND;
 GET$ #2,2,S$ : B1%=CVI(S$) : PUT$ #1,S$
10 ND=ND-2 : IF ND<1 THEN 999
   GET$ #2,2,S$ : B2%=CVI(S$) : DB=B2%/4-B1%/4 : B=B1%
   M%=B+DB : PUT$ #1,MKI$(M%)
   M%=B+DB*2 : PUT$ #1,MKI$(M%)
   M%=B+DB*3 : PUT$ #1,MKI$(M%)
   PUT$ #1,S$ : B1%=B2% : GOTO 10

999 GOSUB 1000
CLOSE #1,#2 : PRINT " Ok" : END
'============= запись шапки программы
1000 SEEK #1,0
L=LOF(1) : S$="RIFF"+MKL$(L-4)+"WAVEfmt " : S0$=CHR$(0)
S$=S$+CHR$(16)+S0$+S0$+S0$ ' 16 bit/sempl
S$=S$+CHR$(1)+S0$+CHR$(1)+S0$ ' 1 - line format + 1 - mono
S$=S$+MKL$(32000)+MKL$(64000) ' 32 sempl/msek + 64 bite/mcek
S$=S$+CHR$(2)+S0$+CHR$(16)+S0$ ' 2 bite/sempl_all + 16 bit/sempl_mono
S$=S$+"data"+MKL$(L-44) ' date size
WHEAD$=S$ : PUT$ #1,WHEAD$
RETURN
'========

  Программа печатает длину исходного файла, проводит линейную интерполяцию звуковых амплитуд и записывает результат в файл "r.wav". В него же она записывает и новую шапку.
  Если Вам понадобится сделать что-либо другое, то Вы легко можете представленную здесь программу видоизменить. Вы вполне можете наложить звук чтения своего стихотворения на некую музыкальную запись, и сделать это без потери качества записи.

  Последний совет - если Вам нужно будет складывать амплитуды, усредняя их, то не делайте этого так - A%=(A1%+A2%)/2, а делайте так - A%=A1%/2+A2%/2. В первом случае возможно превышение допустимого значения целого числа при сложении чисел, и возникнет ошибка переполнения. Во втором случае такой ошибки не будет.
===

  ПРИЛОЖЕНИЕ  Тексты некоторых удобных программ

  Для преобразования одной из дорожек стереозаписи со скоростью раздачи 48000 семпл/сек в монозапись с раздачей 32000 семпл/сек подойдёт следующая программа

' b48.wav (stereo 48000 semp/sec) => r.wav (mono 32000 semp/sec)
OPEN "B",#1,"r.wav"
OPEN "B",#2,"b48.wav"
 SEEK #2,40 : SEEK #1,44
 GET$ #2,4,S$ : ND=CVL(S$) : PRINT ND;
8 GET$ #2,2,S$ : PUT$ #1,S$
  GET$ #2,2,S$
10 ND=ND-8 : IF ND<1 THEN 999
   GET$ #2,2,S$ : B1%=CVI(S$)
   GET$ #2,2,S$
   GET$ #2,2,S$ : B2%=CVI(S$)
   GET$ #2,2,S$
   B=B1%/2+B2%/2
   M%=B : PUT$ #1,MKI$(M%)
   ND=ND-4 : IF ND>=1 THEN 8
999 GOSUB 1000
CLOSE #1,#2 : PRINT " Ok" : END
'и далее следует та же подпрограмма 1000 для записи шапки
===

   Преобразование первой дорожки стерео записи со скоростью раздачи 44100 семпл/сек в монозапись с раздачей 32000 семпл/сек.

' Z44_32.bas b441.wav (stereo 44100 semp/sec) => r.wav (mono 32000 semp/sec)
DEFDBL N,T,D
OPEN "B",#1,"r.wav"
OPEN "B",#2,"b441.wav"
 SEEK #2,40 : SEEK #1,44
 GET$ #2,4,S$ : ND=CVL(S$) : PRINT ND;
 T1=0 : T2=0 : DT1=1/44100 : DT2=1/32000
   GET$ #2,2,S$ : B1%=CVI(S$)
   GET$ #2,2,S$
   GET$ #2,2,S$ : B2%=CVI(S$)
   GET$ #2,2,S$
8 IF T2>=T1 AND T2<T1+DT1 THEN
   A=B1% : B=B2% : C=A+(B-A)*(T2-T1)/DT2
   C%=INT(C+.5) : PUT$ #1,MKI$(C%)
   T2=T2+DT2 : GOTO 8
   END IF
  ND=ND-4 : IF ND<5 THEN 999
   B1%=B2% : GET$ #2,2,S$ : B2%=CVI(S$)
   GET$ #2,2,S$ : T1=T1+DT1
   GOTO 8
999 GOSUB 1000
CLOSE #1,#2 : PRINT " Ok" : END

'=============
1000 SEEK #1,0
L=LOF(1) : S$="RIFF"+MKL$(L-4)+"WAVEfmt " : S0$=CHR$(0)
S$=S$+CHR$(16)+S0$+S0$+S0$ ' 16 bit/sempl
S$=S$+CHR$(1)+S0$+CHR$(1)+S0$ ' 1 - line format + 1 - mono
S$=S$+MKL$(32000)+MKL$(64000) ' 32 sempl/msek + 64 bite/mcek
S$=S$+CHR$(2)+S0$+CHR$(16)+S0$ ' 2 bite/sempl_all + 16 bit/sempl_mono
S$=S$+"data"+MKL$(L-44) ' date size
WHEAD$=S$ : PUT$ #1,WHEAD$
RETURN
'=============================

Универсальная программа, переводящая стерео запись от одной скорости раздачи к другой. В данном примере от 44100 к 64000 семпл/сек
'=== b44 => c64
DEFDBL N,T,D
OPEN "B",#1,"b44.wav"
OPEN "B",#2,"c64.wav"
 SEEK #1,40 : SEEK #2,44
 GET$ #1,4,S$ : ND=CVL(S$) : PRINT ND
 T1=0 : T2=0 : TX=10 : DT1=1/44100 : DT2=1/64000
   GET$ #1,2,S$ : B1%=CVI(S$)
   GET$ #1,2,S$ : E1%=CVI(S$)
   GET$ #1,2,S$ : B2%=CVI(S$)
   GET$ #1,2,S$ : E2%=CVI(S$)
8 IF T2>=T1 AND T2<T1+DT1 THEN
   A=B1% : B=B2% : C=A+(B-A)*(T2-T1)/DT2
   C%=INT(C+.5) : PUT$ #2,MKI$(C%)
   A=E1% : B=E2% : C=A+(B-A)*(T2-T1)/DT2
   C%=INT(C+.5) : PUT$ #2,MKI$(C%)
   T2=T2+DT2 : GOTO 8
   END IF
  ND=ND-4 : IF ND<5 THEN 999
   B1%=B2% : GET$ #1,2,S$ : B2%=CVI(S$)
   E1%=E2% : GET$ #1,2,S$ : E2%=CVI(S$)
   T1=T1+DT1
   IF T1>TX THEN : PRINT TX : TX=TX+10
   GOTO 8
999 GOSUB 1000
CLOSE #1,#2 : PRINT " Ok" : END

'============= for stereo VEL=64000
1000 SEEK #2,0
L=LOF(2) : S$="RIFF"+MKL$(L-4)+"WAVEfmt " : S0$=CHR$(0)
S$=S$+CHR$(16)+S0$+S0$+S0$ ' 16 bit/sempl
S$=S$+CHR$(1)+S0$+CHR$(2)+S0$ ' 1 - line format + 1 - mono
S$=S$+MKL$(64000)+MKL$(128000) ' 32 sempl/msek + 64 bite/mcek
S$=S$+CHR$(4)+S0$+CHR$(16)+S0$ ' 2 bite/sempl_all + 16 bits_per_amplitude
S$=S$+"data"+MKL$(L-44) ' date size
WHEAD$=S$ : PUT$ #2,WHEAD$
RETURN
'=============================