Oppure

Loading
24/04/09 15:47
Furion
Ciao raga, torno a rompervi le scatole. Dovrei fare un programmino per un mio amico che gli visualizzi su schermo il volume master in percentuale. Per farlo, ho trovato online una classe già fatta in grado di darmi le informazioni che volevo. Il problema sta nel fatto che questa classe "dice" di avere 2 eventi: MuteChanged e VolumeChanged che, in teoria, dovrebbero attivarsi quando cambia il volume o lo status di muto. In realtà ciò non avviene e per rilevare queste modifiche, quindi, ho costruito un ciclo infinito che ad ogni istante mi confronta il volume attuale con quello dell'istante precedente. Sapevo già che questa sarebbe stata una soluzione pesantissima, ma mi sono accorto che una volta lanciato questo ciclo infinito, ad ogni iterazione il mio processo aumenta la sua dimensione in memoria, perchè la classe sonora alloca nuova memoria ogni volta che acquisisce il volume corrente e lo stato di muto. All'interno della mia classe (quella del mio progetto) ho utilizzato questo codice per creare un'istanza della classe sonora
Dim WithEvents Snd As New Sound


Questo, invece, è il ciclo infinito
' Controlla gli eventi finchè non si chiude il form
While keepListening
   ' Mantiene l'applicazione aggiornata
   Application.DoEvents()
   ' Calcola il volume attuale
   Dim nowVol As Int32 = Snd.GetVolume()
   ' Se è cambiato il volume
   If volume <> nowVol Then
      volumeChanged(nowVol)
   End If
   ' Se è cambiato lo status di muto
   If mute <> Snd.GetMuted Then
      muteChanged()
   End If
End While


mentre queste sono le signature dei metodi volumeChanged() e muteChanged()
Private Sub volumeChanged(ByVal newVol As Int32)

Private Sub muteChanged()


All'inizio ad entrambi i metodi non passavo nessun parametro e alla fine delle loro sig. avevo scritto, rispettivamente, "Handles Snd.VolumeChanged" e "Handles Snd.MuteChanged". Infine, questo è il codice della fantomatica classe sonora.

Imports System
Imports System.Runtime.InteropServices

Public Class Sound

#Region "   Events"
    Public Event MuteChanged()
    Public Event VolumeChanged()
#End Region

#Region "   Constants"
    Private Const MMSYSERR_NOERROR As Integer = 0
    Private Const MAXPNAMELEN As Integer = 32
    Private Const MIXER_LONG_NAME_CHARS As Integer = 64
    Private Const MIXER_SHORT_NAME_CHARS As Integer = 16
    Private Const MIXER_GETLINEINFOF_COMPONENTTYPE As Integer = &H3
    Private Const MIXER_GETLINECONTROLSF_ONEBYTYPE As Integer = &H2
    Private Const MIXER_GETCONTROLDETAILSF_VALUE As Integer = &H0
    Private Const MIXER_SETCONTROLDETAILSF_VALUE As Integer = &H0
    Private Const MIXERLINE_COMPONENTTYPE_DST_FIRST As Integer = &H0
    Private Const MIXERLINE_COMPONENTTYPE_DST_SPEAKERS As Integer = MIXERLINE_COMPONENTTYPE_DST_FIRST + 4
    'Private Const MIXERLINE_COMPONENTTYPE_SRC_FIRST As Integer = &H1000
    'Private Const MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE As Integer = MIXERLINE_COMPONENTTYPE_SRC_FIRST + 3
    'Private Const MIXERLINE_COMPONENTTYPE_SRC_LINE As Integer = MIXERLINE_COMPONENTTYPE_SRC_FIRST + 2
    'Private Const MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT = (MIXERLINE_COMPONENTTYPE_SRC_FIRST + 8)
    Private Const MIXERCONTROL_CT_CLASS_FADER As Integer = &H50000000
    Private Const MIXERCONTROL_CT_UNITS_UNSIGNED As Integer = &H30000
    Private Const MIXERCONTROL_CT_CLASS_SWITCH As Integer = &H20000000
    Private Const MIXERCONTROL_CT_UNITS_BOOLEAN As Integer = &H10000
    Private Const MIXERCONTROL_CONTROLTYPE_BASS As Integer = (MIXERCONTROL_CONTROLTYPE_FADER + 2)
    Private Const MIXERCONTROL_CONTROLTYPE_FADER As Integer = MIXERCONTROL_CT_CLASS_FADER Or MIXERCONTROL_CT_UNITS_UNSIGNED
    Private Const MIXERCONTROL_CONTROLTYPE_VOLUME As Integer = MIXERCONTROL_CONTROLTYPE_FADER + 1
    Private Const MIXERCONTROL_CONTROLTYPE_MUTE As Integer = (MIXERCONTROL_CONTROLTYPE_BOOLEAN + 2)
    Private Const MIXERCONTROL_CONTROLTYPE_TREBLE As Integer = (MIXERCONTROL_CONTROLTYPE_FADER + 3)
    Private Const MIXERCONTROL_CONTROLTYPE_EQUALIZER As Integer = (MIXERCONTROL_CONTROLTYPE_FADER + 4)
    Private Const MIXERCONTROL_CONTROLTYPE_BOOLEAN As Integer = (MIXERCONTROL_CT_CLASS_SWITCH Or MIXERCONTROL_CT_UNITS_BOOLEAN)
