PureBasic - форум

Информация о пользователе

Привет, Гость! Войдите или зарегистрируйтесь.


Вы здесь » PureBasic - форум » PureBasic для Windows » Звуковой генератор


Звуковой генератор

Сообщений 1 страница 2 из 2

1

Код:
#WAVE_BUFFER_SIZE = 44100 * 4
#WAVE_TICK_PRECS  = 0.0000000000000000001

Define  WaveIn, WaveOut

Define  WaveNoizeBoolean            = 1
Define  WaveNoizeVolumeLevel.d      = 0.00001
Define  WaveNoizeMergeLevel.d       = 0.4986750000000000001
Define  WaveNoizeSeparation.d       = 1 - WaveNoizeMergeLevel
Define  WaveNoizeShapeModulation    = 1
Define  WaveNoizePanopticum         = 1
Define  WaveSinusBoolean            = 1
Define  WaveSinusVolumeLevel.d      = 0.5
Define  WaveSinusTempoPrevious.d
Define  WaveSinusFrequency.d        = 20
Define  WaveSquareBoolean           = 1
Define  WaveSquareVolumeLevel.d     = 0.001965
Define  WaveSquareTempoPrevious.d
Define  WaveSquareFrequency         = 20
Define  WaveTriangleBoolean         = 1
Define  WaveTriangleVolumeLevel.d   = 0.6
Define  WaveTriangleTempoPrevious.d
Define  WaveTriangleFrequency.d     = 20
Define  WaveSawBoolean              = 0
Define  WaveSawVolumeLevel.d        = 0.5
Define  WaveSawFrequency            = 20
Define  WaveSawDeformation.d        = 0.5
Define  WaveLineInBoolean           = 0
Define  WaveLineInVolumeLevel.d     = 0.70
Define  MasterHarmonicsLevel.d      = 0.0025
Define  MasterVolumeLevel.d         = 0.9896605
Define  CurrentOutBuffer            = 1
Define  CurrentInMemoryBlock        = 1

Structure WAVETICKSTEREO
  First.u
  Second.u
EndStructure

WaveFormatEx.WAVEFORMATEX
With  WaveFormatEx
  \wFormatTag       = #WAVE_FORMAT_PCM
  \nChannels        = 2
  \nSamplesPerSec   = 44100
  \nAvgBytesPerSec  = \nSamplesPerSec * \nChannels  * SizeOf(Word)
  \nBlockAlign      = \nChannels  * SizeOf(Word)
  \wBitsPerSample   = 16
  \cbSize           = SizeOf(WAVEFORMATEX)
EndWith

Define  WaveHdr1.WAVEHDR    : WaveHdr2.WAVEHDR    : WaveHdr3.WAVEHDR
Define  WaveRecHdr1.WAVEHDR : WaveRecHdr2.WAVEHDR : WaveRecHdr3.WAVEHDR

