From 98c0065807055225bb97b241c203923bab481a5e Mon Sep 17 00:00:00 2001 From: mtabachenko Date: Mon, 27 May 2013 16:27:17 +0000 Subject: [PATCH] - typo fix, sorry --- desmume/src/mic.cpp | 563 ++++++++++++++++++++++++++++++-------------- 1 file changed, 386 insertions(+), 177 deletions(-) diff --git a/desmume/src/mic.cpp b/desmume/src/mic.cpp index ac9368e0a..a7e3c84a4 100644 --- a/desmume/src/mic.cpp +++ b/desmume/src/mic.cpp @@ -1,208 +1,417 @@ -/* mic.cpp - this file is part of DeSmuME - * - * Copyright (C) 2009-2011 DeSmuME Team - * - * This file is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This file is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ +/* + Copyright (C) 2008-2009 DeSmuME team -#ifndef WIN32 + This file is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + This file is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the this software. If not, see . +*/ + +/* + The NDS microphone produces 8-bit sound sampled at 16khz. + The sound data must be read sample-by-sample through the + ARM7 SPI device (touchscreen controller, channel 6). + + Note : I added these notes because the microphone isn't + documented on GBATek. +*/ -#include -#include "mic.h" #include "NDSSystem.h" +#include "../types.h" +#include "../debug.h" +#include "../mic.h" +#include "../movie.h" #include "readwrite.h" +#include +#include -#define MIC_NULL_SAMPLE_VALUE 0 -#define MIC_MAX_BUFFER_SAMPLES 320 -#define MIC_BUFFER_SIZE (sizeof(u8) * MIC_MAX_BUFFER_SAMPLES) -#define NUM_INTERNAL_NOISE_SAMPLES 32 +int MicDisplay; +int SampleLoaded=0; -static u8 *micSampleBuffer = NULL; // Pointer to the internal sample buffer. -static u8 *micReadPosition = NULL; // Pointer to the read position of the internal sample buffer. -static u8 *micWritePosition = NULL; // Pointer to the write position of the internal sample buffer. -static unsigned int micBufferFillCount; // The number of readable samples in the internal sample buffer. +#define MIC_CHECKERR(hr) if(hr != MMSYSERR_NOERROR) return FALSE; -static void Mic_BufferClear(void) +#define MIC_BUFSIZE 4096 + +static BOOL Mic_Inited = FALSE; + +static u8 Mic_TempBuf[MIC_BUFSIZE]; +static u8 Mic_Buffer[2][MIC_BUFSIZE]; +static u16 Mic_BufPos; +static u8 Mic_WriteBuf; +static u8 Mic_PlayBuf; + +static int micReadSamplePos=0; + +static HWAVEIN waveIn; +static WAVEHDR waveHdr; + +static int CALLBACK waveInProc(HWAVEIN wavein, UINT msg, DWORD instance, DWORD_PTR param1, DWORD_PTR param2) { - if (micSampleBuffer == NULL) { - return; - } + LPWAVEHDR lpWaveHdr; + HRESULT hr; - memset(micSampleBuffer, MIC_NULL_SAMPLE_VALUE, MIC_BUFFER_SIZE); - micReadPosition = micSampleBuffer; - micWritePosition = micSampleBuffer; - micBufferFillCount = 0; -} + if(!Mic_Inited) + return 1; -BOOL Mic_Init(void) -{ - BOOL result = FALSE; - - u8 *newBuffer = (u8 *)malloc(MIC_BUFFER_SIZE); - if (newBuffer == NULL) { - return result; - } - - micSampleBuffer = newBuffer; - Mic_BufferClear(); - result = TRUE; - - return result; -} - -void Mic_DeInit(void) -{ - free(micSampleBuffer); - micSampleBuffer = NULL; -} - -void Mic_Reset(void) -{ - *micReadPosition = MIC_NULL_SAMPLE_VALUE; - micWritePosition = micReadPosition; - micBufferFillCount = 0; -} - -static bool Mic_GetActivate(void) -{ - return NDS_getFinalUserInput().mic.micButtonPressed; -} - -static bool Mic_IsBufferFull(void) -{ - return (micBufferFillCount >= MIC_MAX_BUFFER_SAMPLES); -} - -static bool Mic_IsBufferEmpty(void) -{ - return (micBufferFillCount == 0); -} - -static u8 Mic_DefaultBufferRead(void) -{ - u8 theSample = MIC_NULL_SAMPLE_VALUE; - - if (micSampleBuffer == NULL) { - return theSample; - } - - theSample = *micReadPosition; - - if (Mic_IsBufferEmpty()) { - return theSample; - } - - micReadPosition++; - micBufferFillCount--; - - // Move the pointer back to start if we reach the end of the memory block. - if (micReadPosition >= (micSampleBuffer + MIC_BUFFER_SIZE)) { - micReadPosition = micSampleBuffer; - } - - return theSample; -} - -u8 Mic_ReadSample(void) -{ - // All mic modes other than Physical must have the mic hotkey pressed in order - // to work. - if (CommonSettings.micMode != TCommonSettings::Physical && !Mic_GetActivate()) { - return MIC_NULL_SAMPLE_VALUE; - } - - return Mic_DefaultBufferRead(); -} - -static void Mic_DefaultBufferWrite(u8 theSample) -{ - if (micSampleBuffer == NULL || Mic_IsBufferFull()) { - return; - } - - *micWritePosition = theSample; - micWritePosition++; - micBufferFillCount++; - - // Move the pointer back to start if we reach the end of the memory block. - if (micWritePosition >= (micSampleBuffer + MIC_BUFFER_SIZE)) { - micWritePosition = micSampleBuffer; - } -} - -static u8 Mic_GenerateInternalNoiseSample(void) -{ - const u8 noiseSample[NUM_INTERNAL_NOISE_SAMPLES] = + if(msg == WIM_DATA) { - 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF5, 0xFF, 0xFF, 0xFF, 0xFF, 0x8E, 0xFF, - 0xF4, 0xE1, 0xBF, 0x9A, 0x71, 0x58, 0x5B, 0x5F, 0x62, 0xC2, 0x25, 0x05, 0x01, 0x01, 0x01, 0x01 - }; - static unsigned int i = 0; + lpWaveHdr = (LPWAVEHDR)param1; - if (++i >= NUM_INTERNAL_NOISE_SAMPLES) { - i = 0; + memcpy(Mic_Buffer[Mic_WriteBuf], lpWaveHdr->lpData, MIC_BUFSIZE); + Mic_WriteBuf ^= 1; + + hr = waveInAddBuffer(waveIn, lpWaveHdr, sizeof(WAVEHDR)); + if(hr != MMSYSERR_NOERROR) + return 1; } - return noiseSample[i]; + return 0; } -static u8 Mic_GenerateWhiteNoiseSample(void) -{ - return (u8)(rand() & 0xFF); -} +static char* samplebuffer = NULL; +static int samplebuffersize = 0; +static FILE* fp = NULL; -static u8 Mic_GenerateNullSample(void) -{ - return MIC_NULL_SAMPLE_VALUE; -} +EMUFILE_MEMORY newWavData; -void Mic_DoNoise(BOOL noise) +static bool dataChunk(EMUFILE* inf) { - u8 (*generator) (void) = NULL; + bool found = false; - if (micSampleBuffer == NULL) { - return; + // seek to just after the RIFF header + inf->fseek(12,SEEK_SET); + + // search for a format chunk + for (;;) { + char chunk_id[4]; + u32 chunk_length; + + if(inf->eof()) return found; + if(inf->fread(chunk_id, 4) != 4) return found; + if(!read32le(&chunk_length, inf)) return found; + + // if we found a data chunk, excellent! + if (memcmp(chunk_id, "data", 4) == 0) { + found = true; + u8* temp = new u8[chunk_length]; + if(inf->fread(temp,chunk_length) != chunk_length) { + delete[] temp; + return false; + } + newWavData.fwrite(temp,chunk_length); + delete[] temp; + chunk_length = 0; + } + + inf->fseek(chunk_length,SEEK_CUR); } - if (!noise) { - generator = &Mic_GenerateNullSample; - } else if (CommonSettings.micMode == TCommonSettings::InternalNoise) { - generator = &Mic_GenerateInternalNoiseSample; - } else if (CommonSettings.micMode == TCommonSettings::Random) { - generator = &Mic_GenerateWhiteNoiseSample; - } - - if (generator == NULL) { - return; - } - - while (micBufferFillCount < MIC_MAX_BUFFER_SAMPLES) { - Mic_DefaultBufferWrite(generator()); - } + return found; } -void mic_savestate(EMUFILE* os) +static bool formatChunk(EMUFILE* inf) { - write32le(-1,os); + // seek to just after the RIFF header + inf->fseek(12,SEEK_SET); + + // search for a format chunk + for (;;) { + char chunk_id[4]; + u32 chunk_length; + + inf->fread(chunk_id, 4); + if(!read32le(&chunk_length, inf)) return false; + + // if we found a format chunk, excellent! + if (memcmp(chunk_id, "fmt ", 4) == 0 && chunk_length >= 16) { + + // read format chunk + u16 format_tag; + u16 channel_count; + u32 samples_per_second; + //u32 bytes_per_second = read32_le(chunk + 8); + //u16 block_align = read16_le(chunk + 12); + u16 bits_per_sample; + + if(read16le(&format_tag,inf)!=1) return false; + if(read16le(&channel_count,inf)!=1) return false; + if(read32le(&samples_per_second,inf)!=1) return false; + inf->fseek(6,SEEK_CUR); + if(read16le(&bits_per_sample,inf)!=1) return false; + + chunk_length -= 16; + + // format_tag must be 1 (WAVE_FORMAT_PCM) + // we only support mono 8bit + if (format_tag != 1 || + channel_count != 1 || + bits_per_sample != 8) { + MessageBox(0,"not a valid RIFF WAVE file; must be 8bit mono pcm",0,0); + return false; + } + + return true; + } + + inf->fseek(chunk_length,SEEK_CUR); + } + return false; } -bool mic_loadstate(EMUFILE* is, int size) +bool LoadSample(const char *name) { - is->fseek(size, SEEK_CUR); + SampleLoaded = 0; + if(!name) return true; + + EMUFILE_FILE inf(name,"rb"); + if(inf.fail()) return false; + + //wav reading code adapted from AUDIERE (LGPL) + + // read the RIFF header + u8 riff_id[4]; + u32 riff_length; + u8 riff_datatype[4]; + + inf.fread(riff_id, 4); + read32le(&riff_length,&inf); + inf.fread(riff_datatype, 4); + + if (inf.size() < 12 || + memcmp(riff_id, "RIFF", 4) != 0 || + riff_length == 0 || + memcmp(riff_datatype, "WAVE", 4) != 0) { + MessageBox(0,"not a valid RIFF WAVE file",0,0); + return false; + } + + if (!formatChunk(&inf)) + return false; + + if(!dataChunk(&inf)) { + MessageBox(0,"not a valid WAVE file. some unknown problem.",0,0); + return false; + } + + delete[] samplebuffer; + samplebuffersize = (int)newWavData.size(); + samplebuffer = new char[samplebuffersize]; + memcpy(samplebuffer,newWavData.buf(),samplebuffersize); + new(&newWavData) EMUFILE_MEMORY(); + + SampleLoaded=1; + + return true; +} + +BOOL Mic_DeInit_Physical() +{ + if(!Mic_Inited) + return TRUE; + + INFO("win32 microphone DEinit OK\n"); + + Mic_Inited = FALSE; + + waveInReset(waveIn); + waveInClose(waveIn); + return TRUE; } -#endif +BOOL Mic_Init_Physical() +{ + if(Mic_Inited) + return TRUE; + + Mic_Inited = FALSE; + + HRESULT hr; + WAVEFORMATEX wfx; + + memset(Mic_TempBuf, 0x80, MIC_BUFSIZE); + memset(Mic_Buffer[0], 0x80, MIC_BUFSIZE); + memset(Mic_Buffer[1], 0x80, MIC_BUFSIZE); + Mic_BufPos = 0; + + Mic_WriteBuf = 0; + Mic_PlayBuf = 1; + + memset(&wfx, 0, sizeof(wfx)); + wfx.cbSize = 0; + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nChannels = 1; + wfx.nSamplesPerSec = 16000; + wfx.nBlockAlign = 1; + wfx.nAvgBytesPerSec = 16000; + wfx.wBitsPerSample = 8; + + hr = waveInOpen(&waveIn, WAVE_MAPPER, &wfx, (DWORD_PTR)waveInProc, 0, CALLBACK_FUNCTION); + MIC_CHECKERR(hr) + + memset(&waveHdr, 0, sizeof(waveHdr)); + waveHdr.lpData = (LPSTR)Mic_TempBuf; + waveHdr.dwBufferLength = MIC_BUFSIZE; + + hr = waveInPrepareHeader(waveIn, &waveHdr, sizeof(WAVEHDR)); + MIC_CHECKERR(hr) + + hr = waveInAddBuffer(waveIn, &waveHdr, sizeof(WAVEHDR)); + MIC_CHECKERR(hr) + + hr = waveInStart(waveIn); + MIC_CHECKERR(hr) + + Mic_Inited = TRUE; + INFO("win32 microphone init OK\n"); + + return TRUE; +} + +BOOL Mic_Init() { + + micReadSamplePos = 0; + + return TRUE; +} + +void Mic_Reset() +{ + micReadSamplePos = 0; + + if(!Mic_Inited) + return; + + //reset physical + memset(Mic_TempBuf, 0x80, MIC_BUFSIZE); + memset(Mic_Buffer[0], 0x80, MIC_BUFSIZE); + memset(Mic_Buffer[1], 0x80, MIC_BUFSIZE); + Mic_BufPos = 0; + + Mic_WriteBuf = 0; + Mic_PlayBuf = 1; +} + +void Mic_DeInit() +{ +} + +static const u8 random[32] = +{ + 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF5, 0xFF, 0xFF, 0xFF, 0xFF, 0x8E, 0xFF, + 0xF4, 0xE1, 0xBF, 0x9A, 0x71, 0x58, 0x5B, 0x5F, 0x62, 0xC2, 0x25, 0x05, 0x01, 0x01, 0x01, 0x01, +} ; + + + +u8 Mic_ReadSample() +{ + u8 ret; + u8 tmp; + if(CommonSettings.micMode == TCommonSettings::Physical) + { + if(movieMode == MOVIEMODE_INACTIVE) + { + //normal mic behavior + tmp = (u8)Mic_Buffer[Mic_PlayBuf][Mic_BufPos >> 1]; + } + else + { + //since we're not recording Mic_Buffer to the movie, use silence + tmp = 0x80; + } + } + else + { + if(NDS_getFinalUserInput().mic.micButtonPressed) + { + if(SampleLoaded) + { + //use a sample + //TODO: what if a movie is active? + // for now I'm going to hope that if anybody records a movie with a sample loaded, + // either they know what they're doing and plan to distribute the sample, + // or they're playing a game where it doesn't even matter or they never press the mic button. + tmp = samplebuffer[micReadSamplePos >> 1]; + micReadSamplePos++; + if(micReadSamplePos == samplebuffersize*2) + micReadSamplePos=0; + } + else + { + //use the "random" values + if(CommonSettings.micMode == TCommonSettings::InternalNoise) + tmp = random[micReadSamplePos >> 1]; + else tmp = rand(); + micReadSamplePos++; + if(micReadSamplePos == ARRAY_SIZE(random)*2) + micReadSamplePos=0; + } + } + else + { + tmp = 0x80; + + //reset mic button buffer pos if not pressed + micReadSamplePos=0; + } + } + + if(Mic_BufPos & 0x1) + { + ret = ((tmp & 0x1) << 7); + } + else + { + ret = ((tmp & 0xFE) >> 1); + } + + MicDisplay = tmp; + + Mic_BufPos++; + if(Mic_BufPos >= (MIC_BUFSIZE << 1)) + { + Mic_BufPos = 0; + Mic_PlayBuf ^= 1; + } + + return ret; +} + +// maybe a bit paranoid... +void mic_savestate(EMUFILE* os) +{ + //version + write32le(1,os); + assert(MIC_BUFSIZE == 4096); // else needs new version + + os->fwrite((char*)Mic_Buffer[0], MIC_BUFSIZE); + os->fwrite((char*)Mic_Buffer[1], MIC_BUFSIZE); + write16le(Mic_BufPos,os); + write8le(Mic_WriteBuf,os); // seems OK to save... + write8le(Mic_PlayBuf,os); + write32le(micReadSamplePos,os); +} +bool mic_loadstate(EMUFILE* is, int size) +{ + u32 version; + if(read32le(&version,is) != 1) return false; + if(version > 1 || version == 0) { is->fseek(size-4, SEEK_CUR); return true; } + + is->fread((char*)Mic_Buffer[0], MIC_BUFSIZE); + is->fread((char*)Mic_Buffer[1], MIC_BUFSIZE); + read16le(&Mic_BufPos,is); + read8le(&Mic_WriteBuf,is); + read8le(&Mic_PlayBuf,is); + read32le(&micReadSamplePos,is); + return true; +} +