private static unsafe void SoundCallbackPlaying(IntPtr hwi, uint uMsg, SoundWrapper dwInstance, uint dwParam1, uint dwParam2) { if (uMsg == WOM_DONE) { WAVEHDR *bufptr = (WAVEHDR *)dwParam1; short[] data = new short[dwInstance.bufferLengthBytes / 2]; dwInstance.OnNewDataRequested(data); Marshal.Copy(data, 0, bufptr->lpData, (int)(dwInstance.bufferLengthBytes / 2)); if (!dwInstance.finishing) { uint retval, retval2, retval3; lock (dwInstance.locker) { retval = waveOutUnprepareHeader(dwInstance.waveHandle, ref *bufptr, 32); retval2 = waveOutPrepareHeader(dwInstance.waveHandle, ref *bufptr, 32); retval3 = waveOutWrite(dwInstance.waveHandle, ref *bufptr, 32); } if (retval != 0 || retval2 != 0 || retval3 != 0) { System.Windows.Forms.MessageBox.Show(string.Format("error in WOM_DATA: {0} {1} {2}\n", retval, retval2, retval3)); } } } }
private static unsafe void SoundCallbackRecording(IntPtr hwi, uint uMsg, SoundWrapper dwInstance, uint dwParam1, uint dwParam2) { if (uMsg == WIM_DATA) { WAVEHDR *bufptr = (WAVEHDR *)dwParam1; short[] data = new short[dwInstance.bufferLengthBytes / 2]; Marshal.Copy(bufptr->lpData, data, 0, (int)(bufptr->dwBytesRecorded / 2)); if (!dwInstance.finishing) { uint retval, retval2, retval3; lock (dwInstance.locker) { retval = waveInUnprepareHeader(dwInstance.waveHandle, ref *bufptr, 32); retval2 = waveInPrepareHeader(dwInstance.waveHandle, ref *bufptr, 32); retval3 = waveInAddBuffer(dwInstance.waveHandle, ref *bufptr, 32); } if (retval != 0 || retval2 != 0 || retval3 != 0) { System.Windows.Forms.MessageBox.Show(string.Format("error in WIM_DATA: {0} {1} {2}\n", retval, retval2, retval3)); } } dwInstance.OnNewDataPresent(data); } }
public void Stop() { if (waveHandle == IntPtr.Zero) { return; } finishing = true; lock (locker) { if (mode == Mode.Record) { CheckError(waveInReset(waveHandle)); } else { CheckError(waveOutReset(waveHandle)); } } for (int i = 0; i < 2; ++i) { unsafe { WAVEHDR *bufptr = (WAVEHDR *)bufferIP[i].ToPointer(); if (mode == Mode.Record) { CheckError(waveInUnprepareHeader(waveHandle, ref *bufptr, 32)); } else if (mode == Mode.Play) { CheckError(waveOutUnprepareHeader(waveHandle, ref *bufptr, 32)); } Marshal.FreeHGlobal(bufptr->lpData); Marshal.FreeHGlobal(bufferIP[i]); } } if (mode == Mode.Record) { CheckError(waveInClose(waveHandle)); } else if (mode == Mode.Play) { CheckError(waveOutClose(waveHandle)); } waveHandle = IntPtr.Zero; finishing = false; }
// este funcion callback se llama cuando el circuito de audio termino de procesar el paquete en el contexto de la funcion Play unsafe static void WaveOutDonePlay(IntPtr dev, int uMsg, int dwUser, int dwParam1, int dwParam2) { if ((uMsg == MM_WOM_DONE)) { IntPtr PCMBuffer = (IntPtr)dwParam1; WAVEHDR *header = (WAVEHDR *)PCMBuffer; // tengo que llamar a esta funcion para liberar el header waveOutUnprepareHeader(dev, ref *header, sizeof(WAVEHDR)); // libero la memoria Marshal.FreeHGlobal(PCMBuffer); } }
public static extern MMRESULT waveOutUnprepareHeader(IntPtr hWaveOut, WAVEHDR *lpWaveOutHdr, int uSize);
static extern int waveOutWrite(IntPtr hwo, WAVEHDR *wh, uint cbwh);
static extern int waveOutUnprepareHeader(IntPtr hwo, WAVEHDR *wh, uint cbwh);
public unsafe void Play(float freq, float duration, float vol) { volumen = vol; WAVEFORMATEX Format = new WAVEFORMATEX(); Format.cbSize = 0; Format.wFormatTag = WAVE_FORMAT_PCM; Format.nChannels = 1; Format.nSamplesPerSec = SAMPLE_RATE; Format.wBitsPerSample = 16; Format.nBlockAlign = 2; Format.nAvgBytesPerSec = Format.nSamplesPerSec * Format.nBlockAlign; waveOutOpen(out hWaveOut, WAVE_MAPPER, Format, woDonePlay, (IntPtr)0, CALLBACK_FUNCTION); // sintetizo una onda con la frecuencia y duracion dadas float kv; int cant_samples = (int)(SAMPLE_RATE * duration / 1000.0); // aloco memoria para el header y para los datos pcm pp dichos, todo junto PCMBuffer = Marshal.AllocHGlobal(sizeof(WAVEHDR) + sizeof(short) * cant_samples); WAVEHDR *header = (WAVEHDR *)PCMBuffer; short * pcm = (short *)(PCMBuffer + sizeof(WAVEHDR)); for (int j = 0; j < cant_samples; j++) { // dice que la conversion es redundante.....las pelotas es redundante... si no lo convertis a doble da siempre cero!!! double t = (double)j / (double)SAMPLE_RATE; // tiempo transcurrido float s0 = 0.1f; float s1 = 0.95f; float s = (float)j / (float)cant_samples; if (s < s0) { kv = s / s0; } else if (s > s1) { kv = (1 - s) / (1 - s1); } else { kv = 1; } double value = Math.Sin(2 * Math.PI * freq * t) + 0.3 * Math.Sin(4 * Math.PI * freq * t); value = value * volumen * kv; if (value < -1) { value = -1; } else if (value > 1) { value = 1; } pcm[j] = (short)(value * 32500.0); } header->lpData = (IntPtr)pcm; header->dwAudioBufferLength = (uint)(sizeof(short) * cant_samples); header->dwFlags = WHDR_BEGINLOOP | WHDR_ENDLOOP; header->dwLoops = 1; if (waveOutPrepareHeader(hWaveOut, ref *header, sizeof(WAVEHDR)) == MMSYSERR_NOERROR) { if (waveOutWrite(hWaveOut, ref *header, sizeof(WAVEHDR)) == MMSYSERR_NOERROR) { } } }
public static extern MMRESULT waveInAddBuffer(IntPtr hWaveIn, WAVEHDR *pwh, int cbwh);
// esta funcion se llama continuamente, pero hay que sincronizar, de tal forma que el circuito // de audio nunca se quede sin datos, pero sin llenar el buffer de salida // entonces la idea es que cada vez que mando un paqueta sumo en uno la cantidad de bloques enviados // y cada vez que el circuito termina de tocarlos me avisa y decremento uno. public unsafe void WaveOut() { int cant_bloques = *(int *)p_cant_bloques; if (cant_bloques >= 10) { return; // mejor espero } // Avanzo el tiempo long T1; // address of current frequency QueryPerformanceCounter(out T1); elapsed_time = (double)(T1 - T0) / (double)F; T0 = T1; // quiero generar una onda que dure el mismo tiempo que el lap del timer. (que es el dt del timer) // y un 10% mas, de tal forma de evitar el clack entre switchs // cant samples = SAMPLE_RATE * dt; (dt ==1 ) uint cant_samples = (uint)(SAMPLE_RATE * lap * 1.1f); uint pcm_size = cant_samples * sizeof(short); // el bloque esta compuesto del header (WAVEHDR) y el pcm data pp dicho uint block_size = (uint)sizeof(WAVEHDR) + pcm_size; byte * p_buff = ((byte *)AudioBuffer + AudioBufferIndex); WAVEHDR *header = (WAVEHDR *)p_buff; short * pcm = (short *)(p_buff + sizeof(WAVEHDR)); short * wav = (short *)(PCMBuffer); float k_freq = 1.0f; for (int j = 0; j < cant_samples; j++) { float s = j / cant_samples; int ndx = (int)(index * k_freq) % _cant_samples; pcm[j] = (short)((wav[ndx]) * volumen); ++index; } header->lpData = (IntPtr)pcm; header->dwAudioBufferLength = pcm_size; header->dwFlags = WHDR_BEGINLOOP | WHDR_ENDLOOP; header->dwLoops = 1; // analisis de fourier fft.ComplexFFT(pcm, (int)cant_samples, 16384, 1); if (waveOutPrepareHeader(hWaveOut, ref *header, sizeof(WAVEHDR)) == MMSYSERR_NOERROR) { if (waveOutWrite(hWaveOut, ref *header, sizeof(WAVEHDR)) == MMSYSERR_NOERROR) { // incremento el indice dentro el buffer de audio, que se comporta como una lista circular // cuando me quedo sin lugar arranca de nuevo donde seguro que esos paquetes ya fueron procesados AudioBufferIndex += block_size; if (AudioBufferIndex >= AudioBufferSize) { AudioBufferIndex = 0; } (*(int *)p_cant_bloques)++; // un bloque mas } } }
public static extern uint waveInAddBuffer(HWAVEIN hwi, [NativeTypeName("LPWAVEHDR")] WAVEHDR *pwh, uint cbwh);
public static extern uint waveInUnprepareHeader(HWAVEIN hwi, [NativeTypeName("LPWAVEHDR")] WAVEHDR *pwh, uint cbwh);
public static extern MMRESULT waveOutWrite(IntPtr hWaveOut, WAVEHDR *pwh, int cbwh);
internal static extern MMRESULT waveInPrepareHeader(IntPtr hWaveIn, WAVEHDR *pwh, int cbwh);
internal static extern MMRESULT waveOutPrepareHeader(IntPtr hWaveOut, WAVEHDR *lpWaveOutHdr, int uSize);
public static extern uint waveOutWrite(HWAVEOUT hwo, [NativeTypeName("LPWAVEHDR")] WAVEHDR *pwh, uint cbwh);
public static extern uint waveOutUnprepareHeader(HWAVEOUT hwo, [NativeTypeName("LPWAVEHDR")] WAVEHDR *pwh, uint cbwh);
public void Start(DeviceInfo device) { if (waveHandle != IntPtr.Zero) { return; } var devByName = EnumerateDevices().FirstOrDefault(i => i.name == device.name); uint deviceIndex = ((uint?)devByName?.index) ?? WAVE_MAPPER; WAVEFORMATEX waveFormatEx = new WAVEFORMATEX(); waveFormatEx.wFormatTag = WAVE_FORMAT_PCM; waveFormatEx.nChannels = channels; waveFormatEx.nSamplesPerSec = sampleRate; waveFormatEx.wBitsPerSample = bitsPerSample; waveFormatEx.cbSize = 0; waveFormatEx.nBlockAlign = (ushort)(waveFormatEx.nChannels * waveFormatEx.wBitsPerSample / 8); waveFormatEx.nAvgBytesPerSec = waveFormatEx.nSamplesPerSec * waveFormatEx.nBlockAlign; if (mode == Mode.Record) { soundCallbackDelegate = SoundCallbackRecording; CheckError(waveInOpen(ref waveHandle, deviceIndex, ref waveFormatEx, soundCallbackDelegate, this, CALLBACK_FUNCTION)); } else if (mode == Mode.Play) { soundCallbackDelegate = SoundCallbackPlaying; CheckError(waveOutOpen(ref waveHandle, deviceIndex, ref waveFormatEx, soundCallbackDelegate, this, CALLBACK_FUNCTION)); } warmup = 2; for (int i = 0; i < 2; ++i) { unsafe { bufferIP[i] = Marshal.AllocHGlobal(32); // to prevent GC from deleting WAVEHDR *bufptr = (WAVEHDR *)bufferIP[i].ToPointer(); bufptr->lpData = Marshal.AllocHGlobal((int)bufferLengthBytes); bufptr->dwBufferLength = bufferLengthBytes; bufptr->dwFlags = 0; short[] data = new short[bufferLengthBytes / 2]; Marshal.Copy(data, 0, bufptr->lpData, (int)(bufferLengthBytes / 2)); if (mode == Mode.Record) { CheckError(waveInPrepareHeader(waveHandle, ref *bufptr, 32)); CheckError(waveInAddBuffer(waveHandle, ref *(WAVEHDR *)bufferIP[i].ToPointer(), 32)); } else if (mode == Mode.Play) { CheckError(waveOutPrepareHeader(waveHandle, ref *bufptr, 32)); CheckError(waveOutWrite(waveHandle, ref *(WAVEHDR *)bufferIP[i].ToPointer(), 32)); } } } if (mode == Mode.Record) { CheckError(waveInStart(waveHandle)); } }
public static extern MMRESULT waveInUnprepareHeader(IntPtr hWaveIn, WAVEHDR *pwh, int cbwh);