#End Region

#Region "   API Calls"
    Private Declare Ansi Function mixerClose Lib "winmm.dll" (ByVal hmx As Integer) As Integer
    Private Declare Ansi Function mixerGetControlDetailsA Lib "winmm.dll" (ByVal hmxobj As Integer, ByRef pmxcd As MIXERCONTROLDETAILS, ByVal fdwDetails As Integer) As Integer
    Private Declare Ansi Function mixerGetDevCapsA Lib "winmm.dll" (ByVal uMxId As Integer, ByVal pmxcaps As MIXERCAPS, ByVal cbmxcaps As Integer) As Integer
    Private Declare Ansi Function mixerGetID Lib "winmm.dll" (ByVal hmxobj As Integer, ByVal pumxID As Integer, ByVal fdwId As Integer) As Integer
    Private Declare Ansi Function mixerGetLineControlsA Lib "winmm.dll" (ByVal hmxobj As Integer, ByRef pmxlc As MIXERLINECONTROLS, ByVal fdwControls As Integer) As Integer
    Private Declare Ansi Function mixerGetLineInfoA Lib "winmm.dll" (ByVal hmxobj As Integer, ByRef pmxl As MIXERLINE, ByVal fdwInfo As Integer) As Integer
    Private Declare Ansi Function mixerGetNumDevs Lib "winmm.dll" () As Integer
    Private Declare Ansi Function mixerMessage Lib "winmm.dll" (ByVal hmx As Integer, ByVal uMsg As Integer, ByVal dwParam1 As Integer, ByVal dwParam2 As Integer) As Integer
    Private Declare Ansi Function mixerOpen Lib "winmm.dll" (ByRef phmx As Integer, ByVal uMxId As Integer, ByVal dwCallback As Integer, ByVal dwInstance As Integer, ByVal fdwOpen As Integer) As Integer
    Private Declare Ansi Function mixerSetControlDetails Lib "winmm.dll" (ByVal hmxobj As Integer, ByRef pmxcd As MIXERCONTROLDETAILS, ByVal fdwDetails As Integer) As Integer
    Private Declare Function mixerGetLineInfo Lib "winmm.dll" Alias "mixerGetLineInfoA" (<MarshalAs(UnmanagedType.I4)> ByVal hmxobj As Integer, ByRef pmxl As MIXERLINE, ByVal fdwInfo As Integer) As Integer
    Private Declare Function mixerGetLineControls Lib "winmm.dll" Alias "mixerGetLineControlsA" (<MarshalAs(UnmanagedType.I4)> ByVal hmxobj As Integer, ByRef pmxlc As MIXERLINECONTROLS, ByVal fdwControls As Integer) As Integer
#End Region

