æè¿ãç§ã¯Androidçšã®ã¢ããªã±ãŒã·ã§ã³ãç¹ã«ã²ãŒã ã®éçºãè¡ã£ãŠããŸãã ãã®ããããããããžã§ã¯ãã§ã¯Android ndkã䜿çšããå¿ èŠããããŸããã ååãšããŠããã€ãã£ãã§ã®äœæ¥ã®ãã¹ãŠã®å°é£ãšãã¥ã¢ã³ã¹ã1ã€ã®èšäºã§æ€èšããããšã¯äžå¯èœã§ããããããã®èšäºã§ã¯ndkã®ç°¡åãªçŽ¹ä»ãæžãããšã«ããŸããã
ãããŠããã®èšäºãåå¿è ã ãã§ãªãèå³æ·±ããã®ã«ããããã«ãOpenALãšWAVãOGG圢åŒã§ã®äœæ¥æ¹æ³ã瀺ããŸãã
ã¯ããã«
ç§ã«ãšã£ãŠã¯ãç°å¢ã®ã»ããã¢ããã«ã€ããŠå€ããæžã䟡å€ã¯ãããŸããã éçºããŠããç°å¢ïŒEclipseãIntelliJ IDEAãªã©ïŒã«é¢ä¿ãªããã»ããã¢ããã¯éåžžã«ç°¡åã§ãã
- Android NDKèªäœ ã
- WInã§ãã«ãããã«ã¯ã Cygwinãå¿ èŠã§ãã
- åãEclipseã®ãã©ã°ã€ã³ïŒ CDT ã
åœç¶ããã§ã«ADTãJDKãå¿ èŠã§ãã
ãªãNDKãå¿ èŠãªã®ã§ããïŒ
- OpenGL ESã䜿çšããŸãã NDKã䜿çšããŠãã人ã®ã»ãšãã©ã¯ãã²ãŒã ãæžãããã ãã«NDKã䜿çšããŠãããšæããŸãã
- Cocos2Dxãªã©ã®ã¯ãã¹ãã©ãããã©ãŒã ã²ãŒã ãšã³ãžã³ã䜿çšãã
- æãæãããªã±ãŒã¹ã¯ãC ++ã§ãã§ã«èšè¿°ãããã³ãŒãã䜿çšããå¿ èŠãããå Žåã§ãã äœå幎ãã®éããã¹ãŠã®ãã®ã®å€ããC ++ã§æžãããŠããŸããã ã¯ãããã¹ãŠãæžãæããããšã¯ã§ããŸãããåãopenCVãæžãæããŠãæå³ããããŸããããæ¢è£œã®ãœãŒã¹ãæ¥ç¶ããã ãã§ããŸããŸããã
JavaããC ++ã³ãŒããåŒã³åºã
äžè¬çã«ããã¹ãŠãéåžžã«ç°¡åã§ãäž»ãªæé ã¯æ¬¡ã®ãšããã§ãã
- C ++ã§ãã¡ã€ã«ãäœæãããšã¯ã¹ããŒãçšã®ã¡ãœãããå®çŸ©ããŸãã
- .mkãã¡ã€ã«ã®äœæã
- ã©ã€ãã©ãªçæã
- Javaã§ã©ã€ãã©ãªãæ¥ç¶ããŸãã
MakefileïŒ.mkïŒã«ã€ããŠãã€ã³ãããŸããã ããã§åœŒã«ã€ããŠèªãããšãã§ããŸã ã ãã®äžãhabrã«ã¯BubaVVã®.mkãã¡ã€ã«ã®äœæ¥ã«é¢ããè¯ãèšäºããããŸã ã
ã©ã€ãã©ãªã«ã€ããŠã¯ãndkããããã§èªãããšãã§ããŸã ã
C ++ãã¡ã€ã«ã®äœæ
JavaããåŒã³åºããšã¯ã¹ããŒãã®ã¡ãœãããå®çŸ©ããå¿ èŠããããŸãã äŸãšããŠãã¢ããªã±ãŒã·ã§ã³ã®èµ·åæã«ãOpenALã«é³æ¥œãããŒãããŸãã ãããè¡ãã«ã¯ãã¡ãœãããå®çŸ©ããŸãã
JNIEXPORT void JNICALL Java_ru_suvitruf_androidndk_tutorial4_MainActivity_loadAudio(JNIEnv *pEnv, jobject pThis, jobject pNativeCallListener, jobject assetManager);
ãããã¯ãã¹ãŠãã³ã§èšè¿°ããŸããã javahã®èªåçæã«ã¯äŸ¿å©ãªãŠãŒãã£ãªãã£ããããŸãã
次ã«ããããå®è£ ããå¿ èŠããããŸãããããã«ã€ããŠã¯åŸã§è©³ãã説æããŸãã
ååã«ã€ããŠå°ã
ã¡ãœããã®ååã«ã€ããŠå°ãè¿°ã¹ã䟡å€ããããŸãã Java_ã¯å¿
é ã®ãã¬ãã£ãã¯ã¹ã§ãã ru_suvitruf_androidndk_tutorial4ãru.suvitruf.androidndk.tutorial4ããã±ãŒãžããããããã¯ã©ã¹ãšã¡ãœããã®ååã¯JavaåŽã«ãããŸãã åé¢æ°ã«ã¯åŒæ°ãšããŠJNIEnv *ããããŸããããã¯ãJavaãæäœããããã®ã€ã³ã¿ãŒãã§ãŒã¹ã§ãããJavaã¡ãœãããåŒã³åºããŠJavaãªããžã§ã¯ããäœæã§ããŸãã 2çªç®ã®å¿
é ãã©ã¡ãŒã¿ãŒã¯ãã¡ãœãããéçãã©ããã«å¿ããŠã jobjectãŸãã¯jclassã§ãã ã¡ãœãããéçãªå ŽåãåŒæ°ã¯jclassåïŒã¡ãœããã宣èšãããŠãããªããžã§ã¯ãã®ã¯ã©ã¹ãžã®åç
§ïŒã§ãããéçã§ãªãå Žå-jobject-ã¡ãœãããåŒã³åºããããªããžã§ã¯ããžã®åç
§ã§ãã
Javaã®ã©ã€ãã©ãªæ¥ç¶
ã©ã€ãã©ãªãçæããããJavaã«æ¥ç¶ããå¿ èŠããããŸãã
static { System.loadLibrary("AndroidNDK"); }
ãããŠãC ++ã³ãŒããšåãååã®ã¡ãœãããå®çŸ©ããŸãã
// native public void loadAudio(NativeCalls nativeCallListener, AssetManager mng);
ãã®ãããªåŒã³åºãïŒ
loadAudio(activity, activity.getResources().getAssets());
C ++ããã®JavaåŒã³åºã
ããå°ãè€éã§ãããããã»ã©æããªãã å¿ èŠãªãã®ïŒ
- åŒã³åºãã¯ã©ã¹ã¡ãœããïŒJavaïŒãå®çŸ©ããŸãã
- ç®çã®ã¯ã©ã¹ã®èšè¿°åãååŸããŸãïŒC ++ã§ïŒã
- ã¡ãœããã®ã·ã°ããã£ã説æããŸãã
- ã¡ãœããèå¥åïŒãªã³ã¯ïŒãååŸããŸãã
- ç®çã®ãªããžã§ã¯ãã§ã¡ãœãããåŒã³åºããŸãã
ãã¡ãããã¯ã©ã¹ã®ã¡ãœãããç°¡åã«å®çŸ©ã§ããŸãããã€ã³ã¿ãŒãã§ã€ã¹ã䜿çšããããšããå§ãããŸãã ãã®åŸãå¥ã®ã¯ã©ã¹ã§äœæ¥ããå Žåããã€ãã£ãã³ãŒããå€æŽããå¿ èŠã¯ãããŸããã
äŸãšããŠããã£ã1ã€ã®ã¡ãœããã§ã€ã³ã¿ãŒãã§ãŒã¹ãäœæããŸãã
public interface NativeCalls { public void sendLog(String result); }
CalledFromWrongThreadExceptionãšé©åãªã€ã³ã¿ãŒãã§ã€ã¹ã®å®è£
ããããããã®æµãã åé¡ã¯ãå¥ã®ã¹ããªãŒã ããã®ãã¥ãŒã«åœ±é¿ãäžããããªãããšã§ãã ãããã£ãŠãã€ã³ã¿ãŒãã§ã€ã¹ã®å®è£
å
šäœã¯æ¬¡ã®ããã«ãªããŸãã
protected Handler handler = new Handler() { @Override public void handleMessage(Message msg) { showResult(msg.getData().getString("result")); } }; public void showResult(String result){ ((TextView) findViewById(R.id.log)). setText(((TextView) findViewById(R.id.log)).getText()+result+"\n"); } // @Override public void sendLog(String result){ Message msg = new Message(); Bundle data = new Bundle(); data.putString("result", result); msg.setData(data); handler.sendMessage(msg); }
ã¹ã¬ããã«åé¡ã¯ãªããããããŸããããç§ãã¡ã®å Žåãã¢ããªã±ãŒã·ã§ã³ãèµ·åãããšãã«ããªãœãŒã¹ãããŒãããããã®å¥ã®ã¹ã¬ãããäœæããã®ã§ã質åã¯ç§ãã¡ã«ãšã£ãŠéèŠã§ãã
次ã®ã¯ã©ã¹ã¯ããã€ãã£ãC ++ã³ãŒãã®Javaã€ã³ã¿ãŒãã§ã€ã¹ã«å¯Ÿå¿ããŸãã
NativeCallListener
class NativeCallListener { public: NativeCallListener(JNIEnv* pJniEnv, jobject pWrapperInstance); NativeCallListener() {} // // Java void sendLog(jobject log); // void destroy(); ~NativeCallListener(){ } void loadAudio(); //void play(); //void playOGG(); ALCdevice* device; ALCcontext* context; private: JNIEnv* getJniEnv(); // jmethodID sendLogID; // jobject mObjectRef; JavaVM* mJVM; ALuint soundWAV; ALuint soundOGG; void load(); void clean(); };
ããã§ãèšäºã®æåã®éšåã«ããããŒããã£ãloadAudioã¡ãœããã®å®è£ ã衚瀺ã§ããŸãã
JNIEXPORT void JNICALL Java_ru_suvitruf_androidndk_tutorial4_MainActivity_loadAudio(JNIEnv *pEnv, jobject pThis, jobject pNativeCallListener, jobject assetManager) { listener = NativeCallListener(pEnv, pNativeCallListener); mgr = AAssetManager_fromJava(pEnv, assetManager); listener.loadAudio(); }
ã¯ã©ã¹ã³ã³ã¹ãã©ã¯ã¿ãŒã§ãã¯ã©ã¹èšè¿°åãä¿åãããã®ã¡ãœãããžã®åç §ãååŸããŸãã
NativeCallListener::NativeCallListener(JNIEnv* pJniEnv, jobject pWrappedInstance) { pJniEnv->GetJavaVM(&mJVM); mObjectRef = pJniEnv->NewGlobalRef(pWrappedInstance); jclass cl = pJniEnv->GetObjectClass(pWrappedInstance); // , Java sendLogID = pJniEnv->GetMethodID(cl, "sendLog", "(Ljava/lang/String;)V"); }
ããã§ãJavaã¡ãœãããåŒã³åºãããšãã§ããŸãã
void NativeCallListener::sendLog(jobject log) { JNIEnv* jniEnv = getJniEnv(); jniEnv->CallIntMethod(mObjectRef, sendLogID, log); }
AAssetManager
以åã¯ãã¢ããªã±ãŒã·ã§ã³ãªãœãŒã¹ãæäœããããã«ãªãŒãã³ãœãŒã¹ã®libzipã©ã€ãã©ãªã䜿çšãããŠããŸããã
APIã®ããŒãžã§ã³2.3ã§ã¯ãAndroid ndkã«ã¯C ++ã³ãŒãããçŽæ¥ã¢ã»ãããã£ã¬ã¯ããªãæäœããããã®åªããã¯ã©ã¹ããããŸãã
ãã®æ¹æ³ã¯ãstdio.hã®ãã¡ã€ã«ãæäœããæ¹æ³ã«äŒŒãŠããŸãã fopenã®ä»£ããã«AAssetManager_openãfreadã®ä»£ããã«AAsset_readãfcloseã®ä»£ããã«AAsset_closeã
ç§ã¯åœŒã®ããã«å°ããªã©ãããŒãæžããŸããã äžè¬ã«äœæ¥ã¯éåžžã®FILEãšåããªã®ã§ãããã«ã³ãŒããæ¿å ¥ããŸããã
OpenALã䜿çšãã
ãã®èšäºã¯ãã§ã«éåžžã«å€§ããªèšäºã§ãããç§ã¯æãèå³æ·±ãéšåãå§ããŠããŸããã ãããèš±ããŠãã ãã...
æºåãã
ãŸããOpenALããã«ãããå¿ èŠããããŸãã ããã¯WAVã䜿çšããã®ã«ååã§ãããOGGã䜿çšããå¿ èŠããããŸãã OGGã«ã¯Tremorãã³ãŒããŒãå¿ èŠã§ãã
ãµãŠã³ãã«ã€ããŠã¯ãå¿ èŠãªã¡ãœããã䜿çšããŠã©ãããŒãäœæããŸããã ããã®ãã¹ãŠã®ã³ãŒãã¯æå³ããªããªãã®ã§ãæãèå³æ·±ããã®ãã€ãŸãããŠã³ããŒãã匷調ããŸãã
wavãã¡ã€ã«ãèªã
æåã«ãããããŒã®æ§é ã説æããå¿ èŠããããŸãã
BasicWAVEHeader
typedef struct { char riff[4];//'RIFF' unsigned int riffSize; char wave[4];//'WAVE' char fmt[4];//'fmt ' unsigned int fmtSize; unsigned short format; unsigned short channels; unsigned int samplesPerSec; unsigned int bytesPerSec; unsigned short blockAlign; unsigned short bitsPerSample; char data[4];//'data' unsigned int dataSize; }BasicWAVEHeader;
ä»èªãïŒ
void OALWav::load(AAssetManager *mgr, const char* filename){ this->filename = filename; this->data = 0; // this->data = this->readWAVFull(mgr, &header); // getFormat(); // OpenAL createBufferFromWave(data); source = 0; alGenSources(1, &source); alSourcei(source, AL_BUFFER, buffer); }
readWAVFull
char* OALWav::readWAVFull(AAssetManager *mgr, BasicWAVEHeader* header){ char* buffer = 0; AAssetFile f = AAssetFile(mgr, filename); if (f.null()) { LOGE("no file %s in readWAV",filename); return 0; } int res = f.read(header,sizeof(BasicWAVEHeader),1); if(res){ if (!( // . // , . // =/ memcmp("RIFF",header->riff,4) || memcmp("WAVE",header->wave,4) || memcmp("fmt ",header->fmt,4) || memcmp("data",header->data,4) )){ buffer = (char*)malloc(header->dataSize); if (buffer){ if(f.read(buffer,header->dataSize,1)){ f.close(); return buffer; } free(buffer); } } } f.close(); return 0; }
WAVã«ã€ããŠèšããªããã°ãªããªãããšããããŸãã æã ãPCäžã®ãã¡ã€ã«ã¯æ£åžžã«ãªãã¹ã³ããŠããããã«èŠããŸãããOpenALã§äœæ¥ããŠãããšãã«ãšã©ãŒãçºçããŸãã ããã¯ãç ŽæããããããŒã®çµæã§ãã éåžžã¯dataSize㧠ãããããŒïŒäŸãšããŠããŽïŒã«äœããã®çš®é¡ã®ãŽããæžã蟌ãå€ãã®ã³ã³ããŒã¿ãŒã«äŒããŸããã ãªãæ©èœããªãã®ã«ãPCã§åçããã®ã§ããïŒ
ãªãŒãã£ãªããŒã¿èªäœã¯ãããããŒãšdataSizeã®ãµã€ãºã®åŸã«ä¿åãããŸã ã ãã®ãã£ãŒã«ãã«åé¡ãããå Žåããšã©ãŒãçºçããŸãã ããªãã¯æ¬åœã«é¡ã®ãµã€ãºãèšç®ããããšãã§ããŸãã ããŒã¿ãµã€ãº=ãã¡ã€ã«ãµã€ãº-ããããŒãµã€ãº ã ãããã£ãŠããã¬ã€ã€ãŒã¯ããããŒããã§ã¯ãªããæžç®ã«ãã£ãŠããŒã¿ã®ãµã€ãºãååŸãããšæããŸãã
WAVã䜿çšããå Žåã圢åŒã¯å§çž®ãããŠããªãããããã¹ãŠãåçŽã«æããŸãã .Oggã䜿çšããå Žåãäºæ ã¯ããè€éã«ãªããŸãã
Oggãã¡ã€ã«ãèªã
WAVãšæ¯èŒããOggã®æ©èœã¯äœã§ããïŒ ããã¯å§çž®åœ¢åŒã§ãã ãã®ãããOpenALãããã¡ãŒãžã®ããŒã¿ã®æžã蟌ã¿æ¹æ³ã®åã«ãããŒã¿ããã³ãŒãããå¿ èŠããããŸãã
ãã£ããã¯ãããã©ã«ãã§FILEããã®Vorbisã¹ããªãŒã ãªã®ã§ãããŒã¿ãæäœããããã«ãã¹ãŠã®ã³ãŒã«ããã¯ã¡ãœããããªãŒããŒã©ã€ãããå¿ èŠããããŸãã
ã³ãŒã«ããã¯
static size_t read_func(void* ptr, size_t size, size_t nmemb, void* datasource) { unsigned int uiBytes = Min(suiSize - suiCurrPos, (unsigned int)nmemb * (unsigned int)size); memcpy(ptr, (unsigned char*)datasource + suiCurrPos, uiBytes); suiCurrPos += uiBytes; return uiBytes; } static int seek_func(void* datasource, ogg_int64_t offset, int whence) { if (whence == SEEK_SET) suiCurrPos = (unsigned int)offset; else if (whence == SEEK_CUR) suiCurrPos = suiCurrPos + (unsigned int)offset; else if (whence == SEEK_END) suiCurrPos = suiSize; return 0; } static int close_func(void* datasource) { return 0; } static long tell_func(void* datasource) { return (long)suiCurrPos; }
ä»ãããªãã¯èªãå¿ èŠããããŸãïŒ
Oggãèªã
void OALOgg::getInfo(unsigned int uiOggSize, char* pvOggBuffer){ // ov_callbacks callbacks; callbacks.read_func = &read_func; callbacks.seek_func = &seek_func; callbacks.close_func = &close_func; callbacks.tell_func = &tell_func; suiCurrPos = 0; suiSize = uiOggSize; int iRet = ov_open_callbacks(pvOggBuffer, &vf, NULL, 0, callbacks); // vi = ov_info(&vf, -1); uiPCMSamples = (unsigned int)ov_pcm_total(&vf, -1); } void * OALOgg::ConvertOggToPCM(unsigned int uiOggSize, char* pvOggBuffer) { if(suiSize == 0){ getInfo( uiOggSize, pvOggBuffer); current_section = 0; iRead = 0; uiCurrPos = 0; } void* pvPCMBuffer = malloc(uiPCMSamples * vi->channels * sizeof(short)); // do { iRead = ov_read(&vf, (char*)pvPCMBuffer + uiCurrPos, 4096, €t_section); uiCurrPos += (unsigned int)iRead; } while (iRead != 0); return pvPCMBuffer; } void OALOgg::load(AAssetManager *mgr, const char* filename){ this->filename = filename; char* buf = 0; AAssetFile f = AAssetFile(mgr, filename); if (f.null()) { LOGE("no file %s in readOgg",filename); return ; } buf = 0; buf = (char*)malloc(f.size()); if (buf){ if(f.read(buf,f.size(),1)){ } else { free(buf); f.close(); return; } } char * data = (char *)ConvertOggToPCM(f.size(),buf); f.close(); if (vi->channels == 1) format = AL_FORMAT_MONO16; else format = AL_FORMAT_STEREO16; alGenBuffers(1,&buffer); alBufferData(buffer,format,data,uiPCMSamples * vi->channels * sizeof(short),vi->rate); source = 0; alGenSources(1, &source); alSourcei(source, AL_BUFFER, buffer); }
ã¢ããªã±ãŒã·ã§ã³ãããŒããããšããC ++ã¡ãœããloadAudioãåŒã³åºããŸããããã¯ããµãŠã³ããããŒãããNativeCallListenerã®ããŒããåŒã³åºããŸãã
void NativeCallListener:: load(){ oalContext = new OALContext(); //sound = new OALOgg(); sound = new OALWav(); char * fileName = new char[64]; strcpy(fileName, "audio/industrial_suspense1.wav"); //strcpy(fileName, "audio/Katatonia - Deadhouse_(piano version).ogg"); sound->load(mgr,fileName); }
sound
OALSound
ãŸãã WAVãšOggãæäœããããã«ããããç¶æ¿ããã¯ã©ã¹ããããŸãã åºæ¬ã¯ã©ã¹ã®ã¡ãœãã
virtual void load(AAssetManager *mgr, const char* filename)= 0;
ãªãŒããŒã©ã€ãããããã®ããŒãå®è£ ãèšè¿°ããã ãã§ã
virtual void load(AAssetManager *mgr, const char* filename)= 0;
ããã«ããããµãŠã³ããšäœæ¥ãçµ±åã§ããŸãã
ãããã«
ç¹°ãè¿ãã«ãªããŸããããã®èšäºãéåžžã«èšå€§ã§ãã£ãããšããaã³ããŸãã æ瀺ãããå®è£ ã䜿çšãããšããã©ãããã©ãŒã ã«é¢ä¿ãªããµãŠã³ããæäœã§ããŸãã iOSããã³Androidçšã®ã²ãŒã ãšã³ãžã³ãäœæããŠããå ŽåãèããŠã¿ãŸãããã
ããã«ã¯ãã¥ã¢ã³ã¹ããããŸã-ãªãŒãã£ãªã¯å®å šã«ããŒããããŸãã ãããã£ãŠãé³ã«ã€ããŠã¯ããã®è§£æ±ºçã¯çŽ æŽãããã§ãããé³æ¥œã«ã€ããŠã¯ããã§ã¯ãããŸããã 解åãã.oggã®æãã©ãã ãã®ã¡ã¢ãªãæ¶è²»ãããæ³åããŠã¿ãŠãã ããã ãããã£ãŠã誰ãããã®ãœãªã¥ãŒã·ã§ã³ã«åºã¥ããŠããããã¡ãžã®å šè² è·ã§ã¯ãªããã¹ããªãŒãã³ã°ã§ãªãŒãã£ãªåçãæžã蟌ãã®ã¯çŽ æŽãããããšã§ãã
ãœãŒã¹ã³ãŒã
ãããžã§ã¯ãã¯Eclipseã§æžãããŠããŸãã ãœãŒã¹ã¯githubã§è¡šç€ºã§ããŸãã
PSæ¹å€ãšã¢ããã€ã¹ã楜ãã¿ã«ããŠããŸã
PPSã§ã¯ãããã¹ãã«ææ³çãªèª€ããèŠã€ãã£ãå Žåã¯ãpmã§æžãããšããå§ãããŸãã