Androidフレームワークオーディオサブシステム(05)AudioTrackの使用例

記事のマスターリンクのこのシリーズ:テーマ別のサブディレクトリのAndroid Frameworkクラスオーディオ・サブシステム


この章の要点の概要と説明:

この章では、onに焦点を当てます。上記のマインドマップの左上にあるAudioTrackの部分で十分です。主に2つのテスト手順(C ++レイヤーのshared_mem_testとJavaレイヤーのMediaAudioTrackTest.java)を分析し、JavaレイヤーAudioTrackからC ++レイヤーAudioTrackへの呼び出しプロセス(コンストラクタープロセスと書き込みプロセス)を分析します。


ここでは、C ++レイヤーのshared_mem_testとJavaレイヤーのMediaAudioTrackTest.javaという2つのテストプログラムを使用しています。これら2つのテストプログラムのディレクトリは次のとおりです。

  • frameworks / base / media / tests / audiotests / shared_mem_test.cpp
  • frameworks / base / media / tests / mediaframeworktest / src / com / android / mediaframeworktest / functional / audio / MediaAudioTrackTest.java

1 shared_mem_testテストプログラム分析

shared_mem_testテストプログラムの入り口の主な機能は次のとおりです。

int main(int argc, char *argv[]) {
    return android::main();
}

分析を続けます:android :: main()、コードは次のように実装されます:

int main() {
    ProcessState::self()->startThreadPool();
    AudioTrackTest *test;

    test = new AudioTrackTest();
    test->Execute();
    delete test;

    return 0;
}

ここでは、主に2つの重要なポイントを分析します。AudioTrackTestコンストラクターとそのExecuteメソッドです。

@ 1 AudioTrackTestコンストラクタ

AudioTrackTestコンストラクタコードは次のように実装されます。

AudioTrackTest::AudioTrackTest(void) {
    InitSine();         // init sine table
}

InitSineの分析を続けます。コードの実装は次のとおりです。

void AudioTrackTest::InitSine(void) {
    double phi = 0;
    double dPhi = 2 * M_PI / SIN_SZ;
    for(int i0 = 0; i0<SIN_SZ; i0++) {
        long d0;

        d0 = 32768. * sin(phi);
        phi += dPhi;
        if(d0 >= 32767) d0 = 32767;
        if(d0 <= -32768) d0 = -32768;
        sin1024[i0] = (short)d0;
    }
}

これは主に、次のステップに備えてデータと波形を作成するためです。

@ 2 AudioTrackTestのExecuteメソッド

AudioTrackTestのExecuteメソッドは次のように実装されます。

void AudioTrackTest::Execute(void) {
    if (Test01() == 0) {
        ALOGD("01 passed\n");
    } else {
        ALOGD("01 failed\n");
    }
}

Test01の分析を続けます。コードの実装は次のとおりです。

int AudioTrackTest::Test01() {

    sp<MemoryDealer> heap;
    sp<IMemory> iMem;
    uint8_t* p;

    short smpBuf[BUF_SZ];
    long rate = 44100;
    unsigned long phi;
    unsigned long dPhi;
    long amplitude;
    long freq = 1237;
    float f0;

    f0 = pow(2., 32.) * freq / (float)rate;
    dPhi = (unsigned long)f0;
    amplitude = 1000;
    phi = 0;
    Generate(smpBuf, BUF_SZ, amplitude, phi, dPhi);  // fill buffer

    for (int i = 0; i < 1024; i++) {
        heap = new MemoryDealer(1024*1024, "AudioTrack Heap Base");

        iMem = heap->allocate(BUF_SZ*sizeof(short));

        p = static_cast<uint8_t*>(iMem->pointer());
        memcpy(p, smpBuf, BUF_SZ*sizeof(short));
        //关键点1:创建一个AudioTrack对象,并传递数据
        sp<AudioTrack> track = new AudioTrack(AUDIO_STREAM_MUSIC,// stream type
               rate,
               AUDIO_FORMAT_PCM_16_BIT,// word length, PCM
               AUDIO_CHANNEL_OUT_MONO,
               iMem);

        status_t status = track->initCheck();
        if(status != NO_ERROR) {
            track.clear();
            ALOGD("Failed for initCheck()");
            return -1;
        }

        //关键点2:开始播放
        track->start();
        usleep(20000);

        ALOGD("stop");
        track->stop();
        iMem.clear();
        heap.clear();
        usleep(20000);
    }
    return 0;
}

上記の分析により、ネイティブレイヤーのAudioTrackの使用プロセスは次のとおりであることがわかります。

  1. パラメータを作成し、AudioTrackに渡します。ここではiMemにデータ付きのバッファが含まれていることに注意してください
  2. スタート操作を行って再生を開始

