| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| // Play an audio file using buffer queue |
| |
| #include <assert.h> |
| #include <math.h> |
| #include <pthread.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <time.h> |
| #include <unistd.h> |
| |
| #include <SLES/OpenSLES.h> |
| #include <SLES/OpenSLES_Android.h> |
| #include <system/audio.h> |
| #include <audio_utils/fifo.h> |
| #include <audio_utils/primitives.h> |
| #include <audio_utils/sndfile.h> |
| |
| #define max(a, b) ((a) > (b) ? (a) : (b)) |
| #define min(a, b) ((a) < (b) ? (a) : (b)) |
| |
| unsigned numBuffers = 2; |
| int framesPerBuffer = 512; |
| SNDFILE *sndfile; |
| SF_INFO sfinfo; |
| unsigned which; // which buffer to use next |
| SLboolean eof; // whether we have hit EOF on input yet |
| void *buffers; |
| SLuint32 byteOrder; // desired to use for PCM buffers |
| SLuint32 nativeByteOrder; // of platform |
| audio_format_t transferFormat = AUDIO_FORMAT_DEFAULT; |
| size_t sfframesize = 0; |
| |
| static audio_utils_fifo *fifo; |
| static audio_utils_fifo_reader *fifoReader; |
| static audio_utils_fifo_writer *fifoWriter; |
| static unsigned underruns = 0; |
| |
| static SLuint32 squeeze(void *buffer, SLuint32 nbytes) |
| { |
| if (byteOrder != nativeByteOrder) { |
| // FIXME does not work for non 16-bit |
| swab(buffer, buffer, nbytes); |
| } |
| if (transferFormat == AUDIO_FORMAT_PCM_8_BIT) { |
| memcpy_to_u8_from_i16((uint8_t *) buffer, (const int16_t *) buffer, |
| nbytes / sizeof(int16_t)); |
| nbytes /= 2; |
| } else if (transferFormat == AUDIO_FORMAT_PCM_24_BIT_PACKED) { |
| memcpy_to_p24_from_i32((uint8_t *) buffer, (const int32_t *) buffer, |
| nbytes / sizeof(int32_t)); |
| nbytes = nbytes * 3 / 4; |
| } |
| return nbytes; |
| } |
| |
| // This callback is called each time a buffer finishes playing |
| |
| static void callback(SLBufferQueueItf bufq, void *param) |
| { |
| assert(NULL == param); |
| if (!eof) { |
| void *buffer = (char *)buffers + framesPerBuffer * sfframesize * which; |
| ssize_t count = fifoReader->read(buffer, framesPerBuffer); |
| // on underrun from pipe, substitute silence |
| if (0 >= count) { |
| memset(buffer, 0, framesPerBuffer * sfframesize); |
| count = framesPerBuffer; |
| ++underruns; |
| } |
| if (count > 0) { |
| SLuint32 nbytes = count * sfframesize; |
| nbytes = squeeze(buffer, nbytes); |
| SLresult result = (*bufq)->Enqueue(bufq, buffer, nbytes); |
| assert(SL_RESULT_SUCCESS == result); |
| if (++which >= numBuffers) |
| which = 0; |
| } |
| } |
| } |
| |
| // This thread reads from a (slow) filesystem with unpredictable latency and writes to pipe |
| |
| static void *file_reader_loop(void *arg __unused) |
| { |
| #define READ_FRAMES 256 |
| void *temp = malloc(READ_FRAMES * sfframesize); |
| sf_count_t total = 0; |
| sf_count_t count; |
| for (;;) { |
| switch (transferFormat) { |
| case AUDIO_FORMAT_PCM_FLOAT: |
| count = sf_readf_float(sndfile, (float *) temp, READ_FRAMES); |
| break; |
| case AUDIO_FORMAT_PCM_32_BIT: |
| case AUDIO_FORMAT_PCM_24_BIT_PACKED: |
| count = sf_readf_int(sndfile, (int *) temp, READ_FRAMES); |
| break; |
| case AUDIO_FORMAT_PCM_16_BIT: |
| case AUDIO_FORMAT_PCM_8_BIT: |
| count = sf_readf_short(sndfile, (short *) temp, READ_FRAMES); |
| break; |
| default: |
| count = 0; |
| break; |
| } |
| if (0 >= count) { |
| eof = SL_BOOLEAN_TRUE; |
| break; |
| } |
| const unsigned char *ptr = (unsigned char *) temp; |
| while (count > 0) { |
| ssize_t actual = fifoWriter->write(ptr, (size_t) count); |
| if (actual < 0) { |
| break; |
| } |
| if ((sf_count_t) actual < count) { |
| usleep(10000); |
| } |
| ptr += actual * sfframesize; |
| count -= actual; |
| total += actual; |
| } |
| // simulate occasional filesystem latency |
| if ((total & 0xFF00) == 0xFF00) { |
| usleep(100000); |
| } |
| } |
| free(temp); |
| return NULL; |
| } |
| |
| // Main program |
| |
| int main(int argc, char **argv) |
| { |
| // Determine the native byte order (SL_BYTEORDER_NATIVE not available until 1.1) |
| union { |
| short s; |
| char c[2]; |
| } u; |
| u.s = 0x1234; |
| if (u.c[0] == 0x34) { |
| nativeByteOrder = SL_BYTEORDER_LITTLEENDIAN; |
| } else if (u.c[0] == 0x12) { |
| nativeByteOrder = SL_BYTEORDER_BIGENDIAN; |
| } else { |
| fprintf(stderr, "Unable to determine native byte order\n"); |
| return EXIT_FAILURE; |
| } |
| byteOrder = nativeByteOrder; |
| |
| SLboolean enableReverb = SL_BOOLEAN_FALSE; |
| SLboolean enablePlaybackRate = SL_BOOLEAN_FALSE; |
| SLpermille initialRate = 0; |
| SLpermille finalRate = 0; |
| SLpermille deltaRate = 1; |
| SLmillisecond deltaRateMs = 0; |
| |
| // process command-line options |
| int i; |
| for (i = 1; i < argc; ++i) { |
| char *arg = argv[i]; |
| if (arg[0] != '-') { |
| break; |
| } |
| if (!strcmp(arg, "-b")) { |
| byteOrder = SL_BYTEORDER_BIGENDIAN; |
| } else if (!strcmp(arg, "-l")) { |
| byteOrder = SL_BYTEORDER_LITTLEENDIAN; |
| } else if (!strcmp(arg, "-8")) { |
| transferFormat = AUDIO_FORMAT_PCM_8_BIT; |
| } else if (!strcmp(arg, "-16")) { |
| transferFormat = AUDIO_FORMAT_PCM_16_BIT; |
| } else if (!strcmp(arg, "-24")) { |
| transferFormat = AUDIO_FORMAT_PCM_24_BIT_PACKED; |
| } else if (!strcmp(arg, "-32")) { |
| transferFormat = AUDIO_FORMAT_PCM_32_BIT; |
| } else if (!strcmp(arg, "-32f")) { |
| transferFormat = AUDIO_FORMAT_PCM_FLOAT; |
| } else if (!strncmp(arg, "-f", 2)) { |
| framesPerBuffer = atoi(&arg[2]); |
| } else if (!strncmp(arg, "-n", 2)) { |
| numBuffers = atoi(&arg[2]); |
| } else if (!strncmp(arg, "-p", 2)) { |
| initialRate = atoi(&arg[2]); |
| enablePlaybackRate = SL_BOOLEAN_TRUE; |
| } else if (!strncmp(arg, "-P", 2)) { |
| finalRate = atoi(&arg[2]); |
| enablePlaybackRate = SL_BOOLEAN_TRUE; |
| } else if (!strncmp(arg, "-q", 2)) { |
| deltaRate = atoi(&arg[2]); |
| // deltaRate is a magnitude, so take absolute value |
| if (deltaRate < 0) { |
| deltaRate = -deltaRate; |
| } |
| enablePlaybackRate = SL_BOOLEAN_TRUE; |
| } else if (!strncmp(arg, "-Q", 2)) { |
| deltaRateMs = atoi(&arg[2]); |
| enablePlaybackRate = SL_BOOLEAN_TRUE; |
| } else if (!strcmp(arg, "-r")) { |
| enableReverb = SL_BOOLEAN_TRUE; |
| } else { |
| fprintf(stderr, "option %s ignored\n", arg); |
| } |
| } |
| |
| if (argc - i != 1) { |
| fprintf(stderr, "usage: [-b/l] [-8 | | -16 | -24 | -32 | -32f] [-f#] [-n#] [-p#] [-r]" |
| " %s filename\n", argv[0]); |
| fprintf(stderr, " -b force big-endian byte order (default is native byte order)\n"); |
| fprintf(stderr, " -l force little-endian byte order (default is native byte order)\n"); |
| fprintf(stderr, " -8 output 8-bits per sample (default is that of input file)\n"); |
| fprintf(stderr, " -16 output 16-bits per sample\n"); |
| fprintf(stderr, " -24 output 24-bits per sample\n"); |
| fprintf(stderr, " -32 output 32-bits per sample\n"); |
| fprintf(stderr, " -32f output float 32-bits per sample\n"); |
| fprintf(stderr, " -f# frames per buffer (default 512)\n"); |
| fprintf(stderr, " -n# number of buffers (default 2)\n"); |
| fprintf(stderr, " -p# initial playback rate in per mille (default 1000)\n"); |
| fprintf(stderr, " -P# final playback rate in per mille (default same as -p#)\n"); |
| fprintf(stderr, " -q# magnitude of playback rate changes in per mille (default 1)\n"); |
| fprintf(stderr, " -Q# period between playback rate changes in ms (default 50)\n"); |
| fprintf(stderr, " -r enable reverb (default disabled)\n"); |
| return EXIT_FAILURE; |
| } |
| |
| const char *filename = argv[i]; |
| //memset(&sfinfo, 0, sizeof(SF_INFO)); |
| sfinfo.format = 0; |
| sndfile = sf_open(filename, SFM_READ, &sfinfo); |
| if (NULL == sndfile) { |
| perror(filename); |
| return EXIT_FAILURE; |
| } |
| |
| // verify the file format |
| switch (sfinfo.channels) { |
| case 1: |
| case 2: |
| break; |
| default: |
| fprintf(stderr, "unsupported channel count %d\n", sfinfo.channels); |
| goto close_sndfile; |
| } |
| |
| if (sfinfo.samplerate < 8000 || sfinfo.samplerate > 192000) { |
| fprintf(stderr, "unsupported sample rate %d\n", sfinfo.samplerate); |
| goto close_sndfile; |
| } |
| |
| switch (sfinfo.format & SF_FORMAT_TYPEMASK) { |
| case SF_FORMAT_WAV: |
| break; |
| default: |
| fprintf(stderr, "unsupported format type 0x%x\n", sfinfo.format & SF_FORMAT_TYPEMASK); |
| goto close_sndfile; |
| } |
| |
| switch (sfinfo.format & SF_FORMAT_SUBMASK) { |
| case SF_FORMAT_FLOAT: |
| if (transferFormat == AUDIO_FORMAT_DEFAULT) { |
| transferFormat = AUDIO_FORMAT_PCM_FLOAT; |
| } |
| break; |
| case SF_FORMAT_PCM_32: |
| if (transferFormat == AUDIO_FORMAT_DEFAULT) { |
| transferFormat = AUDIO_FORMAT_PCM_32_BIT; |
| } |
| break; |
| case SF_FORMAT_PCM_16: |
| if (transferFormat == AUDIO_FORMAT_DEFAULT) { |
| transferFormat = AUDIO_FORMAT_PCM_16_BIT; |
| } |
| break; |
| case SF_FORMAT_PCM_U8: |
| if (transferFormat == AUDIO_FORMAT_DEFAULT) { |
| transferFormat = AUDIO_FORMAT_PCM_8_BIT; |
| } |
| break; |
| case SF_FORMAT_PCM_24: |
| if (transferFormat == AUDIO_FORMAT_DEFAULT) { |
| transferFormat = AUDIO_FORMAT_PCM_24_BIT_PACKED; |
| } |
| break; |
| default: |
| fprintf(stderr, "unsupported sub-format 0x%x\n", sfinfo.format & SF_FORMAT_SUBMASK); |
| goto close_sndfile; |
| } |
| |
| SLuint32 bitsPerSample; |
| switch (transferFormat) { |
| case AUDIO_FORMAT_PCM_FLOAT: |
| bitsPerSample = 32; |
| sfframesize = sfinfo.channels * sizeof(float); |
| break; |
| case AUDIO_FORMAT_PCM_32_BIT: |
| bitsPerSample = 32; |
| sfframesize = sfinfo.channels * sizeof(int); |
| break; |
| case AUDIO_FORMAT_PCM_24_BIT_PACKED: |
| bitsPerSample = 24; |
| sfframesize = sfinfo.channels * sizeof(int); // use int size |
| break; |
| case AUDIO_FORMAT_PCM_16_BIT: |
| bitsPerSample = 16; |
| sfframesize = sfinfo.channels * sizeof(short); |
| break; |
| case AUDIO_FORMAT_PCM_8_BIT: |
| bitsPerSample = 8; |
| sfframesize = sfinfo.channels * sizeof(short); // use short size |
| break; |
| default: |
| fprintf(stderr, "unsupported transfer format %#x\n", transferFormat); |
| goto close_sndfile; |
| } |
| |
| { |
| buffers = malloc(framesPerBuffer * sfframesize * numBuffers); |
| |
| // create engine |
| SLresult result; |
| SLObjectItf engineObject; |
| result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL); |
| assert(SL_RESULT_SUCCESS == result); |
| SLEngineItf engineEngine; |
| result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE); |
| assert(SL_RESULT_SUCCESS == result); |
| result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine); |
| assert(SL_RESULT_SUCCESS == result); |
| |
| // create output mix |
| SLObjectItf outputMixObject; |
| SLInterfaceID ids[1] = {SL_IID_ENVIRONMENTALREVERB}; |
| SLboolean req[1] = {SL_BOOLEAN_TRUE}; |
| result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, enableReverb ? 1 : 0, |
| ids, req); |
| assert(SL_RESULT_SUCCESS == result); |
| result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE); |
| assert(SL_RESULT_SUCCESS == result); |
| |
| // configure environmental reverb on output mix |
| SLEnvironmentalReverbItf mixEnvironmentalReverb = NULL; |
| if (enableReverb) { |
| result = (*outputMixObject)->GetInterface(outputMixObject, SL_IID_ENVIRONMENTALREVERB, |
| &mixEnvironmentalReverb); |
| assert(SL_RESULT_SUCCESS == result); |
| SLEnvironmentalReverbSettings settings = SL_I3DL2_ENVIRONMENT_PRESET_STONECORRIDOR; |
| result = (*mixEnvironmentalReverb)->SetEnvironmentalReverbProperties(mixEnvironmentalReverb, |
| &settings); |
| assert(SL_RESULT_SUCCESS == result); |
| } |
| |
| // configure audio source |
| SLDataLocator_BufferQueue loc_bufq; |
| loc_bufq.locatorType = SL_DATALOCATOR_BUFFERQUEUE; |
| loc_bufq.numBuffers = numBuffers; |
| SLAndroidDataFormat_PCM_EX format_pcm; |
| format_pcm.formatType = transferFormat == AUDIO_FORMAT_PCM_FLOAT |
| ? SL_ANDROID_DATAFORMAT_PCM_EX : SL_DATAFORMAT_PCM; |
| format_pcm.numChannels = sfinfo.channels; |
| format_pcm.sampleRate = sfinfo.samplerate * 1000; |
| format_pcm.bitsPerSample = bitsPerSample; |
| format_pcm.containerSize = format_pcm.bitsPerSample; |
| format_pcm.channelMask = 1 == format_pcm.numChannels ? SL_SPEAKER_FRONT_CENTER : |
| SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; |
| format_pcm.endianness = byteOrder; |
| format_pcm.representation = transferFormat == AUDIO_FORMAT_PCM_FLOAT |
| ? SL_ANDROID_PCM_REPRESENTATION_FLOAT : transferFormat == AUDIO_FORMAT_PCM_8_BIT |
| ? SL_ANDROID_PCM_REPRESENTATION_UNSIGNED_INT |
| : SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT; |
| SLDataSource audioSrc; |
| audioSrc.pLocator = &loc_bufq; |
| audioSrc.pFormat = &format_pcm; |
| |
| // configure audio sink |
| SLDataLocator_OutputMix loc_outmix; |
| loc_outmix.locatorType = SL_DATALOCATOR_OUTPUTMIX; |
| loc_outmix.outputMix = outputMixObject; |
| SLDataSink audioSnk; |
| audioSnk.pLocator = &loc_outmix; |
| audioSnk.pFormat = NULL; |
| |
| // create audio player |
| SLInterfaceID ids2[3] = {SL_IID_BUFFERQUEUE, SL_IID_PLAYBACKRATE, SL_IID_EFFECTSEND}; |
| SLboolean req2[3] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE}; |
| SLObjectItf playerObject; |
| result = (*engineEngine)->CreateAudioPlayer(engineEngine, &playerObject, &audioSrc, |
| &audioSnk, enableReverb ? 3 : (enablePlaybackRate ? 2 : 1), ids2, req2); |
| if (SL_RESULT_SUCCESS != result) { |
| fprintf(stderr, "can't create audio player\n"); |
| goto no_player; |
| } |
| |
| { |
| |
| // realize the player |
| result = (*playerObject)->Realize(playerObject, SL_BOOLEAN_FALSE); |
| assert(SL_RESULT_SUCCESS == result); |
| |
| // get the effect send interface and enable effect send reverb for this player |
| if (enableReverb) { |
| SLEffectSendItf playerEffectSend; |
| result = (*playerObject)->GetInterface(playerObject, SL_IID_EFFECTSEND, &playerEffectSend); |
| assert(SL_RESULT_SUCCESS == result); |
| result = (*playerEffectSend)->EnableEffectSend(playerEffectSend, mixEnvironmentalReverb, |
| SL_BOOLEAN_TRUE, (SLmillibel) 0); |
| assert(SL_RESULT_SUCCESS == result); |
| } |
| |
| // get the playback rate interface and configure the rate |
| SLPlaybackRateItf playerPlaybackRate; |
| SLpermille currentRate = 0; |
| if (enablePlaybackRate) { |
| result = (*playerObject)->GetInterface(playerObject, SL_IID_PLAYBACKRATE, |
| &playerPlaybackRate); |
| assert(SL_RESULT_SUCCESS == result); |
| SLpermille defaultRate; |
| result = (*playerPlaybackRate)->GetRate(playerPlaybackRate, &defaultRate); |
| assert(SL_RESULT_SUCCESS == result); |
| SLuint32 defaultProperties; |
| result = (*playerPlaybackRate)->GetProperties(playerPlaybackRate, &defaultProperties); |
| assert(SL_RESULT_SUCCESS == result); |
| printf("default playback rate %d per mille, properties 0x%x\n", defaultRate, |
| defaultProperties); |
| if (initialRate <= 0) { |
| initialRate = defaultRate; |
| } |
| if (finalRate <= 0) { |
| finalRate = initialRate; |
| } |
| currentRate = defaultRate; |
| if (finalRate == initialRate) { |
| deltaRate = 0; |
| } else if (finalRate < initialRate) { |
| deltaRate = -deltaRate; |
| } |
| if (initialRate != defaultRate) { |
| result = (*playerPlaybackRate)->SetRate(playerPlaybackRate, initialRate); |
| if (SL_RESULT_FEATURE_UNSUPPORTED == result) { |
| fprintf(stderr, "initial playback rate %d is unsupported\n", initialRate); |
| deltaRate = 0; |
| } else if (SL_RESULT_PARAMETER_INVALID == result) { |
| fprintf(stderr, "initial playback rate %d is invalid\n", initialRate); |
| deltaRate = 0; |
| } else { |
| assert(SL_RESULT_SUCCESS == result); |
| currentRate = initialRate; |
| } |
| } |
| } |
| |
| // get the play interface |
| SLPlayItf playerPlay; |
| result = (*playerObject)->GetInterface(playerObject, SL_IID_PLAY, &playerPlay); |
| assert(SL_RESULT_SUCCESS == result); |
| |
| // get the buffer queue interface |
| SLBufferQueueItf playerBufferQueue; |
| result = (*playerObject)->GetInterface(playerObject, SL_IID_BUFFERQUEUE, |
| &playerBufferQueue); |
| assert(SL_RESULT_SUCCESS == result); |
| |
| // loop until EOF or no more buffers |
| for (which = 0; which < numBuffers; ++which) { |
| void *buffer = (char *)buffers + framesPerBuffer * sfframesize * which; |
| sf_count_t frames = framesPerBuffer; |
| sf_count_t count; |
| switch (transferFormat) { |
| case AUDIO_FORMAT_PCM_FLOAT: |
| count = sf_readf_float(sndfile, (float *) buffer, frames); |
| break; |
| case AUDIO_FORMAT_PCM_32_BIT: |
| count = sf_readf_int(sndfile, (int *) buffer, frames); |
| break; |
| case AUDIO_FORMAT_PCM_24_BIT_PACKED: |
| count = sf_readf_int(sndfile, (int *) buffer, frames); |
| break; |
| case AUDIO_FORMAT_PCM_16_BIT: |
| case AUDIO_FORMAT_PCM_8_BIT: |
| count = sf_readf_short(sndfile, (short *) buffer, frames); |
| break; |
| default: |
| count = 0; |
| break; |
| } |
| if (0 >= count) { |
| eof = SL_BOOLEAN_TRUE; |
| break; |
| } |
| |
| // enqueue a buffer |
| SLuint32 nbytes = count * sfframesize; |
| nbytes = squeeze(buffer, nbytes); |
| result = (*playerBufferQueue)->Enqueue(playerBufferQueue, buffer, nbytes); |
| assert(SL_RESULT_SUCCESS == result); |
| } |
| if (which >= numBuffers) { |
| which = 0; |
| } |
| |
| // register a callback on the buffer queue |
| result = (*playerBufferQueue)->RegisterCallback(playerBufferQueue, callback, NULL); |
| assert(SL_RESULT_SUCCESS == result); |
| |
| #define FIFO_FRAMES 16384 |
| void *fifoBuffer = malloc(FIFO_FRAMES * sfframesize); |
| fifo = new audio_utils_fifo(FIFO_FRAMES, sfframesize, fifoBuffer); |
| fifoReader = new audio_utils_fifo_reader(*fifo, true /*throttlesWriter*/); |
| fifoWriter = new audio_utils_fifo_writer(*fifo); |
| |
| // create thread to read from file |
| pthread_t thread; |
| int ok = pthread_create(&thread, (const pthread_attr_t *) NULL, file_reader_loop, NULL); |
| assert(0 == ok); |
| |
| // give thread a head start so that the pipe is initially filled |
| sleep(1); |
| |
| // set the player's state to playing |
| result = (*playerPlay)->SetPlayState(playerPlay, SL_PLAYSTATE_PLAYING); |
| assert(SL_RESULT_SUCCESS == result); |
| |
| // get the initial time |
| struct timespec prevTs; |
| clock_gettime(CLOCK_MONOTONIC, &prevTs); |
| long elapsedNs = 0; |
| long deltaRateNs = deltaRateMs * 1000000; |
| |
| // wait until the buffer queue is empty |
| SLBufferQueueState bufqstate; |
| for (;;) { |
| result = (*playerBufferQueue)->GetState(playerBufferQueue, &bufqstate); |
| assert(SL_RESULT_SUCCESS == result); |
| if (0 >= bufqstate.count) { |
| break; |
| } |
| if (!enablePlaybackRate || deltaRate == 0) { |
| sleep(1); |
| } else { |
| struct timespec curTs; |
| clock_gettime(CLOCK_MONOTONIC, &curTs); |
| elapsedNs += (curTs.tv_sec - prevTs.tv_sec) * 1000000000 + |
| // this term can be negative |
| (curTs.tv_nsec - prevTs.tv_nsec); |
| prevTs = curTs; |
| if (elapsedNs < deltaRateNs) { |
| usleep((deltaRateNs - elapsedNs) / 1000); |
| continue; |
| } |
| elapsedNs -= deltaRateNs; |
| SLpermille nextRate = currentRate + deltaRate; |
| result = (*playerPlaybackRate)->SetRate(playerPlaybackRate, nextRate); |
| if (SL_RESULT_SUCCESS != result) { |
| fprintf(stderr, "next playback rate %d is unsupported\n", nextRate); |
| } else if (SL_RESULT_PARAMETER_INVALID == result) { |
| fprintf(stderr, "next playback rate %d is invalid\n", nextRate); |
| } else { |
| assert(SL_RESULT_SUCCESS == result); |
| } |
| currentRate = nextRate; |
| if (currentRate >= max(initialRate, finalRate)) { |
| currentRate = max(initialRate, finalRate); |
| deltaRate = -abs(deltaRate); |
| } else if (currentRate <= min(initialRate, finalRate)) { |
| currentRate = min(initialRate, finalRate); |
| deltaRate = abs(deltaRate); |
| } |
| } |
| |
| } |
| |
| // wait for reader thread to exit |
| ok = pthread_join(thread, (void **) NULL); |
| assert(0 == ok); |
| |
| // set the player's state to stopped |
| result = (*playerPlay)->SetPlayState(playerPlay, SL_PLAYSTATE_STOPPED); |
| assert(SL_RESULT_SUCCESS == result); |
| |
| // destroy audio player |
| (*playerObject)->Destroy(playerObject); |
| |
| delete fifoWriter; |
| fifoWriter = NULL; |
| delete fifoReader; |
| fifoReader = NULL; |
| delete fifo; |
| fifo = NULL; |
| free(fifoBuffer); |
| fifoBuffer = NULL; |
| |
| } |
| |
| no_player: |
| |
| // destroy output mix |
| (*outputMixObject)->Destroy(outputMixObject); |
| |
| // destroy engine |
| (*engineObject)->Destroy(engineObject); |
| |
| } |
| |
| close_sndfile: |
| |
| (void) sf_close(sndfile); |
| |
| return EXIT_SUCCESS; |
| } |