diff --git a/examples/mic/Makefile b/examples/mic/Makefile new file mode 100644 index 0000000..9a74360 --- /dev/null +++ b/examples/mic/Makefile @@ -0,0 +1,139 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITARM)),) +$(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") +endif + +ifeq ($(strip $(CTRULIB)),) +# THIS IS TEMPORARY - in the future it should be at $(DEVKITPRO)/libctru +$(error "Please set CTRULIB in your environment. export CTRULIB=libctru") +endif + +TOPDIR ?= $(CURDIR) +include $(DEVKITARM)/3ds_rules + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# DATA is a list of directories containing data files +# INCLUDES is a list of directories containing header files +# SPECS is the directory containing the important build and link files +#--------------------------------------------------------------------------------- +export TARGET := $(shell basename $(CURDIR)) +BUILD := build +SOURCES := source +DATA := data +INCLUDES := include + + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +ARCH := -march=armv6k -mtune=mpcore + +CFLAGS := -g -Wall -O2 -mword-relocations -save-temps \ + -fomit-frame-pointer -ffast-math \ + $(ARCH) + +CFLAGS += $(INCLUDE) -DARM11 -D_3DS + +CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 + +ASFLAGS := -g $(ARCH) +LDFLAGS = -specs=3dsx.specs -g $(ARCH) \ + -Wl,-Map,$(TARGET).map + +LIBS := -lctru -lm + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(CTRULIB) + + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) +export TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +.PHONY: $(BUILD) clean all + +#--------------------------------------------------------------------------------- +all: $(BUILD) + +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(TARGET).3dsx $(TARGET).elf + + +#--------------------------------------------------------------------------------- +else + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +$(OUTPUT).3dsx : $(OUTPUT).elf +$(OUTPUT).elf : $(OFILES) + +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + $(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/examples/mic/README.md b/examples/mic/README.md new file mode 100644 index 0000000..37a7328 --- /dev/null +++ b/examples/mic/README.md @@ -0,0 +1,5 @@ +arm11u +======= + +Example for using the microphone with ctrulib. Hold down the A button to record, the app will then play the recorded audio once the A button is released. Roughly 32 seconds of audio can be recorded with the default audiobuf size in this app. + diff --git a/examples/mic/source/main.c b/examples/mic/source/main.c new file mode 100644 index 0000000..022902a --- /dev/null +++ b/examples/mic/source/main.c @@ -0,0 +1,95 @@ +#include +#include +#include +#include +#include <3ds.h> + +int main() +{ + u8 *framebuf; + u32 *sharedmem = NULL, sharedmem_size = 0x30000; + u8 *audiobuf; + u32 audiobuf_size = 0x100000, audiobuf_pos = 0; + u8 control=0x40; + + srvInit(); + aptInit(); + gfxInit(); + hidInit(NULL); + aptSetupEventHandler(); + + CSND_initialize(NULL); + + sharedmem = (u32*)memalign(0x1000, sharedmem_size); + audiobuf = linearAlloc(audiobuf_size); + + MIC_Initialize(sharedmem, sharedmem_size, control, 0, 3, 1, 1);//See mic.h. + + APP_STATUS status; + while((status=aptGetStatus())!=APP_EXITING) + { + if(status==APP_RUNNING) + { + framebuf = gfxGetFramebuffer(GFX_TOP, GFX_LEFT, NULL, NULL); + hidScanInput(); + + if(hidKeysDown() & KEY_A) + { + audiobuf_pos = 0; + + CSND_setchannel_playbackstate(0x8, 0);//Stop audio playback. + CSND_sharedmemtype0_cmdupdatestate(0); + + MIC_SetRecording(1); + + memset(framebuf, 0x20, 0x46500); + } + + if((hidKeysHeld() & KEY_A) && audiobuf_pos < audiobuf_size) + { + audiobuf_pos+= MIC_ReadAudioData(&audiobuf[audiobuf_pos], audiobuf_size-audiobuf_pos, 1); + if(audiobuf_pos > audiobuf_size)audiobuf_pos = audiobuf_size; + + memset(framebuf, 0x60, 0x46500); + } + + if(hidKeysUp() & KEY_A) + { + MIC_SetRecording(0); + GSPGPU_FlushDataCache(NULL, audiobuf, audiobuf_pos); + CSND_playsound(0x8, CSND_LOOP_DISABLE, CSND_ENCODING_PCM16, 16000, (u32*)audiobuf, NULL, audiobuf_pos, 2, 0); + + memset(framebuf, 0xe0, 0x46500); + + gfxFlushBuffers(); + gfxSwapBuffers(); + + framebuf = gfxGetFramebuffer(GFX_TOP, GFX_LEFT, NULL, NULL); + memset(framebuf, 0xe0, 0x46500); + } + + gfxFlushBuffers(); + gfxSwapBuffers(); + } + else if(status == APP_SUSPENDING) + { + aptReturnToMenu(); + } + else if(status == APP_SLEEPMODE) + { + aptWaitStatusEvent(); + } + gspWaitForVBlank(); + } + + MIC_Shutdown(); + + CSND_shutdown(); + + hidExit(); + gfxExit(); + aptExit(); + srvExit(); + return 0; +} + diff --git a/libctru/include/3ds.h b/libctru/include/3ds.h index f2a9367..1f17a92 100644 --- a/libctru/include/3ds.h +++ b/libctru/include/3ds.h @@ -23,6 +23,7 @@ extern "C" { #include <3ds/services/ir.h> #include <3ds/services/ptm.h> #include <3ds/services/soc.h> +#include <3ds/services/mic.h> #include <3ds/gpu/gx.h> #include <3ds/gpu/gpu.h> diff --git a/libctru/include/3ds/services/mic.h b/libctru/include/3ds/services/mic.h new file mode 100644 index 0000000..e0c0cae --- /dev/null +++ b/libctru/include/3ds/services/mic.h @@ -0,0 +1,20 @@ +#pragma once + +//See also: http://3dbrew.org/wiki/MIC_Services + +Result MIC_Initialize(u32 *sharedmem, u32 sharedmem_size, u8 control, u8 recording, u8 unk0, u8 unk1, u8 unk2);//sharedmem_size = audiodata size + 4, aligned to 0x1000-bytes. The sharedmem ptr must be 0x1000-bytes aligned. The offical 3ds-sound app uses the following values for unk0-unk2: 3, 1, and 1. +Result MIC_Shutdown(); +u32 MIC_GetSharedMemOffsetValue(); +u32 MIC_ReadAudioData(u8 *outbuf, u32 readsize, u32 waitforevent);//Reads MIC audio data. When waitforevent is non-zero, this clears the event, then waits for MIC-module to signal it again when audio data is written to shared-mem. The return value is the actual byte-size of the read data. + +Result MIC_MapSharedMem(Handle handle, u32 size); +Result MIC_UnmapSharedMem(); +Result MIC_cmd3_Initialize(u8 unk0, u8 unk1, u32 sharedmem_baseoffset, u32 sharedmem_endoffset, u8 unk2); +Result MIC_cmd5(); +Result MIC_GetCNTBit15(u8 *out); +Result MIC_GetEventHandle(Handle *handle); +Result MIC_SetControl(u8 value);//See here: http://3dbrew.org/wiki/MIC_Services +Result MIC_GetControl(u8 *value); +Result MIC_SetRecording(u8 value); +Result MIC_IsRecoding(u8 *value); + diff --git a/libctru/source/services/mic.c b/libctru/source/services/mic.c new file mode 100644 index 0000000..395792b --- /dev/null +++ b/libctru/source/services/mic.c @@ -0,0 +1,270 @@ +#include +#include +#include <3ds.h> + +//See also: http://3dbrew.org/wiki/MIC_Services + +Handle MIC_handle; + +static u8 *MIC_sharedmem; +static u32 MIC_sharedmem_size; +static u32 *MIC_sharedmem_offsetfield, MIC_sharedmem_offsetfield_location; +static Handle MIC_sharedmem_handle; +static Handle MIC_event; + +static u32 MIC_prev_endpos, MIC_cur_endpos; + +Result MIC_Initialize(u32 *sharedmem, u32 sharedmem_size, u8 control, u8 recording, u8 unk0, u8 unk1, u8 unk2) +{ + Result ret=0; + + MIC_sharedmem = (u8*)sharedmem; + MIC_sharedmem_size = sharedmem_size; + MIC_sharedmem_offsetfield_location = MIC_sharedmem_size - 4; + MIC_sharedmem_offsetfield = (u32*)&MIC_sharedmem[MIC_sharedmem_offsetfield_location]; + MIC_event = 0; + MIC_prev_endpos = 0; + MIC_cur_endpos = 0; + + ret = srvGetServiceHandle(&MIC_handle, "mic:u"); + if(ret!=0)return ret; + + ret = svcCreateMemoryBlock(&MIC_sharedmem_handle, (u32)MIC_sharedmem, MIC_sharedmem_size, 3, 3); + if(ret!=0)return ret; + + ret = MIC_SetControl(control); + if(ret!=0)return ret; + + ret = MIC_MapSharedMem(MIC_sharedmem_handle, sharedmem_size); + if(ret!=0)return ret; + + ret = MIC_SetRecording(recording); + if(ret!=0)return ret; + + ret = MIC_cmd3_Initialize(unk0, unk1, 0, MIC_sharedmem_size-4, unk2); + if(ret!=0)return ret; + + ret = MIC_GetEventHandle(&MIC_event); + if(ret!=0)return ret; + svcClearEvent(MIC_event); + + return 0; +} + +Result MIC_Shutdown() +{ + Result ret=0; + + MIC_cmd5(); + MIC_SetRecording(0); + + ret = MIC_UnmapSharedMem(); + if(ret!=0)return ret; + + MIC_cmd5(); + + ret = svcUnmapMemoryBlock(MIC_sharedmem_handle, (u32)MIC_sharedmem); + if(ret!=0)return ret; + + ret = svcCloseHandle(MIC_sharedmem_handle); + if(ret!=0)return ret; + + ret = svcCloseHandle(MIC_event); + if(ret!=0)return ret; + + ret = svcCloseHandle(MIC_handle); + if(ret!=0)return ret; + + MIC_sharedmem_offsetfield = NULL; + MIC_sharedmem = NULL; + MIC_sharedmem_size = 0; + MIC_handle = 0; + MIC_event = 0; + + return 0; +} + +u32 MIC_GetSharedMemOffsetValue() +{ + u32 pos = 0; + + if(MIC_sharedmem_offsetfield==NULL)return pos; + pos = *MIC_sharedmem_offsetfield; + if(pos > MIC_sharedmem_offsetfield_location)pos = MIC_sharedmem_offsetfield_location; + + return pos; +} + +u32 MIC_ReadAudioData(u8 *outbuf, u32 readsize, u32 waitforevent) +{ + u32 pos = 0, bufpos = 0; + + if(waitforevent) + { + svcClearEvent(MIC_event); + svcWaitSynchronization(MIC_event, U64_MAX); + } + + MIC_prev_endpos = MIC_cur_endpos; + MIC_cur_endpos = MIC_GetSharedMemOffsetValue(); + pos = MIC_prev_endpos; + + while(pos != MIC_cur_endpos) + { + if(pos >= MIC_sharedmem_offsetfield_location)pos = 0; + if(bufpos>=readsize)break; + + outbuf[bufpos] = MIC_sharedmem[pos]; + bufpos++; + pos++; + } + + return bufpos; +} + +Result MIC_MapSharedMem(Handle handle, u32 size) +{ + Result ret=0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = 0x00010042; + cmdbuf[1] = size; + cmdbuf[2] = 0; + cmdbuf[3] = handle; + + if((ret = svcSendSyncRequest(MIC_handle))!=0)return ret; + + return (Result)cmdbuf[1]; +} + +Result MIC_UnmapSharedMem() +{ + Result ret=0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = 0x00020000; + + if((ret = svcSendSyncRequest(MIC_handle))!=0)return ret; + + return (Result)cmdbuf[1]; +} + +Result MIC_cmd3_Initialize(u8 unk0, u8 unk1, u32 sharedmem_baseoffset, u32 sharedmem_endoffset, u8 unk2) +{ + Result ret=0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = 0x00030140; + cmdbuf[1] = unk0; + cmdbuf[2] = unk1; + cmdbuf[3] = sharedmem_baseoffset; + cmdbuf[4] = sharedmem_endoffset; + cmdbuf[5] = unk2; + + if((ret = svcSendSyncRequest(MIC_handle))!=0)return ret; + + return (Result)cmdbuf[1]; +} + +Result MIC_cmd5() +{ + Result ret=0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = 0x00050000; + + if((ret = svcSendSyncRequest(MIC_handle))!=0)return ret; + + return (Result)cmdbuf[1]; +} + +Result MIC_GetCNTBit15(u8 *out) +{ + Result ret=0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = 0x00060000; + + if((ret = svcSendSyncRequest(MIC_handle))!=0)return ret; + + if(out)*out = cmdbuf[2]; + + return (Result)cmdbuf[1]; +} + +Result MIC_GetEventHandle(Handle *handle) +{ + Result ret=0; + u32 *cmdbuf = getThreadCommandBuffer(); + + if(MIC_event) + { + *handle = MIC_event; + return 0; + } + + cmdbuf[0] = 0x00070000; + + if((ret = svcSendSyncRequest(MIC_handle))!=0)return ret; + + if(handle)*handle = cmdbuf[2]; + + return (Result)cmdbuf[1]; +} + +Result MIC_SetControl(u8 value) +{ + Result ret=0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = 0x00080040; + cmdbuf[1] = value; + + if((ret = svcSendSyncRequest(MIC_handle))!=0)return ret; + + return (Result)cmdbuf[1]; +} + +Result MIC_GetControl(u8 *value) +{ + Result ret=0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = 0x00090000; + + if((ret = svcSendSyncRequest(MIC_handle))!=0)return ret; + + if(value)*value = cmdbuf[2]; + + return (Result)cmdbuf[1]; +} + +Result MIC_SetRecording(u8 value) +{ + Result ret=0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = 0x000A0040; + cmdbuf[1] = value; + + if((ret = svcSendSyncRequest(MIC_handle))!=0)return ret; + + if(value==1)MIC_cur_endpos = MIC_GetSharedMemOffsetValue(); + + return (Result)cmdbuf[1]; +} + +Result MIC_IsRecoding(u8 *value) +{ + Result ret=0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = 0x000B0000; + + if((ret = svcSendSyncRequest(MIC_handle))!=0)return ret; + + if(value)*value = cmdbuf[2]; + + return (Result)cmdbuf[1]; +} +