2 MediaAudioTrackTestテストプログラムの分析

MediaAudioTrackTestには多くのテストケースがあり、そのうちの1つをここで分析します。コードの実装は次のとおりです。

public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {    
    private String TAG = "MediaAudioTrackTest";
    //...
    //Test case 1: setPlaybackHeadPosition() on playing track
    @LargeTest
    public void testSetPlaybackHeadPositionPlaying() throws Exception {
        // constants for test
        final String TEST_NAME = "testSetPlaybackHeadPositionPlaying";
        final int TEST_SR = 22050;
        final int TEST_CONF = AudioFormat.CHANNEL_OUT_MONO;
        final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
        final int TEST_MODE = AudioTrack.MODE_STREAM;
        final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
        
        //-------- initialization --------------
        int minBuffSize = AudioTrack.getMinBufferSize(TEST_SR, TEST_CONF, TEST_FORMAT);
        //关键点1:构建并传递参数给AudioTrack对象
        AudioTrack track = new AudioTrack(TEST_STREAM_TYPE, TEST_SR, TEST_CONF, TEST_FORMAT, 
                2*minBuffSize, TEST_MODE);
        byte data[] = new byte[minBuffSize];
        //--------    test        --------------
        assumeTrue(TEST_NAME, track.getState() == AudioTrack.STATE_INITIALIZED);
        track.write(data, 0, data.length);
        track.write(data, 0, data.length);
        //关键点2:播放
        track.play();
        assertTrue(TEST_NAME,
                track.setPlaybackHeadPosition(10) == AudioTrack.ERROR_INVALID_OPERATION);
        //-------- tear down      --------------
        track.release();
    }
    //...
}

上記の分析により、JavaレイヤーでのAudioTrackの使用プロセスは次のとおりであることがわかります。

  1. パラメータを作成し、AudioTrackに渡します。
  2. wrtieを実行してデータを書き込むことは、shared_mem_testのiMemと同等です。
  3. スタート操作を行うと、再生が始まります。

次に、JavaレイヤーからC ++レイヤーのオーディオトラックへのAudioTrackの呼び出しプロセスを分析します。1つはコンストラクターで、もう1つは書き込み関数です。


3 JavaレイヤーからC ++レイヤーのAudioTrackへのAudioTrackの分析

3.1 JavaレイヤーAudioTrackからC ++レイヤーAudioTrackへのコンストラクター分析

同時に、JavaレイヤーにAudioTrackオブジェクトを作成すると、最終的にネイティブレイヤーにAudioTrackオブジェクトが作成されます。JavaレイヤーでAudioTrackを分析しますコードの実装は次のとおりです。

public AudioTrack(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,
        int mode, int sessionId)
                throws IllegalArgumentException {
	//...
    // native initialization
    int initResult = native_setup(new WeakReference<AudioTrack>(this), mAttributes,
            mSampleRate, mChannels, mAudioFormat,
            mNativeBufferSizeInBytes, mDataLoadMode, session);
    //...
    mSessionId = session[0];
    if (mDataLoadMode == MODE_STATIC) {
        mState = STATE_NO_STATIC_DATA;
    } else {
        mState = STATE_INITIALIZED;
    }
}

JNIのマッピング関係によると:

{"native_setup",     "(Ljava/lang/Object;Ljava/lang/Object;IIIII[I)I",
                                         (void *)android_media_AudioTrack_setup},

android_media_AudioTrack_setupの実装の分析を続けます。コードは次のとおりです。

static jint
android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this,
        jobject jaa,
        jint sampleRateInHertz, jint javaChannelMask,
        jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession) {
	//...
    //关键点1:创建native AudioTrack对象
    sp<AudioTrack> lpTrack = new AudioTrack();
	//...
    switch (memoryMode) {//这里开始针对 两种模式MODE_STREAM 和 MODE_STATIC进行不同参数的设置
    case MODE_STREAM:
        //关键点2.1:set方法,设置参数
        status = lpTrack->set(
                AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument)
                sampleRateInHertz,
                format,// word length, PCM
                nativeChannelMask,
                frameCount,
                AUDIO_OUTPUT_FLAG_NONE,
                audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)
                0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
                0,// shared mem
                true,// thread can call Java
                sessionId,// audio session ID
                AudioTrack::TRANSFER_SYNC,
                NULL,                         // default offloadInfo
                -1, -1,                       // default uid, pid values
                paa);
        break;
    case MODE_STATIC:
        //应用端申请共享内存
        if (!lpJniStorage->allocSharedMem(buffSizeInBytes)) {
            ALOGE("Error creating AudioTrack in static mode: error creating mem heap base");
            goto native_init_failure;
        }
        //关键点2.2:set方法,设置参数
        status = lpTrack->set(
                AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument)
                sampleRateInHertz,
                format,// word length, PCM
                nativeChannelMask,
                frameCount,
                AUDIO_OUTPUT_FLAG_NONE,
                audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user));
                0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
                lpJniStorage->mMemBase,// shared mem
                true,// thread can call Java
                sessionId,// audio session ID
                AudioTrack::TRANSFER_SHARED,
                NULL,                         // default offloadInfo
                -1, -1,                       // default uid, pid values
                paa);
        break;
	//...
    default:
        ALOGE("Unknown mode %d", memoryMode);
        goto native_init_failure;
    }
	//...
    return (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
}

