#include "gvDevice.h" #include "gvCodec.h" #include "gvSource.h" #include "gvUtil.h" #include #if !defined(_PSP) #error This file should only be used with the PSP #endif /************ ** DEFINES ** ************/ // the number of samples to capture at a time // is a multiple of 64 and 160 // at 11025hz, corresponds to about 30ms #define GVI_INPUT_LEN 320 #define GVI_CAPTURE_THREAD_STACK_SIZE (1024 * 4) // ADC settings #define GVI_ADC_ALC -6 // -6 dB #define GVI_ADC_GAIN +30 // +30 dB #define GVI_ADC_NOIZ -60 // -60 dB #define GVI_ADC_HOLD 0 // 0 ms #define GVI_ADC_DECAY 3 // 192 ms #define GVI_ADC_ATTACK 2 // 24 ms /********** ** TYPES ** **********/ typedef struct { GVBool m_capturing; GVScalar m_captureVolume; GVFrameStamp m_captureClock; GVScalar m_captureThreshold; GVFrameStamp m_captureLastCrossedThresholdTime; GVSample * m_captureBuffer; size_t m_captureBufferLen; // len in samples (not bytes) size_t m_captureBufferWritePos; size_t m_captureBufferReadPos; SceUID m_captureBufferSemaphore; SceUID m_captureThreadID; GVBool m_captureThreadStop; } GVIHardwareData; /************ ** GLOBALS ** ************/ static GVIDevice * GVIPSPDevice = NULL; /************** ** FUNCTIONS ** **************/ static void gviHardwareFreeDevice(GVIDevice * device); static GVBool gviWaitSemaphore(GVIHardwareData * data) { int rcode = sceKernelWaitSema(data->m_captureBufferSemaphore, 1, NULL); if(rcode == 0) return GVTrue; return GVFalse; } static GVBool gviSignalSemaphore(GVIHardwareData * data) { int rcode = sceKernelSignalSema(data->m_captureBufferSemaphore, 1); if(rcode == 0) return GVTrue; return GVFalse; } GVBool gviHardwareStartup(void) { SceAudioInputParam param; int rcode; // init mic capture param.alc = GVI_ADC_ALC; param.gain = GVI_ADC_GAIN; param.noiz = GVI_ADC_NOIZ; param.hold = GVI_ADC_HOLD; param.decay = GVI_ADC_DECAY; param.attack = GVI_ADC_ATTACK; rcode = sceAudioInputInitEx(¶m); if(rcode < 0) return GVFalse; return GVTrue; } void gviHardwareCleanup(void) { if(GVIPSPDevice) { gviHardwareFreeDevice(GVIPSPDevice); GVIPSPDevice = NULL; } } void gviHardwareThink(void) { // no thinking needed } int gviHardwareListDevices(GVDeviceInfo devices[], int maxDevices, GVDeviceType types) { if(maxDevices < 1) return 0; if(!(types & GV_CAPTURE)) return 0; memset(&devices[0], 0, sizeof(GVDeviceInfo)); devices[0].m_id = 0; strcpy(devices[0].m_name, _T("Headset Mic")); devices[0].m_deviceType = GV_CAPTURE; devices[0].m_defaultDevice = GV_CAPTURE; devices[0].m_hardwareType = GVHardwarePSPHeadset; return 1; } static void gviHardwareFreeDevice(GVIDevice * device) { GVIHardwareData * data = (GVIHardwareData *)device->m_data; // don't track this device anymore GVIPSPDevice = NULL; // tell the thread to stop // the thread will free the device after it stops data->m_captureThreadStop = GVTrue; } static GVBool gviHardwareStartDevice(GVIDevice * device, GVDeviceType type) { GVIHardwareData * data = (GVIHardwareData *)device->m_data; if(type != GV_CAPTURE) return GVFalse; // already capturing? if(data->m_capturing == GVTrue) return GVTrue; // set vars data->m_captureBufferWritePos = 0; data->m_captureBufferReadPos = 0; // start capturing data->m_capturing = GVTrue; return GVTrue; } static void gviHardwareStopDevice(GVIDevice * device, GVDeviceType type) { GVIHardwareData * data = (GVIHardwareData *)device->m_data; if(type != GV_CAPTURE) return; // stop capturing data->m_capturing = GVFalse; // increment the clock so new audio isn't contiguous data->m_captureClock++; } static GVBool gviHardwareIsDeviceStarted(GVIDevice * device, GVDeviceType type) { GVIHardwareData * data = (GVIHardwareData *)device->m_data; if(type != GV_CAPTURE) return GVFalse; return data->m_capturing; } static void gviHardwareSetDeviceVolume(GVIDevice * device, GVDeviceType type, GVScalar volume) { GVIHardwareData * data = (GVIHardwareData *)device->m_data; if(type != GV_CAPTURE) return; data->m_captureVolume = volume; } static GVScalar gviHardwareGetDeviceVolume(GVIDevice * device, GVDeviceType type) { GVIHardwareData * data = (GVIHardwareData *)device->m_data; if(type != GV_CAPTURE) return (GVScalar)0; return data->m_captureVolume; } static void gviHardwareSetCaptureThreshold(GVIDevice * device, GVScalar threshold) { GVIHardwareData * data = (GVIHardwareData *)device->m_data; data->m_captureThreshold = threshold; } static GVScalar gviHardwareGetCaptureThreshold(GVIDevice * device) { GVIHardwareData * data = (GVIHardwareData *)device->m_data; return data->m_captureThreshold; } static int gviHardwareGetAvailableCaptureBytes(GVDevice device) { GVIHardwareData * data = (GVIHardwareData *)device->m_data; int numSamples; if(!data->m_capturing) return GVFalse; // figure out how many samples are available gviWaitSemaphore(data); if(data->m_captureBufferWritePos < data->m_captureBufferReadPos) numSamples = (data->m_captureBufferLen - data->m_captureBufferReadPos); else numSamples = (data->m_captureBufferWritePos - data->m_captureBufferReadPos); gviSignalSemaphore(data); return numSamples; } static GVBool gviHardwareCapturePacket(GVDevice device, GVByte * packet, int * len, GVFrameStamp * frameStamp, GVScalar * volume) { GVIHardwareData * data = (GVIHardwareData *)device->m_data; int numBytes; int numSamples; int numFrames; GVBool overThreshold = GVFalse; int lenAvailable; int framesAvailable; int i, j; GVSample * readPtr; if(!data->m_capturing) return GVFalse; // figure out how many encoded bytes they can handle lenAvailable = *len; // clear the len and volume *len = 0; if(volume) *volume = 0; // figure out how many bytes can be captured numSamples = gviHardwareGetAvailableCaptureBytes(device); numBytes = (numSamples * GV_BYTES_PER_SAMPLE); // figure out how many frames that is numFrames = (numBytes / GVIBytesPerFrame); if(numFrames == 0) return GVFalse; // figure out how many frames they can handle framesAvailable = (lenAvailable / GVIEncodedFrameSize); // don't give them more frames than they can handle numFrames = min(numFrames, framesAvailable); if(!numFrames) return GVFalse; // figure out how many bytes to capture numBytes = (numFrames * GVIBytesPerFrame); numSamples = (numBytes / GV_BYTES_PER_SAMPLE); // get the read pointer readPtr = (data->m_captureBuffer + data->m_captureBufferReadPos); // get the volume if they're interested if(volume) *volume = gviGetSamplesVolume(readPtr, numSamples); // check against the threshold if(volume) { // we already got the volume, so use that to check overThreshold = (*volume >= data->m_captureThreshold); } else { // we didn't get a volume, so check the samples directly overThreshold = gviIsOverThreshold(readPtr, numSamples, data->m_captureThreshold); } // did the audio cross the threshold? if(overThreshold) { // update the time at which we crossed data->m_captureLastCrossedThresholdTime = data->m_captureClock; } else { // check if we are still within the hold time overThreshold = ((GVFrameStamp)(data->m_captureClock - data->m_captureLastCrossedThresholdTime) < GVI_HOLD_THRESHOLD_FRAMES); } if(overThreshold) { // store the framestamp *frameStamp = data->m_captureClock; // handle the data one frame at a time for(i = 0 ; i < numFrames ; i++) { // scale the data if(data->m_captureVolume < 1.0) { for(j = 0 ; j < GVISamplesPerFrame ; j++) readPtr[j] = (GVSample)(readPtr[j] * data->m_captureVolume); } // filter if(device->m_captureFilterCallback) device->m_captureFilterCallback(device, readPtr, (GVFrameStamp)(data->m_captureClock + i)); // encode the buffer into the packet gviEncode(packet + (GVIEncodedFrameSize * i), readPtr); // update the loop info as needed readPtr += GVISamplesPerFrame; } } // advance the read position and clock data->m_captureBufferReadPos += numSamples; data->m_captureBufferReadPos %= data->m_captureBufferLen; data->m_captureClock += numFrames; // set the len *len = (numFrames * GVIEncodedFrameSize); // return false if we didn't get a packet if(!overThreshold) return GVFalse; return GVTrue; } static int gviPSPCaptureThread(SceSize args, void * argp) { GVIDevice * device = *(GVIDevice **)argp; GVIHardwareData * data = (GVIHardwareData *)device->m_data; int rcode; assert(device); assert(args == sizeof(GVIDevice*)); // loop until we're told to stop while(!data->m_captureThreadStop) { // are we capturing? if(data->m_capturing) { // get some input rcode = sceAudioInputBlocking(GVI_INPUT_LEN, GV_SAMPLES_PER_SECOND, data->m_captureBuffer + data->m_captureBufferWritePos); if(rcode < 0) { gviDeviceUnplugged(device); } else { gviWaitSemaphore(data); data->m_captureBufferWritePos += GVI_INPUT_LEN; data->m_captureBufferWritePos %= data->m_captureBufferLen; gviSignalSemaphore(data); } } else { // sleep msleep(10); } } // end sampling // if another device has already been created, don't do this if(GVIPSPDevice != NULL) sceAudioInputBlocking(GVI_INPUT_LEN, GV_SAMPLES_PER_SECOND, NULL); // free the device and its memory gsifree(data->m_captureBuffer); sceKernelDeleteSema(data->m_captureBufferSemaphore); gviFreeDevice(device); sceKernelExitThread(0); return 0; } static GVBool gviPSPAudioInitDevice(GVIDevice *device) { GVIHardwareData * data = (GVIHardwareData *)device->m_data; size_t size; size_t oldSize; int rcode; // create a semaphore data->m_captureBufferSemaphore = sceKernelCreateSema("capture buffer", SCE_KERNEL_SA_THFIFO, 1, 1, NULL); if(data->m_captureBufferSemaphore <= 0) return GVFalse; // figure out the buffer size size = gviMultiplyByBytesPerMillisecond(GVI_CAPTURE_BUFFER_MILLISECONDS); size /= GV_BYTES_PER_SAMPLE; // convert from bytes to samples do { // it needs to be a multiple of both the frame size and the input len oldSize = size; size = gviRoundUpToNearestMultiple(size, GVISamplesPerFrame); size = gviRoundUpToNearestMultiple(size, GVI_INPUT_LEN); } while(size != oldSize); data->m_captureBufferLen = size; // allocate the buffer data->m_captureBuffer = (GVSample *)gsimalloc(size * GV_BYTES_PER_SAMPLE); if(!data->m_captureBuffer) { sceKernelDeleteSema(data->m_captureBufferSemaphore); return GVFalse; } // create capture thread data->m_captureThreadID = sceKernelCreateThread("capture",gviPSPCaptureThread, SCE_KERNEL_USER_HIGHEST_PRIORITY, GVI_CAPTURE_THREAD_STACK_SIZE, 0, NULL); if(data->m_captureThreadID < 0) { gsifree(data->m_captureBuffer); sceKernelDeleteSema(data->m_captureBufferSemaphore); return GVFalse; } // start capture thread rcode = sceKernelStartThread(data->m_captureThreadID, sizeof(GVIDevice*), &device); if(rcode < 0) { sceKernelDeleteThread(data->m_captureThreadID); gsifree(data->m_captureBuffer); sceKernelDeleteSema(data->m_captureBufferSemaphore); return GVFalse; } return GVTrue; } GVDevice gviHardwareNewDevice(GVDeviceID deviceID, GVDeviceType type) { GVIDevice * device; GVIHardwareData * data; GVBool result; // we only do capture if(type != GV_CAPTURE) return NULL; // there can only be one device at a time if(GVIPSPDevice != NULL) return NULL; // create the device device = gviNewDevice(deviceID, GVHardwarePSPHeadset, type, sizeof(GVIHardwareData)); if(!device) return NULL; // init the device result = gviPSPAudioInitDevice(device); if(result == GVFalse) { gviFreeDevice(device); return NULL; } // store the pointers device->m_methods.m_freeDevice = gviHardwareFreeDevice; device->m_methods.m_startDevice = gviHardwareStartDevice; device->m_methods.m_stopDevice = gviHardwareStopDevice; device->m_methods.m_isDeviceStarted = gviHardwareIsDeviceStarted; device->m_methods.m_setDeviceVolume = gviHardwareSetDeviceVolume; device->m_methods.m_getDeviceVolume = gviHardwareGetDeviceVolume; device->m_methods.m_setCaptureThreshold = gviHardwareSetCaptureThreshold; device->m_methods.m_getCaptureThreshold = gviHardwareGetCaptureThreshold; device->m_methods.m_getAvailableCaptureBytes = gviHardwareGetAvailableCaptureBytes; device->m_methods.m_capturePacket = gviHardwareCapturePacket; // get a pointer to the data data = (GVIHardwareData *)device->m_data; // init vars data->m_captureVolume = (GVScalar)1.0; data->m_captureClock = 0; data->m_captureLastCrossedThresholdTime = (data->m_captureClock - GVI_HOLD_THRESHOLD_FRAMES - 1); GVIPSPDevice = device; return device; }