Newsgroup: it.comp.programmare.win32
Titolo: winmm.dll alcune delucidazione

Salve,
Prima di iniziare a postare codice e a parlare della winmm.dll e dei 
metodi waveInxxx
avrei qualche domanda in generale su come viene gestito l'audio su 
windows.
Che cos'è un device?
Una scheda audio o un dispositivo hardware esterno qualunque?
Ad esempio quando attacco lo spinotto esterno del mic come faccio a 
vederlo?
che cos'è una soundcard?
come spieghero dopo nel codice io ottengo come device "sb200" che credo
che stia per "sound blaster" .. che cos'è?
viene rappresentata dal quel mixer nell'applicazione "multimedia" nel 
pannello di controllo?
questa era la confusione che avevo al riguardo (in quanto newbie del 
mondo win come di vb)
Ora avrei alcune domande sui metodi waveInxxx della winmm:
--> waveInGetNumDevs
Ritorna il numero dei "dispositivi" waveIn disponibili
cosa vuol dire "dispositivi" in questo caso? una scheda audio o il mic 
esterno?
[vb]
Private Type WaveInCaps
   ManufacturerID As Integer
   ProductID As Integer
   DriverVersion As Long
   ProductName(1 To 32) As Byte
   Formats As Long
   Channels As Integer
   Reserved As Integer
End Type
Private Declare Function waveInGetNumDevs Lib "winmm" () As Long
Private Declare Function waveInGetDevCaps Lib "winmm" Alias 
"waveInGetDevCapsA" (ByVal uDeviceID As Long, Byval lpCaps As Long, 
ByVal uSize As Long) As Long
sub InitDevices()
   Dim Caps As WaveInCaps, Which As Long, testo As String
   For Which = 0 To waveInGetNumDevs - 1
      Call waveInGetDevCaps(Which, VarPtr(Caps), Len(Caps))
      testo = StrConv(Caps.ProductName, vbUnicode) & Which
      msgbox testo
   Next
 
   If Which = 0 Then
      MsgBox "You have no audio input devices!", vbCritical, "Ack!"
      End
   End If
 
End Sub
[/vb]
eseguendo questo codice ottengo SB200 che dovrebbe essere un valido 
waveIn ...
cos'altro avrei potuto ottenere? waveInGetNumDevs può essere maggiore di 
1?
Ora vi posto tutto il codice che ho messo insieme finora per catturare 
l'audio da mic esterno
anche se ancora non so come e in quale variabile (o descrittore) va a 
finire l'audio...
[vb]
'
' waveInGetNumDevs(): Determines the number of audio drivers available 
for input.
' waveInOpen(): Creates an instance of the specified audio device for 
input.
' waveInPrepareHeader(): Prepares a WAVEHDR and data block for input.
' waveInUnprepareHeader(): Releases a previously prepared WAVEHDR and 
data block.
' waveInClose(): Closes the specified instance of the audio device.
' waveInReset(): Stops recording and empties the queue.
' waveInStart(): Starts recording to the queued buffer.
' waveInStop(): Stops recording.
' waveInAddBuffer(): Adds a prepared buffer to the record queue.
' waveInGetDevCaps(): Gets the capabilities of the specified device
'
' WAVEINCAPS: Describes the capabilities of an input device.
'
Option Explicit
Private DevHandle As Long
Private InData(0 To 511) As Byte
Private Type WaveFormatEx
   FormatTag As Integer
   Channels As Integer
   SamplesPerSec As Long
   AvgBytesPerSec As Long
   BlockAlign As Integer
   BitsPerSample As Integer
   ExtraDataSize As Integer
End Type
Private Type WaveHdr
   lpData As Long
   dwBufferLength As Long
   dwBytesRecorded As Long
   dwUser As Long
   dwFlags As Long
   dwLoops As Long
   lpNext As Long
   Reserved As Long
End Type
Private Type WaveInCaps
   ManufacturerID As Integer
   ProductID As Integer
   DriverVersion As Long
   ProductName(1 To 32) As Byte
   Formats As Long
   Channels As Integer
   Reserved As Integer