ここでは2つの主要な操作が実行されます。1つはC ++レイヤーのAudioTrackオブジェクトを作成することであり、もう1つはそのsetメソッドを実行することです。C++レイヤーのAudioTrackオブジェクトのコンストラクターコードは次のとおりです。

AudioTrack::AudioTrack()//无参对象,后再调用set方法
    : mStatus(NO_INIT),
      mIsTimed(false),
      mPreviousPriority(ANDROID_PRIORITY_NORMAL),
      mPreviousSchedulingGroup(SP_DEFAULT),
      mPausedPosition(0)
{
    mAttributes.content_type = AUDIO_CONTENT_TYPE_UNKNOWN;
    mAttributes.usage = AUDIO_USAGE_UNKNOWN;
    mAttributes.flags = 0x0;
    strcpy(mAttributes.tags, "");
}

AudioTrack::AudioTrack(//有参对象,不需再调用set方法
        audio_stream_type_t streamType,
        uint32_t sampleRate,
        audio_format_t format,
        audio_channel_mask_t channelMask,
        size_t frameCount,
        audio_output_flags_t flags,
        callback_t cbf,
        void* user,
        uint32_t notificationFrames,
        int sessionId,
        transfer_type transferType,
        const audio_offload_info_t *offloadInfo,
        int uid,
        pid_t pid,
        const audio_attributes_t* pAttributes)
    : mStatus(NO_INIT),
      mIsTimed(false),
      mPreviousPriority(ANDROID_PRIORITY_NORMAL),
      mPreviousSchedulingGroup(SP_DEFAULT),
      mPausedPosition(0)
{
    mStatus = set(streamType, sampleRate, format, channelMask,
            frameCount, flags, cbf, user, notificationFrames,
            0 /*sharedBuffer*/, false /*threadCanCallJava*/, sessionId, transferType,
            offloadInfo, uid, pid, pAttributes);
}

AudioTrackの使用には2つの状況があることがわかります。

  • パラメータコンストラクタはありません。後でパラメータを設定するためにsetメソッドを実行する必要があります。
  • パラメータを設定するためにsetメソッドを直接呼び出すパラメータコンストラクタがあります。

要約すると、サウンドを再生するときにAudioTrackオブジェクトを作成する必要があります。JavaAudioTrackオブジェクトを使用すると、C ++ AudioTrackオブジェクトが作成されます。そのため、分析のコアはC ++ AudioTrackクラスであり、重要な関数であるsetが含まれています。同時に、AudioTrackはサウンドの属性のみを判別できますが、どのデバイスからサウンドが再生されるかは判別できません。set関数については、第6章で詳しく説明しています。

3.2 C ++レイヤーAudioTrackへの書き込み関数のJavaレイヤーAudioTrack

ここには、次のような多くの書き込み関数があります。

public int write(byte[] audioData, int offsetInBytes, int sizeInBytes) {
    //...
    int ret = native_write_byte(audioData, offsetInBytes, sizeInBytes, mAudioFormat,
            true /*isBlocking*/);
    //...
    return ret;
}

public int write(short[] audioData, int offsetInShorts, int sizeInShorts) {
    //...
    int ret = native_write_short(audioData, offsetInShorts, sizeInShorts, mAudioFormat);
    //...
    return ret;
}

public int write(float[] audioData, int offsetInFloats, int sizeInFloats,
        @WriteMode int writeMode) {
    //...
    int ret = native_write_float(audioData, offsetInFloats, sizeInFloats, mAudioFormat,
            writeMode == WRITE_BLOCKING);
    //...
    return ret;
}

public int write(ByteBuffer audioData, int sizeInBytes,
        @WriteMode int writeMode) {
    int ret = 0;
    if (audioData.isDirect()) {
        ret = native_write_native_bytes(audioData,
                audioData.position(), sizeInBytes, mAudioFormat,
                writeMode == WRITE_BLOCKING);
    } else {
        ret = native_write_byte(NioUtils.unsafeArray(audioData),
                NioUtils.unsafeArrayOffset(audioData) + audioData.position(),
                sizeInBytes, mAudioFormat,
                writeMode == WRITE_BLOCKING);
    }
    return ret;
}