#Region "   Structures"
    Private Structure MIXERCAPS
        Public wMid As Integer
        Public wPid As Integer
        Public vDriverVersion As Integer
        <MarshalAs(UnmanagedType.ByValTStr, SizeConst:=MAXPNAMELEN)> Public szPname As String
        Public fdwSupport As Integer
        Public cDestinations As Integer
    End Structure 'MIXERCAPS
       _
    <StructLayout(LayoutKind.Sequential)> _
    Private Structure MIXERCONTROL
        <FieldOffset(0)> Public cbStruct As Integer           '  size in Byte of MIXERCONTROL
        <FieldOffset(4)> Public dwControlID As Integer        '  unique control id for mixer device
        <FieldOffset(8)> Public dwControlType As Integer      '  MIXERCONTROL_CONTROLTYPE_xxx
        <FieldOffset(12)> Public fdwControl As Integer         '  MIXERCONTROL_CONTROLF_xxx
        <FieldOffset(16)> Public cMultipleItems As Integer     '  if MIXERCONTROL_CONTROLF_MULTIPLE set
        <FieldOffset(20), MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst:=MIXER_SHORT_NAME_CHARS)> Public szShortName As String ' * MIXER_SHORT_NAME_CHARS  ' short name of control
        <FieldOffset(36), MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst:=MIXER_LONG_NAME_CHARS)> Public szName As String '  * MIXER_LONG_NAME_CHARS ' Integer name of control
        <FieldOffset(100)> Public lMinimum As Integer           '  Minimum value
        <FieldOffset(104)> Public lMaximum As Integer           '  Maximum value
        <FieldOffset(108), MarshalAs(UnmanagedType.ByValArray, SizeConst:=11, ArraySubType:=UnmanagedType.AsAny)> Public reserved() As Integer      '  reserved structure space
    End Structure

    <StructLayout(LayoutKind.Sequential)> _
    Private Structure MIXERCONTROLDETAILS
        <FieldOffset(0)> Public cbStruct As Integer       '  size in Byte of MIXERCONTROLDETAILS
        <FieldOffset(4)> Public dwControlID As Integer    '  control id to get/set details on
        <FieldOffset(8)> Public cChannels As Integer      '  number of channels in paDetails array
        <FieldOffset(12)> Public item As Integer           '  hwndOwner or cMultipleItems
        <FieldOffset(16)> Public cbDetails As Integer      '  size of _one_ details_XX struct
        <FieldOffset(20)> Public paDetails As IntPtr       '  pointer to array of details_XX structs
    End Structure

    <StructLayout(LayoutKind.Sequential)> _
    Private Structure MIXERCONTROLDETAILS_UNSIGNED
        <FieldOffset(0)> Public dwValue As Integer        '  value of the control
    End Structure

    <StructLayout(LayoutKind.Sequential)> _
    Private Structure MIXERLINE
        <FieldOffset(0)> Public cbStruct As Integer                '  size of MIXERLINE structure
        <FieldOffset(4)> Public dwDestination As Integer          '  zero based destination index
        <FieldOffset(8)> Public dwSource As Integer               '  zero based source index (if source)
        <FieldOffset(12)> Public dwLineID As Integer               '  unique line id for mixer device
        <FieldOffset(16)> Public fdwLine As Integer                '  state/information about line
        <FieldOffset(20)> Public dwUser As Integer                 '  driver specific information
        <FieldOffset(24)> Public dwComponentType As Integer        '  component type line connects to
        <FieldOffset(28)> Public cChannels As Integer              '  number of channels line supports
        <FieldOffset(32)> Public cConnections As Integer           '  number of connections (possible)
        <FieldOffset(36)> Public cControls As Integer              '  number of controls at this line
        <FieldOffset(40), MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst:=MIXER_SHORT_NAME_CHARS)> Public szShortName As String  ' * MIXER_SHORT_NAME_CHARS
        <FieldOffset(56), MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst:=MIXER_LONG_NAME_CHARS)> Public szName As String ' * MIXER_LONG_NAME_CHARS
        <FieldOffset(120)> Public dwType As Integer
        <FieldOffset(124)> Public dwDeviceID As Integer
        <FieldOffset(128)> Public wMid As Integer
        <FieldOffset(132)> Public wPid As Integer
        <FieldOffset(136)> Public vDriverVersion As Integer
        <FieldOffset(168), MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst:=MAXPNAMELEN)> Public szPname As String ' * MAXPNAMELEN
    End Structure

    <StructLayout(LayoutKind.Sequential)> _
    Private Structure MIXERLINECONTROLS
        <FieldOffset(0)> Public cbStruct As Integer       '  size in Byte of MIXERLINECONTROLS
        <FieldOffset(4)> Public dwLineID As Integer       '  line id (from MIXERLINE.dwLineID)
        <FieldOffset(8)> Public dwControl As Integer      '  MIXER_GETLINECONTROLSF_ONEBYTYPE
        <FieldOffset(12)> Public cControls As Integer      '  count of controls pmxctrl points to
        <FieldOffset(16)> Public cbmxctrl As Integer       '  size in Byte of _one_ MIXERCONTROL
        <FieldOffset(20)> Public pamxctrl As IntPtr       '  pointer to first MIXERCONTROL array
    End Structure

