#!/perl
use strict;
use warnings;
use Data::Dumper;
use Win32::API;
use threads;
use Thread::Queue;
use threads::shared;
#use Inline => Config => LIBS => '-L/usr/local/mylib -lmylib';
#use Inline => Config => INC => '-I/usr/local/mylib';
use Inline 'C';
use Devel::Pointer;
my $kill_thread :shared = 0;
my $nullPoll :shared = 0;
my $result;
my @buffers = ();
my $devNumber = 1;
my $sps = 44100;
my $channels = 1;
my $bps = 16;
# Private Type WAVEFORMATEX
Win32::API::Struct->typedef( 'WAVEFORMATEX', qw{ USHORT FormatTag;
USHORT Channels;
ULONG SamplesPerSec;
ULONG AvgBytesPerSec;
USHORT BlockAlign;
USHORT BitsPerSample;
USHORT ExtraDataSize;
}
) or die "Typedef error $!\n";
# Private Type WAVEHDR
Win32::API::Struct->typedef( 'WAVEHDR', qw{ LPSTR lpData;
DWORD dwBufferLength;
DWORD dwBytesRecorded;
DWORD_PTR dwUser;
DWORD dwFlags;
DWORD dwLoops;
LONG lpNext;
DWORD_PTR Reserved;
}
) or die "Typedef error $!\n";
# Private Type WAVEINCAPS
Win32::API::Struct->typedef( 'WAVEINCAPS', qw{ USHORT ManufacturerID;
USHORT ProductID;
UINT DriverVersion;
TCHAR ProductName[32];
ULONG Formats;
USHORT Channels;
USHORT Reserved;
}
) or die "Typedef error $!\n";
# Private Type MIXERLINECONTROLS
Win32::API::Struct->typedef( 'MIXERLINECONTROLS', qw{ DWORD cbStruct;
DWORD dwLineID;
DWORD dwControlIDorType;
DWORD cControls;
DWORD cbmxctrl;
LPMIXERCONTROL pamxctrl;
}
) or die "Typedef error $!\n";
Win32::API::Struct->typedef( 'CALLBACK', qw{DWORD_PTR val} ) or die "Typedef error $!\n";
Win32::API::Struct->typedef( 'LRESULT', qw{UINT val} ) or die "Typedef error $!\n";
Win32::API::Struct->typedef( 'ERR_TXT', qw{TCHAR str[1024]} ) or die "Typedef error $!\n";
my $WHDR_DONE = 0x0001;
my $WHDR_PREPARED = 0x0002;
my $WHDR_BEGINLOOP = 0x0004;
my $WHDR_ENDLOOP = 0x0008;
my $WHDR_INQUEUE = 0x0010;
# Declare Function ...
Win32::API->Import( 'winmm', 'UINT waveInGetNumDevs()');
Win32::API->Import( 'winmm', 'LRESULT waveInGetErrorText(LRESULT mmrError, LPERR_TXT pszText, UINT cchText ); ' );
Win32::API->Import( 'winmm', 'LRESULT waveInGetDevCaps(UINT DeviceID, LPWAVEINCAPS pwic, UINT cbwic)' );
Win32::API->Import( 'winmm', 'LRESULT waveInGetID( UINT hwi, PUINT puDeviceID )' );
Win32::API->Import( 'winmm', 'LRESULT waveInAddBuffer(UINT hwi,LPWAVEHDR pwh,UINT cbwh)' );
Win32::API->Import( 'winmm', 'LRESULT waveInOpen(PUINT phwi, UINT uDeviceID, LPWAVEFORMATEX pwfx, DWORD dwCallback, DWORD dwInstance, DWORD fdwOpen )' );
Win32::API->Import( 'winmm', 'LRESULT waveInPrepareHeader(UINT hwi,LPWAVEHDR pwh,UINT cbwh)' );
Win32::API->Import( 'winmm', 'LRESULT waveInUnprepareHeader(UINT hwi,LPWAVEHDR pwh,UINT cbwh)' );
Win32::API->Import( 'winmm', 'LRESULT waveInStart(UINT hwi)' );
Win32::API->Import( 'winmm', 'LRESULT waveInStop(UINT hwi)' );
Win32::API->Import( 'winmm', 'LRESULT waveInReset(UINT hwi)' );
Win32::API->Import( 'winmm', 'LRESULT waveInClose(UINT hwi)' );
Win32::API->Import( 'winmm', 'LRESULT waveOutGetVolume(UINT hwi, PUINT pdwVolume)' );
Win32::API->Import( 'winmm', 'LRESULT waveOutSetVolume(UINT hwi, UINT dwVolume)' );
Win32::API->Import( 'winmm', 'LRESULT mixerOpen(PUINT phmx, UINT uMxId, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen)' );
Win32::API->Import( 'winmm', 'LRESULT mixerGetID( UINT hmxobj, UINT* puMxId, DWORD fdwId )' );
Win32::API->Import( 'winmm', 'LRESULT mixerClose(UINT hwi)' );
my $numOfDevs = waveInGetNumDevs()."\n";
printf "the computer found %d audio inputs\n", $numOfDevs;
my $i = 0;
my $caps = Win32::API::Struct->new('WAVEINCAPS');
while ($i < $numOfDevs) {
&handleError ( 'open,',
waveInGetDevCaps($i, $caps, $caps->sizeof('WAVEINCAPS'))
);
print "Device ID: $i ,ProductName: ".$caps->{ProductName}."\n";
$i+=1;
}
$i = 0;
my $WaveFormat = Win32::API::Struct->new('WAVEFORMATEX'); # Static WaveFormat As WAVEFORMATEX
$WaveFormat->{FormatTag} = 1; # WAVE_FORMAT_PCM
$WaveFormat->{Channels} = $channels;
$WaveFormat->{SamplesPerSec} = $sps;
$WaveFormat->{BitsPerSample} = $bps;
$WaveFormat->{BlockAlign} = &round( ($WaveFormat->{Channels} * $WaveFormat->{BitsPerSample}) / 8);
$WaveFormat->{AvgBytesPerSec} = $WaveFormat->{SamplesPerSec} * $WaveFormat->{BlockAlign} * $WaveFormat->{Channels};
$WaveFormat->{ExtraDataSize} = $WaveFormat->sizeof('WAVEFORMATEX');
#$Win32::API::DEBUG = 1;
my $scalar;
my $devHandle = pack "I", 0;
## open device
&handleError ( 'open,',
waveInOpen( $devHandle,
$devNumber,
$WaveFormat,
getPointer(),
\$scalar, #\&processHeader,
#0x00000000 # CALLBACK_NULL
#0x00050000 # CALLBACK_EVENT
#0x00010000 # CALLBACK_WINDOW
#0x00020000 # CALLBACK_TASK
#0x00020000 # CALLBACK_THREAD
0x00030000 # CALLBACK_FUNCTION
)
);
$devHandle = unpack ("I", $devHandle);
&prepareBuffers ($devHandle, $WaveFormat->{AvgBytesPerSec}, \@buffers, 8);
my $thr = threads->new(\&polling, \$nullPoll, $WaveFormat->{SamplesPerSec} * $WaveFormat->{BlockAlign} * $WaveFormat->{Channels});
&handleError ( 'start ID,',
waveInStart( $devHandle
)
);
sleep 5;
print "aaa\n";
sleep 5;
&handleError ( 'stop ID,',
waveInStop( $devHandle
)
);
while ($nullPoll < 5) {
}
&releaseBuffers($devHandle, \@buffers, 8);
#&handleError ( 'close,',
waveInClose($devHandle)
;# );
&stop_polling;
sub round {
my $n = shift;
$n = ($n + 0.5);
return sprintf "%d", $n;
}
sub getAddress {
return hex scalar((split(/[\(|\)]/, shift))[1]);
}
sub handleError {
my $cmd = shift;
my $errNum = shift;
return unless (defined $errNum);
return if ($errNum == 0);
my $eText = Win32::API::Struct->new('ERR_TXT');
print $cmd." error ".$errNum." result => ".waveInGetErrorText($errNum, $eText, 1024);
print " :\n".$eText->{str}."\n";
}
sub polling {
my $np = shift;
my $l = shift;
open (RAW, ">"."wav.raw") or die "cannot open:".$@;
binmode RAW;
while ($kill_thread == 0) {
if (getValid() != 0) {
my $scalar;
packHeader($scalar);
print RAW $scalar;
clearValid();
$$np = 0;
}else{
$$np += 1;
}
select(undef, undef, undef, 0.5)
}
close RAW;
}
sub stop_polling {
$kill_thread = 1;
$thr->detach;
}
sub prepareBuffers{
my $DH = shift;
my $AvgBytesPerSec = shift;
my $array = shift;
my $maxBuffers = shift;
my $i = 0;
while ($i < $maxBuffers) {
my $inData = pack "a".$AvgBytesPerSec, \0;
my $Wave = Win32::API::Struct->new('WAVEHDR'); # Static Wave As WAVEHDR
$Wave->{dwUser} = $i;
$Wave->{lpData} = $inData;
$Wave->{dwBufferLength} = $AvgBytesPerSec;
$Wave->{dwFlags} = 0;
&handleError ( 'prepare header before start,',
waveInPrepareHeader( $DH,
$Wave,
$Wave->sizeof('WAVEHDR')
)
);
&handleError ( 'add buffer,',
waveInAddBuffer( $DH,
$Wave,
$Wave->sizeof('WAVEHDR')
)
);
push @$array, \$Wave;
$i += 1;
}
}
sub releaseBuffers{
my $DH = shift;
my $array = shift;
my $maxBuffers = shift;
my $i = 0;
while ($i < $maxBuffers) {
my $Wave = ${pop @$array};
&handleError ( 'unprepare header before close,',
waveInUnprepareHeader( $DH,
$Wave,
$Wave->sizeof('WAVEHDR')
)
) if ($Wave->{dwBytesRecorded} > 0);
$Wave = undef;
$i += 1;
}
}
__END__
__C__
#include <stdio.h>
#include <windows.h>
#include <mmsystem.h>
unsigned short valid = 0;
unsigned int pHdr = 0;
unsigned int handle = 0;
void CALLBACK waveInProc(UINT hwi, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2);
unsigned int getPointer();
void clearValid();
unsigned short getValid();
unsigned int getHandle();
unsigned short getValid(){
return valid;
}
unsigned int getHandle(){
return handle;
}
unsigned int getPointerHdr(){
return pHdr;
}
void packHeader(SV* hdr){
WAVEHDR * lH = (WAVEHDR *)pHdr;
sv_setpvn(hdr,
(char *)(lH->lpData),
lH->dwBufferLength //dwBytesRecorded
);
}
void clearValid(){
valid = 0;
}
unsigned int getPointer(){
return &waveInProc;
}
void CALLBACK waveInProc(UINT hwi, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2) {
unsigned char nMark = 0;
switch(uMsg) {
case WIM_CLOSE:
break;
case WIM_DATA:{
pHdr = dwParam1;
handle = hwi;
valid = 1;
waveInAddBuffer( hwi,
(WAVEHDR *)dwParam1,
sizeof(WAVEHDR)
);
}
break;
case WIM_OPEN:
break;
default:
break;
}
}
__REMARK__
};