<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
	<title type="html"><![CDATA[Серый форум &mdash; VBS/WSH: Синтез звуков и SAPI]]></title>
	<link rel="self" href="https://forum.script-coding.com/extern.php?action=feed&amp;tid=11131&amp;type=atom" />
	<updated>2015-12-08T18:55:52Z</updated>
	<generator>PunBB</generator>
	<id>https://forum.script-coding.com/viewtopic.php?id=11131</id>
		<entry>
			<title type="html"><![CDATA[Re: VBS/WSH: Синтез звуков и SAPI]]></title>
			<link rel="alternate" href="https://forum.script-coding.com/viewtopic.php?pid=99585#p99585" />
			<content type="html"><![CDATA[<p>Довольно классно. Достойно коллекции <img src="//forum.script-coding.com/img/smilies/smile.png" width="15" height="15" /></p>]]></content>
			<author>
				<name><![CDATA[JSmаn]]></name>
				<uri>https://forum.script-coding.com/profile.php?id=24434</uri>
			</author>
			<updated>2015-12-08T18:55:52Z</updated>
			<id>https://forum.script-coding.com/viewtopic.php?pid=99585#p99585</id>
		</entry>
		<entry>
			<title type="html"><![CDATA[VBS/WSH: Синтез звуков и SAPI]]></title>
			<link rel="alternate" href="https://forum.script-coding.com/viewtopic.php?pid=99568#p99568" />
			<content type="html"><![CDATA[<p>Из любопытства решил поэкспериментироавть с синтезом звуков и проигрыванием с помощью SAPI.SpVoice. Приведенный ниже код генерирует гармонические сигналы заданной частоты и длительности, миксует их, накладывает рандомное эхо, нормализует, обрезает тишину, конвертирует в поток и воспроизводит звук:</p><div class="codebox"><pre><code>Option Explicit

&#039; Стандартные форматы PCM
Const SAFT8kHz8BitMono = 4
Const SAFT8kHz8BitStereo = 5
Const SAFT8kHz16BitMono = 6
Const SAFT8kHz16BitStereo = 7
Const SAFT11kHz8BitMono = 8
Const SAFT11kHz8BitStereo = 9
Const SAFT11kHz16BitMono = 10
Const SAFT11kHz16BitStereo = 11
Const SAFT12kHz8BitMono = 12
Const SAFT12kHz8BitStereo = 13
Const SAFT12kHz16BitMono = 14
Const SAFT12kHz16BitStereo = 15
Const SAFT16kHz8BitMono = 16
Const SAFT16kHz8BitStereo = 17
Const SAFT16kHz16BitMono = 18
Const SAFT16kHz16BitStereo = 19
Const SAFT22kHz8BitMono = 20
Const SAFT22kHz8BitStereo = 21
Const SAFT22kHz16BitMono = 22
Const SAFT22kHz16BitStereo = 23
Const SAFT24kHz8BitMono = 24
Const SAFT24kHz8BitStereo = 25
Const SAFT24kHz16BitMono = 26
Const SAFT24kHz16BitStereo = 27
Const SAFT32kHz8BitMono = 28
Const SAFT32kHz8BitStereo = 29
Const SAFT32kHz16BitMono = 30
Const SAFT32kHz16BitStereo = 31
Const SAFT44kHz8BitMono = 32
Const SAFT44kHz8BitStereo = 33
Const SAFT44kHz16BitMono = 34
Const SAFT44kHz16BitStereo = 35
Const SAFT48kHz8BitMono = 36
Const SAFT48kHz8BitStereo = 37
Const SAFT48kHz16BitMono = 38
Const SAFT48kHz16BitStereo = 39
Dim objNotes, lngSpAudioFormat, dblSndDur, dblSndDelay, dblSndFadeCut, dblSndVolume, lngSamplingFrequency, arrSounds, arrSamples1, objStream1, arrSamples2, objStream2

&#039; вычисленние частот каждой ноты
Set objNotes = GetPitches()

&#039; установка параметров звука
lngSpAudioFormat = SAFT16kHz16BitMono &#039; формат
dblSndDur = 3 &#039; длительность звука в секундах
dblSndDelay = 0.45 &#039; задержка между звуками в секундах
dblSndFadeCut = 0.001 &#039; уровень отсечки
dblSndVolume = 0.5 &#039; громкость
lngSpAudioFormat = lngSpAudioFormat And 254 &#039; принудительно моно
lngSamplingFrequency = Array(8000, 11000, 12000, 16000, 22000, 24000, 32000, 44000, 48000)((lngSpAudioFormat - 4) \ 4) &#039; определение частоты дискретизации в Гц

arrSounds = Array(&quot;Соль-диез 1 октава&quot;, &quot;До 2 октава&quot;, &quot;Ре-диез 2 октава&quot;, &quot;Соль-диез 2 октава&quot;) &#039; частоты звуков в Гц / названия нот
RenderSound lngSamplingFrequency, arrSounds, dblSndDur, dblSndDelay, dblSndFadeCut, arrSamples1
arrSounds = Array(&quot;Соль-диез 2 октава&quot;, &quot;Ре-диез 2 октава&quot;, &quot;До 2 октава&quot;, &quot;Соль-диез 1 октава&quot;)
RenderSound lngSamplingFrequency, arrSounds, dblSndDur, dblSndDelay, dblSndFadeCut, arrSamples2
Set objStream1 = CreateSpMemStream(lngSpAudioFormat, arrSamples1, dblSndVolume)
Set objStream2 = CreateSpMemStream(lngSpAudioFormat, arrSamples2, dblSndVolume)

PlaySound objStream1
CreateObject(&quot;WScript.Shell&quot;).PopUp &quot;Press OK to continue&quot;, 0, , 64
PlaySound objStream2

Sub PlaySound(objSpMemStream)
    objSpMemStream.Seek 0
    With CreateObject(&quot;SAPI.SpVoice&quot;)
        .SpeakStream objSpMemStream, 1
        .WaitUntilDone -1
    End With
End Sub

Sub RenderSound(lngSamplingFrequency, arrSounds, dblSndDur, dblSndDelay, dblSndFadeCut, arrSamples)
    Dim n, dblSndFreq, lngDelaySamples, arrTone
    &#039; генерация и смешивание звуков
    lngDelaySamples = CLng(dblSndDelay * lngSamplingFrequency)
    arrSamples = Array()
    For n = 0 To UBound(arrSounds)
        If IsNumeric(arrSounds(n)) Then dblSndFreq = arrSounds(n) Else dblSndFreq = objNotes(arrSounds(n))
        GenerateTone dblSndFreq, dblSndDur, dblSndFadeCut, lngSamplingFrequency, arrTone
        Mix arrSamples, arrTone, 1, n * lngDelaySamples
    Next
    &#039; наложение эхо
    Echo arrSamples, lngSamplingFrequency, 12
    &#039; нормализация
    Normalize arrSamples
    &#039; обрезка тишины в конце
    TrimSilence arrSamples, dblSndFadeCut
End Sub

Sub GenerateTone(dblSndFreq, dblSndDur, dblSndFadeCut, lngSamplingFrequency, arrSamples)
    Dim i, dblPi, dblRadsPerSample, lngTotalSamples, lngFadeInSamples, lngFadeOutSamples, dblFadeIn, dblFadeOut, dblAmplitude
    &#039; определение параметров звука
    dblPi = 4 * Atn(1)
    dblRadsPerSample = 2 * dblPi * dblSndFreq / lngSamplingFrequency &#039; radians per sample
    lngTotalSamples = CLng(dblSndDur * lngSamplingFrequency)
    lngFadeInSamples = CLng(lngTotalSamples * 0.01)
    lngFadeOutSamples = lngTotalSamples - lngFadeInSamples
    dblFadeIn = 1 / Exp(Log(dblSndFadeCut) / lngFadeInSamples)
    dblFadeOut = Exp(Log(dblSndFadeCut) / lngFadeOutSamples)
    arrSamples = Array()
    ReDim arrSamples(lngTotalSamples - 1)
    &#039; генерация нарастания
    dblAmplitude = dblSndFadeCut
    For i = 0 To lngFadeInSamples - 1
        arrSamples(i) = dblAmplitude * (Sin(i * dblRadsPerSample) + 0.5 * Sin(2 * i * dblRadsPerSample) + 0.15 * Sin(4 * i * dblRadsPerSample))
        &#039; arrSamples(i) = dblAmplitude * Sin(i * dblRadsPerSample)
        dblAmplitude = dblAmplitude * dblFadeIn
    Next
    &#039; генерация спада
    dblAmplitude = 1
    For i = lngFadeInSamples To lngTotalSamples - 1
        arrSamples(i) = dblAmplitude * (Sin(i * dblRadsPerSample) + 0.5 * Sin(2 * i * dblRadsPerSample) + 0.15 * Sin(4 * i * dblRadsPerSample))
        &#039; arrSamples(i) = dblAmplitude * Sin(i * dblRadsPerSample)
        dblAmplitude = dblAmplitude * dblFadeOut
    Next
End Sub

Sub Mix(arrSamples, arrMixing, dblGain, lngDelaySamples)
    Dim i, lngAddSamples
    lngAddSamples = lngDelaySamples + UBound(arrMixing)
    If lngAddSamples &gt; UBound(arrSamples) Then ReDim Preserve arrSamples(lngAddSamples)
    For i = 0 To UBound(arrMixing)
        arrSamples(i + lngDelaySamples) = arrSamples(i + lngDelaySamples) + dblGain * arrMixing(i)
    Next
End Sub

Sub Echo(arrSamples, lngSamplingFrequency, lngDepth)
    Dim n, dblDelay, lngOffset, dblAttenuation, arrReplica
    For n = 1 To lngDepth
        Randomize
        dblDelay = 0.2 + Rnd * 0.7 &#039; задержка эхо в секундах
        lngOffset = CLng(lngSamplingFrequency * dblDelay)
        dblAttenuation = 10 ^ -(0.75 + Rnd * 0.6) &#039; уровень ослабления
        arrReplica = arrSamples
        Mix arrSamples, arrReplica, dblAttenuation, lngOffset
    Next
End Sub

Sub Normalize(arrSamples)
    Dim dblPeak, dblRatio, i
    dblPeak = 0
    For i = 0 To UBound(arrSamples)
        If Abs(arrSamples(i)) &gt; dblPeak Then dblPeak = Abs(arrSamples(i))
    Next
    dblRatio = 1 / dblPeak
    For i = 0 To UBound(arrSamples)
        arrSamples(i) = arrSamples(i) * dblRatio
    Next
End Sub

Sub TrimSilence(arrSamples, dblSndFadeCut)
    Dim i
    For i = UBound(arrSamples) To 0 Step -1
        If Abs(arrSamples(i)) &gt; dblSndFadeCut Then Exit For
    Next
    ReDim Preserve arrSamples(i)
End Sub

Function CreateSpMemStream(lngSpAudioFormat, arrSamples, dblSndVolume)
    Dim bool16Bit, lngMax, dblAmplitude, lngValue, lngMSB, lngLSB, i
    &#039; создание потока
    Set CreateSpMemStream = CreateObject(&quot;SAPI.SpMemoryStream&quot;)
    CreateSpMemStream.Format.Type = lngSpAudioFormat
    bool16Bit = (lngSpAudioFormat \ 2) Mod 2 &#039; определение разрядности 8 / 16 бит
    &#039; преобразование величин и заполнение потока
    If bool16Bit Then &#039; 16 бит
        lngMax = 2 ^ 15 - 1 &#039; 32767
        dblAmplitude = dblSndVolume * lngMax
        For i = 0 To UBound(arrSamples)
            lngValue = Fix(arrSamples(i) * dblAmplitude)
            If lngValue &lt; 0 Then lngValue = 65536 + lngValue
            lngMSB = lngValue \ 256
            lngLSB = lngValue Mod 256
            CreateSpMemStream.Write CByte(lngLSB)
            CreateSpMemStream.Write CByte(lngMSB)
        Next
    Else &#039; 8 бит
        lngMax = 2 ^ 7 - 1
        dblAmplitude = dblSndVolume * lngMax
        For i = 0 To UBound(arrSamples)
            CreateSpMemStream.Write CByte(arrSamples(i) * dblAmplitude + lngMax)
        Next
    End If
End Function

Function GetPitches()
    Dim objDictionary, n, m, f
    Set objDictionary = CreateObject(&quot;Scripting.Dictionary&quot;)
    For m = 0 To 8 &#039; октавы
        For n = 0 To 11 &#039; ноты
            f = 27.5 * 2 ^ (m + (n - 9) / 12)
            objDictionary(Array(&quot;C&quot;, &quot;C#&quot;, &quot;D&quot;, &quot;D#&quot;, &quot;E&quot;, &quot;F&quot;, &quot;F#&quot;, &quot;G&quot;, &quot;G#&quot;, &quot;A&quot;, &quot;A#&quot;, &quot;B&quot;)(n) &amp; m) = f &#039; scientific pitch notation
            objDictionary(Array(&quot;До&quot;, &quot;До-диез&quot;, &quot;Ре&quot;, &quot;Ре-диез&quot;, &quot;Ми&quot;, &quot;Фа&quot;, &quot;Фа-диез&quot;, &quot;Соль&quot;, &quot;Соль-диез&quot;, &quot;Ля&quot;, &quot;Ля-диез&quot;, &quot;Си&quot;)(n) &amp; &quot; &quot; &amp; Array(&quot;Субконтpоктава&quot;, &quot;Контpоктава&quot;, &quot;Большая октава&quot;, &quot;Малая октава&quot;, &quot;1 октава&quot;, &quot;2 октава&quot;, &quot;3 октава&quot;, &quot;4 октава&quot;, &quot;5 октава&quot;)(m)) = f
        Next
    Next
    Set GetPitches = objDictionary
End Function</code></pre></div><p>Форматы не ниже SAFT16kHz16BitMono и с частотой дискретизации, кратной 8 кГц, звучат вполне прилично, по крайней мере у меня. Но вот длительность подготовки оставила желать лучшего: вся подготовка заняла 9,5 секунд, из них заливка в поток - 1,7, реверберация - 5 (в общем-то, можно и без эхо, поскольку улучшения незначительны, да и алгоритм примитивный <img src="//forum.script-coding.com/img/smilies/big_smile.png" width="15" height="15" />). Существует немало способов оптимизации быстродействия, но это уже следующий шаг.</p>]]></content>
			<author>
				<name><![CDATA[omegastripes]]></name>
				<uri>https://forum.script-coding.com/profile.php?id=29228</uri>
			</author>
			<updated>2015-12-08T00:47:10Z</updated>
			<id>https://forum.script-coding.com/viewtopic.php?pid=99568#p99568</id>
		</entry>
</feed>