これがネイティブレイヤーの呼び出しです。引き続きフォローアップしてください。コードは次のとおりです。

static jint android_media_AudioTrack_write_byte(JNIEnv *env,  jobject thiz,
                                                  jbyteArray javaAudioData,
                                                  jint offsetInBytes, jint sizeInBytes,
                                                  jint javaAudioFormat,
                                                  jboolean isWriteBlocking) {
    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
    //...
    jint written = writeToTrack(lpTrack, javaAudioFormat, cAudioData, offsetInBytes, sizeInBytes,
            isWriteBlocking == JNI_TRUE /* blocking */);
    //...
    return written;
}

static jint android_media_AudioTrack_write_native_bytes(JNIEnv *env,  jobject thiz,
        jbyteArray javaBytes, jint byteOffset, jint sizeInBytes,
        jint javaAudioFormat, jboolean isWriteBlocking) {
    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
    //...
    jint written = writeToTrack(lpTrack, javaAudioFormat, bytes.get(), byteOffset,
            sizeInBytes, isWriteBlocking == JNI_TRUE /* blocking */);
    return written;
}

static jint android_media_AudioTrack_write_short(JNIEnv *env,  jobject thiz,
                                                  jshortArray javaAudioData,
                                                  jint offsetInShorts, jint sizeInShorts,
                                                  jint javaAudioFormat) {
    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
    //...
    jint written = writeToTrack(lpTrack, javaAudioFormat, (jbyte *)cAudioData,
                                offsetInShorts * sizeof(short), sizeInShorts * sizeof(short),
            true /*blocking write, legacy behavior*/);
    //...
    return written;
}

static jint android_media_AudioTrack_write_float(JNIEnv *env,  jobject thiz,
                                                  jfloatArray javaAudioData,
                                                  jint offsetInFloats, jint sizeInFloats,
                                                  jint javaAudioFormat,
                                                  jboolean isWriteBlocking) {

    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
    //...
    jint written = writeToTrack(lpTrack, javaAudioFormat, (jbyte *)cAudioData,
                                offsetInFloats * sizeof(float), sizeInFloats * sizeof(float),
                                isWriteBlocking == JNI_TRUE /* blocking */);
    //...
    return written;
}

ここでネイティブ関連関数が最後にwriteToTrack関数を呼び出します。コードの実装は次のとおりです。

jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, const jbyte* data,
                  jint offsetInBytes, jint sizeInBytes, bool blocking = true) {
    ssize_t written = 0;
    //playbackthread提供共享内存,调用C++层track的write函数
    if (track->sharedBuffer() == 0) {
        written = track->write(data + offsetInBytes, sizeInBytes, blocking);
        if (written == (ssize_t) WOULD_BLOCK) {
            written = 0;
        }
    } else {//应用端 提供共享内存,直接执行memcpy
        const audio_format_t format = audioFormatToNative(audioFormat);
        switch (format) {

        default:
        case AUDIO_FORMAT_PCM_FLOAT:
        case AUDIO_FORMAT_PCM_16_BIT: {
            if ((size_t)sizeInBytes > track->sharedBuffer()->size()) {
                sizeInBytes = track->sharedBuffer()->size();
            }
            //这里将data数据拷贝给 共享内存
            memcpy(track->sharedBuffer()->pointer(), data + offsetInBytes, sizeInBytes);
            written = sizeInBytes;
            } break;

        case AUDIO_FORMAT_PCM_8_BIT: {
            //功能同上,只是8位需要中间的数据转换环节
            if (((size_t)sizeInBytes)*2 > track->sharedBuffer()->size()) {
                sizeInBytes = track->sharedBuffer()->size() / 2;
            }
            int count = sizeInBytes;
            int16_t *dst = (int16_t *)track->sharedBuffer()->pointer();
            const uint8_t *src = (const uint8_t *)(data + offsetInBytes);
            memcpy_to_i16_from_u8(dst, src, count);
            written = sizeInBytes;
            } break;
        }
    }
    return written;
}

次に、C ++レイヤートラックのいくつかの基本操作を呼び出しますこのコードは、次のように簡単に解釈されます。

  1. track-> sharedBuffer()== 0、つまり共有メモリがplaybackthreadによって提供される場合、C ++レイヤートラックの書き込みメソッドが実行されます。
  2. track-> sharedBuffer()!= 0の場合、つまり、共有メモリはAPP側によって提供され、memcpy操作を直接実行して、track-> sharedBuffer()に値を割り当てます。

第7章では、トラック書き込み方法を詳しく紹介ます

 

 

 

 

元の記事289件を公開 賞賛された47件 30,000回以上の閲覧

おすすめ

転載: blog.csdn.net/vviccc/article/details/105286614
おすすめ