Electric motor control with stereophonic output

Share your advanced PureBasic knowledge/code with the community.
User avatar
Psychophanta
Addict
Addict
Posts: 4976
Joined: Wed Jun 11, 2003 9:33 pm
Location: Lípetsk, Russian Federation
Contact:

Electric motor control with stereophonic output

Post by Psychophanta »

This video shows that, using a suitable software and a unique stereophonic output from a voltage amplifier, there can be obtained an absolute control over the torque and the speed of an electric motor: syncronous motors, async motors, brushles or not, triphase, monophase, stepper, etc.
Please notice the shown program is just a simple tip to display the aim of this video, this is:
to expose the fact that using a computer with at least a dual PCM output; like a PINE64, ODROID, Banana PI, Orange PI, CubieBoard, Raspberry PI, Teensy, PC i386, Amiga, etc. there is possible the more absolute control about electrical amplitudes and frequencies, not just digital pulses, but also analogically.
All it without electronic designs, like H-bridges, oscillators, impulses generators (bad called "inverters"), frequency variators, etc., but purely:
voltage amplifiers and optimal software.
https://www.youtube.com/watch?v=a6rFB0d0_gI

Tip program used:

Code: Select all

Structure Vector3D
  x.f:y.f:z.f:m.f
EndStructure
Structure KSAUDIO_CHANNEL_CONFIG
  ;https://msdn.microsoft.com/library/windows/hardware/ff537083
  ActiveSpeakerPositions.l
EndStructure
Enumeration
  #MainWin=0
  #OutDevice_1=0
  #nhiloscolor=$27CF24
  #SpinLostFocus=512
  #Input_Finished=#PB_EventType_FirstCustomValue
  #maxnhilos=6
  ;
  #ACM_MPEG_LAYER1 =$0001
  #ACM_MPEG_LAYER2 =$0002
  #ACM_MPEG_LAYER3 =$0004
  #ACM_MPEG_STEREO =$0001
  #ACM_MPEG_JOINTSTEREO =$0002
  #ACM_MPEG_DUALCHANNEL =$0004
  #ACM_MPEG_SINGLECHANNEL =$0008
  #ACM_MPEG_PRIVATEBIT =$0001
  #ACM_MPEG_COPYRIGHT =$0002
  #ACM_MPEG_ORIGINALHOME =$0004
  #ACM_MPEG_PROTECTIONBIT =$0008
  #ACM_MPEG_ID_MPEG1 =$0010
  #SPEAKER_FRONT_LEFT =$1
  #SPEAKER_FRONT_RIGHT =$2
  #SPEAKER_FRONT_CENTER =$4
  #SPEAKER_LOW_FREQUENCY =$8
  #SPEAKER_BACK_LEFT =$10
  #SPEAKER_BACK_RIGHT =$20
  #SPEAKER_FRONT_LEFT_OF_CENTER =$40
  #SPEAKER_FRONT_RIGHT_OF_CENTER =$80
  #SPEAKER_BACK_CENTER =$100
  #SPEAKER_SIDE_LEFT =$200
  #SPEAKER_SIDE_RIGHT =$400
  #SPEAKER_TOP_CENTER =$800
  #SPEAKER_TOP_FRONT_LEFT 	=$1000
  #SPEAKER_TOP_FRONT_CENTER =$2000
  #SPEAKER_TOP_FRONT_RIGHT =$4000
  #SPEAKER_TOP_BACK_LEFT =$8000
  #SPEAKER_TOP_BACK_CENTER =$10000
  #SPEAKER_TOP_BACK_RIGHT =$20000
  #WAVE_FORMAT_UNKNOWN                 =$0000
  #WAVE_FORMAT_PCM                     =$0001
  #WAVE_FORMAT_ADPCM                   =$0002
  #WAVE_FORMAT_IEEE_FLOAT              =$0003
  #WAVE_FORMAT_VSELP                   =$0004
  #WAVE_FORMAT_IBM_CVSD                =$0005
  #WAVE_FORMAT_ALAW                    =$0006
  #WAVE_FORMAT_MULAW                   =$0007
  #WAVE_FORMAT_OKI_ADPCM               =$0010
  #WAVE_FORMAT_DVI_ADPCM               =$0011
  #WAVE_FORMAT_MEDIASPACE_ADPCM        =$0012
  #WAVE_FORMAT_SIERRA_ADPCM            =$0013
  #WAVE_FORMAT_G723_ADPCM              =$0014
  #WAVE_FORMAT_DIGIFIX                 =$0016
  #WAVE_FORMAT_DIALOGIC_OKI_ADPCM      =$0017
  #WAVE_FORMAT_MEDIAVISION_ADPCM       =$0018
  #WAVE_FORMAT_CU_CODEC                =$0019
  #WAVE_FORMAT_YAMAHA_ADPCM            =$0020
  #WAVE_FORMAT_SONARC                  =$0021
  #WAVE_FORMAT_DSPGROUP_TRUESPEECH     =$0022
  #WAVE_FORMAT_ECHOSC1                 =$0023
  #WAVE_FORMAT_AUDIOFILE_AF36          =$0024
  #WAVE_FORMAT_APTX                    =$0025
  #WAVE_FORMAT_AUDIOFILE_AF10          =$0026
  #WAVE_FORMAT_PROSODY_1612            =$0027
  #WAVE_FORMAT_LRC                     =$0028
  #WAVE_FORMAT_DOLBY_AC2               =$0030
  #WAVE_FORMAT_GSM610                  =$0031
  #WAVE_FORMAT_MSNAUDIO                =$0032
  #WAVE_FORMAT_ANTEX_ADPCME            =$0033
  #WAVE_FORMAT_CONTROL_RES_VQLPC       =$0034
  #WAVE_FORMAT_DIGIREAL                =$0035
  #WAVE_FORMAT_DIGIADPCM               =$0036
  #WAVE_FORMAT_CONTROL_RES_CR10        =$0037
  #WAVE_FORMAT_NMS_VBXADPCM            =$0038
  #WAVE_FORMAT_ROLAND_RDAC             =$0039
  #WAVE_FORMAT_ECHOSC3                 =$003A
  #WAVE_FORMAT_ROCKWELL_ADPCM          =$003B
  #WAVE_FORMAT_ROCKWELL_DIGITALK       =$003C
  #WAVE_FORMAT_XEBEC                   =$003D
  #WAVE_FORMAT_G721_ADPCM              =$0040
  #WAVE_FORMAT_G728_CELP               =$0041
  #WAVE_FORMAT_MSG723                  =$0042
  #WAVE_FORMAT_MPEG                    =$0050
  #WAVE_FORMAT_RT24                    =$0052
  #WAVE_FORMAT_PAC                     =$0053
  #WAVE_FORMAT_MPEGLAYER3              =$0055
  #WAVE_FORMAT_LUCENT_G723             =$0059
  #WAVE_FORMAT_CIRRUS                  =$0060
  #WAVE_FORMAT_ESPCM                   =$0061
  #WAVE_FORMAT_VOXWARE                 =$0062
  #WAVE_FORMAT_CANOPUS_ATRAC           =$0063
  #WAVE_FORMAT_G726_ADPCM              =$0064
  #WAVE_FORMAT_G722_ADPCM              =$0065
  #WAVE_FORMAT_DSAT                    =$0066
  #WAVE_FORMAT_DSAT_DISPLAY            =$0067
  #WAVE_FORMAT_VOXWARE_BYTE_ALIGNED    =$0069
  #WAVE_FORMAT_VOXWARE_AC8             =$0070
  #WAVE_FORMAT_VOXWARE_AC10            =$0071
  #WAVE_FORMAT_VOXWARE_AC16            =$0072
  #WAVE_FORMAT_VOXWARE_AC20            =$0073
  #WAVE_FORMAT_VOXWARE_RT24            =$0074
  #WAVE_FORMAT_VOXWARE_RT29            =$0075
  #WAVE_FORMAT_VOXWARE_RT29HW          =$0076
  #WAVE_FORMAT_VOXWARE_VR12            =$0077
  #WAVE_FORMAT_VOXWARE_VR18            =$0078
  #WAVE_FORMAT_VOXWARE_TQ40            =$0079
  #WAVE_FORMAT_SOFTSOUND               =$0080
  #WAVE_FORMAT_VOXWARE_TQ60            =$0081
  #WAVE_FORMAT_MSRT24                  =$0082
  #WAVE_FORMAT_G729A                   =$0083
  #WAVE_FORMAT_MVI_MV12                =$0084
  #WAVE_FORMAT_DF_G726                 =$0085
  #WAVE_FORMAT_DF_GSM610               =$0086
  #WAVE_FORMAT_ISIAUDIO                =$0088
  #WAVE_FORMAT_ONLIVE                  =$0089
  #WAVE_FORMAT_SBC24                   =$0091
  #WAVE_FORMAT_DOLBY_AC3_SPDIF         =$0092
  #WAVE_FORMAT_ZYXEL_ADPCM             =$0097
  #WAVE_FORMAT_PHILIPS_LPCBB           =$0098
  #WAVE_FORMAT_PACKED                  =$0099
  #WAVE_FORMAT_RHETOREX_ADPCM          =$0100
  #WAVE_FORMAT_IRAT                    =$0101
  #WAVE_FORMAT_VIVO_G723               =$0111
  #WAVE_FORMAT_VIVO_SIREN              =$0112
  #WAVE_FORMAT_DIGITAL_G723            =$0113
  #WAVE_FORMAT_CREATIVE_ADPCM          =$0200
  #WAVE_FORMAT_CREATIVE_FASTSPEECH8    =$0202
  #WAVE_FORMAT_CREATIVE_FASTSPEECH10   =$0203
  #WAVE_FORMAT_QUARTERDECK             =$0220
  #WAVE_FORMAT_FM_TOWNS_SND            =$0300
  #WAVE_FORMAT_BTV_DIGITAL             =$0400
  #WAVE_FORMAT_VME_VMPCM               =$0680
  #WAVE_FORMAT_OLIGSM                  =$1000
  #WAVE_FORMAT_OLIADPCM                =$1001
  #WAVE_FORMAT_OLICELP                 =$1002
  #WAVE_FORMAT_OLISBC                  =$1003
  #WAVE_FORMAT_OLIOPR                  =$1004
  #WAVE_FORMAT_LH_CODEC                =$1100
  #WAVE_FORMAT_NORRIS                  =$1400
  #WAVE_FORMAT_ISIAUDIO_                =$1401
  #WAVE_FORMAT_SOUNDSPACE_MUSICOMPRESS =$1500
  #WAVE_FORMAT_DVM                     =$2000