With  WaveHdr1
  \lpData         = AllocateMemory(#WAVE_BUFFER_SIZE)
  \dwBufferLength = #WAVE_BUFFER_SIZE
  \dwLoops        = 1
EndWith
With  WaveHdr2
  \lpData         = AllocateMemory(#WAVE_BUFFER_SIZE)
  \dwBufferLength = #WAVE_BUFFER_SIZE
  \dwLoops        = 1
EndWith
With  WaveHdr3
  \lpData         = AllocateMemory(#WAVE_BUFFER_SIZE)
  \dwBufferLength = #WAVE_BUFFER_SIZE
  \dwLoops        = 1
EndWith

With  WaveRecHdr1
  \lpData         = AllocateMemory(#WAVE_BUFFER_SIZE)
  \dwBufferLength = #WAVE_BUFFER_SIZE        
EndWith
With  WaveRecHdr2
  \lpData         = AllocateMemory(#WAVE_BUFFER_SIZE)
  \dwBufferLength = #WAVE_BUFFER_SIZE
EndWith
With  WaveRecHdr3
  \lpData         = AllocateMemory(#WAVE_BUFFER_SIZE)
  \dwBufferLength = #WAVE_BUFFER_SIZE
EndWith

Define  *CurrentInPointer.WAVETICKSTEREO  = WaveRecHdr1\lpData

Global  Dim *CurrentInBuffer(3)
*CurrentInBuffer(1)  = WaveRecHdr1\lpData  + #WAVE_BUFFER_SIZE
*CurrentInBuffer(2)  = WaveRecHdr2\lpData  + #WAVE_BUFFER_SIZE
*CurrentInBuffer(3)  = WaveRecHdr1\lpData  + #WAVE_BUFFER_SIZE

Procedure GenerateWaveLine(*Buffer)
  Shared  WaveNoizeShapeModulation, WaveNoizePanopticum
  Shared  WaveNoizeBoolean, WaveSinusBoolean, WaveSquareBoolean,  WaveTriangleBoolean, WaveSawBoolean,  WaveLineInBoolean
  Shared  WaveNoizeVolumeLevel,     WaveNoizeMergeLevel,    WaveNoizeSeparation
  Shared  WaveSinusVolumeLevel,     WaveSinusFrequency,     WaveSinusTempoPrevious
  Shared  WaveSquareVolumeLevel,    WaveSquareFrequency,    WaveSquareTempoPrevious
  Shared  WaveTriangleVolumeLevel,  WaveTriangleFrequency,  WaveTriangleTempoPrevious
  Shared  WaveSawVolumeLevel,       WaveSawFrequency,       WaveSawDeformation
  Shared  *CurrentInPointer, CurrentInMemoryBlock, WaveLineInVolumeLevel
  Shared  MasterVolumeLevel,  MasterHarmonicsLevel
  wSin.d      = 2 * #PI * WaveSinusFrequency
  wSquare.d   = 2 * #PI * WaveSquareFrequency
  wTriangle.d = 2 * #PI * WaveTriangleFrequency
  *u16CurrentTick.WAVETICKSTEREO  = *Buffer
  *Overflow                       = *u16CurrentTick + #WAVE_BUFFER_SIZE
  While (*u16CurrentTick  < *Overflow)
    If  (WaveNoizeBoolean = 1)
      If  (WaveNoizePanopticum = 1)
        FirstChannelPanMeter.d  = 1
        SecondChannelPanMeter.d = 0.99
        WaveNoizePanopticum     = 2
      Else
        FirstChannelPanMeter  = 0.99
        SecondChannelPanMeter = 1
        WaveNoizePanopticum   = 1
      EndIf
      NoizeTickValueFirst.d = (Random(65535) - WaveNoizeShapeModulation) * WaveNoizeVolumeLevel  * FirstChannelPanMeter
      If  (NoizeTickValueFirst  <= 0)
        NoizeTickValueFirst = #WAVE_TICK_PRECS
      EndIf
      NoizeTickValueSecond.d = (Random(65535) - WaveNoizeShapeModulation) * WaveNoizeVolumeLevel  * FirstChannelPanMeter
      If  (NoizeTickValueSecond  <= 0)
        NoizeTickValueSecond = #WAVE_TICK_PRECS
      EndIf
      NoizeAverage.d          = 0.5 *  (NoizeTickValueFirst  + NoizeTickValueSecond)
      NoizeTickValueFirst   = (NoizeTickValueFirst  * WaveNoizeSeparation)  + (NoizeAverage  * WaveNoizeMergeLevel)
      NoizeTickValueSecond  = (NoizeTickValueSecond * WaveNoizeSeparation)  + (NoizeAverage  * WaveNoizeMergeLevel)
    Else
      NoizeTickValueFirst   = 0.5 * 65536
      NoizeTickValueSecond  = 0.5 * 65536
    EndIf
    
    If  (WaveSinusBoolean = 1)
      tSin.d          = GeneratorCounter / 44100 + WaveSinusTempoPrevious
      SinusTickValue.d  = ((0.5 *(Sin(wSin * tSin)  + 1))  * 65535  * WaveSinusVolumeLevel)
    EndIf
    
    If  (WaveSquareBoolean  = 1)
      tSquare.d = GeneratorCounter  / 44100 + WaveSquareTempoPrevious
      SinWT.d   = Sin(wSquare * tSquare)
      If  (SinWT  >=  0)
        SquareTickValueSigned.d =   (32767 * WaveSquareVolumeLevel)
      Else
        SquareTickValueSigned = - (32768 * WaveSquareVolumeLevel)
      EndIf
      SquareTickValue.d = SquareTickValueSigned + 32767.5
    EndIf
    
    If  (WaveTriangleBoolean  = 1)
      tTriangle = 44100 / WaveTriangleFrequency
      xTriangle = (((GeneratorCounter - tTriangle / 4)  % tTriangle)  + tTriangle)  % tTriangle - (0.5 * tTriangle)
      TriangleTickValue.d = 4 * 32768 / tTriangle * Abs(xTriangle) - 32768
      TriangleTickValue + 32767.5
      TriangleTickValue * WaveTriangleVolumeLevel
    EndIf
    
    If  (WaveSawBoolean = 1)
      tSaw          = 44100 / WaveSawFrequency
      SawTickValue  = 2  * (32768  * WaveSawVolumeLevel)
      SawTickValue  * ((GeneratorCounter  + tSaw) % tSaw) / tSaw - (32768 * WaveSawVolumeLevel)
      If  (SawPhase = 0)
        SawTickValue  + Random(Abs(SawTickValue  * WaveSawDeformation))
        SawPhase      = 1
      Else
        SawTickValue  - Random(Abs(SawTickValue * WaveSawDeformation))
        SawPhase      = 0
      EndIf
    EndIf
    
    BusCount  = WaveNoizeBoolean  + WaveSinusBoolean  + WaveSquareBoolean + WaveTriangleBoolean + WaveSawBoolean  + WaveLineInBoolean
    If (BusCount  = 0)
      BusCount  = 1
    EndIf
    FirstChannelTickValue.d   = (NoizeTickValueFirst  + SinusTickValue  + SquareTickValue + TriangleTickValue + SawTickValue  + *CurrentInPointer\First)  / BusCount  * MasterVolumeLevel
    SecondChannelTickValue.d  = (NoizeTickValueSecond + SinusTickValue  + SquareTickValue + TriangleTickValue + SawTickValue  + *CurrentInPointer\Second) / BusCount  * MasterVolumeLevel
    
    If  (FirstChannelTickValue  <  0)
      FirstChannelTickValue = 0
    EndIf
    If  (SecondChannelTickValue <  0)
      SecondChannelTickValue  = 0
    EndIf
    
    If  (MasterHarmonicsPhase = 0)
      FirstChannelTickValue   + (Random(FirstChannelTickValue)  * MasterHarmonicsLevel)
      SecondChannelTickValue  + (Random(SecondChannelTickValue) * MasterHarmonicsLevel)
      MasterHarmonicsPhase  = 1
    Else
      FirstChannelTickValue   - (Random(FirstChannelTickValue)  * MasterHarmonicsLevel)
      SecondChannelTickValue  - (Random(SecondChannelTickValue) * MasterHarmonicsLevel)
      MasterHarmonicsPhase  = 0
    EndIf
    
    DeformationOperationId  = Random(2)
    If  (DeformationOperationId = 0)
      *u16CurrentTick\First   = Int(Round(FirstChannelTickValue,  #PB_Round_Nearest))
      *u16CurrentTick\Second  = Int(Round(SecondChannelTickValue, #PB_Round_Nearest))
    ElseIf  (DeformationOperationId = 1)
      *u16CurrentTick\First   = Int(Round(FirstChannelTickValue,  #PB_Round_Up))
      *u16CurrentTick\Second  = Int(Round(SecondChannelTickValue, #PB_Round_Up))
    Else
      *u16CurrentTick\First   = Int(Round(FirstChannelTickValue,  #PB_Round_Down))
      *u16CurrentTick\Second  = Int(Round(SecondChannelTickValue, #PB_Round_Down))
    EndIf
    
    If  (WaveNoizeShapeModulation = 1)
      WaveNoizeShapeModulation  = 0
    EndIf
    
    *CurrentInPointer       + SizeOf(WAVETICKSTEREO)
    *CurrentInPointerBuffer = *CurrentInBuffer(CurrentInMemoryBlock)
    *CurrentInOverflow      = *CurrentInPointerBuffer + #WAVE_BUFFER_SIZE
    While (*CurrentInPointer  >= *CurrentInOverflow)
      *CurrentInPointer = *CurrentInPointerBuffer
    Wend
    
    GeneratorCounter + 1
    
    *u16CurrentTick   + SizeOf(WAVETICKSTEREO)
  Wend
  *u16CurrentTick.WAVETICKSTEREO  = *Buffer
      
  WaveSinusTempoPrevious    = tSin
  WaveSquareTempoPrevious   = tSquare
EndProcedure

Procedure WaveOutOpenCallback(hwo,  Message,  Instance.l, Param1.l, Param2.l)
  Shared  WaveOut
  Shared  CurrentOutBuffer
  Shared  WaveHdr1, WaveHdr2, WaveHdr3
  If  (Message  = #WOM_DONE)
    If  (CurrentOutBuffer  = 1)
      waveOutWrite_   (WaveOut,  @WaveHdr2,  SizeOf(WAVEHDR))
      GenerateWaveLine(WaveHdr2\lpData)
      CurrentOutBuffer = 2 ;Next
    ElseIf  (CurrentOutBuffer  = 2)
      waveOutWrite_   (WaveOut,  @WaveHdr3,  SizeOf(WAVEHDR))
      GenerateWaveLine(WaveHdr3\lpData)
      CurrentOutBuffer = 3 ;Next
    Else
      waveOutWrite_   (WaveOut,  @WaveHdr1,  SizeOf(WAVEHDR))
      GenerateWaveLine(WaveHdr1\lpData)
      CurrentOutBuffer = 1 ;Next
    EndIf
  EndIf
EndProcedure

Procedure WaveInCallback(hwi, Message,  Instance.l, Param1.l, Param2.l)
  Shared  WaveRecHdr1,  WaveRecHdr2,  WaveRecHdr3,  CurrentInMemoryBlock, *CurrentInPointer
  If  (Message  = #WIM_DATA)
    CurrentInMemoryBlock  + 1
    If  (CurrentInMemoryBlock = 1)
      *CurrentInPointer = WaveRecHdr1\lpData
    ElseIf  (CurrentInMemoryBlock = 2)
      *CurrentInPointer = WaveRecHdr2\lpData
    ElseIf  (CurrentInMemoryBlock = 3)
      *CurrentInPointer = WaveRecHdr3\lpData
    ElseIf  (CurrentInMemoryBlock > 3)
      CurrentInMemoryBlock  = 1
    EndIf
  EndIf
EndProcedure

Procedure SetSliderPosition(SliderObj,  FloatValue.d)
  SetGadgetState(SliderObj, FloatValue  * 580)
EndProcedure

Macro SetEditValue(EditorObj, FloatValue)
  SetGadgetText(EditorObj, StrD(FloatValue))
EndMacro

Procedure.d GetSliderValue(SliderObj)
  ProcedureReturn GetGadgetState(SliderObj) / 580
EndProcedure

Procedure IsValidStrFloat(LpStrValue$)
  If  (StrD(ValD(LpStrValue$)) = LpStrValue$)
    ProcedureReturn #True
  EndIf
  ProcedureReturn #False
EndProcedure

Procedure.d GetEditValue(EditorGadget)
  LpStrValue$ = RemoveString  (GetGadgetText(EditorGadget),  " ")
  If  (LpStrValue$ = "")
    LpStrValue$ = "0"
  EndIf
  LpStrValue$ = ReplaceString (LpStrValue$, ",",  ".")
  If  (Right(LpStrValue$, 1)  = ".")
    LpStrValue$ + StrD(#WAVE_TICK_PRECS)
  EndIf
  If  (IsValidStrFloat(LpStrValue$))
    ProcedureReturn ValD(LpStrValue$)
  EndIf
  ProcedureReturn 0
EndProcedure

Procedure GenerateWaveInSilence()
  For i = 1 To  3
    FillMemory(*CurrentInBuffer(i), #WAVE_BUFFER_SIZE,  0,  #PB_Word)
  Next
EndProcedure

waveOutOpen_(@WaveOut,  #WAVE_MAPPER, @WaveFormatEx,  @WaveOutOpenCallback(), 0,  #CALLBACK_FUNCTION | #WAVE_ALLOWSYNC)
waveInOpen_ (@WaveIn,   #WAVE_MAPPER, @WaveFormatEx,  @WaveOutOpenCallback(), 0,  #CALLBACK_FUNCTION)

GenerateWaveLine(WaveHdr1\lpData)
GenerateWaveLine(WaveHdr2\lpData)
GenerateWaveLine(WaveHdr3\lpData)
waveOutPrepareHeader_(WaveOut,  @WaveHdr1,  SizeOf(WAVEHDR))
waveOutPrepareHeader_(WaveOut,  @WaveHdr2,  SizeOf(WAVEHDR))
waveOutPrepareHeader_(WaveOut,  @WaveHdr3,  SizeOf(WAVEHDR))
waveInPrepareHeader_(WaveIn,  @WaveRecHdr1, SizeOf(WAVEHDR))
waveInPrepareHeader_(WaveIn,  @WaveRecHdr2, SizeOf(WAVEHDR))
waveInPrepareHeader_(WaveIn,  @WaveRecHdr3, SizeOf(WAVEHDR))

WndMain = OpenWindow(#PB_Any, 0,  0,  720,  350,  "Noize Generator Frequency Modulationable", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
Panel = PanelGadget(#PB_Any,  1,  1,  720,  500)
  AddGadgetItem(Panel,  -1, "Noize")
    TextGadget(#PB_Any, 5,  5,  150,  25, "Level:")
    cSliderNoizeVolume  = TrackBarGadget(#PB_Any, 5,  30,   580,  25, 1,  580)
    cEditNoizeVolume    = StringGadget  (#PB_Any, 585,  30,  125, 25, "")
    TextGadget(#PB_Any, 5,  70, 150,  25, "Merge Channels:")
    cSliderNoizeMerge   = TrackBarGadget(#PB_Any, 5,  95,  580,  25, 1,  580)
    cEditNoizeMerge     = StringGadget  (#PB_Any, 585,  95, 125,  25, "")
    cCheckNoize         = CheckBoxGadget(#PB_Any, 5,  120,  75, 25, "ON")
    If  (WaveNoizeBoolean)
      SetGadgetState(cCheckNoize, #PB_Checkbox_Checked)
    EndIf
    SetSliderPosition (cSliderNoizeVolume, WaveNoizeVolumeLevel)
    SetSliderPosition (cSliderNoizeMerge,  WaveNoizeMergeLevel)
    SetEditValue      (cEditNoizeVolume,   WaveNoizeVolumeLevel)
    SetEditValue      (cEditNoizeMerge,    WaveNoizeMergeLevel)
  AddGadgetItem(Panel,  -1, "Sinus")
    TextGadget(#PB_Any, 5,  5,  150,  25, "Frequency:")
    cSliderSineFreq = TrackBarGadget(#PB_Any, 5,  30, 580,  25, 1,  580)
    cEditSineFreq   = StringGadget  (#PB_Any, 585,  30,  125, 25, "")
    TextGadget(#PB_Any, 5,  70, 150,  25, "Level:")
    cSliderSineVolume = TrackBarGadget(#PB_Any, 5,  95,  580,  25, 1,  580)
    cEditSineVolume   = StringGadget  (#PB_Any, 585,  95, 125,  25, "")
    cCheckSine        = CheckBoxGadget(#PB_Any, 5,  120,  75, 25, "ON")
    If  (WaveSinusBoolean)
      SetGadgetState(cCheckSine,  #PB_Checkbox_Checked)
    EndIf
    SetSliderPosition (cSliderSineFreq,   WaveSinusFrequency  / 44100 / 2)
    SetSliderPosition (cSliderSineVolume, WaveSinusVolumeLevel)
    SetEditValue      (cEditSineFreq,     WaveSinusFrequency)
    SetEditValue      (cEditSineVolume,   WaveSinusVolumeLevel)
  AddGadgetItem(Panel,  -1, "Square")
    TextGadget(#PB_Any, 5,  5,  150,  25, "Frequency:")
    cSliderSquareFreq = TrackBarGadget(#PB_Any, 5,  30, 580,  25, 1,  580)
    cEditSquareFreq   = StringGadget  (#PB_Any, 585,  30,  125, 25, "")
    TextGadget(#PB_Any, 5,  70, 150,  25, "Level:")
    cSliderSquareVolume = TrackBarGadget(#PB_Any, 5,  95,  580,  25, 1,  580)
    cEditSquareVolume   = StringGadget  (#PB_Any, 585,  95, 125,  25, "")
    cCheckSquare        = CheckBoxGadget(#PB_Any, 5,  120,  75, 25, "ON")
    If  (WaveSquareBoolean)
      SetGadgetState(cCheckSquare,  #PB_Checkbox_Checked)
    EndIf
    SetSliderPosition (cSliderSquareFreq,   WaveSquareFrequency / 44100 / 2)
    SetSliderPosition (cSliderSquareVolume, WaveSquareVolumeLevel)
    SetEditValue      (cEditSquareFreq,     WaveSquareFrequency)
    SetEditValue      (cEditSquareVolume,   WaveSquareVolumeLevel)
  AddGadgetItem(Panel,  -1, "Triangle")
    TextGadget(#PB_Any, 5,  5,  150,  25, "Frequency:")
    cSliderTriangleFreq = TrackBarGadget(#PB_Any, 5,  30, 580,  25, 1,  580)
    cEditTriangleFreq   = StringGadget  (#PB_Any, 585,  30,  125, 25, "")
    TextGadget(#PB_Any, 5,  70, 150,  25, "Level:")
    cSliderTriangleVolume = TrackBarGadget(#PB_Any, 5,  95,  580,  25, 1,  580)
    cEditTriangleVolume   = StringGadget  (#PB_Any, 585,  95, 125,  25, "")
    cCheckTriangle        = CheckBoxGadget(#PB_Any, 5,  120,  75, 25, "ON")
    If  (WaveTriangleBoolean)
      SetGadgetState(cCheckTriangle,  #PB_Checkbox_Checked)
    EndIf
    SetSliderPosition (cSliderTriangleFreq,   WaveTriangleFrequency / 44100 / 2)
    SetSliderPosition (cSliderTriangleVolume, WaveTriangleVolumeLevel)
    SetEditValue      (cEditTriangleFreq,     WaveTriangleFrequency)
    SetEditValue      (cEditTriangleVolume,   WaveTriangleVolumeLevel)
  AddGadgetItem(Panel,  -1, "Saw")
    TextGadget(#PB_Any, 5,  5,  150,  25, "Frequency:")
    cSliderSawFreq  = TrackBarGadget(#PB_Any, 5,  30, 580,  25, 1,  580)
    cEditSawFreq    = StringGadget  (#PB_Any, 585,  30,  125, 25, "")
    TextGadget(#PB_Any, 5,  70, 150,  25, "Level:")
    cSliderSawVolume  = TrackBarGadget(#PB_Any, 5,  95,  580,  25, 1,  580)
    cEditSawVolume    = StringGadget  (#PB_Any, 585,  95, 125,  25, "")
    cCheckSaw         = CheckBoxGadget(#PB_Any, 5,  120,  75, 25, "ON")
    If  (WaveSawBoolean)
      SetGadgetState(cCheckSaw, #PB_Checkbox_Checked)
    EndIf
    SetSliderPosition (cSliderSawFreq,    WaveSawFrequency  / 44100 / 2)
    SetSliderPosition (cSliderSawVolume,  WaveSawVolumeLevel)
    SetEditValue      (cEditSawFreq,      WaveSawFrequency)
    SetEditValue      (cEditSawVolume,    WaveSawVolumeLevel)
  AddGadgetItem(Panel,  -1, "Line In")
    TextGadget(#PB_Any, 5,  5, 150,  25, "Level:")
    cSliderLineInVolume = TrackBarGadget(#PB_Any, 5,  30, 580,  25, 1,  580)
    cEditLineInVolume   = StringGadget(#PB_Any, 585,  30,  125, 25, "")
    cCheckLineIn        = CheckBoxGadget(#PB_Any, 5,  70,  75, 25, "ON")
    If  (WaveLineInBoolean)
      SetGadgetState(cCheckLineIn,  #PB_Checkbox_Checked)
      waveInStart_  (WaveIn)
    EndIf
    SetSliderPosition(cSliderLineInVolume,  WaveLineInVolumeLevel)
    SetEditValue(cEditLineInVolume, WaveLineInVolumeLevel)
  AddGadgetItem(Panel,  -1, "Mixer")
  TextGadget(#PB_Any, 5,  5, 150,  25, "Harmonics:")
  cSliderMixerHarms   = TrackBarGadget(#PB_Any, 5,  30, 580,  25, 1,  580)
  cEditMixerHarms     = StringGadget(#PB_Any, 585,  30,  125, 25, "")
  TextGadget(#PB_Any, 5,  70, 150,  25, "Level:")
  cSliderMixerVolume  = TrackBarGadget(#PB_Any, 5,  95,  580,  25, 1,  580)
  cEditMixerVolume    = StringGadget  (#PB_Any, 585,  95, 125,  25, "")
  SetSliderPosition (cSliderMixerHarms,  MasterHarmonicsLevel)
  SetSliderPosition (cSliderMixerVolume, MasterVolumeLevel)
  SetEditValue      (cEditMixerHarms,    MasterHarmonicsLevel)
  SetEditValue      (cEditMixerVolume,   MasterVolumeLevel)
  
waveOutWrite_(WaveOut,  @WaveHdr1,  SizeOf(WAVEHDR))

While #True
  WndEvent = WaitWindowEvent()   
  Select  WndEvent
    Case  #PB_Event_CloseWindow
      End
    Case  #PB_Event_Gadget
      Select  EventGadget()
        Case  cSliderNoizeVolume
          WaveNoizeVolumeLevel  = GetSliderValue(cSliderNoizeVolume)
          SetEditValue(cEditNoizeVolume,  WaveNoizeVolumeLevel)
        Case  cEditNoizeVolume
          WaveNoizeVolumeLevel  = GetEditValue(cEditNoizeVolume)
          SetSliderPosition(cSliderNoizeVolume, WaveNoizeVolumeLevel)
        Case  cSliderNoizeMerge
          WaveNoizeMergeLevel   = GetSliderValue(cSliderNoizeMerge)
          SetEditValue(cEditNoizeMerge,  WaveNoizeMergeLevel)
        Case  cEditNoizeMerge
          WaveNoizeMergeLevel = GetEditValue(cEditNoizeMerge)
          SetSliderPosition(cSliderNoizeMerge,  WaveNoizeMergeLevel)
        Case  cCheckNoize
          If  (GetGadgetState(cCheckNoize)  = #PB_Checkbox_Checked)
            WaveNoizeBoolean  = 1
          Else
            WaveNoizeBoolean  = 0
          EndIf
        
        Case  cSliderSineFreq
          WaveSinusFrequency  = 44100 / 2 * GetSliderValue(cSliderSineFreq)
          SetEditValue(cEditSineFreq, WaveSinusFrequency)
        Case  cEditSineFreq
          WaveSinusFrequency  = GetEditValue(cEditSineFreq)
          SetSliderPosition(cSliderSineFreq,  WaveSinusFrequency  / (44100 / 2))
        Case  cSliderSineVolume
          WaveSinusVolumeLevel  = GetSliderValue(cSliderSineVolume)
          SetEditValue(cEditSineVolume, WaveSinusVolumeLevel)
        Case  cEditSineVolume
          WaveSinusVolumeLevel  = GetEditValue(cEditSineVolume)
          SetSliderPosition(cSliderSineVolume,  WaveSinusVolumeLevel)
        Case  cCheckSine
          If  (GetGadgetState(cCheckSine) = #PB_Checkbox_Checked)
            WaveSinusBoolean  = 1
          Else
            WaveSinusBoolean  = 0
          EndIf
        
        Case  cSliderSquareFreq
          WaveSquareFrequency = 44100 / 2 * GetSliderValue(cSliderSquareFreq)
          SetEditValue(cEditSquareFreq, WaveSquareFrequency)
        Case  cEditSquareFreq
          WaveSquareFrequency = GetEditValue(cEditSquareFreq)
          SetSliderPosition(cEditSquareFreq, WaveSquareFrequency  / (44100 / 2))
        Case  cSliderSquareVolume
          WaveSquareVolumeLevel = GetSliderValue(cSliderSquareVolume)
          SetEditValue(cEditSquareVolume, WaveSquareVolumeLevel)
        Case  cEditSquareVolume
          WaveSquareVolumeLevel = GetEditValue(cEditSquareVolume)
          SetSliderPosition(cSliderSquareVolume,  WaveSquareVolumeLevel)
        Case  cCheckSquare
          If  (GetGadgetState(cCheckSquare) = #PB_Checkbox_Checked)
            WaveSquareBoolean = 1
          Else
            WaveSquareBoolean = 0
          EndIf
        
        Case  cSliderTriangleFreq
          WaveTriangleFrequency = 44100 / 2 * GetSliderValue(cSliderTriangleFreq)
          SetEditValue(cEditTriangleFreq, WaveTriangleFrequency)
        Case  cEditTriangleFreq
          WaveTriangleFrequency = GetEditValue(cEditTriangleFreq)
          SetSliderPosition(cSliderTriangleFreq,  WaveTriangleFrequency / (44100 / 2))
        Case  cSliderTriangleVolume
          WaveTriangleVolumeLevel = GetSliderValue(cSliderTriangleVolume)
          SetEditValue(cEditTriangleVolume, WaveTriangleVolumeLevel)
        Case  cEditTriangleVolume
          WaveTriangleVolumeLevel = GetEditValue(cEditTriangleVolume)
          SetSliderPosition(cSliderTriangleVolume,  cEditTriangleVolume)
        Case  cCheckTriangle
          If  (GetGadgetState(cCheckTriangleeck)  = #PB_Checkbox_Checked)
            WaveTriangleBoolean = 1
          Else
            WaveTriangleBoolean = 0
          EndIf
          
        Case  cSliderSawFreq
          WaveSawFrequency  = 44100 / 2 * GetSliderValue(cSliderSawFreq)
          SetEditValue(cEditSawFreq,  WaveSawFrequency)
        Case  cEditSawFreq
          WaveSawFrequency  = GetEditValue(cEditSawFreq)
          SetSliderPosition(cSliderSawFreq, WaveSawFrequency  / (44100  / 2))
        Case  cSliderSawVolume
          WaveSawVolumeLevel  = GetSliderValue(cSliderSawVolume)
          SetEditValue(cEditSawVolume,  WaveSawVolumeLevel)
        Case  cEditSawVolume
          WaveSawVolumeLevel  = GetEditValue(cEditSawVolume)
          SetSliderPosition(cSliderSawVolume, WaveSawVolumeLevel)
        Case  cCheckSaw
          If  (GetGadgetState(cCheckSaw)  = #PB_Checkbox_Checked)
            WaveSawBoolean  = 1
          Else
            WaveSawBoolean  = 0
          EndIf
        
        Case  cSliderLineInVolume
          WaveLineInVolumeLevel = GetSliderValue(cSliderLineInVolume)
          SetEditValue(cEditLineInVolume, WaveLineInVolumeLevel)
        Case  cEditLineInVolume
          WaveLineInVolumeLevel = GetEditValue(cEditLineInVolume)
          SetSliderPosition(cSliderLineInVolume,  WaveLineInVolumeLevel)
        Case  cCheckLineIn
          If  (GetGadgetState(cCheckLineIn) = #PB_Checkbox_Checked)
            WaveLineInBoolean = 1
            waveInStart_(WaveIn)
          Else
            WaveLineInBoolean = 0
            waveInStop_(WaveIn)
            GenerateWaveInSilence()
          EndIf
          
        Case  cSliderMixerHarms
          MasterHarmonicsLevel  = GetSliderValue(cSliderMixerHarms)
          SetEditValue(cEditMixerHarms, MasterHarmonicsLevel)
        Case  cEditMixerHarms
          MasterHarmonicsLevel  = GetEditValue(cEditMixerHarms)
          SetSliderPosition(cSliderMixerHarms,  MasterHarmonicsLevel)
        Case  cSliderMixerVolume
          MasterVolumeLevel = GetSliderValue(cSliderMixerVolume)
          SetEditValue(cEditMixerVolume,  MasterVolumeLevel)
        Case  cEditMixerVolume
          MasterVolumeLevel = GetEditValue(cEditMixerVolume)
          SetSliderPosition(cSliderMixerVolume, MasterVolumeLevel)
      EndSelect
  EndSelect
  ;waveOutWrite_   (WaveOut,  @WaveHdr1,  SizeOf(WAVEHDR))
  If  (WaveLineInBoolean)
    waveInAddBuffer_(WaveIn,  @WaveRecHdr1, SizeOf(WAVEHDR))
  EndIf
  ;Delay(5)
  ;waveOutWrite_   (WaveOut,  @WaveHdr2,  SizeOf(WAVEHDR))
  If  (WaveLineInBoolean)
    waveInAddBuffer_(WaveIn,  @WaveRecHdr2, SizeOf(WAVEHDR))
  EndIf
  ;Delay(5)
  ;waveOutWrite_   (WaveOut,  @WaveHdr3,  SizeOf(WAVEHDR))
  If  (WaveLineInBoolean)
    waveInAddBuffer_(WaveIn,  @WaveRecHdr3, SizeOf(WAVEHDR))
  EndIf
  ;Delay(5)
Wend

waveInStop_(WaveIn)
waveOutUnprepareHeader_(WaveOut,  @WaveHdr1,  SizeOf(WAVEHDR))
waveOutUnprepareHeader_(WaveOut,  @WaveHdr2,  SizeOf(WAVEHDR))
waveOutUnprepareHeader_(WaveOut,  @WaveHdr3,  SizeOf(WAVEHDR))
waveInUnprepareHeader_(WaveIn,  @WaveRecHdr1, SizeOf(WAVEHDR))
waveInUnprepareHeader_(WaveIn,  @WaveRecHdr2, SizeOf(WAVEHDR))
waveInUnprepareHeader_(WaveIn,  @WaveRecHdr3, SizeOf(WAVEHDR))
waveInClose_  (WaveIn)
waveOutClose_ (WaveOut)

В звуке - щелчки. Как избавится от них? Писал пару лет назад по-приколу...

Отредактировано PSY (20.09.2023 11:07:15)

0

2

Вот несколько звуковых генераторов. Посмотрите как они устроены.

Код:
; ****************************************************************

; An investigation into using 'Waveform Audio' for Audio output,
; specifically to produce a stereo tone generator. Then I added
; a 'scope display and an input channel and it became a bit like
; Topsy and just grow'd!

; Status: Works and meets my needs. Bug fixes only.

; (C) Richard Leman 2013
; Free to copy, use or ignore entirely at your own risk.

; Rev 2.1 2nd May 2013 - Posted on PB website
; Added : Descriptions to device selection menu
; Added : Draggable voltage cursors with display of absolute and delta values
; Added : Draggable time cursors
; Added : Draggable baseline for both traces

; Rev 2.2 3rd May 2013
; Added : More fixed pitch font for cursor readings
; Added : Input device input selector
; Added : Open input channel
; Added : Option to switch between viewing sig-gen and incoming waveforms
; Changed: Change terminolgy : Left becomes ChanA, Right become ChanB

; --- FOR ANOTHER DAY!
; Voltage calibration
; Sig-gen frequency trimmer
; Voltage cursors to move in sync with trace datum when selected 

; ****************************************************************

;{- Useful references 
; http://msdn.microsoft.com/en-gb/library/windows/desktop/dd743833(v=vs.85).aspx
; http://www.ex-designz.net/apidetail.asp?api_id=531
; http://stilzchen.kfunigraz.ac.at/skripten/comput07/oszi_sound/Mod_sound.bas
;}
;{- Procedure declarations
Declare StartSoundOutput()
Declare StopSoundOutput()
Declare StartSoundInput()
Declare StopSoundInput()
Declare CalcWave(*SBuf,nSamples)                       ;- Calculate the waveform for ChanA / ChanB channels
Declare ScopeRefresh(*P)
Declare ShowCursorReport()
Declare WinCallback(hwnd, uMsg, wParam, lParam)        ;- Window callback to service frequqncy and volume sliders
Declare SetGadgetMouseXY(Win,Gadget,Mx,My,flags=%111)  ;- Set mouse position over a gadget
;}
;{- Globals and constants
Enumeration  ; Windows, Menus, Gadgets, Hot keys
  ; WINDOWS
  #Win_SNDWin   = 100
  
  ;GADGETS
  #Gad_SwitchInOut
  #Gad_Scope    = 1000
  #Gad_WavL    
  #Gad_WavR    
  #Gad_SwitchL 
  #Gad_SwitchR 
  #Gad_FreqL   
  #Gad_FreqR  
  #Gad_VolumeL  
  #Gad_VolumeR  
  
  #Gad_Lock     
  #Gad_LockOfs  
  #Gad_SwitchDF 
  #Gad_SwitchDP 
  
  #Gad_PlayWAV
  
  #Gad_CursTimeSwitch
  #Gad_CursVoltSwitch
  #Gad_CursResults
  #Gad_VoltRef
  
  ; MENU ITEMS
  #Men_OutDevice1 = 2000
  #Men_InDevice1  = 2010
EndEnumeration
Enumeration  ; Things that might be dragged on the 'scope display
  #DragNone
  #DragChanAY
  #DragChanBY
  #DragTime1
  #DragTime2
  #DragVolts1
  #DragVolts2
EndEnumeration
Global Dragging = #DragNone
Global DragX, DragY

Global ScopeImage
Global CAPTURECLOCK   = 44100 ; Sampling/Replay frequency in 'samples per second' 
Global BlockSize      = 2048  ; Number of samples in capture/play block 
Global BytesPerSample = 2     ; Number of bytes needed for each sample 
Global Channels       = 2     ; Number of channels, 1 for mono, 2 for stereo.
Global DevOut         = 1
Global DevIn          = 1
Global hWaveOut
Global hWaveIn
Global nBuf           =16
Global LockLR, LockOffset
Global ScopeImage
Global La.f, Ra.f , DoDP, DoDF
Global hWAV
WAVPath$ = "C:\Temp\"

#PIx2 = 2*#PI
#Twiddle = 7  ; How close a click needs to be to a dragable line

; ========================================================
Global PlayFormat.WAVEFORMATEX
Global RecFormat.WAVEFORMATEX
Global MyOutDevs.WAVEOUTCAPS
Global MyInDevs.WAVEINCAPS
Global Dim outHdr.WAVEHDR(nBuf)
Global Dim inHdr.WAVEHDR(nBuf)

Structure Draggable
  Time1X.i
  Time2X.i
  Volts1Y.i
  Volts2Y.i
EndStructure
Global Drag.Draggable
Drag\Time1X    = 20
Drag\Time2X    = 40
Drag\Volts1Y   = 50
Drag\Volts2Y   = 100

Structure ScopeDisplay
  width.i
  height.i
  TimeCursorSwitch.i
  VoltsCursorSwitch.i
  PixPermSec.f
  PixPerVolt.f
  VoltRefIndex.i
  VoltRefDisc$
  ShowInOut.i
EndStructure
Global Scope.ScopeDisplay

Scope\width  = 512
Scope\height = 256
Scope\TimeCursorSwitch.i  = #False
Scope\VoltsCursorSwitch.i = #False
Scope\PixPerVolt = 50              ; This is arbitrary 
Scope\VoltRefIndex = 1
Scope\VoltRefDisc$ = "ChanA (Red)|ChanB (Green)|Bottom|Centre|"
Scope\ShowInOut = 1                ; 0=Input   1=Output

Structure Waves
  WaveForm.i
  Frequency.i
  Volume.f
  Switch.i
  YPos.i
  DatumImage.i
EndStructure
Global ChanA.Waves
Global ChanB.Waves

; Initial signal generator frequencies etc
With ChanA                ; 1000 Hz Sinewave on ChanA, On
  \Frequency = 1000
  \Volume    = 0.2
  \Switch    = #True
  \WaveForm  = 0
EndWith
With ChanB
  \Frequency = 2000       ; 2000 Hz Sinewave on ChanB, On
  \Volume    = 0.2
  \Switch    = #True
  \WaveForm  = 0
EndWith

LockLR     = #False       ; Option to lock ChanB frequency to ChanA
DoDF       = #True        ; When A&B frequencies are BOTH controlled by the ChanA channel there is an
DoDP       = #False       ; option for having a phase or frequency offset.

;}
;{- Build Control panel
; Create a backdrop for the oscilloscope with 1mSec grid lines
ScopeImage = CreateImage(#PB_Any,Scope\width,Scope\height)
StartDrawing(ImageOutput(ScopeImage))
  FrontColor($0089C89)
  X.f = 0
  Scope\PixPermSec =  Scope\width /(1000*BlockSize/(CAPTURECLOCK*4)) ; Pixels per mSec
  f.f = Scope\PixPermSec
  
  While X <Scope\width
    LineXY(X,0,X,Scope\height-1) 
    X + f.f
  Wend
  Y.f = 0  ; Horizontal lines are purely cosmetic at present!
  T = Scope\height/2
  While Y < T
    LineXY(0,128-Y,Scope\width,Scope\height/2-Y)
    LineXY(0,128+Y,Scope\width,T+Y)
    Y + f.f
  Wend
  ChanB\YPos = Int(T + f.f)
  ChanA\YPos  = Int(T - f.f)
StopDrawing()

; Create two time cursors
Global CursorTime1 = CreateImage(#PB_Any,1,Scope\height)
StartDrawing(ImageOutput(CursorTime1))
For n = 0 To Scope\height Step 10
  For m = n To n+5
    Plot(0,m,#White)
  Next
Next
StopDrawing()
Global CursorTime2 = CopyImage(CursorTime1,#PB_Any)

; Create two volts cursors
Global CursorV1 = CreateImage(#PB_Any,Scope\width,1)
StartDrawing(ImageOutput(CursorV1))
  For n = 0 To Scope\width-6 Step 10
    For m = n To n+5  
      Plot(m,0,#White)
    Next
  Next
StopDrawing()
Global CursorV2 = CopyImage(CursorV1,#PB_Any)

; Dotted baselines for the two channels
ChanA\DatumImage  = CreateImage(#PB_Any,Scope\width,1)
StartDrawing(ImageOutput(ChanA\DatumImage))
  For n = 0 To Scope\width-6 Step 10
    For m = n To n+5  
      Plot(m,0,#Red)
    Next
  Next
StopDrawing()

ChanB\DatumImage = CreateImage(#PB_Any,Scope\width,1)
StartDrawing(ImageOutput(ChanB\DatumImage))
  For n = 0 To Scope\width-6 Step 10
    For m = n To n+5  
      Plot(m,0,#Green)
    Next
  Next
StopDrawing()
  
; ====== Main Window, buttons etc. =======
OpenWindow(#Win_SNDWin,0,0,600,480,"'Waveform Audio' Sound Generator - R2.2 (C) RichardL 2013",#PB_Window_SystemMenu |#PB_Window_ScreenCentered)
CanvasGadget(#Gad_Scope,44,10,Scope\width,Scope\height,#PB_Canvas_ClipMouse)

py = 270
CheckBoxGadget(#Gad_CursTimeSwitch,44 ,py,  90, 15, "Time cursors")
CheckBoxGadget(#Gad_CursVoltSwitch,144,py,  90, 18, "Volts cursors")
EditorGadget(#Gad_CursResults,240,     py, 230, 38, #PB_Editor_ReadOnly)
SpinGadget(#Gad_VoltRef,144,py+17,90,20,1,CountString(Scope\VoltRefDisc$,"|"),#PB_Spin_ReadOnly)
LoadFont(1,"Courier New",8) : SetGadgetFont(#Gad_CursResults,FontID(1))
ButtonGadget(#Gad_SwitchInOut,477,py,80,38,"Show Input")

py + 40
Frame3DGadget(#PB_Any,34,py,532,145,"Signal Sources")
py+15
ComboBoxGadget(#Gad_WavL,44,py,80,20)
ComboBoxGadget(#Gad_WavR,356,py,80,20)
CheckBoxGadget(#Gad_SwitchL,154, py,90, 20,"ChanA On/Off")
CheckBoxGadget(#Gad_SwitchR,464,py, 90,20,"ChanB On/Off",#PB_CheckBox_Right)

py + 30
TextGadget(#PB_Any,244,py,110,20,"Frequency",#PB_Text_Center)
ScrollBarGadget(#Gad_FreqL,44, py,200,20,100,10000,1)
ScrollBarGadget(#Gad_FreqR,356,py,200,20,100,10000,1)

py + 30
TextGadget(#PB_Any,244,py,110,20,"Amplitude",#PB_Text_Center)
ScrollBarGadget(#Gad_VolumeL,44 ,py,200,20,0,100,1)
ScrollBarGadget(#Gad_VolumeR,356,py,200,20,0,100,1)

py + 30 
CheckBoxGadget(#Gad_Lock,44,py,80,20,"Lock L+R")
StringGadget(#Gad_LockOfs,134,py,60,20,"0", #PB_String_Numeric)
OptionGadget(#Gad_SwitchDF,204,py-8,100,20,"Freq offset (Hz)")
OptionGadget(#Gad_SwitchDP,204,py+13,110,20,"Phase offset (Deg)")
ButtonGadget(#Gad_PlayWAV,356,py,200,20,"Play WAV")

; Outgoing waveform choices
WaveType$ = "Sine|Square|Sawtooth|Noise|WAV File|"
For n = 1 To CountString(WaveType$,"|")
  AddGadgetItem(#Gad_WavL,-1,StringField(WaveType$,n,"|"))
  AddGadgetItem(#Gad_WavR,-1,StringField(WaveType$,n,"|"))
Next

; ======= Set controls to match initial conditions =======
SetGadgetState(#Gad_FreqL,ChanA\Frequency)  : SetGadgetState(#Gad_VolumeL,100*ChanA\Volume) : SetGadgetState(#Gad_SwitchL,ChanA\Switch)
SetGadgetState(#Gad_FreqR,ChanB\Frequency) : SetGadgetState(#Gad_VolumeR,100*ChanB\Volume): SetGadgetState(#Gad_SwitchR,ChanB\Switch)
SetGadgetState(#Gad_WavL,ChanA\WaveForm)    : SetGadgetState(#Gad_WavR,ChanB\WaveForm)

SetGadgetState(#Gad_Lock,LockLR)
SetGadgetState(#Gad_SwitchDF,DoDF) : SetGadgetState(#Gad_SwitchDP,DoDP)
DisableGadget(#Gad_PlayWAV,#True)

; Build a menu for some config items
CreateMenu(1,WindowID(#Win_SNDWin))

; Output device selection
OpenSubMenu("Sound Output Devices")
NumOutDevs = waveOutGetNumDevs_()
If NumOutDevs
  For n = 0 To NumOutDevs - 1
    If waveOutGetDevCaps_(n,@MyOutDevs,SizeOf(WAVEOUTCAPS)) = 0
      MenuItem(n + #Men_OutDevice1,PeekS(@MyOutDevs\szPname))
    EndIf
  Next
EndIf
CloseSubMenu()

; Input device selection
OpenSubMenu("Sound Input Devices")
NumInDevs = waveInGetNumDevs_()
If NumInDevs
  For n = 0 To NumInDevs - 1
    If waveInGetDevCaps_(n,@MyInDevs,SizeOf(WAVEINCAPS)) = 0
      MenuItem(n + #Men_InDevice1,PeekS(@MyInDevs\szPname))
    EndIf
  Next
EndIf
CloseSubMenu()

; Set initial description for voltage cursor reference
Scope\VoltRefIndex = 1
SetGadgetText(#Gad_VoltRef,StringField(Scope\VoltRefDisc$,Scope\VoltRefIndex,"|"))
DisableGadget( #Gad_VoltRef,#True)

; Cursor to be used when mouse over the Canvas
SetGadgetAttribute(#Gad_Scope,#PB_Canvas_Cursor,#PB_Cursor_Cross)
;}
;{- Preparation and startup
SetMenuItemState(1,#Men_OutDevice1,#True)
DevOut = 0
StartSoundOutput()
SetWindowCallback(@WinCallback())             ; Handles Scrollbars and Sound Output callback
StartSoundOutput()
StartSoundInput()
;}
;{- Dispatch
Finish = #False
Repeat
  Select WaitWindowEvent()
    Case #PB_Event_CloseWindow        ;{ Close window
      Finish = #True
      ;}
    Case #PB_Event_Gadget             ;{ Buttons and gadgets
      Select EventGadget()
          
          ; Switch channels On/Off
        Case #Gad_SwitchL : ChanA\Switch  = GetGadgetState(#Gad_SwitchL)
        Case #Gad_SwitchR : ChanB\Switch = GetGadgetState(#Gad_SwitchR)
          
          ; Lock ChanB to ChanA channel
        Case #Gad_Lock 
          LockLR = GetGadgetState(#Gad_Lock)
          DisableGadget(#Gad_FreqR,LockLR)
          If LockLR = #False
            ChanB\Frequency = GetGadgetState(#Gad_FreqR)
          Else
            La.f = Ra.f + 3.142
          EndIf
          
          ; Choose phase or frequency offset
        Case #Gad_SwitchDF, #Gad_SwitchDP
          DoDF = GetGadgetState(#Gad_SwitchDF)    ; A flip-flop pair
          DoDP = DoFR ! 1
          
          ; ChanA waveform selection
        Case #Gad_WavL
          ChanA\WaveForm = GetGadgetState(#Gad_WavL) ; Get selected waveform designator
          
          ; TEMPORARY: Sound file (must be WAV, 16 bit, mono, 44100, non-compressed
          If ChanA\WaveForm = 4 ; Choose file to be played...
            WAVFile$ = OpenFileRequester("Load a WAV File",WAVPath$,"WAV Files|*.WAV",0)
            If WAVFile$
              WAVPath$ = GetPathPart(WAVFile$)
              DisableGadget(#Gad_PlayWAV,#False)
              SetGadgetText(#Gad_PlayWAV,"Play<"+GetFilePart(WAVFile$)+">")
            EndIf
          EndIf
          
          ; ChanB waveform selection
        Case #Gad_WavR
          ChanB\WaveForm = GetGadgetState(#Gad_WavR)
          
          ; Kludge to play a 16bit Mono 44.1K sample rate WAV file
        Case #Gad_PlayWAV 
          ; No header checks, no nuffin'...
          If IsFile(hWAV) : CloseFile(hWAV) : hWAV = 0 : EndIf ; Close a prior file.
          If WAVFile$                                          ; If filename defined,
            hWAV  = OpenFile(#PB_Any,WAVFile$)                 ; open the new file,
            FileSeek(hWAV,36)                                  ; Seek to <<normal>> start of data.
          EndIf
          
          ; Switch time cursors On / Off  
        Case #Gad_CursTimeSwitch
          Scope\TimeCursorSwitch = GetGadgetState(#Gad_CursTimeSwitch)
          ShowCursorReport()
           
          ; Switch volts cursors On/Off
        Case #Gad_CursVoltSwitch
          Scope\VoltsCursorSwitch = GetGadgetState(#Gad_CursVoltSwitch)
          ShowCursorReport()
          DisableGadget( #Gad_VoltRef,Scope\VoltsCursorSwitch ! 1)
          
          ; Choose the zero datum for voltage cursors
        Case #Gad_VoltRef
          Scope\VoltRefIndex = GetGadgetState(#Gad_VoltRef)
          SetGadgetText(#Gad_VoltRef,StringField(Scope\VoltRefDisc$,Scope\VoltRefIndex,"|"))
          ShowCursorReport()
          
          ; Select display to show sig-gen or input
        Case #Gad_SwitchInOut
          Scope\ShowInOut ! 1
          SetGadgetText(#Gad_SwitchInOut,StringField("Show SigGen|Show Input",Scope\ShowInOut+1,"|"))
          
        Case #Gad_Scope
          Select EventType()
              ; ChanA Mouse down over the display will be to select a feature for dragging.
              ; Some elasticity is provided so the user does not need to click precisely
              ; on the line, if it is near enough the cursor jumps to the precise
              ; position of the line... which does NOT move until it is dragged.
              
            Case #PB_EventType_LeftButtonDown
              DragX = GetGadgetAttribute(#Gad_Scope, #PB_Canvas_MouseX)
              DragY = GetGadgetAttribute(#Gad_Scope, #PB_Canvas_MouseY)
              
              If Abs(DragY - ChanA\YPos) < #Twiddle                       ; If close to baseline for ChanA channel waveform...
                SetGadgetMouseXY(#Win_SNDWin,#Gad_Scope,DragX,ChanA\YPos) ; snap mouse to the exact position,
                Beep_(5000,10)                                            ; indicate to user,
                Dragging = #DragChanAY                                    ; set flag specifying the cursor to be dragged.
              EndIf
              
              If (Not Dragging) And (Abs(DragY - ChanB\YPos) < #Twiddle)  ; ChanA channel...
                SetGadgetMouseXY(#Win_SNDWin,#Gad_Scope,DragX,ChanB\YPos)
                Beep_(5000,10)
                Dragging = #DragChanBY
              EndIf
              
              If (Not Dragging) And (Abs(DragX - Drag\Time1X) < #Twiddle) ; T1 cursor....
                SetGadgetMouseXY(#Win_SNDWin,#Gad_Scope,Drag\Time1X,DragY)
                Beep_(5000,10)
                Dragging = #DragTime1  
              EndIf
              
              If (Not Dragging) And (Abs(DragX - Drag\Time2X) < #Twiddle) ; T2 Cursor...
                SetGadgetMouseXY(#Win_SNDWin,#Gad_Scope,Drag\Time2X,DragY)
                Beep_(5000,10)
                Dragging = #DragTime2  
              EndIf
              
              If (Not Dragging) And (Abs(DragY - Drag\Volts1Y) < #Twiddle) ; V1 cursor...
                SetGadgetMouseXY(#Win_SNDWin,#Gad_Scope,DragX,Drag\Volts1Y)
                Beep_(5000,10)
                Dragging = #DragVolts1
              EndIf
              
              If (Not Dragging) And (Abs(DragY - Drag\Volts2Y) < #Twiddle) ; V2 cursor...
                SetGadgetMouseXY(#Win_SNDWin,#Gad_Scope,DragX,Drag\Volts2Y)
                Beep_(5000,10)
                Dragging = #DragVolts2
              EndIf
              
            Case #PB_EventType_LeftButtonUp
              Dragging = #DragNone
              
          EndSelect
          
      EndSelect
       
      ;}
    Case #PB_Event_Menu               ;{ Menu items and hot keys 
      T = EventMenu()
      Select T
          
          ; Input device selection
        Case #Men_InDevice1 To #Men_InDevice1 + 9
          For n = #Men_InDevice1 To #Men_InDevice1 + 9        ; Clear all 'ticks'
            SetMenuItemState(1,n,#False)
          Next
          SetMenuItemState(1,T,#True)                         ; Set 'tick' to match selection
          DevIn = T - #Men_InDevice1 + 1                      ; Calc new number number
          StopSoundInput()                                    ; Close current input device 
          StartSoundInput()                                   ; Open New input device
          
          ; Output device selection
        Case #Men_OutDevice1 To #Men_OutDevice1 + 9
          For n = #Men_OutDevice1 To #Men_OutDevice1 + 9     
            SetMenuItemState(1,n,#False)
          Next
          SetMenuItemState(1,T,#True) 
          DevOut = T - #Men_OutDevice1 + 1  
          StopSoundOutput()        
          StartSoundOutput()    
          
      EndSelect
      ;}
  EndSelect
  
  Select EventType()
      
      ; Frequency / Phase offset StringGadget()
    Case #PB_EventType_Change                                 ; If gadget contents changed,
      If EventGadget() = #Gad_LockOfs                         ; and offsets are enables,
        LockOffset = Val(GetGadgetText(#Gad_LockOfs))         ; get the current offset value.
      EndIf
      
  EndSelect
 
Until Finish
;}
;{- PackUpFallOut
StopSoundOutput()
StopSoundInput()
;}
End

Procedure WinCallback(hwnd, uMsg, wParam, lParam) ;- Window callback to service scrollbar and sound output message
  Static T
  Static *PriorCopyPointer
  Select uMsg
      
    Case  #WM_HSCROLL 
      ; Two frequency and two amplitude controls
      Select lParam
        Case GadgetID(#Gad_FreqL)   : ChanA\Frequency  = GetGadgetState(#Gad_FreqL)         ; Frequency value directly from control
        Case GadgetID(#Gad_FreqR)   : ChanB\Frequency = GetGadgetState(#Gad_FreqR) 
        Case GadgetID(#Gad_VolumeL) : ChanA\Volume     = GetGadgetState(#Gad_VolumeL) / 100 ; Scale volume over range 0 to 1. FLOAT
        Case GadgetID(#Gad_VolumeR) : ChanB\Volume    = GetGadgetState(#Gad_VolumeR) / 100
      EndSelect
     
      ; Sound output.
    Case #MM_WOM_DONE                                 ; A play buffer has been returned.
      *hWaveO.WAVEHDR = lParam                        ; lParam has the address of WAVEHDR
      *P    = *hWaveO\lpData                          ; Where to write NEW data
      CalcWave(*P,2048/4)                             ; ?/4 one WORD each ChanA and ChanB
      *hWaveO\dwBytesRecorded = 2048                  ; Number of bytes written into buffer
      waveOutWrite_(hWaveOut,lParam, SizeOf(WAVEHDR)) ; Send to sound device
      If Scope\ShowInOut
        ScopeRefresh(*P)                              ; Update the display
      EndIf
    
      ; Sound input.
    Case #MM_WIM_DATA
      *hWave.WAVEHDR   = lParam
      *P = *hWave\lpData
      waveInAddBuffer_(wParam, lParam, SizeOf(WAVEHDR))
      If Scope\ShowInOut = 0
        ScopeRefresh(*P)
      EndIf
      
  EndSelect
  ProcedureReturn #PB_ProcessPureBasicEvents
EndProcedure
Procedure StartSoundOutput()
  Protected T,i
  Static *OutBufMem
  
  With PlayFormat
    \wFormatTag      = #WAVE_FORMAT_PCM
    \nChannels       = 2
    \wBitsPerSample  = 16
    \nSamplesPerSec  = CAPTURECLOCK
    \nBlockAlign     = (\nChannels * \wBitsPerSample)/8 ; = 4
    \nAvgBytesPerSec = \nSamplesPerSec * \nBlockAlign   ; = 176400
  EndWith
  
  If *OutBufMem : FreeMemory(*OutBufMem) : EndIf                       ; Free a prior assignement
  *OutBufMem = AllocateMemory(BlockSize * nBuf)                        ; Reserve memory for all the buffers
  
  T =  waveOutOpen_(@hWaveOut, #WAVE_MAPPER+DevOut, @PlayFormat, WindowID(#Win_SNDWin), #True, #CALLBACK_WINDOW | #WAVE_FORMAT_DIRECT)
  If T = #MMSYSERR_NOERROR
    
    ; NOTE:  'n' contiguous buffers are more convenient to debug with.
    *P = *OutBufMem                                                    ; Pointer to start of memory
    For i = 0 To nBuf-1                                                ; For each buffer...
      outHdr(i)\lpData         = *P                                    ; start of buffer
      outHdr(i)\dwBufferLength = BlockSize                             ; size of buffer
      outHdr(i)\dwFlags        = 0
      outHdr(i)\dwLoops        = 0
      T | waveOutPrepareHeader_(hWaveOut, outHdr(i), SizeOf(WAVEHDR))  
      *P + BlockSize
    Next
    
    For i = 0 To nBuf-1
      PostMessage_(WindowID(#Win_SNDWin),#MM_WOM_DONE,0,outHdr(i))
    Next 
    
  EndIf
  
  ProcedureReturn T
  
EndProcedure
Procedure StopSoundOutput()
  waveOutReset_(hWaveOut)
  For i = 0 To nBuf - 1
    waveOutUnprepareHeader_(hWaveOut, outHdr(i), SizeOf(WAVEHDR))
  Next
  waveOutClose_(hWaveOut)
EndProcedure
Procedure StartSoundInput()
  Protected T, i
  Static *InBufMem
  
  ; Could use PlayFormat... but they could be changed separately.
  With RecFormat 
    \wFormatTag      = #WAVE_FORMAT_PCM
    \nChannels       = 2
    \wBitsPerSample  = 16
    \nSamplesPerSec  = CAPTURECLOCK
    \nBlockAlign     = (\nChannels * \wBitsPerSample)/8 ; = 4
    \nAvgBytesPerSec = \nSamplesPerSec * \nBlockAlign   ; = 176400
  EndWith
  
  If *InBufMem : FreeMemory(*InBufMem) : EndIf
  *InBufMem = AllocateMemory(BlockSize * nBuf)             ; Reserve memory for all the input buffers

  T =  waveInOpen_(@hWaveIn, #WAVE_MAPPER+DevIn, @RecFormat,  WindowID(#Win_SNDWin), #Null, #CALLBACK_WINDOW | #WAVE_FORMAT_DIRECT)
  If T = #MMSYSERR_NOERROR
    *P = *InBufMem
    For i = 0 To nBuf-1
       inHdr(i)\lpData         = *P
       inHdr(i)\dwBufferLength = BlockSize
      T | waveInPrepareHeader_(hWaveIn, inHdr(i), SizeOf(WAVEHDR)) ; Note: inHdr(i) returns pointer to the i'th structure... not contents of first element
      T | waveInAddBuffer_(hWaveIn, inHdr(i), SizeOf(WAVEHDR))
      *P + BlockSize
    Next
    ; 
    If waveInStart_(hWaveIn) = #MMSYSERR_NOERROR 
      SetTimer_(WindowID(#Win_SNDWin), 42, 5, 0); Why?
    EndIf
    ; 
  EndIf
  ProcedureReturn T
EndProcedure
Procedure StopSoundInput()
  waveInReset_(hWaveIn)
  For i = 0 To nBuf - 1
    waveInUnprepareHeader_(hWaveIn, inHdr(i), SizeOf(WAVEHDR))
  Next
  waveInClose_(hWaveIn)
EndProcedure
Procedure ScopeRefresh(*P)                        ;- Redraw the oscilloscope display
  
  Protected Copy, LastVLw, LastVR.w, STrig,Vl.w, Vr.w,X
  Protected OldX, OldYL, OldYR
  Static CrsRep$
  
  ; Adjust positions of any features being dragged..
  If Dragging
    Select Dragging ; 
      Case #DragChanAY  : ChanA\YPos   = WindowMouseY(#Win_SNDWin) - GadgetY(#Gad_Scope)  ; Red trace base line
      Case #DragChanBY : ChanB\YPos  = WindowMouseY(#Win_SNDWin) - GadgetY(#Gad_Scope)  ; Green trace base line
      Case #DragTime1  : Drag\Time1X = WindowMouseX(#Win_SNDWin) - GadgetX(#Gad_Scope)  ; Time cursor T1
      Case #DragTime2  : Drag\Time2X = WindowMouseX(#Win_SNDWin) - GadgetX(#Gad_Scope)  ; Time Cursor T2
      Case #DragVolts1 : Drag\Volts1Y= WindowMouseY(#Win_SNDWin) - GadgetY(#Gad_Scope)
      Case #DragVolts2 : Drag\Volts2Y= WindowMouseY(#Win_SNDWin) - GadgetY(#Gad_Scope)
    EndSelect
    ShowCursorReport() ; Update the report text
  EndIf
  
  Copy = CopyImage(ScopeImage,#PB_Any)     ; Make temporary copy of 'scope backdrop
  StartDrawing(ImageOutput(Copy))          ; Draw on the copy
    
    ; Show the two channel frequencies
    DrawText(8,8,Str(ChanA\Frequency)+" Hz",#Red) 
    DrawText(450,8,Str(ChanB\Frequency)+" Hz",#Green)
    
    ; Draw the base lines (Zero volts) for each trace... dotted lines
    If ChanA\Switch : DrawImage(ImageID(ChanA\DatumImage),0,ChanA\YPos) : EndIf
    If ChanB\Switch: DrawImage(ImageID(ChanB\DatumImage),0,ChanB\YPos): EndIf
    
    ; Draw the time and voltage cursors... dotted lines 
    If  Scope\TimeCursorSwitch
      DrawImage(ImageID(CursorTime1),Drag\Time1X-1,0) : DrawText(Drag\Time1X+2,240,"T1")
      DrawImage(ImageID(CursorTime2),Drag\Time2X-1,0) : DrawText(Drag\Time2X+2,240,"T2") 
    EndIf
    
    If Scope\VoltsCursorSwitch
      DrawImage(ImageID(CursorV1),0,Drag\Volts1Y)     : DrawText(490,Drag\Volts1Y-18,"V1")
      DrawImage(ImageID(CursorV2),0,Drag\Volts2Y)     : DrawText(490,Drag\Volts2Y-18,"V2")
    EndIf
    
    ; Prime the 'scope trigger
    LastVL = 1000 : LastVR = 1000           
    STrig = 0
    
    ; Plot the display
    X = 0
    For n = 0 To 511
      
      ; Get the ChanA and ChanB signal levels
      Vl = PeekW(*P) : *P + 2              
      Vr = PeekW(*P) : *P + 2
      
      ; Detect positive zero-crossing to trigger the 'scope
      If ChanA\Switch                           ; If ChanA channel ON...
        
        If (LastVL <= 0) And (Vl > 0)          ; Rising though zero?
          STrig = #True                        ; Set trigger flag...
        EndIf                                
        LastVL = Vl                            ; Keep current ChanA value to compare with next
      ElseIf ChanB\Switch                      ; If ChanB channel ON...
        If (LastVR <= 0) And (Vr > 0)          ; Rising though zero?
          STrig = #True                        ; Set trigger flag...
        EndIf                                
        LastVR = Vr                            ; Keep current ChanA value to compare with next
      Else                                     ; No channels...
        DrawText(200,120,"  No signals...  ",#White)
        Break
      EndIf
      
      ; Plot scope display of points after the zero crossing
      ; (NB: Using Round() would be better than Int()...) 
      If (X < 512) And STrig
        Yl = ChanA\YPos - Int(Vl)>>8            ; Scale and offset the ChanA waveform ('-' makes positive = up)
        Yr = ChanB\YPos- Int(Vr)>>8            ; Scale and offset the ChanB waveform
        If X                                   ; After the first point...
          If ChanA\Switch
            LineXY(OldX,OldYL,X,Yl,#Red)       ; join to previous point
          EndIf
          If ChanB\Switch
            LineXY(OldX,OldYR,X,Yr,#Green)
          EndIf
        EndIf
        OldX  = X                              ; Keep values to become the previous, next time
        OldYL = Yl
        OldYR = Yr
        X + 1
      EndIf
      
    Next
    
  StopDrawing()
  
  ; Transfer the new display to the display gadget
  StartDrawing(CanvasOutput(#Gad_Scope))   
    DrawImage(ImageID(Copy),0,0)
  StopDrawing()
  FreeImage(Copy)                            ; Dispense with temporary image
  
EndProcedure
Procedure ShowCursorReport()
  
  CrsRep$ = ""
  If  Scope\TimeCursorSwitch 
    CrsRep$ + "T1=" + RSet(StrF(Drag\Time1X / Scope\PixPermSec,2),5," ") + " "
    CrsRep$ + "T2=" + RSet(StrF(Drag\Time2X / Scope\PixPermSec,2),5," ") + " "
    CrsRep$ + "dT=" + RSet(StrF((Drag\Time2X-Drag\Time1X) / Scope\PixPermSec,2),5," ") + " mSec" + Chr(10)
  EndIf
  
  If Scope\VoltsCursorSwitch 
    
    ; Select the reference location for the voltage cursors
    Select Scope\VoltRefIndex      ; 1 => 4  = "ChanA|ChanB|Bottom|Centre|"
      Case 1 : T = ChanA\YPos
      Case 2 : T = ChanB\YPos
      Case 3 : T = Scope\height
      Case 4 : T = Scope\height /2
    EndSelect
    
    ; Volts cursors...
    CrsRep$ + "V1=" + RSet(StrF((T - Drag\Volts1Y) / Scope\PixPerVolt,2),5," ") + " "
    CrsRep$ + "V2=" + RSet(StrF((T - Drag\Volts2Y) / Scope\PixPerVolt,2),5," ") + " "
    CrsRep$ + "dV=" + RSet(StrF((Drag\Volts2Y-Drag\Volts1Y) / Scope\PixPerVolt,2),5," ")
    
  EndIf
  SetGadgetText(#Gad_CursResults,CrsRep$)
  
EndProcedure
Procedure CalcWave(*SBuf,nSamples)                ;- Calculate the waveform for ChanA / ChanB channels
  ; This routine  generates ChanA and ChanB waveforms.
  ; Both channels are phase continuous between multiple calls.
  ; ChanA and ChanB samples are interleaved and each sample is a WORD value. (L.w, R.w, L.w, R.w, L.w, R.w.... etc
  ; The receiving buffer MUST have a length that is a MULTIPLE of 4 
  
  Static Angle.f,Vl.f,Vr.f,Kl.f,Kr.f
  Protected *P, sample
  ;{ Calculate the frequency scaling factors - 
  Kl = ChanA\Frequency / ((CAPTURECLOCK)/#PIx2)
  If LockLR                                  ; If channels are locked together, there are two lock modes...
    If DoDF                                  ; (1) With a user specified frequency offset...
      ChanB\Frequency = ChanA\Frequency + LockOffset
    Else                                     ; (2) With a phase offest...
      ChanB\Frequency = ChanA\Frequency
      Ra = La + ((LockOffset/360) * #PIx2)
    EndIf
    
  EndIf
  Kr = ChanB\Frequency /((CAPTURECLOCK)/#PIx2)
  ;}
  ;{ Generate waveform data
  *P = *SBuf
  For sample = 0 To nSamples-1
    
    ; Derive ChanA channel waveform points.
    If ChanA\Switch                                           ; If ChanA channel is switched ON...
      Select ChanA\WaveForm
        Case 0 : Vl = Sin(La) * 32767 * ChanA\Volume          ; SineWave
        Case 1 : Vl = 32767 * ChanA\Volume                    ; Square
          If La > #PI : Vl = -Vl : EndIf 
        Case 2 : Vl = 32767 * ChanA\Volume * (La-#PI)/#PI     ; Sawtooth
        Case 3 : Vl = (Random(65535)-32768) *  ChanA\Volume   ; Noise
        Case 4                                               ; WAV file ~ Temporary
          If hWAV And Not Eof(hWAV)
            Vl = ReadWord(hWAV) *  ChanA\Volume 
          Else
            If hWAV : CloseFile(hWAV) : hWAV = 0 : EndIf
            Vl = 0
          EndIf
      EndSelect
      La + Kl                                                ; Calculate angle for next time
      If La > #PIx2 : La - #PIx2 : EndIf                     ; limit to 2*PI radians
    Else                                                     ; Not ON so point is zero
      Vl = 0
    EndIf
    PokeW(*P,Vl)                                             ; Put point in buffer
    *P + BytesPerSample                                      ; move buffer pointer to next ChanB sample
    
    ; Derive ChanB channel waveform point
    If ChanB\Switch
      Select ChanB\WaveForm
        Case 0 : Vr = Sin(Ra) * 32767 * ChanB\Volume         
        Case 1 : Vr = 32767 * ChanB\Volume
          If Ra > #PI : Vr = -Vr : EndIf                      
        Case 2 : Vr = 32767 * ChanB\Volume * (Ra-#PI)/#PI    
        Case 3 : Vr = (Random(65535)-32768) *  ChanB\Volume  
        Case 4 : ; Nothing!  
       EndSelect
      Ra + Kr
      If Ra > #PIx2 : Ra - #PIx2 : EndIf
    Else
      Vr = 0
    EndIf
    PokeW(*P,Vr)
    *P + BytesPerSample
    
  Next
  ;}
  
EndProcedure
Procedure SetGadgetMouseXY(Win,Gadget,Mx,My,flags=%111)      ;- Set mouse position over a gadget
  Static OldWin = -1
  Static XFix
  Static YFix
  
  ; Flags : Bitwise indicators of the prsence of a BORDER, TITLE and MENUBAR
  ; All default to TRUE.  (%BMT)
  
  ; Get the corrections needed to take account of borders, titles and menus
  If Win <> OldWin
    YFix = 0
    If flags & %001 : YFix + GetSystemMetrics_(#SM_CYCAPTION)    : EndIf
    If flags & %010 : YFix + GetSystemMetrics_(#SM_CYMENU)       : EndIf
    If flags & %100 : YFix + GetSystemMetrics_(#SM_CYFIXEDFRAME) : EndIf
    
    XFix = 0 
    If flags & %100 : XFix + GetSystemMetrics_(#SM_CXFIXEDFRAME) : EndIf
     
    OldWin = Win
  EndIf
  
  Mx + WindowX(Win)+GadgetX(Gadget) + XFix
  My + WindowY(Win)+GadgetY(Gadget) + YFix     
  SetCursorPos_(Mx,My)
  
EndProcedure
Код:
; AudioGenerator.pb
; by BasicallyPure
; 11.22.2013
; windows only
; PB 5.20 LTS

EnableExplicit

;{ Constants
#MainWin = 0
#OutDevice_1 = 0
#PIx2 = 2 * #PI
#Mono = 1
#Stereo = 2
#ScopeBkgndColor = $276724
#LeftColor  = $27CF24
#RighttColor = $27E4D3
#SpinLostFocus = 512
#Input_Finished = #PB_EventType_FirstCustomValue

; gadgets
#BtnHop     = 0
#BtnPause   = 1
#SpinLeft   = 2
#SpinRight  = 3
#TrackLeft  = 4
#TrackRight = 5

; menus
#Menu_1 = 0
;}

;{ Procedure declarations
Declare WinCallback(hwnd, uMsg, wParam, lParam)
Declare StartSoundOutput()
Declare StopSoundOutput()
Declare MAKE_WAVE(*SBuf)
Declare Init_GUI()
Declare EventLoop()
Declare ProcessSpin(nGad,*Freq)
;}

;{ Global variables
Global SampleClock     = 44100   ; Sampling frequency in 'samples per second'
Global BlockSize       = 8192    ; Number of samples in block
Global BytesPerSample  = 2       ; Number of bytes needed for each sample, don't change this
Global Channels        = #Stereo ; Number of channels, 1 for mono, 2 for stereo.
Global nBuf            = 8       ; Number of buffers
Global DevOut          = 1       ; default audio output device
Global Frequency_Left  = 1000
Global Frequency_Right = 440
Global Volume_Left.f   = 0.25
Global Volume_Right.f  = 0.25
Global hWaveOut
Global Hop = #False
Global Pause = #False
Global NumOutDevs

Global PlayFormat.WAVEFORMATEX
Global MyOutDevs.WAVEOUTCAPS
Global Dim outHdr.WAVEHDR(nBuf)
;}

If Init_GUI()
  EventLoop()
  StopSoundOutput()
EndIf

End

Procedure Init_GUI()
  Protected n, result = 1
  
  If OpenWindow(#MainWin,0,0,350,100,"Audio generator",#PB_Window_SystemMenu |#PB_Window_ScreenCentered)
    
    ButtonGadget(#BtnHop,10,10,50,25,"Hop",#PB_Button_Toggle)
    ButtonGadget(#BtnPause,70,10,50,25,"Pause",#PB_Button_Toggle)
    SpinGadget(#SpinLeft,130,02,70,25,10,5000)
    SetGadgetState(#SpinLeft,Frequency_Left) : SetGadgetText(#SpinLeft,Str(Frequency_Left))
    SetGadgetColor(#SpinLeft,#PB_Gadget_BackColor,#LeftColor)
    SpinGadget(#SpinRight,235,02,70,25,10,5000)
    SetGadgetState(#SpinRight,Frequency_Right) : SetGadgetText(#SpinRight,Str(Frequency_Right))
    SetGadgetColor(#SpinRight,#PB_Gadget_BackColor,#RighttColor)
    TrackBarGadget(#TrackLeft,125,30,100,25,0,100) : SetGadgetState(#TrackLeft,Volume_Left * 100)
    TrackBarGadget(#TrackRight,230,30,100,25,0,100) : SetGadgetState(#TrackRight,Volume_Right * 100)
    
    CreateMenu(#Menu_1, WindowID(#MainWin))
    
    ; locate all sound output devices
    OpenSubMenu("Sound Output Devices")
    NumOutDevs = waveOutGetNumDevs_()
    If NumOutDevs <> 0
      For n = 0 To NumOutDevs - 1
        If waveOutGetDevCaps_(n,@MyOutDevs,SizeOf(WAVEOUTCAPS)) = 0
          MenuItem(n + #OutDevice_1,PeekS(@MyOutDevs\szPname))
        EndIf
      Next
      CloseSubMenu()
      SetMenuItemState(#Menu_1,#OutDevice_1,#True)
      SetWindowCallback(@WinCallback()) ; Handle Sound Output callback
      StartSoundOutput()
    Else
      MessageRequester("Error!","No audio output device found.")
      result = 0
    EndIf
  Else
    result = 0
  EndIf
  
  ProcedureReturn result
EndProcedure

Procedure EventLoop()
  Protected menuSelection, ActiveGadget, n, Quit = #False
  
  Repeat
    Select WaitWindowEvent()
      Case #PB_Event_CloseWindow
        Quit = #True
      Case #PB_Event_Gadget
        Select EventGadget()
          Case #BtnHop
            Hop = GetGadgetState(#BtnHop)
            Frequency_Left = GetGadgetState(#SpinLeft)
            Frequency_Right = GetGadgetState(#SpinRight)
          Case #BtnPause
            Pause = GetGadgetState(#BtnPause)
            If Pause
              waveOutPause_(hWaveOut)
            Else
              waveOutRestart_(hWaveOut)
            EndIf
          Case #SpinLeft
            ProcessSpin(#SpinLeft, @Frequency_Left)
          Case #SpinRight
            ProcessSpin(#SpinRight, @Frequency_Right)
          Case #TrackLeft
            Volume_Left = GetGadgetState(#TrackLeft) / 100
          Case #TrackRight
            Volume_Right = GetGadgetState(#TrackRight) / 100
        EndSelect
      Case #PB_Event_Menu
        menuSelection = EventMenu()
        If GetMenuItemState(#Menu_1,menuSelection) = #False
          Select menuSelection
            Case #OutDevice_1 To #OutDevice_1 + NumOutDevs - 1 ; Output device selection
              For n = #OutDevice_1 To #OutDevice_1 + NumOutDevs - 1
                If n = menuSelection
                  SetMenuItemState(#Menu_1,menuSelection,#True)
                Else
                  SetMenuItemState(#Menu_1,n,#False)
                EndIf
              Next
              DevOut = menuSelection - #OutDevice_1 + 1
              StopSoundOutput()
              StartSoundOutput()
          EndSelect
        EndIf
      Case #WM_KEYUP
        ActiveGadget = GetActiveGadget()
        Select ActiveGadget
          Case #SpinLeft To #SpinRight
            If EventwParam() = #VK_RETURN
              PostEvent(#PB_Event_Gadget,#MainWin,ActiveGadget,#Input_Finished)
            EndIf
        EndSelect
    EndSelect
  Until Quit = #True
  
EndProcedure

Procedure WinCallback(hwnd, uMsg, wParam, lParam)
  ; Window callback to service sound output message
  Static *hWaveO.WAVEHDR
  
  Select uMsg
    Case #MM_WOM_DONE           ; Sound output, a play buffer has been returned.
      *hWaveO.WAVEHDR = lParam  ; lParam has the address of WAVEHDR
      MAKE_WAVE(*hWaveO\lpData) ; send pointer where to write NEW data
      *hWaveO\dwBytesRecorded = BlockSize             ; Number of bytes written into buffer
      waveOutWrite_(hWaveOut,lParam, SizeOf(WAVEHDR)) ; Send to sound device => jack socket => cable =>
  EndSelect
  
  ProcedureReturn #PB_ProcessPureBasicEvents
EndProcedure

Procedure StartSoundOutput()
  Protected T,i, *P
  Static *OutBufMem
  
  With PlayFormat
    \wFormatTag      = #WAVE_FORMAT_PCM
    \nChannels       = Channels
    \wBitsPerSample  = BytesPerSample * 8
    \nSamplesPerSec  = SampleClock
    \nBlockAlign     = Channels * BytesPerSample
    \nAvgBytesPerSec = \nSamplesPerSec * \nBlockAlign
  EndWith
  
  If *OutBufMem : FreeMemory(*OutBufMem) : EndIf   ; Free a prior assignement
  *OutBufMem = AllocateMemory(BlockSize * nBuf)    ; Reserve memory for all the buffers
  
  T =  waveOutOpen_(@hWaveOut, #WAVE_MAPPER+DevOut, @PlayFormat, WindowID(#MainWin), #True, #CALLBACK_WINDOW | #WAVE_FORMAT_DIRECT)
  If T = #MMSYSERR_NOERROR
    
    *P = *OutBufMem                               ; Pointer to start of memory
    For i = 0 To nBuf-1                           ; For each buffer
      outHdr(i)\lpData         = *P               ; start of buffer
      outHdr(i)\dwBufferLength = BlockSize        ; size of buffer
      outHdr(i)\dwFlags        = 0
      outHdr(i)\dwLoops        = 0
      T | waveOutPrepareHeader_(hWaveOut, outHdr(i), SizeOf(WAVEHDR))
      *P + BlockSize
    Next
    
    For i = 0 To nBuf-1
      PostMessage_(WindowID(#MainWin),#MM_WOM_DONE,0,outHdr(i))
    Next 
    
  EndIf
  
  If T = #MMSYSERR_NOERROR : ProcedureReturn 1 : Else : ProcedureReturn 0 : EndIf
  
EndProcedure

Procedure StopSoundOutput()
  Protected i
  waveOutReset_(hWaveOut)
  For i = 0 To nBuf - 1
    waveOutUnprepareHeader_(hWaveOut, outHdr(i), SizeOf(WAVEHDR))
  Next
  waveOutClose_(hWaveOut)
EndProcedure

Procedure MAKE_WAVE(*SBuf)
  ; This routine generates Left and Right waveforms.
  
  Static.d Angle, Kl, Kr, La, Ra
  Static.i sample
  Static.l Vl, Vr
  
  If Hop
    Frequency_Left = Random(1220,220)
    Frequency_Right = Random(1220,220)
  EndIf
  
  ; Calculate the frequency scaling factors
  Kl = #PIx2 * Frequency_Left  / SampleClock
  Kr = #PIx2 * Frequency_Right / SampleClock
  
  sample = 1
  Repeat ; Generate waveform data
         ; Left sample
    Vl = Sin(La) * 32767 * Volume_Left
    La + Kl                            ; calculate angle for next time
    If La > #PIx2 : La - #PIx2 : EndIf ; limit to 2*PI radians
    PokeW(*SBuf,Vl)                    ; Put point in buffer
    *SBuf + BytesPerSample             ; move buffer pointer to next sample
    
    ; Right sample
    Vr = Sin(Ra) * 32767 * Volume_Right
    Ra + Kr
    If Ra > #PIx2 : Ra - #PIx2 : EndIf
    PokeW(*SBuf,Vr)
    *SBuf + BytesPerSample
    
    sample + PlayFormat\nBlockAlign
  Until sample > BlockSize
  
EndProcedure

Procedure ProcessSpin(nGad,*Freq)
  Select EventType()
    Case #PB_EventType_Up, #PB_EventType_Down
      SetGadgetText(nGad, Str(GetGadgetState(nGad)))
      PokeI(*Freq, GetGadgetState(nGad))
    Case #Input_Finished, #SpinLostFocus
      SetGadgetState(nGad,Val(GetGadgetText(nGad)))
      SetGadgetText(nGad, Str(GetGadgetState(nGad)))
      PokeI(*Freq, GetGadgetState(nGad))
  EndSelect
EndProcedure

0


Вы здесь » PureBasic - форум » PureBasic для Windows » Звуковой генератор