End Type
Private Const WAVE_FORMAT_4M16 = &H400& '/* 44.1 kHz, Mono, 6-bit
Private Const WAVE_FORMAT_PCM = 1
Private Declare Function waveInGetNumDevs Lib "winmm" () As Long
Private Declare Function waveInGetDevCaps Lib "winmm" Alias 
"waveInGetDevCapsA" (ByVal uDeviceID As Long, Byval lpCaps As Long, 
ByVal uSize As Long) As Long
Private Declare Function waveInAddBuffer Lib "winmm" (ByVal 
InputDeviceHandle As Long, ByVal WaveHdrPointer As Long, ByVal 
WaveHdrStructSize As Long) As Long
Private Declare Function waveInPrepareHeader Lib "winmm" (ByVal 
InputDeviceHandle As Long, ByVal WaveHdrPointer As Long, ByVal 
WaveHdrStructSize As Long) As Long
Private Declare Function waveInUnprepareHeader Lib "winmm" (ByVal 
InputDeviceHandle As Long, ByVal WaveHdrPointer As Long, ByVal 
WaveHdrStructSize As Long) As Long
Private Declare Function waveInOpen Lib "winmm" (WaveDeviceInputHandle 
As Long, ByVal WhichDevice As Long, ByVal WaveFormatExPointer As Long, 
ByVal CallBack As Long, ByVal CallBackInstance As Long, ByVal Flags As 
Long) As Long
Private Declare Function waveInClose Lib "winmm" (ByVal 
WaveDeviceInputHandle As Long) As Long
Private Declare Function waveInStart Lib "winmm" (ByVal 
WaveDeviceInputHandle As Long) As Long
Private Declare Function waveInReset Lib "winmm" (ByVal 
WaveDeviceInputHandle As Long) As Long
Private Declare Function waveInStop Lib "winmm" (ByVal 
WaveDeviceInputHandle As Long) As Long
sub InitDevices()
   Dim Caps As WaveInCaps, Which As Long, testo As String
   For Which = 0 To waveInGetNumDevs - 1
      Call waveInGetDevCaps(Which, VarPtr(Caps), Len(Caps))
      testo = StrConv(Caps.ProductName, vbUnicode) & Which
      msgbox testo
   Next
 
   If Which = 0 Then
      MsgBox "You have no audio input devices!", vbCritical, "Ack!"
      End
   End If
 
End Sub
Private Sub Form_Load()
   Call InitDevices
End Sub
Private Sub StartButton_Click()
   Static WaveFormat As WaveFormatEx
 
   With WaveFormat
      .FormatTag = WAVE_FORMAT_PCM
      .Channels = 2
      .SamplesPerSec = 44100
      .BitsPerSample = 8
      .BlockAlign = (.Channels * .BitsPerSample) \ 8
      .AvgBytesPerSec = .BlockAlign * .SamplesPerSec
      .ExtraDataSize = 0
    End With
 
   Debug.Print "waveInOpen:"; waveInOpen(DevHandle, 0, 
VarPtr(WaveFormat), 0, 0, 0)
 
   If DevHandle = 0 Then
      Call MsgBox("Wave input device didn't open!", vbExclamation, 
"Ack!")
      Exit Sub
   End If
 
   Debug.Print " "; DevHandle
   Call waveInStart(DevHandle)
 
   Static Wave As WaveHdr
 
   Wave.lpData = VarPtr(InData(0))
   Wave.dwBufferLength = 512 'This is now 512 so there's still 256 
samples per channel
   Wave.dwFlags = 0
 
   '
   ' Call waveInPrepareHeader(DevHandle, VarPtr(Wave), Len(Wave))
   ' Call waveInAddBuffer(DevHandle, VarPtr(Wave), Len(Wave))
   '
   ' Arrivati a questo punto come prendo l'output dal microfono
   ' per poi redirigerlo ad una convertitore MP3
   ' o ad un Socket che lo trasmette ad un Client
   ' o per salvarlo su file ???
   '
   ' Call waveInUnprepareHeader(DevHandle, VarPtr(Wave), Len(Wave))
   '
 
End Sub
Private Sub StopButton_Click()
   Call waveInReset(DevHandle)
   Call waveInClose(DevHandle)
   DevHandle = 0
End Sub
[/vb]
Come ottengo fisicamente l'audio?
Se volessi salvarlo o redirigerlo ad un Socket di tipo server?
(o magari comprimerlo in MP3?)
Grazie a tutti!

includi ovviamente nel linker la libreria winmm.lib, e prova questo codice
registra un secondo di musica, e lo risuona
un secondo di musica, a 44100 campioni al secondo, un canale (mono), ogni 
campione di 2 byte (short)
totale: un bufero di 44100 short, o, se vogliamo, di 88200 byte
(puoi usare frequenza di campionamento piu' basse, per risparmiare 
memoria... vedi la documentazione)
non usa procedure CALLABCK, ma un semplice Sleep(1100) (un po' piu' di un 
secondo, per sicurezza)
#include <windows.h>
HWAVEIN hwi;
HWAVEOUT hwo;
WAVEHDR wh;
short buffer[44100];
INT WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR, INT)
{
     WAVEFORMATEX wf;
     wf.wFormatTag = WAVE_FORMAT_PCM;
     wf.nSamplesPerSec = 44100;  //stessa frequenza dei CD
     wf.nChannels = 1;  //mono
     wf.wBitsPerSample = 16;  //2 byte, ogni frame (campione) e' un short 
(da -32767 a 32767)
     wf.nBlockAlign = 2;  //2 byte per campione, per 1 canale, in tutto 2 
byte
     wf.nAvgBytesPerSec = 44100 * 2; //totale bytes al secondo, 44100 
campioni, per 2 bytes per campione, per un canale
     wf.cbSize = 0;
     waveInOpen(&hwi, WAVE_MAPPER, &wf, 0, 0, 0);
     waveOutOpen(&hwo, WAVE_MAPPER, &wf, 0, 0, 0);
     wh.lpData = (char*)buffer;  //non accetta short ma char
     wh.dwBufferLength = 44100 * 2;  //vuole il nuemro esatto di bytes
     waveInPrepareHeader(hwi, &wh, sizeof(WAVEHDR));
     waveInAddBuffer(hwi, &wh, sizeof(WAVEHDR));
     MessageBox(0, "premi un tasto per registrare un secondo di musica", 
"prova waveIn", 0);
     waveInStart(hwi);
     Sleep(1100);
     waveInUnprepareHeader(hwi, &wh, sizeof(WAVEHDR));
     waveOutPrepareHeader(hwo, &wh, sizeof(WAVEHDR));
     MessageBox(0, "premi un tasto per ascoltare", "prova waveOut", 0);
     waveOutWrite(hwo, &wh, sizeof(WAVEHDR));
     Sleep(1100);
     waveOutUnprepareHeader(hwo, &wh, sizeof(WAVEHDR));
     waveInClose(hwi);
     waveOutClose(hwo);
     return 0;
}

 secondo, per sicurezza)