; /* http://msdn.microsoft.com/en-us/library/aa372553%28VS.85%29.aspx */
  #WAVE_FORMAT_RAW_AAC1                =$00FF
  #WAVE_FORMAT_MPEG_HEAAC              =$1610
  #WAVE_FORMAT_MPEG_ADTS_AAC           =$1600
  #WAVE_FORMAT_DRM                     =$0009
  #WAVE_FORMAT_DTS                     =$0008
  #WAVE_FORMAT_WMAVOICE9               =$000A
  #WAVE_FORMAT_WMASPDIF                =$0164
  #WAVE_FORMAT_WMAUDIO_LOSSLESS        =$0163
  #WAVE_FORMAT_WMAUDIO2                =$0161
  #WAVE_FORMAT_WMAUDIO3                =$0162
; /* http://msdn.microsoft.com/en-us/library/dd317599%28VS.85%29.aspx */
  #WAVE_FORMAT_MPEG_LOAS               =$1602
  #WAVE_FORMAT_RAW_SPORT               =$0240
  #WAVE_FORMAT_ESST_AC3                =$0241
  #WAVE_FORMAT_DTS2                    =$2001
  ; Audio quality constants
  #KSAUDIO_QUALITY_WORST          =$0
  #KSAUDIO_QUALITY_PC             =$1
  #KSAUDIO_QUALITY_BASIC          =$2
  #KSAUDIO_QUALITY_ADVANCED       =$3
  ; Audio CPU resource constants
  #KSAUDIO_CPU_RESOURCES_NOT_HOST_CPU=$00000000
  #KSAUDIO_CPU_RESOURCES_HOST_CPU =$7FFFFFFF
  ; Speaker Positions:
  #SPEAKER_FRONT_LEFT         =$1
  #SPEAKER_FRONT_RIGHT        =$2
  #SPEAKER_FRONT_CENTER       =$4
  #SPEAKER_LOW_FREQUENCY      =$8
  #SPEAKER_BACK_LEFT          =$10
  #SPEAKER_BACK_RIGHT         =$20
  #SPEAKER_FRONT_LEFT_OF_CENTER  =$40
  #SPEAKER_FRONT_RIGHT_OF_CENTER =$80
  #SPEAKER_BACK_CENTER        =$100
  #SPEAKER_SIDE_LEFT          =$200
  #SPEAKER_SIDE_RIGHT         =$400
  #SPEAKER_TOP_CENTER         =$800
  #SPEAKER_TOP_FRONT_LEFT     =$1000
  #SPEAKER_TOP_FRONT_CENTER   =$2000
  #SPEAKER_TOP_FRONT_RIGHT    =$4000
  #SPEAKER_TOP_BACK_LEFT      =$8000
  #SPEAKER_TOP_BACK_CENTER    =$10000
  #SPEAKER_TOP_BACK_RIGHT     =$20000
  ; Bit mask locations reserved For future use
  #SPEAKER_RESERVED           =$7FFC0000
  ; Used To specify that any possible permutation of speaker configurations
  #SPEAKER_ALL =$80000000
  #KSAUDIO_SPEAKER_MONO=#SPEAKER_FRONT_CENTER
  #KSAUDIO_SPEAKER_STEREO=#SPEAKER_FRONT_LEFT | #SPEAKER_FRONT_RIGHT
  #KSAUDIO_SPEAKER_QUAD=#SPEAKER_FRONT_LEFT | #SPEAKER_FRONT_RIGHT | #SPEAKER_BACK_LEFT  | #SPEAKER_BACK_RIGHT
  #KSAUDIO_SPEAKER_SURROUND=#SPEAKER_FRONT_LEFT | #SPEAKER_FRONT_RIGHT | #SPEAKER_FRONT_CENTER | #SPEAKER_BACK_CENTER
  #KSAUDIO_SPEAKER_5POINT1=#SPEAKER_FRONT_LEFT | #SPEAKER_FRONT_RIGHT | #SPEAKER_FRONT_CENTER | #SPEAKER_LOW_FREQUENCY | #SPEAKER_BACK_LEFT  | #SPEAKER_BACK_RIGHT
  #KSAUDIO_SPEAKER_7POINT1=#SPEAKER_FRONT_LEFT | #SPEAKER_FRONT_RIGHT | #SPEAKER_FRONT_CENTER | #SPEAKER_LOW_FREQUENCY | #SPEAKER_BACK_LEFT | #SPEAKER_BACK_RIGHT | #SPEAKER_FRONT_LEFT_OF_CENTER | #SPEAKER_FRONT_RIGHT_OF_CENTER
  #KSAUDIO_SPEAKER_5POINT1_SURROUND=#SPEAKER_FRONT_LEFT | #SPEAKER_FRONT_RIGHT | #SPEAKER_FRONT_CENTER | #SPEAKER_LOW_FREQUENCY | #SPEAKER_SIDE_LEFT  | #SPEAKER_SIDE_RIGHT
  #KSAUDIO_SPEAKER_7POINT1_SURROUND=#SPEAKER_FRONT_LEFT | #SPEAKER_FRONT_RIGHT | #SPEAKER_FRONT_CENTER | #SPEAKER_LOW_FREQUENCY | #SPEAKER_BACK_LEFT | #SPEAKER_BACK_RIGHT | #SPEAKER_SIDE_LEFT | #SPEAKER_SIDE_RIGHT
  #KSAUDIO_SPEAKER_DIRECTOUT=0
  ; The following are obsolete 5.1 And 7.1 settings (they lack side speakers).  Note this means that the Default 5.1 And 7.1 settings (KSAUDIO_SPEAKER_5POINT1 And KSAUDIO_SPEAKER_7POINT1 are similarly obsolete but are unchanged For compatibility reasons).
  #KSAUDIO_SPEAKER_5POINT1_BACK=#KSAUDIO_SPEAKER_5POINT1
  #KSAUDIO_SPEAKER_7POINT1_WIDE=#KSAUDIO_SPEAKER_7POINT1
  ; DVD Speaker Positions
  #KSAUDIO_SPEAKER_GROUND_FRONT_LEFT=#SPEAKER_FRONT_LEFT
  #KSAUDIO_SPEAKER_GROUND_FRONT_CENTER=#SPEAKER_FRONT_CENTER
  #KSAUDIO_SPEAKER_GROUND_FRONT_RIGHT=#SPEAKER_FRONT_RIGHT
  #KSAUDIO_SPEAKER_GROUND_REAR_LEFT=#SPEAKER_BACK_LEFT
  #KSAUDIO_SPEAKER_GROUND_REAR_RIGHT=#SPEAKER_BACK_RIGHT
  #KSAUDIO_SPEAKER_TOP_MIDDLE=#SPEAKER_TOP_CENTER
  #KSAUDIO_SPEAKER_SUPER_WOOFER=#SPEAKER_LOW_FREQUENCY
  ; menus
  #Devices=0
  #InputDevice
  #OutputDevice
  #formatodeondaentrada
  #formatodeondasalida
  ; gadgets
  #Canvas=100
  #Canvasmemoria
  #TrackbarA
  #TrackbarB
  #TrackbarC
  #TrackbarE
  #TextA
  #TextB
  #TextC
  #TextE
  #BotonResetView
  #BotonPausa
  #nhilos
  #TrackLeft
  #TrackRight
  #formadeonda
  #formadeondahilo1
  #formadeondahilo2
  #formadeondahilo3
  #formadeondahilo4
  #formadeondahilo5
  #formadeondahilo6
  #seleccionhilo1
  #seleccionhilo2
  #seleccionhilo3
  #seleccionhilo4
  #seleccionhilo5
  #seleccionhilo6
  #fase
  #fasehilo1
  #fasehilo2
  #fasehilo3
  #fasehilo4
  #fasehilo5
  #fasehilo6
  #CheckboxrelacionFA
  #SpinrelacionFA
