Microphone input - Unseen Machine - 12-25-2025
I think my mic is broken or something so can someone test this please...
Microphone.h
Code: (Select All)
#ifndef QBLIVE_AUDIO_H
#define QBLIVE_AUDIO_H
#include <windows.h>
#include <mmsystem.h>
#include <shellapi.h>
#include <vector>
#pragma comment(lib, "winmm.lib")
#pragma comment(lib, "shell32.lib")
#define SAMPLE_RATE 44100
#define CHUNK_SIZE 4096
static HWAVEIN hWaveIn = NULL;
static HWAVEOUT hWaveOut = NULL;
static WAVEHDR waveHdrIn;
static short captureBuffer[CHUNK_SIZE];
static std::vector<short> recordedData;
static bool isRecording = false;
// Driver Callback: Background thread catches audio chunks as they arrive
void CALLBACK waveInProc(HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) {
if (uMsg == WIM_DATA && isRecording) {
WAVEHDR* pHdr = (WAVEHDR*)dwParam1;
short* raw = (short*)pHdr->lpData;
// Efficiently append raw 16-bit samples to the storage vector
recordedData.insert(recordedData.end(), raw, raw + (pHdr->dwBytesRecorded / 2));
// Re-queue the buffer to keep recording without gaps
waveInAddBuffer(hwi, pHdr, sizeof(WAVEHDR));
}
}
extern "C" {
__declspec(dllexport) int InitAudio() {
// CD Quality: Mono, 44100Hz, 16-bit PCM
WAVEFORMATEX wfx = { WAVE_FORMAT_PCM, 1, SAMPLE_RATE, SAMPLE_RATE * 2, 2, 16, 0 };
// Open Microphone
MMRESULT res = waveInOpen(&hWaveIn, WAVE_MAPPER, &wfx, (DWORD_PTR)waveInProc, 0, CALLBACK_FUNCTION);
if (res != MMSYSERR_NOERROR) return (int)res;
waveHdrIn.lpData = (LPSTR)captureBuffer;
waveHdrIn.dwBufferLength = CHUNK_SIZE * 2;
waveHdrIn.dwFlags = 0;
waveInPrepareHeader(hWaveIn, &waveHdrIn, sizeof(WAVEHDR));
// Open Speakers
if (waveOutOpen(&hWaveOut, WAVE_MAPPER, &wfx, 0, 0, CALLBACK_NULL) != MMSYSERR_NOERROR) return -99;
return 0;
}
__declspec(dllexport) void StartRecord() {
recordedData.clear();
isRecording = true;
waveInAddBuffer(hWaveIn, &waveHdrIn, sizeof(WAVEHDR));
waveInStart(hWaveIn);
}
__declspec(dllexport) void StopRecord() {
isRecording = false;
waveInStop(hWaveIn);
waveInReset(hWaveIn);
}
__declspec(dllexport) int GetRecordedSize() {
return (int)recordedData.size();
}
__declspec(dllexport) void PlayRecording() {
if (recordedData.empty()) return;
static WAVEHDR playHdr; // Static ensures memory persists during playback
playHdr.lpData = (LPSTR)recordedData.data();
playHdr.dwBufferLength = (DWORD)recordedData.size() * 2;
playHdr.dwFlags = 0;
waveOutPrepareHeader(hWaveOut, &playHdr, sizeof(WAVEHDR));
waveOutWrite(hWaveOut, &playHdr, sizeof(WAVEHDR));
}
__declspec(dllexport) void OpenMicSettings() {
ShellExecuteA(NULL, "open", "ms-settings:privacy-microphone", NULL, NULL, SW_SHOWNORMAL);
}
__declspec(dllexport) void ShutdownAudio() {
if (hWaveIn) { waveInReset(hWaveIn); waveInClose(hWaveIn); }
if (hWaveOut) { waveOutReset(hWaveOut); waveOutClose(hWaveOut); }
}
// Converts 16-bit samples to 32-bit floats for QB64 _SNDRAW
// buffer = the QB64 float array, offset = starting sample, count = samples to get
__declspec(dllexport) void Audio_Get_SndRaw(float* buffer, int offset, int count) {
if (recordedData.empty() || offset < 0) return;
for (int i = 0; i < count; i++) {
int index = offset + i;
if (index < (int)recordedData.size()) {
// Convert signed 16-bit (-32768 to 32767) to float (-1.0 to 1.0)
buffer[i] = (float)recordedData[index] / 32768.0f;
} else {
buffer[i] = 0.0f; // Fill remaining buffer with silence if out of range
}
}
}
}
#endif
ANd the QB64 stuff
Code: (Select All)
' Finalized QBLive Audio Studio 2025
DECLARE LIBRARY "Microphone"
FUNCTION InitAudio& ()
SUB StartRecord ()
SUB StopRecord ()
FUNCTION GetRecordedSize& ()
SUB PlayRecording ()
SUB OpenMicSettings ()
SUB ShutdownAudio ()
SUB Audio_Get_SndRaw (buffer AS SINGLE, BYVAL offset AS LONG, BYVAL count AS LONG)
END DECLARE
SCREEN _NEWIMAGE(800, 600, 32)
_TITLE "QBLive Audio Recorder 2025"
' 1. Check Permissions
res& = InitAudio
IF res& <> 0 THEN
PRINT "--- AUDIO INITIALIZATION ERROR ---"
PRINT "Result Code:"; res&
PRINT "Hardware not found or Privacy Permissions denied."
PRINT
INPUT "Open Windows Microphone Settings? (Y/N) ", c$
IF UCASE$(c$) = "Y" THEN OpenMicSettings
END
END IF
isRecording = 0
PRINT "Ready. Press SPACE to Record."
DO
k$ = INKEY$
IF k$ = " " THEN
IF isRecording = 0 THEN
StartRecord
isRecording = 1
ELSE
StopRecord
isRecording = 0
CLS
PRINT "Playing back"; GetRecordedSize; "samples..."
'PlayRecording ''// This maybe the error
samples& = GetRecordedSize
IF samples& > 0 THEN
PRINT "Playing back via _SNDRAW..."
' We process in chunks of 4096 (matching your C++ CHUNK_SIZE)
chunkSize& = 4096
REDIM tempBuffer(chunkSize& - 1) AS SINGLE
FOR currentOffset& = 0 TO samples& STEP chunkSize&
' 1. Fill our QB64 float buffer from the C++ recordedData vector
Audio_Get_SndRaw tempBuffer(0), currentOffset&, chunkSize&
' 2. Call our standalone sub to handle the actual playback
PlayFloatBuffer tempBuffer(), chunkSize&
NEXT currentOffset&
PRINT "Playback finished."
END IF
PRINT "Press SPACE to record again."
END IF
_DELAY .3 ' Toggle debounce
END IF
IF isRecording THEN
LOCATE 1, 1: PRINT "RECORDING... ["; GetRecordedSize; "samples]"
END IF
_LIMIT 60
LOOP UNTIL _EXIT
ShutdownAudio
SYSTEM
SUB PlayFloatBuffer (floatArray() AS SINGLE, count AS LONG)
' Designed for 44100Hz Mono Audio
IF count <= 0 THEN EXIT SUB
FOR i& = 0 TO count - 1
' 1. Send the sample to _SNDRAW (mono automatically goes to both speakers)
_SNDRAW floatArray(i&)
' 2. Check the sound pipe length.
' If we queue too much data (> 0.2 seconds), we wait.
' If we don't wait, QB64 will eat up all your RAM trying to store the samples.
IF i& MOD 100 = 0 THEN ' Check every 100 samples to save CPU
DO WHILE _SNDRAWLEN > 0.2
_LIMIT 100 ' Give the CPU a rest while the sound card works
LOOP
END IF
NEXT i&
END SUB
@Petr 90% there i reckon...needs a better brain!
Unseeen
RE: Microphone input - Petr - 12-25-2025
Hi, your program works here as expected. Is microphone recording enabled in system settings?
But, try this (save it as microphone2.h), BAS source remains the same.
Code: (Select All) #ifndef QBLIVE_AUDIO_H
#define QBLIVE_AUDIO_H
// IMPORTANT: This is C++ (uses std::vector). QB64PE compiles with a C++ toolchain.
#include <windows.h>
#include <mmsystem.h>
#include <shellapi.h>
#include <vector>
#ifndef SAMPLE_RATE
#define SAMPLE_RATE 44100
#endif
#ifndef CHUNK_SAMPLES
#define CHUNK_SAMPLES 4096 // samples per buffer (mono, 16-bit)
#endif
#ifndef NUM_INPUT_BUFFERS
#define NUM_INPUT_BUFFERS 4 // >=2 recommended; 4..8 typical
#endif
// -----------------------------------------------------------------------------
// Internal state
// -----------------------------------------------------------------------------
static HWAVEIN g_hWaveIn = NULL;
static HWAVEOUT g_hWaveOut = NULL;
static WAVEFORMATEX g_wfx = { 0 };
static WAVEHDR g_inHdr[NUM_INPUT_BUFFERS];
static short g_inBuf[NUM_INPUT_BUFFERS][CHUNK_SAMPLES];
static std::vector<short> g_recorded; // shared between threads -> must lock
static std::vector<short> g_playback; // stable snapshot for async waveOut
static CRITICAL_SECTION g_cs;
static LONG g_csInit = 0;
static volatile LONG g_isInitialized = 0;
static volatile LONG g_isRecording = 0;
static volatile LONG g_isShuttingDown = 0;
static HANDLE g_worker = NULL;
static DWORD g_workerTid = 0;
static HANDLE g_workerReady = NULL;
static const UINT WM_QBLIVE_REQUEUE = (WM_APP + 0x334);
static WAVEHDR g_playHdr;
static LONG g_playHdrPrepared = 0;
static MMRESULT g_lastMM = MMSYSERR_NOERROR;
// -----------------------------------------------------------------------------
// Tiny helpers
// -----------------------------------------------------------------------------
static void qbl_lock() { if (InterlockedCompareExchange(&g_csInit, 0, 0)) EnterCriticalSection(&g_cs); }
static void qbl_unlock() { if (InterlockedCompareExchange(&g_csInit, 0, 0)) LeaveCriticalSection(&g_cs); }
static void qbl_set_last(MMRESULT mm) { g_lastMM = mm; }
// -----------------------------------------------------------------------------
// waveIn callback
// -----------------------------------------------------------------------------
/*
IMPORTANT FIX #1:
The original code called waveInAddBuffer() inside the waveInProc callback.
That is a known deadlock/reentrancy risk on some drivers.
Correct pattern:
- Do *minimal* work in the callback.
- Hand off the WAVEHDR pointer to a worker thread.
- The worker thread copies audio + calls waveInAddBuffer().
*/
static void CALLBACK qbl_waveInProc(HWAVEIN, UINT uMsg, DWORD_PTR, DWORD_PTR dwParam1, DWORD_PTR)
{
if (uMsg != WIM_DATA) return;
if (g_workerTid) PostThreadMessage(g_workerTid, WM_QBLIVE_REQUEUE, (WPARAM)dwParam1, 0);
}
// -----------------------------------------------------------------------------
// Worker thread proc
// -----------------------------------------------------------------------------
/*
IMPORTANT FIX #2:
std::vector is not thread-safe. The original code appended to recordedData in
the driver callback thread while QB code could read it concurrently.
That can cause reallocations + invalid pointers + crashes / silence.
Here we serialize access with a CRITICAL_SECTION and do the copy outside the callback.
*/
static DWORD WINAPI qbl_workerProc(void*)
{
// Ensure message queue exists
MSG msg;
PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
if (g_workerReady) SetEvent(g_workerReady);
while (GetMessage(&msg, NULL, 0, 0))
{
if (msg.message != WM_QBLIVE_REQUEUE) continue;
if (InterlockedCompareExchange(&g_isShuttingDown, 0, 0)) continue;
WAVEHDR* pHdr = (WAVEHDR*)msg.wParam;
if (!pHdr) continue;
if (InterlockedCompareExchange(&g_isRecording, 0, 0))
{
const int sampleCount = (int)(pHdr->dwBytesRecorded / sizeof(short));
if (sampleCount > 0)
{
qbl_lock();
g_recorded.insert(g_recorded.end(),
(short*)pHdr->lpData,
(short*)pHdr->lpData + sampleCount);
qbl_unlock();
}
// Re-queue buffer here (NOT in callback)
waveInAddBuffer(g_hWaveIn, pHdr, sizeof(WAVEHDR));
}
}
return 0;
}
// =============================================================================
// Public API for QB64 (must be C linkage, no name mangling)
// =============================================================================
extern "C" {
// Optional debug helpers (not required by your BAS file)
int GetLastMMResult()
{
return (int)g_lastMM;
}
void GetLastMMErrorTextA(char* outText, int outTextBytes)
{
if (!outText || outTextBytes <= 0) return;
outText[0] = 0;
char tmp[256] = { 0 };
if (waveInGetErrorTextA(g_lastMM, tmp, (UINT)sizeof(tmp)) == MMSYSERR_NOERROR ||
waveOutGetErrorTextA(g_lastMM, tmp, (UINT)sizeof(tmp)) == MMSYSERR_NOERROR)
{
lstrcpynA(outText, tmp, outTextBytes);
}
else
{
lstrcpynA(outText, "Unknown MMRESULT", outTextBytes);
}
}
int InitAudio()
{
if (InterlockedCompareExchange(&g_isInitialized, 0, 0))
return 0;
g_lastMM = MMSYSERR_NOERROR;
if (!InterlockedCompareExchange(&g_csInit, 1, 0))
InitializeCriticalSection(&g_cs);
// Start worker thread
g_workerReady = CreateEvent(NULL, TRUE, FALSE, NULL);
g_worker = CreateThread(NULL, 0, qbl_workerProc, NULL, 0, &g_workerTid);
if (!g_worker)
{
qbl_set_last(MMSYSERR_ERROR);
return (int)MMSYSERR_ERROR;
}
WaitForSingleObject(g_workerReady, INFINITE);
CloseHandle(g_workerReady);
g_workerReady = NULL;
// CD-ish format: Mono, 44100Hz, 16-bit PCM
g_wfx.wFormatTag = WAVE_FORMAT_PCM;
g_wfx.nChannels = 1;
g_wfx.nSamplesPerSec = SAMPLE_RATE;
g_wfx.wBitsPerSample = 16;
g_wfx.nBlockAlign = (g_wfx.nChannels * g_wfx.wBitsPerSample) / 8;
g_wfx.nAvgBytesPerSec = g_wfx.nSamplesPerSec * g_wfx.nBlockAlign;
g_wfx.cbSize = 0;
// Open Microphone
MMRESULT res = waveInOpen(&g_hWaveIn, WAVE_MAPPER, &g_wfx, (DWORD_PTR)qbl_waveInProc, 0, CALLBACK_FUNCTION);
if (res != MMSYSERR_NOERROR) { qbl_set_last(res); return (int)res; }
// Prepare multiple input buffers (IMPORTANT FIX #3: not just one)
ZeroMemory(g_inHdr, sizeof(g_inHdr));
for (int i = 0; i < NUM_INPUT_BUFFERS; i++)
{
g_inHdr[i].lpData = (LPSTR)g_inBuf[i];
g_inHdr[i].dwBufferLength = (DWORD)sizeof(g_inBuf[i]);
g_inHdr[i].dwFlags = 0;
res = waveInPrepareHeader(g_hWaveIn, &g_inHdr[i], sizeof(WAVEHDR));
if (res != MMSYSERR_NOERROR) { qbl_set_last(res); return (int)res; }
}
// Open Speakers
res = waveOutOpen(&g_hWaveOut, WAVE_MAPPER, &g_wfx, 0, 0, CALLBACK_NULL);
if (res != MMSYSERR_NOERROR) { qbl_set_last(res); return (int)res; }
ZeroMemory(&g_playHdr, sizeof(g_playHdr));
InterlockedExchange(&g_playHdrPrepared, 0);
InterlockedExchange(&g_isShuttingDown, 0);
InterlockedExchange(&g_isInitialized, 1);
return 0;
}
void StartRecord()
{
if (!InterlockedCompareExchange(&g_isInitialized, 0, 0)) return;
qbl_lock();
g_recorded.clear();
qbl_unlock();
// Queue buffers BEFORE starting
for (int i = 0; i < NUM_INPUT_BUFFERS; i++)
waveInAddBuffer(g_hWaveIn, &g_inHdr[i], sizeof(WAVEHDR));
InterlockedExchange(&g_isRecording, 1);
waveInStart(g_hWaveIn);
}
void StopRecord()
{
if (!InterlockedCompareExchange(&g_isInitialized, 0, 0)) return;
InterlockedExchange(&g_isRecording, 0);
waveInStop(g_hWaveIn);
waveInReset(g_hWaveIn);
}
int GetRecordedSize()
{
int n = 0;
qbl_lock();
n = (int)g_recorded.size();
qbl_unlock();
return n;
}
void PlayRecording()
{
if (!InterlockedCompareExchange(&g_isInitialized, 0, 0)) return;
/*
IMPORTANT FIX #4:
waveOutWrite is asynchronous. The original code passed recordedData.data()
directly; but recordedData can reallocate / clear later -> invalid pointer.
We snapshot to g_playback so the memory remains stable during playback.
*/
qbl_lock();
g_playback = g_recorded;
qbl_unlock();
if (g_playback.empty()) return;
// Stop current playback if any
waveOutReset(g_hWaveOut);
if (InterlockedCompareExchange(&g_playHdrPrepared, 0, 0))
{
waveOutUnprepareHeader(g_hWaveOut, &g_playHdr, sizeof(WAVEHDR));
InterlockedExchange(&g_playHdrPrepared, 0);
}
ZeroMemory(&g_playHdr, sizeof(g_playHdr));
g_playHdr.lpData = (LPSTR)g_playback.data();
g_playHdr.dwBufferLength = (DWORD)(g_playback.size() * sizeof(short));
g_playHdr.dwFlags = 0;
MMRESULT res = waveOutPrepareHeader(g_hWaveOut, &g_playHdr, sizeof(WAVEHDR));
if (res != MMSYSERR_NOERROR) { qbl_set_last(res); return; }
InterlockedExchange(&g_playHdrPrepared, 1);
res = waveOutWrite(g_hWaveOut, &g_playHdr, sizeof(WAVEHDR));
if (res != MMSYSERR_NOERROR) { qbl_set_last(res); return; }
}
void OpenMicSettings()
{
// If Windows privacy blocks the mic, capture APIs will appear to "work" but deliver silence.
ShellExecuteA(NULL, "open", "ms-settings:privacy-microphone", NULL, NULL, SW_SHOWNORMAL);
}
void ShutdownAudio()
{
if (!InterlockedCompareExchange(&g_isInitialized, 0, 0)) return;
InterlockedExchange(&g_isShuttingDown, 1);
InterlockedExchange(&g_isRecording, 0);
if (g_hWaveIn)
{
waveInStop(g_hWaveIn);
waveInReset(g_hWaveIn);
for (int i = 0; i < NUM_INPUT_BUFFERS; i++)
waveInUnprepareHeader(g_hWaveIn, &g_inHdr[i], sizeof(WAVEHDR));
waveInClose(g_hWaveIn);
g_hWaveIn = NULL;
}
if (g_hWaveOut)
{
waveOutReset(g_hWaveOut);
if (InterlockedCompareExchange(&g_playHdrPrepared, 0, 0))
{
waveOutUnprepareHeader(g_hWaveOut, &g_playHdr, sizeof(WAVEHDR));
InterlockedExchange(&g_playHdrPrepared, 0);
}
waveOutClose(g_hWaveOut);
g_hWaveOut = NULL;
}
// Stop worker thread
if (g_workerTid) PostThreadMessage(g_workerTid, WM_QUIT, 0, 0);
if (g_worker)
{
WaitForSingleObject(g_worker, INFINITE);
CloseHandle(g_worker);
g_worker = NULL;
}
g_workerTid = 0;
qbl_lock();
g_recorded.clear();
g_playback.clear();
qbl_unlock();
InterlockedExchange(&g_isInitialized, 0);
if (InterlockedCompareExchange(&g_csInit, 0, 0))
{
DeleteCriticalSection(&g_cs);
InterlockedExchange(&g_csInit, 0);
}
}
// QB64 helper: fills a float buffer (mono) for _SNDRAW
void Audio_Get_SndRaw(float* buffer, int offset, int count)
{
if (!buffer || count <= 0) return;
if (offset < 0) offset = 0;
qbl_lock();
const int n = (int)g_recorded.size();
for (int i = 0; i < count; i++)
{
const int idx = offset + i;
if (idx < n)
buffer[i] = (float)g_recorded[idx] / 32768.0f; // [-1, 1)
else
buffer[i] = 0.0f;
}
qbl_unlock();
}
} // extern "C"
#endif // QBLIVE_AUDIO_H
RE: Microphone input - Kernelpanic - 12-25-2025
I'm getting an error message in Microphone.h. Otherwise, I can't help, as I've never dealt with such topics before. 
RE: Microphone input - Petr - 12-25-2025
@Kernelpanic: H file is C or C++ program. Save it as microphone.h to folder with program. Do not insert it to QB64 IDE. Then copy BAS source code to IDE, save it to the same folder as H file, compile Bas to exe and run it.
RE: Microphone input - Kernelpanic - 12-25-2025
Okay, that was it! Thanks! It works with both versions of the C file; however, the name in the BAS file needs to be adjusted in `Microphone2.h` for your file (... BAS source remains the same). Of course , the QB IDE can't do anything with the C file. The error message only appears because no microphone is connected.
![[Image: Audio-funktioniert2025-12-25-170058.jpg]](https://i.ibb.co/fYdkD2D3/Audio-funktioniert2025-12-25-170058.jpg)
I tried something similar in a smaller format, where the external h file had to be declared in the BAS file. I'm surprised it works without that here.
Code: (Select All)
Option _Explicit
'In QB64 mit "Declare Library" wie angegeben
Declare Library "D:\Lab\QuickBasic64\Extern-nicht-Basic\Basic-ruft-C\bfibonacci"
Function fibo& (ByVal N As Integer)
End Declare
Dim As Integer N, rueckgabe
Dim As Single zeitstart, zeitende
Timer On
Cls
Locate 3, 3
Input "Berechnet die Fibonacci-Zahl von: ", N
zeitstart = Timer
Locate 5, 3
rueckgabe = fibo&(N%)
If rueckgabe = -1 Then
Print "Eingabe zu gross!"
Sleep: System
Else
Print Using "Die Fibonacci-Zahl von ## = ###,########"; N; fibo&(N%)
End If
zeitende = Timer
Locate 7, 3
Print Using "Sekunden: ##.#### "; zeitende - zeitstart
End
Code: (Select All)
#include <stdio.h>
#include <stdlib.h>
long fibo(int n)
{
long x, y;
int i;
//Um bei 3 eine Anzeige von: 1 2 3 zu erreichen
x = y = i = 1;
if (n == 0 || n == 1)
{
return(n);
}
if (n > 40)
{
return(-1);
//sonst Überlauf
}
else while (i < n)
{
x = x + y; y = x - y; i++;
}
return(x);
}
RE: Microphone input - Unseen Machine - 12-25-2025
So me mic is defo broke!
@Petr Thanks for the mods...now im thinking with the enet server/client idea and microphone and webcams it's viable to think about a VOIP and Video Calling interface!
Thanks for fixing it too!
Unseen
RE: Microphone input - Kernelpanic - 12-25-2025
Okay, it's working. I've connected a microphone.
What do I do with it now?
RE: Microphone input - Petr - 12-25-2025
@Unseen Machine
If you succeed, it’ll be a revolutionary breakthrough for QB64PE. The first video client over a network in this language—that would be the absolute peak. The language has the potential for it. Text clients already exist, so this is a brilliant idea. I’m not going to compete with you on this; I really want to see you pull it off. I think it’s going to work.
I have plenty of ideas, but first things first—this will be a great foundation. Of course, if you’re looking for a collaborator, I’m in. But I won't develop anything in this direction without your go-ahead. I’d suggest making it rock-solid on LAN first. See how it handles a 'stress test,' like someone pulling the cable and plugging it back in... I'm not sure how you're handling packet loss yet, as I haven't checked the code, but networks can be real monsters...
RE: Microphone input - Petr - 12-25-2025
@Kernelpanic
Well, for example, you could start complaining: 'What kind of program did you give me?! That guy I hear singing is way too loud! Who is that? Who’s talking to me?!' Or maybe: 'Good grief, you nearly gave me a heart attack!
RE: Microphone input - Unseen Machine - 12-25-2025
Im never in competition...I know that the WinAPI and C++ are my learning points and TBH they make me feel dumb as F sometimes! Like, with the extra omphh of C++ comes way more aggro and pathways....Im enjoying the process though!
If the webcam lib can capture an image then the eNet lib is 100% a viable way to use it...my demo of it is just that, a demo! But it should show how MMO games and VOIP and Video Chat are pheasible...also as eNet has 3 layers of delivery of data and this seems to remove the "Useless" aspect from UDP!
YOU are above me on knowledge in this arena so heres the baton! Take POINT!
Unseen
|