#End Region

    Private Shared Function GetVolumeControl(ByVal hmixer As Integer, ByVal componentType As Integer, ByVal ctrlType As Integer, ByRef mxc As MIXERCONTROL, ByRef vCurrentVol As Integer) As Boolean
        Dim mxlc As New MIXERLINECONTROLS
        Dim mxl As New MIXERLINE
        Dim pmxcd As New MIXERCONTROLDETAILS
        Dim du As New MIXERCONTROLDETAILS_UNSIGNED
        mxc = New MIXERCONTROL
        Dim rc As Integer
        Dim retValue As Boolean
        vCurrentVol = -1

        mxl.cbStruct = Marshal.SizeOf(mxl)
        mxl.dwComponentType = componentType

        rc = mixerGetLineInfoA(hmixer, mxl, MIXER_GETLINEINFOF_COMPONENTTYPE)

        If MMSYSERR_NOERROR = rc Then
            Dim sizeofMIXERCONTROL As Integer = 152
            Dim ctrl As Integer = Marshal.SizeOf(GetType(MIXERCONTROL))
            mxlc.pamxctrl = Marshal.AllocCoTaskMem(sizeofMIXERCONTROL)
            mxlc.cbStruct = Marshal.SizeOf(mxlc)
            mxlc.dwLineID = mxl.dwLineID
            mxlc.dwControl = ctrlType
            mxlc.cControls = 1
            mxlc.cbmxctrl = sizeofMIXERCONTROL

            ' Allocate a buffer for the control 
            mxc.cbStruct = sizeofMIXERCONTROL

            ' Get the control 
            rc = mixerGetLineControlsA(hmixer, mxlc, MIXER_GETLINECONTROLSF_ONEBYTYPE)

            If MMSYSERR_NOERROR = rc Then
                retValue = True

                ' Copy the control into the destination structure 
                mxc = CType(Marshal.PtrToStructure(mxlc.pamxctrl, GetType(MIXERCONTROL)), MIXERCONTROL)
            Else
                retValue = False
            End If
            Dim sizeofMIXERCONTROLDETAILS As Integer = Marshal.SizeOf(GetType(MIXERCONTROLDETAILS))
            Dim sizeofMIXERCONTROLDETAILS_UNSIGNED As Integer = Marshal.SizeOf(GetType(MIXERCONTROLDETAILS_UNSIGNED))
            pmxcd.cbStruct = sizeofMIXERCONTROLDETAILS
            pmxcd.dwControlID = mxc.dwControlID
            pmxcd.paDetails = Marshal.AllocCoTaskMem(sizeofMIXERCONTROLDETAILS_UNSIGNED)
            pmxcd.cChannels = 1
            pmxcd.item = 0
            pmxcd.cbDetails = sizeofMIXERCONTROLDETAILS_UNSIGNED

            rc = mixerGetControlDetailsA(hmixer, pmxcd, MIXER_GETCONTROLDETAILSF_VALUE)

            du = Marshal.PtrToStructure(pmxcd.paDetails, GetType(MIXERCONTROLDETAILS_UNSIGNED))

            vCurrentVol = du.dwValue

            Return retValue
        End If
        retValue = False
        Return retValue
    End Function 'GetVolumeControl

    Private Shared Function SetVolumeControl(ByVal hmixer As Integer, ByVal mxc As MIXERCONTROL, ByVal volume As Integer) As Boolean
        ' This function sets the value for a volume control. 
        ' Returns True if successful 
        Dim retValue As Boolean
        Dim rc As Integer
        Dim mxcd As New MIXERCONTROLDETAILS
        Dim vol As New MIXERCONTROLDETAILS_UNSIGNED

        mxcd.item = 0
        mxcd.dwControlID = mxc.dwControlID
        mxcd.cbStruct = Marshal.SizeOf(mxcd)
        mxcd.cbDetails = Marshal.SizeOf(vol)

        ' Allocate a buffer for the control value buffer 
        mxcd.cChannels = 1
        vol.dwValue = volume

        ' Copy the data into the control value buffer 
        mxcd.paDetails = Marshal.AllocCoTaskMem(Marshal.SizeOf(GetType(MIXERCONTROLDETAILS_UNSIGNED)))
        Marshal.StructureToPtr(vol, mxcd.paDetails, False)

        ' Set the control value 
        rc = mixerSetControlDetails(hmixer, mxcd, MIXER_SETCONTROLDETAILSF_VALUE)

        If MMSYSERR_NOERROR = rc Then
            retValue = True
        Else
            retValue = False
        End If
        Return retValue
    End Function 'SetVolumeControl 

    Public Function GetVolume() As Integer
        Dim mixer As Integer
        Dim volCtrl As New MIXERCONTROL
        Dim currentVol As Integer
        mixerOpen(mixer, 0, 0, 0, 0) 'Returns the mixer control
        GetVolumeControl(mixer, MIXERLINE_COMPONENTTYPE_DST_SPEAKERS, MIXERCONTROL_CONTROLTYPE_VOLUME, volCtrl, currentVol)
        mixerClose(mixer)
        Return currentVol
    End Function 'GetVolume
    Public Function GetMax() As Integer
        Dim mixer As Integer
        Dim volCtrl As New MIXERCONTROL
        Dim currentVol As Integer
        mixerOpen(mixer, 0, 0, 0, 0)
        Dim type As Integer = MIXERCONTROL_CONTROLTYPE_VOLUME
        GetVolumeControl(mixer, MIXERLINE_COMPONENTTYPE_DST_SPEAKERS, type, volCtrl, currentVol)
        mixerClose(mixer)
        Return volCtrl.lMaximum
    End Function 'Gets max volume
    Public Function GetMuted() As String
        Dim mixer As Integer
        Dim volCtrl As New MIXERCONTROL
        Dim Muted As Integer
        mixerOpen(mixer, 0, 0, 0, 0)
        Dim type As Integer = MIXERCONTROL_CONTROLTYPE_MUTE
        GetVolumeControl(mixer, MIXERLINE_COMPONENTTYPE_DST_SPEAKERS, type, volCtrl, Muted)
        mixerClose(mixer)
        Return Muted
    End Function 'GetMuted status

    Public Sub SetVolume(ByVal vVolume As Integer)
        Dim mixer As Integer
        Dim volCtrl As New MIXERCONTROL
        Dim currentVol As Integer
        mixerOpen(mixer, 0, 0, 0, 0)
        Dim type As Integer = MIXERCONTROL_CONTROLTYPE_VOLUME
        GetVolumeControl(mixer, MIXERLINE_COMPONENTTYPE_DST_SPEAKERS, type, volCtrl, currentVol)
        If vVolume > volCtrl.lMaximum Then
            vVolume = volCtrl.lMaximum
        End If
        If vVolume < volCtrl.lMinimum Then
            vVolume = volCtrl.lMinimum
        End If
        SetVolumeControl(mixer, volCtrl, vVolume)
        GetVolumeControl(mixer, MIXERLINE_COMPONENTTYPE_DST_SPEAKERS, type, volCtrl, currentVol)
        If vVolume <> currentVol Then
            Throw New Exception("Cannot Set Volume")
        Else
            RaiseEvent VolumeChanged()
        End If
        mixerClose(mixer)
    End Sub 'SetVolume

    Public Sub SetMuted(ByVal boolMute As Boolean)
        ' This routine sets the volume setting of the current unit depending on the value passed through
        Dim mixer As Integer
        Dim volCtrl As New MIXERCONTROL
        Dim lngReturn As Integer
        Dim type As Integer = MIXERCONTROL_CONTROLTYPE_MUTE
        Dim currentVol As Integer
        ' Obtain the hmixer struct
        lngReturn = mixerOpen(mixer, 0, 0, 0, 0)
        ' Error check
        If lngReturn <> 0 Then Exit Sub
        ' Obtain the volumne control object
        GetVolumeControl(mixer, MIXERLINE_COMPONENTTYPE_DST_SPEAKERS, type, volCtrl, currentVol)
        ' Then set the volume
        SetVolumeControl(mixer, volCtrl, boolMute)
        mixerClose(mixer)
        RaiseEvent MuteChanged()
    End Sub 'Set the muted status