EndEnumeration
Global .GUID KSDATAFORMAT_SUBTYPE_ANALOG,KSDATAFORMAT_SUBTYPE_PCM,KSDATAFORMAT_SUBTYPE_IEEE_FLOAT,KSDATAFORMAT_SUBTYPE_DRM,KSDATAFORMAT_SUBTYPE_ALAW,KSDATAFORMAT_SUBTYPE_MULAW,KSDATAFORMAT_SUBTYPE_ADPCM,KSDATAFORMAT_SUBTYPE_MPEG,KSDATAFORMAT_SPECIFIER_VC_ID,KSDATAFORMAT_SPECIFIER_WAVEFORMATEX,KSDATAFORMAT_SPECIFIER_DSOUND
Procedure.b HexByteStringToDecByteSequence(HexString.s,*seq.ascii)
  ;This function converts a given Hex string (.s) to a byte sequence stored in a given base address of sequential memory:
  Protected c.a,Nibbles.l=Len(HexString.s),t.l
  If Nibbles.l&1:HexString.s="0"+HexString.s:Nibbles.l+1:EndIf
  For t.l=1 To Nibbles.l
    c.a=Asc(LCase(Mid(HexString.s,t.l,1)))
    If c<'0' Or c>'f' Or (c>'9' And c<'a')
      ProcedureReturn 0
    EndIf
    If c>'9':c=(c&$5F)-$07:EndIf
    c-'0'
    If t.l&1
      *seq\a=c<<4
    Else
      *seq\a|c
      *seq+1
    EndIf
  Next
  ProcedureReturn 1