mi spieghi questa cosa delle CALLABCK?
e perchč lo Sleep?
grazie mille!

in visual basic non so bene come chiamare le API (ho VB.net, e le cose sono 
abbastanza cambiate)
in ogni caso, provo a spiegarti un po' come funziona
per prima cosa, devi pensare ad un file wav come ad un grafico, che disegna 
un andamento (la forma d'onda)
le informazioni che devi avere all'inizio riguardano proprio la maggior o 
minor definizione dell'onda
pensa allo schermo, puoi avere piu' o meno punti nelle assi XY (800x600, 
1024x768, 1600x1200...)
cosi' l'onda puo' avere piu' o meno campioni (frame) al secondo (8000, 
11025, 22050, 44100...)
come lo schemro puo' avere piu' o meno colori, a seconda di quanti bit 
vengono usati per ogni punto (8 bit (1byte), 255 colori, 16 bit (2byte) 
65000 colori, 24 bit (3 byte) 16 milioni e rotti...)
cosi' l'onda puo' avere campioni piu' o meno definiti (da 8 bit (0 - 255), o 
da 16 bit (-32767 +32767))
in piu' ogni onda puo' essere composta da due onde distinte che verranno 
passate nelle due casse, una a sinistra e una a destra
(ma puoi anche decidere di risparmiare tempo e memoria e avere in entrambe 
le casse la stessa onda)
i CD che ascolti sono a 44100 campioni al secondo, ogni campione da 16 bit, 
a 2 canali
queste sono le informazioni che vengono salvate nel file WAVEFORMATEX
-------------------------------------------------------------------------------------
ora, sia che decidi di suonare, sia che decidi di registrare, devi avere a 
disposizione un buffer su cui scrivere o leggere...
se registri, gli darai il bufer e lui ci scrivera' sopra i numeri che 
rappresentano l'onda
se suoni, ce li devi scrivere tu (o disegni qualche bella sinusoide o altre 
cose... delle rette suonano come i vecchi spari dei vecchi videogames....), 
e poi gli passi il buffer e lui lo eseguira'
in ogni caso dovrai avere un buffer, ok?
il buffer va passato nella struttura WAVE_HDR (wave header)
in partivcolare due parametri sono importanti
il puntatore al buffer (che deve puntare al tuo buffer)
e la lunghezza del buffer
se sei esperto in programmazione saprai che sono le minime informazioni 
necessarie alla gestione di un buffer in memoria
------------------------------------------------------------------------------------------------
quindi
1) apri il dispositivo e gli passi le informazioni sul tipo di campionamento 
con waveIn/OutOpen()
2) passi il buffer al sistema con waveIn/OutPrepareHeader()
3) fai partire l'azione (registrazione o riproduzione)
        registrazione con waveInAddBuffer() e poi waveInStart()
        riproduzione con waveOutWrite()
4) aspetti che finisca (usando Sleep, o ua funzione di CALLBACK che devi 
definire pero' in apertura... ma intanto tu prova con Sleep)
5) liberi il buffer con waveIn/OutUnprepareHeader() (ATTENZIONE! se non ha 
finito non lo liberera', e quindi non lo chiudera', e forse anche si 
inchiodera'...)
6) chiudi con waveIn/OutClose()
diciamo che usare la Sleep() non e' la migliore delle cose
con una procedura di CALLBACK ti avvisa lui quando ha finito, e non corri 
rischi
non solo, ma mentre registra, siccome la cosa avviene in un altro processo, 
il computer e' libero di fare quello che gli pare (ad esempio interrompere 
la registrazione/riproduzione)... ma secondo me, visto che stai ancora 
provando, prima fallo funzionare con Sleep, e poi gli aggiungi una funzione 
di CALLBACK, e infinine una gestione di tipo duoble-bufering per la 
registrazione, e magari ti studi i file RIFF per salvare su un file .WAV 
quello che hai registrato...
le cose sono lunghe, e ci vuole tempo e pazienza,
una volta erano, secondo me, anche meglio documentate
c'era, nell'SDK, un bel capitolo su tutto cio... ora non si trova piu' 
niente..
spero di essere stato chiaro
saluti e sappiami dire chi a vinto, se tu o il PC