End Class


EDIT: scusate il poema ma non sono riuscito a mettere il codice della classe Sound come allegato :-|
Ultima modifica effettuata da Furion 25/04/09 8:29
aaa
25/04/09 8:35
Il Totem
Metti il controllo in un timer, o in un thread diverso con una pausa di qualche decina (o centinaia) di millisecondi. Una frequenza di refresh di 10-20Hz mi sembra già più accettabile, che non il ciclo infinito. Poi puoi provare a chiamare GC.Collect() per forzare una garbage collection della memoria (operazione, comunque, altamente sconsigliata).
Se posso fare un commento, mi sembrano inutili gli eventi esposti in quella classe quando per generarli è necessaria una chiamata esplicita ad un metodo d'istanza.
aaa
25/04/09 9:38
Furion
Grazie per i consigli Totem. Infatti anche a me quegli eventi messi così lasciano perplesso. Il problema non sono tanto gli eventi, quanto il fatto che ogni volta che uso le funzioni getVolume e getMuted mi si alloca memoria aggiuntiva e non so come ripulirla. Grazie ancora, comunque ^_^
aaa
28/04/09 16:13
Furion
Raga, scusate il doppio post ma sono ancora in alto mare. Con il consiglio di Totem la velocità con cui il mio processo aumenta di dimensione in ram è calata di molto, ma il problema persiste (e quindi io consulto il medico XD). Ho notato, se può essere utile, che la dimensione del processo adesso cresce con una velocità di circa 4 kb/sec e che la colpa dovrebbe essere quasi esclusivamente della funzione GetVolumeControl(). Aiutatemi vi prego:d
aaa
29/04/09 8:01
Il Totem
Puoi al limite inizializzare le strutture usate all'interno di quella funzione nel costruttore New e dichiararle come variabili a livello di classe. Questo processo evita la creazione di nuove istanze ad ogni controllo... Tuttavia le variabili temporanee dovrebbero essere distrutte a fine procedura e non capisco perchè questo non avvenga.
aaa
29/04/09 8:30
theprogrammer
Nella GetVolumeControl vedo che usi due volte la Marshal.AllocCoTaskMem ma non usi mai le corrispondenti Marshal.FreeCoTaskMem quando la memoria non ti serve piu'.
aaa
29/04/09 9:11
Furion
Bhe la classe non l'ho scritta io e come potete facilmente immaginare le mie abilità di programmatore vb net non sono così approfondite. Potete rispiegarmi bene cosa devo fare? In pratica devo far diventare le variabili locali di classe e devo usare quel comando che citava theProgrammer per deallocare la memoria? Se funziona vi faccio una statua ^____^
aaa
29/04/09 16:21
Thejuster
non fai prima ad usare le DirectAudio?

oltre a gestire il livello del volume master in modo facilmente
puoi anche tenere sott'occhio anche il volume di tutti i suoni che potrai riprodurre.
e applicare effetti come echo, reverb e tante altre cose.

evitando tutto quel poema.

mire.forumfree.it/ - Mire Engine
C# UI Designer