EndProcedure
Macro DEFINE_GUIDSTRUCT(caracteres,nombre)
  HexByteStringToDecByteSequence(StringField(caracteres#,1,"-"),@nombre#\Data1)
  !mov eax,dword[v_#nombre#]
  !bswap eax
  !mov dword[v_#nombre#],eax
  HexByteStringToDecByteSequence(StringField(caracteres#,2,"-"),@nombre#\Data2)
  !rol word[v_#nombre#+4],8
  HexByteStringToDecByteSequence(StringField(caracteres#,3,"-"),@nombre#\Data3)
  !rol word[v_#nombre#+6],8
  HexByteStringToDecByteSequence(StringField(caracteres#,4,"-"),@nombre#\Data4[0])
  HexByteStringToDecByteSequence(StringField(caracteres#,5,"-"),@nombre#\Data4[2])
EndMacro
DEFINE_GUIDSTRUCT("6dba3190-67bd-11cf-a0f7-0020afd156e4", KSDATAFORMAT_SUBTYPE_ANALOG)
DEFINE_GUIDSTRUCT("00000001-0000-0010-8000-00aa00389b71", KSDATAFORMAT_SUBTYPE_PCM)
DEFINE_GUIDSTRUCT("00000003-0000-0010-8000-00aa00389b71", KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)
DEFINE_GUIDSTRUCT("00000009-0000-0010-8000-00aa00389b71", KSDATAFORMAT_SUBTYPE_DRM)
DEFINE_GUIDSTRUCT("00000006-0000-0010-8000-00aa00389b71", KSDATAFORMAT_SUBTYPE_ALAW)
DEFINE_GUIDSTRUCT("00000007-0000-0010-8000-00aa00389b71", KSDATAFORMAT_SUBTYPE_MULAW)
DEFINE_GUIDSTRUCT("00000002-0000-0010-8000-00aa00389b71", KSDATAFORMAT_SUBTYPE_ADPCM)
DEFINE_GUIDSTRUCT("00000050-0000-0010-8000-00aa00389b71", KSDATAFORMAT_SUBTYPE_MPEG)
DEFINE_GUIDSTRUCT("AD98D184-AAC3-11D0-A41C-00A0C9223196", KSDATAFORMAT_SPECIFIER_VC_ID)
DEFINE_GUIDSTRUCT("05589f81-c356-11ce-bf01-00aa0055595a", KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)
DEFINE_GUIDSTRUCT("518590a2-a184-11d0-8522-00c04fd9baf3", KSDATAFORMAT_SPECIFIER_DSOUND)
Structure gadgetmenu
  nombre$
  disponible.b
  salida.b
EndStructure
Global NumDevicesIn.i,NumDevicesOut.i,NewList menuoptions.gadgetmenu()
#PIx2=2*#PI
; Global variables
Global a.f=80,b.f=200,c.f=60,e.f=3,i.i,j.i
Global DevOut.l=1,DevIn.l=1; default audio output device; default audio input device
Global nhilos.l=3,nhilosIn.l=3
Global BlockSize.l=4096,nBuf.l=16,*memoria=ReAllocateMemory(*memoria,nBuf*BlockSize),BlockSizeIn.l=4096,nBufIn.l=16,*memoriaIn=ReAllocateMemory(*memoriaIn,nBufIn*BlockSizeIn)
Global Dim formadeondahilo.u(#maxnhilos)
Global Dim fasehilo.f(#maxnhilos):fasehilo(1)=0:fasehilo(2)=120:fasehilo(3)=240:fasehilo(4)=0:fasehilo(5)=120:fasehilo(6)=240
Global Dim frecuenciahilo.f(#maxnhilos):frecuenciahilo(1)=1000:frecuenciahilo(2)=440:frecuenciahilo(3)=1000:frecuenciahilo(4)=440:frecuenciahilo(5)=1000:frecuenciahilo(6)=440
Global Dim alturahilo.f(#maxnhilos)
Global Dim amplitudhilo.f(#maxnhilos):amplitudhilo(1)=0.25*$7FFF:amplitudhilo(2)=0.25*$7FFF:amplitudhilo(3)=0.25*$7FFF:amplitudhilo(4)=0.25*$7FFF:amplitudhilo(5)=0.25*$7FFF:amplitudhilo(6)=0.25*$7FFF
Global Dim relacionAmplitudFrecuencia.f(#maxnhilos)
Global Dim xmem.f(#maxnhilos):For i=1 To #maxnhilos:xmem(i)=Radian(fasehilo(i)):Next
Global Dim paso.f(#maxnhilos)
Global hWaveOut.i,hWaveIn.i
Global FormatoOut.WAVEFORMATEXTENSIBLE,FormatoIn.WAVEFORMATEXTENSIBLE,CapsOut.WAVEOUTCAPS,CapsIn.WAVEINCAPS,Dim outHdr.WAVEHDR(nBuf),Dim inHdr.WAVEHDR(nBuf)
FormatoOut\format\wFormatTag=#WAVE_FORMAT_PCM ;#WAVE_FORMAT_EXTENSIBLE
FormatoOut\format\nChannels=2;nhilos-1
FormatoOut\format\nSamplesPerSec=44100
FormatoOut\format\wBitsPerSample=16
FormatoOut\format\nBlockAlign=FormatoOut\format\nChannels*FormatoOut\format\wBitsPerSample>>3
FormatoOut\format\nAvgBytesPerSec=FormatoOut\format\nSamplesPerSec*FormatoOut\format\nBlockAlign
FormatoOut\wValidBitsPerSample=FormatoOut\format\wBitsPerSample
FormatoOut\dwChannelMask=$63F ; 7.1 (ojo poner en la configuración de altavoces de windows el 7.1) ; https://docs.microsoft.com/en-us/windows-hardware/drivers/audio/channel-mask
;Por ejemplos; si solo front-left y front-center son especificados, entonces front-left y front-center deben ser los canales 0 y 1 repectivamente en el intercalado en la memoria.
;Si el miembro 'format\nChannels' vale 4 y 'dwChannelMask' es $33, los canales se entienden como front-left, front-right, back-left y back-right.
;los datos en el intercalado en la memoria deben estar en ese orden.
FormatoOut\SubFormat=KSDATAFORMAT_SUBTYPE_PCM
FormatoIn\format\wFormatTag=#WAVE_FORMAT_PCM ;#WAVE_FORMAT_EXTENSIBLE
FormatoIn\format\nChannels=2;nhilos-1
FormatoIn\format\nSamplesPerSec=44100
FormatoIn\format\wBitsPerSample=16
FormatoIn\format\nBlockAlign=FormatoIn\format\nChannels*FormatoIn\format\wBitsPerSample>>3
FormatoIn\format\nAvgBytesPerSec=FormatoIn\format\nSamplesPerSec*FormatoOut\format\nBlockAlign
FormatoIn\wValidBitsPerSample=FormatoIn\format\wBitsPerSample
FormatoIn\dwChannelMask=$63F
FormatoIn\SubFormat=KSDATAFORMAT_SUBTYPE_PCM
Global canvasx.u=0,canvasy.u=0,canvasw.u=600,canvash.u=400
Global sliderax.u=canvasx+canvasw,slideray.u=canvasy,slideraw.u=25,sliderah.u=canvash
Global sliderbx.u=canvasx,sliderby.u=canvasy+canvash,sliderbw.u=canvasw,sliderbh.u=25
Global slidercx.u=canvasx,slidercy.u=sliderby+25,slidercw.u=canvasw,sliderch.u=25
Global sliderex.u=canvasx,sliderey.u=slidercy+25,sliderew.u=canvasw,slidereh.u=25
Global Dim colorhilo.l(#maxnhilos):colorhilo(1)=$CAD0EE:colorhilo(2)=$AAEE90:colorhilo(3)=$27D3E4:colorhilo(4)=$D327E4:colorhilo(5)=$E4D327:colorhilo(6)=$27A4F3
Global editando.b,hilo.a=1,referencia.u=canvash,canvasmousex.u,canvasmousey.u,canvassumdeltamousex.f=1.0,canvassumdeltamousey.f
Macro RND(v1,v2,ndecimales=3); maximo 'ndecimales' = 9
  ;genera un número aleatorio entre los valores v1 y v2
  ;NOTE: usar con variables de punto flotante para que esta macro funcione correctamente
  (Random(1E#ndecimales#)*(v2#-v1#)/1E#ndecimales#+v1#)
EndMacro
Macro seno(f=0,desvx=0,desvy=0)
  (a+desvy#)*Sin(Radian(f#)+(e*desvx#)*x.u/c)-b
EndMacro
Macro rectangular(f=0,desvx=0,desvy=0)
  (a+desvy#)*Sign(Sin(Radian(f#)+x.u/(e*desvx#))-1+2*c/canvasw)-b
EndMacro
Macro canvashilos()
  If editando
    If EventType()=#PB_EventType_MouseMove
      prevcanvasmousey.u=canvasmousey.u
      canvasmousey.u=GetGadgetAttribute(#Canvas,#PB_Canvas_MouseY)
      canvasdeltamousey.f=prevcanvasmousey-canvasmousey
      prevcanvasmousex.u=canvasmousex.u
      canvasmousex.u=GetGadgetAttribute(#Canvas,#PB_Canvas_MouseX)
      canvasdeltamousex.f=prevcanvasmousex-canvasmousex
    EndIf
    canvassumdeltamousex.f+canvasdeltamousex.f/frecuenciahilo(1)/100:If canvassumdeltamousex<0:canvassumdeltamousex=0:EndIf
    canvassumdeltamousey.f+canvasdeltamousey.f
  EndIf
  ;dibuja:
  StartDrawing(CanvasOutput(#Canvas))
  Box(canvasx,canvasy,canvasw,canvash,0)
  ; dibujo todos los hilos menos el enfocado
  For i=1 To nhilos
    If i<>hilo
      For x.u=canvasx To canvasw-1
        y.f=a*(Sin(Radian(fasehilo(hilo))+(e*canvassumdeltamousex)*x.u/c)-Sin(Radian(fasehilo(i))+(e*canvassumdeltamousex)*x.u/c)); <- diferencia entre 'hilo' y 'i' en el instante 'x'
        y.f+referencia-seno(fasehilo(hilo),canvassumdeltamousex,canvassumdeltamousey)
        Circle(x,y.f,1,Colorhilo(i))
      Next
      alturahilo(i)
    EndIf
  Next
  ; dibujo el hilo enfocado el último para que quede visible sobre los demás
  For x.u=canvasx To canvasw-1
;     xp.u=x.u:yp.u=y.f
    y.f=referencia-seno(fasehilo(hilo),canvassumdeltamousex,canvassumdeltamousey)
    Circle(x,y.f,1,Colorhilo(hilo))
;     LineXY(xp,yp,x,y.f,$AAEE90)
  Next
  StopDrawing()
  
  ;Canvas memoria hilos:
;   CREAR_ONDA(*memoria,BlockSize/FormatoOut\format\nBlockAlign)
  ;dibuja:
  StartDrawing(CanvasOutput(#Canvasmemoria))
  Box(canvasx,canvasy,canvasw,canvash,0)
  x.u=0:ymem.l=0:ref_2.u=referencia/2:mult.f=referencia/Pow(2,FormatoOut\format\wBitsPerSample)
  Repeat
    Plot(x*canvasw/BlockSize,ref_2,Colorhilo(1)); <- linea central = masa = hilo0
;     Select FormatoOut\format\wBitsPerSample
;     Case 8,16
      y.f=ref_2-PeekW(*memoria+x)*mult
      Plot(x*canvasw/BlockSize,y.f,Colorhilo(2))
      x+FormatoOut\format\wBitsPerSample>>3
      y.f=ref_2-PeekW(*memoria+x)*mult
      Plot(x*canvasw/BlockSize,y.f,Colorhilo(3))
;     Case 32
;       y.f=ref_2-PeekL(*memoria+x)*mult
;       Plot(x*canvasw/BlockSize,y.f,Colorhilo(2))
;       x+FormatoOut\format\wBitsPerSample>>3
;       y.f=ref_2-PeekL(*memoria+x)*mult
;       Plot(x*canvasw/BlockSize,y.f,Colorhilo(3))
;     Case 24
;       ymem.l=PeekL(*memoria+x); extender el signo de 24bit a 32bit
;       !mov al,byte[p.v_ymem+2]
;       !cbw
;       !shl eax,16
;       !mov ax,word[p.v_ymem]
;       !mov dword[p.v_ymem],eax
;       y.f=ref_2-ymem*mult
;       Plot(x*canvasw/BlockSize,y.f,Colorhilo(2))
;       x+FormatoOut\format\wBitsPerSample>>3
;       ymem.l=PeekL(*memoria+x); extender el signo de 24bit a 32bit
;       !mov al,byte[p.v_ymem+2]
;       !cbw
;       !shl eax,16
;       !mov ax,word[p.v_ymem]
;       !mov dword[p.v_ymem],eax
;       y.f=ref_2-ymem*mult
;       Plot(x*canvasw/BlockSize,y.f,Colorhilo(3))
;     EndSelect
    x+FormatoOut\format\wBitsPerSample>>3
  Until x>=BlockSize-1
  StopDrawing()
EndMacro
Procedure CREAR_ONDA(*SBuf,nsamples.i)
  ; Esta rutina genera las formas de onda
  ; los samples se almecenan en la memoria alternativamente entre el canal L y el R: (para resolución de 8 bit o 16 bit es así: L.w, R.w, L.w, R.w, L.w, R.w....)
  ; Así es que el buffer donde se almacenan debe tener una longitud múltiplo de 'FormatoOut\format\nBlockAlign' = 'FormatoOut\format\nChannels * FormatoOut\format\wBitsPerSample>>3'
  Protected V.f
  paso(2)=#PIx2*frecuenciahilo(2)/FormatoOut\format\nSamplesPerSec:paso(3)=#PIx2*frecuenciahilo(3)/FormatoOut\format\nSamplesPerSec
  For i=1 To nsamples
    V=amplitudhilo(2)*Sin(xmem(2))+alturahilo(2)-Pow(2,FormatoOut\format\wBitsPerSample)/2
    xmem(2)+paso(2):If xmem(2)>=#PIx2:xmem(2)-#PIx2:EndIf
    PokeW(*SBuf,V):*SBuf+FormatoOut\format\wBitsPerSample>>3
    If nhilos=2:V=0; <- si es mono
    Else:V=amplitudhilo(3)*Sin(xmem(3))+alturahilo(3)-Pow(2,FormatoOut\format\wBitsPerSample)/2
    EndIf
    xmem(3)+paso(3):If xmem(3)>=#PIx2:xmem(3)-#PIx2:EndIf
    PokeW(*SBuf,V):*SBuf+FormatoOut\format\wBitsPerSample>>3
  Next
EndProcedure
Procedure.i WinCallback(hwnd, uMsg, wParam, lParam)
  ; Window callback to service sound output message
  Static *hWaveO.WAVEHDR
  If uMsg=#MM_WOM_DONE           ; Sound output, a play buffer has been returned.
    *hWaveO.WAVEHDR=lParam                        ; lParam has the address of WAVEHDR
    CREAR_ONDA(*hWaveO\lpData,BlockSize/FormatoOut\format\nBlockAlign)    ; 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=>
  EndIf
  ProcedureReturn #PB_ProcessPureBasicEvents
EndProcedure
Procedure.i StartSoundOutput()
  Protected T.i,*P.byte
;   FormatoOut\format\wFormatTag=#WAVE_FORMAT_PCM
;   FormatoOut\format\nChannels=2;nhilos-1
;   FormatoOut\format\nSamplesPerSec=44100
;   FormatoOut\format\wBitsPerSample=16
;   FormatoOut\format\nBlockAlign=FormatoOut\format\nChannels*FormatoOut\format\wBitsPerSample>>3
;   FormatoOut\format\nAvgBytesPerSec=FormatoOut\format\nSamplesPerSec*FormatoOut\format\nBlockAlign
;   FormatoOut\wValidBitsPerSample=FormatoOut\format\wBitsPerSample
;   FormatoOut\dwChannelMask=$63F ; 7.1 (ojo poner en la configuración de altavoces de windows el 7.1) ; https://docs.microsoft.com/en-us/windows-hardware/drivers/audio/channel-mask
;   ;Por ejemplos; si solo front-left y front-center son especificados, entonces front-left y front-center deben ser los canales 0 y 1 repectivamente en el intercalado en la memoria.
;   ;Si el miembro 'format\nChannels' vale 4 y 'dwChannelMask' es $33, los canales se entienden como front-left, front-right, back-left, and back-right.
;   ;los datos en el intercalado en la memoria deben estar en ese orden.
;   FormatoOut\SubFormat=KSDATAFORMAT_SUBTYPE_PCM
  *memoria=ReAllocateMemory(*memoria,BlockSize*nBuf,#PB_Memory_NoClear)    ; Reserve memory for all the buffers
  T=waveOutOpen_(@hWaveOut,#WAVE_MAPPER+DevOut,@FormatoOut\format,WindowID(#MainWin),#True,#CALLBACK_WINDOW|#WAVE_FORMAT_DIRECT)
  If T=#MMSYSERR_NOERROR
    *P=*memoria                               ; 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
  ProcedureReturn T
EndProcedure
Procedure StopSoundOutput()
  waveOutReset_(hWaveOut)
  For i=0 To nBuf-1
    waveOutUnprepareHeader_(hWaveOut,outHdr(i),SizeOf(WAVEHDR))
  Next
  waveOutClose_(hWaveOut)
EndProcedure
Procedure.i caracteristicas(win.i)
  Protected DeviceID.i
  CreateMenu(#Devices,WindowID(win))
  ; locate all sound input devices:
  NumDevicesIn.i=waveInGetNumDevs_()
  If NumDevicesIn
    MenuTitle("Input Devices")
    For DeviceID=0 To NumDevicesIn-1
      If waveInGetDevCaps_(DeviceID,@CapsIn, SizeOf(WAVEINCAPS))=#MMSYSERR_NOERROR
        OpenSubMenu(PeekS(@CapsIn\szPname,#MAXPNAMELEN))
        If CapsIn\dwFormats&#WAVE_FORMAT_1M08:AddElement(menuoptions()):menuoptions()\nombre$="11025 Hz, MONO, 8 bit":menuoptions()\disponible=1:menuoptions()\salida=0:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        If CapsIn\dwFormats&#WAVE_FORMAT_1S08:AddElement(menuoptions()):menuoptions()\nombre$="11025 Hz, STEREO, 8 bit":menuoptions()\disponible=1:menuoptions()\salida=0:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        If CapsIn\dwFormats&#WAVE_FORMAT_1M16:AddElement(menuoptions()):menuoptions()\nombre$="11025 Hz, MONO, 16 bit":menuoptions()\disponible=1:menuoptions()\salida=0:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        If CapsIn\dwFormats&#WAVE_FORMAT_1S16:AddElement(menuoptions()):menuoptions()\nombre$="11025 Hz, STEREO, 16 bit":menuoptions()\disponible=1:menuoptions()\salida=0:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        If CapsIn\dwFormats&#WAVE_FORMAT_2M08:AddElement(menuoptions()):menuoptions()\nombre$="22050 Hz, MONO, 8 bit":menuoptions()\disponible=1:menuoptions()\salida=0:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        If CapsIn\dwFormats&#WAVE_FORMAT_2S08:AddElement(menuoptions()):menuoptions()\nombre$="22050 Hz, STEREO, 8 bit":menuoptions()\disponible=1:menuoptions()\salida=0:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        If CapsIn\dwFormats&#WAVE_FORMAT_2M16:AddElement(menuoptions()):menuoptions()\nombre$="22050 Hz, MONO, 16 bit":menuoptions()\disponible=1:menuoptions()\salida=0:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        If CapsIn\dwFormats&#WAVE_FORMAT_2S16:AddElement(menuoptions()):menuoptions()\nombre$="22050 Hz, STEREO, 16 bit":menuoptions()\disponible=1:menuoptions()\salida=0:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        If CapsIn\dwFormats&#WAVE_FORMAT_4M08:AddElement(menuoptions()):menuoptions()\nombre$="44100 Hz, MONO, 8 bit":menuoptions()\disponible=1:menuoptions()\salida=0:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        If CapsIn\dwFormats&#WAVE_FORMAT_4S08:AddElement(menuoptions()):menuoptions()\nombre$="44100 Hz, STEREO, 8 bit":menuoptions()\disponible=1:menuoptions()\salida=0:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        If CapsIn\dwFormats&#WAVE_FORMAT_4M16:AddElement(menuoptions()):menuoptions()\nombre$="44100 Hz, MONO, 16 bit":menuoptions()\disponible=1:menuoptions()\salida=0:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        If CapsIn\dwFormats&#WAVE_FORMAT_4S16:AddElement(menuoptions()):menuoptions()\nombre$="44100 Hz, STEREO, 16 bit":menuoptions()\disponible=1:menuoptions()\salida=0:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        If CapsIn\dwFormats&#WAVE_FORMAT_48M08:AddElement(menuoptions()):menuoptions()\nombre$="48000 Hz, MONO, 8 bit":menuoptions()\disponible=1:menuoptions()\salida=0:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        If CapsIn\dwFormats&#WAVE_FORMAT_48S08:AddElement(menuoptions()):menuoptions()\nombre$="48000 Hz, STEREO, 8 bit":menuoptions()\disponible=1:menuoptions()\salida=0:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        If CapsIn\dwFormats&#WAVE_FORMAT_48M16:AddElement(menuoptions()):menuoptions()\nombre$="48000 Hz, MONO, 16 bit":menuoptions()\disponible=1:menuoptions()\salida=0:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        If CapsIn\dwFormats&#WAVE_FORMAT_48S16:AddElement(menuoptions()):menuoptions()\nombre$="48000 Hz, STEREO, 16 bit":menuoptions()\disponible=1:menuoptions()\salida=0:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        If CapsIn\dwFormats&#WAVE_FORMAT_96M08:AddElement(menuoptions()):menuoptions()\nombre$="96000 Hz, MONO, 8 bit":menuoptions()\disponible=1:menuoptions()\salida=0:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        If CapsIn\dwFormats&#WAVE_FORMAT_96S08:AddElement(menuoptions()):menuoptions()\nombre$="96000 Hz, STEREO, 8 bit":menuoptions()\disponible=1:menuoptions()\salida=0:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        If CapsIn\dwFormats&#WAVE_FORMAT_96M16:AddElement(menuoptions()):menuoptions()\nombre$="96000 Hz, MONO, 16 bit":menuoptions()\disponible=1:menuoptions()\salida=0:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        If CapsIn\dwFormats&#WAVE_FORMAT_96S16:AddElement(menuoptions()):menuoptions()\nombre$="96000 Hz, STEREO, 16 bit":menuoptions()\disponible=1:menuoptions()\salida=0:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        CloseSubMenu()
      EndIf
    Next
    If ListSize(menuoptions())<2
      MessageRequester("Error","No suitable wave capture devices found!"):ProcedureReturn 0
    EndIf
  Else
    MessageRequester("Error","No input audio device found!"):ProcedureReturn 0
  EndIf
  ; locate all sound output devices:
  NumDevicesOut=waveOutGetNumDevs_(); <- numero de tarjetas de audio conectadas
  If NumDevicesOut
    MenuTitle("Output Devices")
    For DeviceID=0 To NumDevicesOut-1
      If waveOutGetDevCaps_(DeviceID,@CapsOut,SizeOf(WAVEOUTCAPS))=#MMSYSERR_NOERROR
        OpenSubMenu(PeekS(@CapsOut\szPname,#MAXPNAMELEN))
        If CapsOut\dwFormats&#WAVE_FORMAT_1M08:AddElement(menuoptions()):menuoptions()\nombre$="11025 Hz, MONO, 8 bit":menuoptions()\disponible=1:menuoptions()\salida=1:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        If CapsOut\dwFormats&#WAVE_FORMAT_1S08:AddElement(menuoptions()):menuoptions()\nombre$="11025 Hz, STEREO, 8 bit":menuoptions()\disponible=1:menuoptions()\salida=1:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        If CapsOut\dwFormats&#WAVE_FORMAT_1M16:AddElement(menuoptions()):menuoptions()\nombre$="11025 Hz, MONO, 16 bit":menuoptions()\disponible=1:menuoptions()\salida=1:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        If CapsOut\dwFormats&#WAVE_FORMAT_1S16:AddElement(menuoptions()):menuoptions()\nombre$="11025 Hz, STEREO, 16 bit":menuoptions()\disponible=1:menuoptions()\salida=1:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        If CapsOut\dwFormats&#WAVE_FORMAT_2M08:AddElement(menuoptions()):menuoptions()\nombre$="22050 Hz, MONO, 8 bit":menuoptions()\disponible=1:menuoptions()\salida=1:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        If CapsOut\dwFormats&#WAVE_FORMAT_2S08:AddElement(menuoptions()):menuoptions()\nombre$="22050 Hz, STEREO, 8 bit":menuoptions()\disponible=1:menuoptions()\salida=1:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        If CapsOut\dwFormats&#WAVE_FORMAT_2M16:AddElement(menuoptions()):menuoptions()\nombre$="22050 Hz, MONO, 16 bit":menuoptions()\disponible=1:menuoptions()\salida=1:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        If CapsOut\dwFormats&#WAVE_FORMAT_2S16:AddElement(menuoptions()):menuoptions()\nombre$="22050 Hz, STEREO, 16 bit":menuoptions()\disponible=1:menuoptions()\salida=1:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        If CapsOut\dwFormats&#WAVE_FORMAT_4M08:AddElement(menuoptions()):menuoptions()\nombre$="44100 Hz, MONO, 8 bit":menuoptions()\disponible=1:menuoptions()\salida=1:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        If CapsOut\dwFormats&#WAVE_FORMAT_4S08:AddElement(menuoptions()):menuoptions()\nombre$="44100 Hz, STEREO, 8 bit":menuoptions()\disponible=1:menuoptions()\salida=1:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        If CapsOut\dwFormats&#WAVE_FORMAT_4M16:AddElement(menuoptions()):menuoptions()\nombre$="44100 Hz, MONO, 16 bit":menuoptions()\disponible=1:menuoptions()\salida=1:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        If CapsOut\dwFormats&#WAVE_FORMAT_4S16:AddElement(menuoptions()):menuoptions()\nombre$="44100 Hz, STEREO, 16 bit":menuoptions()\disponible=1:menuoptions()\salida=1:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        If CapsOut\dwFormats&#WAVE_FORMAT_48M08:AddElement(menuoptions()):menuoptions()\nombre$="48000 Hz, MONO, 8 bit":menuoptions()\disponible=1:menuoptions()\salida=1:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        If CapsOut\dwFormats&#WAVE_FORMAT_48S08:AddElement(menuoptions()):menuoptions()\nombre$="48000 Hz, STEREO, 8 bit":menuoptions()\disponible=1:menuoptions()\salida=1:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        If CapsOut\dwFormats&#WAVE_FORMAT_48M16:AddElement(menuoptions()):menuoptions()\nombre$="48000 Hz, MONO, 16 bit":menuoptions()\disponible=1:menuoptions()\salida=1:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        If CapsOut\dwFormats&#WAVE_FORMAT_48S16:AddElement(menuoptions()):menuoptions()\nombre$="48000 Hz, STEREO, 16 bit":menuoptions()\disponible=1:menuoptions()\salida=1:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        If CapsOut\dwFormats&#WAVE_FORMAT_96M08:AddElement(menuoptions()):menuoptions()\nombre$="96000 Hz, MONO, 8 bit":menuoptions()\disponible=1:menuoptions()\salida=1:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        If CapsOut\dwFormats&#WAVE_FORMAT_96S08:AddElement(menuoptions()):menuoptions()\nombre$="96000 Hz, STEREO, 8 bit":menuoptions()\disponible=1:menuoptions()\salida=1:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        If CapsOut\dwFormats&#WAVE_FORMAT_96M16:AddElement(menuoptions()):menuoptions()\nombre$="96000 Hz, MONO, 16 bit":menuoptions()\disponible=1:menuoptions()\salida=1:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        If CapsOut\dwFormats&#WAVE_FORMAT_96S16:AddElement(menuoptions()):menuoptions()\nombre$="96000 Hz, STEREO, 16 bit":menuoptions()\disponible=1:menuoptions()\salida=1:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        MenuBar()
        If CapsOut\dwSupport&#WAVECAPS_LRVOLUME:AddElement(menuoptions()):menuoptions()\nombre$="Supports separate left And right volume control":menuoptions()\disponible=0:menuoptions()\salida=1:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        If CapsOut\dwSupport&#WAVECAPS_PITCH:AddElement(menuoptions()):menuoptions()\nombre$="Supports pitch control":menuoptions()\disponible=0:menuoptions()\salida=1:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        If CapsOut\dwSupport&#WAVECAPS_PLAYBACKRATE:AddElement(menuoptions()):menuoptions()\nombre$="Supports playback rate control":menuoptions()\disponible=0:menuoptions()\salida=1:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        If CapsOut\dwSupport&#WAVECAPS_SYNC:AddElement(menuoptions()):menuoptions()\nombre$="The driver is synchronous And will block While playing a buffer":menuoptions()\disponible=1:menuoptions()\salida=1:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        If CapsOut\dwSupport&#WAVECAPS_VOLUME:AddElement(menuoptions()):menuoptions()\nombre$="Supports volume control":menuoptions()\disponible=0:menuoptions()\salida=1:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        If CapsOut\dwSupport&#WAVECAPS_SAMPLEACCURATE:AddElement(menuoptions()):menuoptions()\nombre$="Returns sample-accurate position information":menuoptions()\disponible=0:menuoptions()\salida=1:MenuItem(ListIndex(menuoptions())+1,menuoptions()\nombre$):EndIf
        CloseSubMenu()
      EndIf
    Next
    If ListSize(menuoptions())<4
      MessageRequester("Error","No suitable wave output devices found!"):ProcedureReturn 0
    EndIf
  Else
    MessageRequester("Error!","No output audio device found!"):ProcedureReturn 0
  EndIf
  ProcedureReturn ListSize(menuoptions())
EndProcedure
Macro gadgethilos()
  If nhilos<6:DisableGadget(#seleccionhilo6,1):DisableGadget(#formadeondahilo6,1):DisableGadget(#fasehilo6,1)
    If nhilos<5:DisableGadget(#seleccionhilo5,1):DisableGadget(#formadeondahilo5,1):DisableGadget(#fasehilo5,1)
      If nhilos<4:DisableGadget(#seleccionhilo4,1):DisableGadget(#formadeondahilo4,1):DisableGadget(#fasehilo4,1)
        If nhilos<3:DisableGadget(#seleccionhilo3,1):DisableGadget(#formadeondahilo3,1):DisableGadget(#fasehilo3,1)
          If nhilos<2:DisableGadget(#seleccionhilo2,1):DisableGadget(#formadeondahilo2,1):DisableGadget(#fasehilo2,1)
          Else:DisableGadget(#seleccionhilo2,0):DisableGadget(#formadeondahilo2,0):DisableGadget(#fasehilo2,0)
          EndIf
        Else:DisableGadget(#seleccionhilo3,0):DisableGadget(#formadeondahilo3,0):DisableGadget(#fasehilo3,0)
        EndIf
      Else:DisableGadget(#seleccionhilo4,0):DisableGadget(#formadeondahilo4,0):DisableGadget(#fasehilo4,0)
      EndIf
    Else:DisableGadget(#seleccionhilo5,0):DisableGadget(#formadeondahilo5,0):DisableGadget(#fasehilo5,0)
    EndIf
  Else:DisableGadget(#seleccionhilo6,0):DisableGadget(#formadeondahilo6,0):DisableGadget(#fasehilo6,0)
  EndIf
EndMacro
Procedure.i Init_GUI()
  If OpenWindow(#MainWin,0,0,canvasw+220,canvash+100+410,"Amplitudes y Frecuencias",#PB_Window_SystemMenu|#PB_Window_ScreenCentered|#PB_Window_SizeGadget)=0:ProcedureReturn 0:EndIf
  CanvasGadget(#Canvas,canvasx,canvasy,canvasw,canvash)
  CanvasGadget(#Canvasmemoria,canvasx,canvasy+490,canvasw,canvash)
  TrackBarGadget(#TrackbarA,sliderax,slideray,slideraw,sliderah,0,1E4,#PB_TrackBar_Vertical); <- a
  TrackBarGadget(#TrackbarB,sliderbx,sliderby,sliderbw,sliderbh,0,1E4); <- b
  TrackBarGadget(#TrackbarC,slidercx,slidercy,slidercw,sliderch,0,1E4); <- c
  TrackBarGadget(#TrackbarE,sliderex,sliderey,sliderew,slidereh,0,1E4); <- e
  SetGadgetState(#TrackbarA,a*1E4/canvash)
  SetGadgetState(#TrackbarB,b*1E4/canvash)
  SetGadgetState(#TrackbarC,c*1E4/canvasw)
  SetGadgetState(#TrackbarE,e*1E4/canvasw)
  For i=1 To nhilos
    amplitudhilo(i)=a*Pow(2,FormatoOut\format\wBitsPerSample)/canvash
    alturahilo(i)=b*Pow(2,FormatoOut\format\wBitsPerSample)/canvash
    frecuenciahilo(i)=100/c*e
  Next
  TextGadget(#TextA,sliderax+slideraw,slideray,slideraw*1.8,slideraw,Str(amplitudhilo(1)))
  TextGadget(#TextB,sliderbx+sliderbw,sliderby,sliderbh*1.8,sliderbh,Str(alturahilo(1)-Pow(2,FormatoOut\format\wBitsPerSample-1)))
  TextGadget(#TextC,slidercx+slidercw,slidercy,sliderch*10,sliderch,StrF(frecuenciahilo(1),3)+" Hz = "+StrF(frecuenciahilo(1)*60,3)+" RPM")
  TextGadget(#TextE,sliderex+sliderew,sliderey,slidereh*1.8,slidereh,StrF(e.f,3))
  ButtonGadget(#BotonResetView,canvasw+70,0,50,25,"ResetView")
  ButtonGadget(#BotonPausa,canvasw+120,0,50,25,"Pause",#PB_Button_Toggle)
  OptionGadget(#seleccionhilo1,canvasw+25,60,50,20,"hilo1")
  OptionGadget(#seleccionhilo2,canvasw+25,80,50,20,"hilo2")
  OptionGadget(#seleccionhilo3,canvasw+25,100,50,20,"hilo3")
  OptionGadget(#seleccionhilo4,canvasw+25,120,50,20,"hilo4")
  OptionGadget(#seleccionhilo5,canvasw+25,140,50,20,"hilo5")
  OptionGadget(#seleccionhilo6,canvasw+25,160,50,20,"hilo6")
  SetGadgetState(#seleccionhilo1,1)
  TextGadget(#formadeonda,canvasw+90,30,40,25,"forma de onda",#PB_Text_Center)
  ComboBoxGadget(#formadeondahilo1,canvasw+75,57,70,25):AddGadgetItem(#formadeondahilo1,-1,"seno"):AddGadgetItem(#formadeondahilo1,-1,"rectangular"):AddGadgetItem(#formadeondahilo1,-1,"triangular1"):SetGadgetState(#formadeondahilo1,formadeondahilo(1))
  ComboBoxGadget(#formadeondahilo2,canvasw+75,77,70,25):AddGadgetItem(#formadeondahilo2,-1,"seno"):AddGadgetItem(#formadeondahilo2,-1,"rectangular"):AddGadgetItem(#formadeondahilo2,-1,"triangular1"):SetGadgetState(#formadeondahilo2,formadeondahilo(2))
  ComboBoxGadget(#formadeondahilo3,canvasw+75,97,70,25):AddGadgetItem(#formadeondahilo3,-1,"seno"):AddGadgetItem(#formadeondahilo3,-1,"rectangular"):AddGadgetItem(#formadeondahilo3,-1,"triangular1"):SetGadgetState(#formadeondahilo3,formadeondahilo(3))
  ComboBoxGadget(#formadeondahilo4,canvasw+75,117,70,25):AddGadgetItem(#formadeondahilo4,-1,"seno"):AddGadgetItem(#formadeondahilo4,-1,"rectangular"):AddGadgetItem(#formadeondahilo4,-1,"triangular1"):SetGadgetState(#formadeondahilo4,formadeondahilo(4))
  ComboBoxGadget(#formadeondahilo5,canvasw+75,137,70,25):AddGadgetItem(#formadeondahilo5,-1,"seno"):AddGadgetItem(#formadeondahilo5,-1,"rectangular"):AddGadgetItem(#formadeondahilo5,-1,"triangular1"):SetGadgetState(#formadeondahilo5,formadeondahilo(5))
  ComboBoxGadget(#formadeondahilo6,canvasw+75,157,70,25):AddGadgetItem(#formadeondahilo6,-1,"seno"):AddGadgetItem(#formadeondahilo6,-1,"rectangular"):AddGadgetItem(#formadeondahilo6,-1,"triangular1"):SetGadgetState(#formadeondahilo6,formadeondahilo(6))
  SpinGadget(#nhilos,canvasw+80,360,70,25,1,6,#PB_Spin_Numeric|#PB_Spin_ReadOnly):SetGadgetState(#nhilos,nhilos):SetGadgetColor(#nhilos,#PB_Gadget_BackColor,#nhiloscolor)
  TextGadget(#fase,canvasw+160,40,30,25,"fase",#PB_Text_Center)
  SpinGadget(#fasehilo1,canvasw+150,57,50,20,0,0,#PB_Spin_Numeric):SetGadgetState(#fasehilo1,fasehilo(1))
  SpinGadget(#fasehilo2,canvasw+150,77,50,20,0,360,#PB_Spin_Numeric):SetGadgetState(#fasehilo2,fasehilo(2))
  SpinGadget(#fasehilo3,canvasw+150,97,50,20,0,360,#PB_Spin_Numeric):SetGadgetState(#fasehilo3,fasehilo(3))
  SpinGadget(#fasehilo4,canvasw+150,117,50,20,0,360,#PB_Spin_Numeric):SetGadgetState(#fasehilo4,fasehilo(4))
  SpinGadget(#fasehilo5,canvasw+150,137,50,20,0,360,#PB_Spin_Numeric):SetGadgetState(#fasehilo5,fasehilo(5))
  SpinGadget(#fasehilo6,canvasw+150,157,50,20,0,360,#PB_Spin_Numeric):SetGadgetState(#fasehilo6,fasehilo(6))
  CheckBoxGadget(#CheckboxrelacionFA,canvasw+40,300,180,20,"Ligado Frecuencia/Amplitud"):SetGadgetState(#CheckboxrelacionFA,0)
  SpinGadget(#SpinrelacionFA,canvasw+40,320,50,20,0,360,#PB_Spin_Numeric):SetGadgetAttribute(#SpinrelacionFA,#PB_Spin_Minimum,-1000):SetGadgetAttribute(#SpinrelacionFA,#PB_Spin_Maximum,1000):SetGadgetState(#SpinrelacionFA,0):DisableGadget(#SpinrelacionFA,GetGadgetState(#CheckboxrelacionFA)!1)
  gadgethilos()
  ProcedureReturn caracteristicas(#MainWin)
EndProcedure
Procedure EventLoop()
  Protected Event.i,eventgadget.i,eventmenu.i
  Repeat
    Event.i=WaitWindowEvent()
    Select Event
    Case #PB_Event_Gadget
      eventgadget.i=EventGadget()
      Select eventgadget
      Case #TrackbarA;:EventType()
        For i=1 To #maxnhilos:amplitudhilo(i)=GetGadgetState(#TrackbarA)*Pow(2,FormatoOut\format\wBitsPerSample)/1E4:Next
        a.f=amplitudhilo(1)*canvash/Pow(2,FormatoOut\format\wBitsPerSample)
        SetGadgetText(#TextA,Str(amplitudhilo(1)))
      Case #TrackbarB;:EventType()
        For i=1 To #maxnhilos:alturahilo(i)=GetGadgetState(#TrackbarB)*Pow(2,FormatoOut\format\wBitsPerSample)/1E4:Next
        b.f=alturahilo(1)*canvash/Pow(2,FormatoOut\format\wBitsPerSample)
        SetGadgetText(#TextB,Str(alturahilo(1)-Pow(2,FormatoOut\format\wBitsPerSample-1)))
      Case #TrackbarC;:EventType()
        c.f=GetGadgetState(#TrackbarC)*canvasw/1E4
        If c<2:c=2:EndIf
        SetGadgetState(#TrackbarC,c*1E4/canvasw)
        For i=1 To #maxnhilos:frecuenciahilo(i)=100/c*e:Next
        SetGadgetText(#TextC,StrF(frecuenciahilo(1),3)+" Hz = "+StrF(frecuenciahilo(1)*60,3)+" RPM")
      Case #TrackbarE;:EventType()
        e.f=GetGadgetState(#TrackbarE)*canvasw/1E4
        SetGadgetText(#TextE,StrF(e.f,3))
        For i=1 To #maxnhilos:frecuenciahilo(i)=100/c*e:Next
        SetGadgetText(#TextC,StrF(frecuenciahilo(1),3)+" Hz = "+StrF(frecuenciahilo(1)*60,3)+" RPM")
      Case #formadeondahilo1 To #formadeondahilo6:formadeondahilo(eventgadget-#formadeondahilo1+1)=GetGadgetState(eventgadget)
      Case #nhilos:hilo=1:SetGadgetState(#seleccionhilo1,1)
        ; nhilos=GetGadgetState(#nhilos)
        gadgethilos()
      Case #fasehilo1 To #fasehilo6
        For i=1 To #maxnhilos:xmem(i)-Radian(fasehilo(i)):Next
        fasehilo(eventgadget-#fasehilo1+1)=GetGadgetState(eventgadget)
        gadgethilos()
        For i=1 To #maxnhilos:xmem(i)+Radian(fasehilo(i)):Next
      Case #Canvas
        If EventType()=#PB_EventType_LeftButtonDown:editando=1
          canvasmousex.u=GetGadgetAttribute(#Canvas,#PB_Canvas_MouseX)
          canvasmousey.u=GetGadgetAttribute(#Canvas,#PB_Canvas_MouseY)
          Beep_(800,10)
        EndIf
        If EventType()=#PB_EventType_LeftButtonUp:editando=0
          Beep_(200,10)
        EndIf
      Case #seleccionhilo1 To #seleccionhilo6:hilo=eventgadget-#seleccionhilo1+1
      Case #BotonResetView:canvassumdeltamousex=1.0:canvassumdeltamousey=0
      Case #CheckboxrelacionFA:DisableGadget(#SpinrelacionFA,GetGadgetState(#CheckboxrelacionFA)!1)
        For i=1 To #maxnhilos:frecuenciahilo(i)=100/c*e:Next
        SetGadgetText(#TextC,StrF(frecuenciahilo(1),3)+" Hz = "+StrF(frecuenciahilo(1)*60,3)+" RPM")
      Case #SpinrelacionFA
        For i=1 To #maxnhilos
          relacionAmplitudFrecuencia(i)=GetGadgetState(#SpinrelacionFA)/10000
        Next
      EndSelect
      For i=1 To #maxnhilos
        amplitudhilo(i)=a*Pow(2,FormatoOut\format\wBitsPerSample)/canvash
        frecuenciahilo(i)=100/c*e
        alturahilo(i)=b*Pow(2,FormatoOut\format\wBitsPerSample)/canvash
      Next
      If GetGadgetState(#CheckboxrelacionFA)
        For i=1 To #maxnhilos
          frecuenciahilo(i)+amplitudhilo(i)*relacionAmplitudFrecuencia(i)
        Next
        SetGadgetText(#TextC,StrF(frecuenciahilo(1),3)+" Hz = "+StrF(frecuenciahilo(1)*60,3)+" RPM")
      EndIf
    Case #PB_Event_Menu
      eventmenu.i=EventMenu()
      SelectElement(menuoptions(),eventmenu.i-1)
      If menuoptions()\disponible
        StopSoundOutput()
        If menuoptions()\salida
          If FindString(menuoptions()\nombre$,"8 bit",1,#PB_String_NoCase):FormatoOut\format\wBitsPerSample=16
          ElseIf FindString(menuoptions()\nombre$,"16 bit",1,#PB_String_NoCase):FormatoOut\format\wBitsPerSample=16
          ElseIf FindString(menuoptions()\nombre$,"24 bit",1,#PB_String_NoCase):FormatoOut\format\wBitsPerSample=32
          ElseIf FindString(menuoptions()\nombre$,"32 bit",1,#PB_String_NoCase):FormatoOut\format\wBitsPerSample=32
          EndIf
          If FindString(menuoptions()\nombre$,"mono",1,#PB_String_NoCase):nhilos.l=2
          ElseIf FindString(menuoptions()\nombre$,"stereo",1,#PB_String_NoCase):nhilos.l=3
          EndIf
          SetGadgetText(#nhilos,Str(nhilos))
          gadgethilos()
              ;frecuenciahilo(1)=Val(StringField(menuoptions()\nombre$,1," Hz"))
          ForEach menuoptions()
            If menuoptions()\salida
              SetMenuItemState(#Devices,ListIndex(menuoptions())+1,0)
            EndIf
          Next
          SetWindowCallback(@WinCallback()) ; Handle Sound Output callback
          StartSoundOutput()
        Else
          If FindString(menuoptions()\nombre$,"8 bit",1,#PB_String_NoCase):FormatoIn\format\wBitsPerSample=16
          ElseIf FindString(menuoptions()\nombre$,"16 bit",1,#PB_String_NoCase):FormatoIn\format\wBitsPerSample=16
          ElseIf FindString(menuoptions()\nombre$,"24 bit",1,#PB_String_NoCase):FormatoIn\format\wBitsPerSample=32
          ElseIf FindString(menuoptions()\nombre$,"32 bit",1,#PB_String_NoCase):FormatoIn\format\wBitsPerSample=32
          EndIf
          If FindString(menuoptions()\nombre$,"mono",1,#PB_String_NoCase):nhilosIn.l=2
          ElseIf FindString(menuoptions()\nombre$,"stereo",1,#PB_String_NoCase):nhilosIn.l=3
          EndIf
              ;frecuenciahiloin(1)=Val(StringField(menuoptions()\nombre$,1," Hz"))
          ForEach menuoptions()
            If menuoptions()\salida=0
              SetMenuItemState(#Devices,ListIndex(menuoptions())+1,0)
            EndIf
          Next
        EndIf
        SetMenuItemState(#Devices,eventmenu,1)
;         Debug Str(FormatoOut\format\nSamplesPerSec)+" , "+Str(nhilos)+" , "+Str(FormatoOut\format\wBitsPerSample)+" bit"
;         Debug Str(FormatoIn\format\nSamplesPerSec)+" , "+Str(nhilosIn)+" , "+Str(FormatoIn\format\wBitsPerSample)+" bit"
      EndIf
    Case #WM_KEYUP
      ActiveGadget=GetActiveGadget()
      Select ActiveGadget
      EndSelect
    EndSelect
    canvashilos()
  Until Event=#PB_Event_CloseWindow
EndProcedure
If Init_GUI()
  EventLoop()
  StopSoundOutput()
EndIf
http://www.zeitgeistmovie.com

While world=business:world+mafia:Wend
Will never leave this forum until the absolute bugfree PB :mrgreen:
infratec
Always Here
Always Here
Posts: 6817
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Electric motor control with stereophonic output

Post by infratec »

Well done :!:

I used PB and the stereo sound output to generate a rpm signal for an ECU of motorcycles.
Problen in this case: it has to emulate a sinus signal with some missing waves.
Moto Guzzi uses a 'tonewheel' with 42 teeth and 2 missing ones. (To detect the position of the crankshaft)
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5342
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: Electric motor control with stereophonic output

Post by Kwai chang caine »

Thanks for sharing 8)

That's works but i have an error, when i push in all right the thrackbar to the right "Plot() is out of the draw zone" in line "canvashilos()"
ImageThe happiness is a road...
Not a destination
User avatar
Psychophanta
Addict
Addict
Posts: 4976
Joined: Wed Jun 11, 2003 9:33 pm
Location: Lípetsk, Russian Federation
Contact:

Re: Electric motor control with stereophonic output

Post by Psychophanta »

Hi you. :!:
infratec wrote:Well done :!:

I used PB and the stereo sound output to generate a rpm signal for an ECU of motorcycles.
Problen in this case: it has to emulate a sinus signal with some missing waves.
Moto Guzzi uses a 'tonewheel' with 42 teeth and 2 missing ones. (To detect the position of the crankshaft)
You can do it, because audio output allows to perform any kind of wave shape upto 96KHz (depending on soundcard features up to 192KHz and more).
Kwai chang caine wrote:Thanks for sharing 8)

That's works but i have an error, when i push in all right the thrackbar to the right "Plot() is out of the draw zone" in line "canvashilos()"
The tip is just unfinished, but i think bug free. I can not reproduce here that behaviour. I use PB 5.61
http://www.zeitgeistmovie.com

While world=business:world+mafia:Wend
Will never leave this forum until the absolute bugfree PB :mrgreen:
Post Reply