float AudioTransport::ticksToMillis(float t_ticks) const { return samplesToMillis(ticksToSamples(t_ticks)); }
int AudioSource::mixWith( struct timespec ticks, uint8_t* outSamples, int outBytes, int outBitDepth, int outNbChannels, int outFrequency, float outVolume) { if (state != SOURCE_PLAYING) return -1; if (buffer_queue.empty()) return -1; debuglog(LCF_SOUND | LCF_FRAME, "Start mixing source ", id); AudioBuffer* curBuf = buffer_queue[queue_index]; #if defined(LIBTAS_ENABLE_AVDUMPING) || defined(LIBTAS_ENABLE_SOUNDPLAYBACK) /* Get the sample format */ AVSampleFormat inFormat, outFormat; switch (curBuf->format) { case SAMPLE_FMT_U8: inFormat = AV_SAMPLE_FMT_U8; break; case SAMPLE_FMT_S16: case SAMPLE_FMT_MSADPCM: inFormat = AV_SAMPLE_FMT_S16; break; case SAMPLE_FMT_S32: inFormat = AV_SAMPLE_FMT_S32; break; case SAMPLE_FMT_FLT: inFormat = AV_SAMPLE_FMT_FLT; break; case SAMPLE_FMT_DBL: inFormat = AV_SAMPLE_FMT_DBL; break; default: debuglog(LCF_SOUND | LCF_FRAME | LCF_ERROR, "Unknown sample format"); break; } if (outBitDepth == 8) outFormat = AV_SAMPLE_FMT_U8; if (outBitDepth == 16) outFormat = AV_SAMPLE_FMT_S16; /* Check if SWR context is initialized. * If not, set parameters and init it */ if (! swr_is_initialized(swr)) { /* Set channel layout */ if (curBuf->nbChannels == 1) av_opt_set_int(swr, "in_channel_layout", AV_CH_LAYOUT_MONO, 0); if (curBuf->nbChannels == 2) av_opt_set_int(swr, "in_channel_layout", AV_CH_LAYOUT_STEREO, 0); if (outNbChannels == 1) av_opt_set_int(swr, "out_channel_layout", AV_CH_LAYOUT_MONO, 0); if (outNbChannels == 2) av_opt_set_int(swr, "out_channel_layout", AV_CH_LAYOUT_STEREO, 0); /* Set sample format */ av_opt_set_sample_fmt(swr, "in_sample_fmt", inFormat, 0); av_opt_set_sample_fmt(swr, "out_sample_fmt", outFormat, 0); /* Set sampling frequency */ av_opt_set_int(swr, "in_sample_rate", curBuf->frequency, 0); av_opt_set_int(swr, "out_sample_rate", outFrequency, 0); /* Open the context */ if (swr_init(swr) < 0) { debuglog(LCF_SOUND | LCF_FRAME | LCF_ERROR, "Error initializing swr context"); return 0; } } #endif /* Mixing source volume and master volume. * Taken from openAL doc: * "The implementation is free to clamp the total gain (effective gain * per-source multiplied by the listener gain) to one to prevent overflow." * * TODO: This is where we can support panning. */ float resultVolume = (volume * outVolume) > 1.0?1.0:(volume*outVolume); int lvas = (int)(resultVolume * 65536.0f); int rvas = (int)(resultVolume * 65536.0f); /* Number of samples to advance in the buffer. */ int inNbSamples = ticksToSamples(ticks, curBuf->frequency); int oldPosition = position; int newPosition = position + inNbSamples; /* Allocate the mixed audio array */ #if defined(LIBTAS_ENABLE_AVDUMPING) || defined(LIBTAS_ENABLE_SOUNDPLAYBACK) int outNbSamples = outBytes / (outNbChannels * outBitDepth / 8); mixedSamples.resize(outBytes); uint8_t* begMixed = &mixedSamples[0]; #endif int convOutSamples = 0; uint8_t* begSamples; int availableSamples = curBuf->getSamples(begSamples, inNbSamples, oldPosition); if (availableSamples == inNbSamples) { /* We did not reach the end of the buffer, easy case */ position = newPosition; debuglog(LCF_SOUND | LCF_FRAME, " Buffer ", curBuf->id, " in read in range ", oldPosition, " - ", position); #if defined(LIBTAS_ENABLE_AVDUMPING) || defined(LIBTAS_ENABLE_SOUNDPLAYBACK) convOutSamples = swr_convert(swr, &begMixed, outNbSamples, (const uint8_t**)&begSamples, inNbSamples); #endif } else { /* We reached the end of the buffer */ debuglog(LCF_SOUND | LCF_FRAME, " Buffer ", curBuf->id, " is read from ", oldPosition, " to its end ", curBuf->sampleSize); #if defined(LIBTAS_ENABLE_AVDUMPING) || defined(LIBTAS_ENABLE_SOUNDPLAYBACK) if (availableSamples > 0) swr_convert(swr, nullptr, 0, (const uint8_t**)&begSamples, availableSamples); #endif int remainingSamples = inNbSamples - availableSamples; if (source == SOURCE_CALLBACK) { /* We refill our buffer using the callback function, * until we got enough bytes for this frame */ while (remainingSamples > 0) { /* Before doing the callback, we must fake that the timer has * advanced by the number of samples already read */ int64_t extraTicks = ((int64_t) 1000000000) * (-remainingSamples); extraTicks /= curBuf->frequency; detTimer.fakeAdvanceTimer({extraTicks / 1000000000, extraTicks % 1000000000}); callback(curBuf); detTimer.fakeAdvanceTimer({0, 0}); availableSamples = curBuf->getSamples(begSamples, remainingSamples, 0); #if defined(LIBTAS_ENABLE_AVDUMPING) || defined(LIBTAS_ENABLE_SOUNDPLAYBACK) swr_convert(swr, nullptr, 0, (const uint8_t**)&begSamples, availableSamples); #endif debuglog(LCF_SOUND | LCF_FRAME, " Buffer ", curBuf->id, " is read again from 0 to ", availableSamples); if (remainingSamples == availableSamples) position = availableSamples; remainingSamples -= availableSamples; } #if defined(LIBTAS_ENABLE_AVDUMPING) || defined(LIBTAS_ENABLE_SOUNDPLAYBACK) /* Get the mixed samples */ convOutSamples = swr_convert(swr, &begMixed, outNbSamples, nullptr, 0); #endif } else { int queue_size = buffer_queue.size(); int finalIndex; int finalPos; /* Our for loop conditions are different if we are looping or not */ if (looping) { for (int i=(queue_index+1)%queue_size; remainingSamples>0; i=(i+1)%queue_size) { AudioBuffer* loopbuf = buffer_queue[i]; availableSamples = loopbuf->getSamples(begSamples, remainingSamples, 0); debuglog(LCF_SOUND | LCF_FRAME, " Buffer ", loopbuf->id, " in read in range 0 - ", availableSamples); #if defined(LIBTAS_ENABLE_AVDUMPING) || defined(LIBTAS_ENABLE_SOUNDPLAYBACK) swr_convert(swr, nullptr, 0, (const uint8_t**)&begSamples, availableSamples); #endif if (remainingSamples == availableSamples) { finalIndex = i; finalPos = availableSamples; } remainingSamples -= availableSamples; } } else { for (int i=queue_index+1; (remainingSamples>0) && (i<queue_size); i++) { AudioBuffer* loopbuf = buffer_queue[i]; availableSamples = loopbuf->getSamples(begSamples, remainingSamples, 0); debuglog(LCF_SOUND | LCF_FRAME, " Buffer ", loopbuf->id, " in read in range 0 - ", availableSamples); #if defined(LIBTAS_ENABLE_AVDUMPING) || defined(LIBTAS_ENABLE_SOUNDPLAYBACK) swr_convert(swr, nullptr, 0, (const uint8_t**)&begSamples, availableSamples); #endif if (remainingSamples == availableSamples) { finalIndex = i; finalPos = availableSamples; } remainingSamples -= availableSamples; } } #if defined(LIBTAS_ENABLE_AVDUMPING) || defined(LIBTAS_ENABLE_SOUNDPLAYBACK) /* Get the mixed samples */ convOutSamples = swr_convert(swr, &begMixed, outNbSamples, nullptr, 0); #endif if (remainingSamples > 0) { /* We reached the end of the buffer queue */ init(); state = SOURCE_STOPPED; debuglog(LCF_SOUND | LCF_FRAME, " End of the queue reached"); } else { /* Update the position in the buffer */ queue_index = finalIndex; position = finalPos; } } } #if defined(LIBTAS_ENABLE_AVDUMPING) || defined(LIBTAS_ENABLE_SOUNDPLAYBACK) #define clamptofullsignedrange(x,lo,hi) (((unsigned int)((x)-(lo))<=(unsigned int)((hi)-(lo)))?(x):(((x)<0)?(lo):(hi))) /* Add mixed source to the output buffer */ if (outBitDepth == 8) { for (int s=0; s<convOutSamples*outNbChannels; s+=outNbChannels) { int myL = ((uint8_t*)&mixedSamples[0])[s]; int otherL = ((uint8_t*)outSamples)[s]; int sumL = otherL + ((myL * lvas) >> 16) - 256; ((uint8_t*)outSamples)[s] = clamptofullsignedrange(sumL, 0, (1<<8)-1); if (outNbChannels == 2) { int myR = ((uint8_t*)&mixedSamples[0])[s+1]; int otherR = ((uint8_t*)outSamples)[s+1]; int sumR = otherR + ((myR * rvas) >> 16); ((uint8_t*)outSamples)[s+1] = clamptofullsignedrange(sumR, 0, (1<<8)-1); } } }