Eric Laurent 4bb21c496b Fix issue 3479042.
The problem is that when an AudioRecord using the resampler is restarted,
the resampler state is not reset (as there is no reset function in the resampler).
The consequence is that the first time the record thread loop runs, it calls the resampler
which consumes the remaining data in the input buffer and when this buffer is released
the input index is incremented over the limit.

The fix consists in implementing a reset function in the resampler.

A similar problem was also present for playback but unoticed because the track buffer is always
drained by the mixer when a track stops. The only problem for playback was that the initial
phase fraction was wrong when restarting a track after stop (it was correct after a pause).

Change-Id: Ifc2585d685f4402d29f4afc63f6efd1d69265de3
2011-02-28 16:52:51 -08:00

210 lines
6.5 KiB
C++

/* //device/include/server/AudioFlinger/AudioMixer.h
**
** Copyright 2007, 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.
*/
#ifndef ANDROID_AUDIO_MIXER_H
#define ANDROID_AUDIO_MIXER_H
#include <stdint.h>
#include <sys/types.h>
#include "AudioBufferProvider.h"
#include "AudioResampler.h"
namespace android {
// ----------------------------------------------------------------------------
#define LIKELY( exp ) (__builtin_expect( (exp) != 0, true ))
#define UNLIKELY( exp ) (__builtin_expect( (exp) != 0, false ))
// ----------------------------------------------------------------------------
class AudioMixer
{
public:
AudioMixer(size_t frameCount, uint32_t sampleRate);
~AudioMixer();
static const uint32_t MAX_NUM_TRACKS = 32;
static const uint32_t MAX_NUM_CHANNELS = 2;
static const uint16_t UNITY_GAIN = 0x1000;
enum { // names
// track units (32 units)
TRACK0 = 0x1000,
// enable/disable
MIXING = 0x2000,
// setParameter targets
TRACK = 0x3000,
RESAMPLE = 0x3001,
RAMP_VOLUME = 0x3002, // ramp to new volume
VOLUME = 0x3003, // don't ramp
// set Parameter names
// for target TRACK
CHANNEL_COUNT = 0x4000,
FORMAT = 0x4001,
MAIN_BUFFER = 0x4002,
AUX_BUFFER = 0x4003,
// for TARGET RESAMPLE
SAMPLE_RATE = 0x4100,
RESET = 0x4101,
// for TARGET VOLUME (8 channels max)
VOLUME0 = 0x4200,
VOLUME1 = 0x4201,
AUXLEVEL = 0x4210,
};
int getTrackName();
void deleteTrackName(int name);
status_t enable(int name);
status_t disable(int name);
status_t setActiveTrack(int track);
status_t setParameter(int target, int name, void *value);
status_t setBufferProvider(AudioBufferProvider* bufferProvider);
void process();
uint32_t trackNames() const { return mTrackNames; }
static void ditherAndClamp(int32_t* out, int32_t const *sums, size_t c);
private:
enum {
NEEDS_CHANNEL_COUNT__MASK = 0x00000003,
NEEDS_FORMAT__MASK = 0x000000F0,
NEEDS_MUTE__MASK = 0x00000100,
NEEDS_RESAMPLE__MASK = 0x00001000,
NEEDS_AUX__MASK = 0x00010000,
};
enum {
NEEDS_CHANNEL_1 = 0x00000000,
NEEDS_CHANNEL_2 = 0x00000001,
NEEDS_FORMAT_16 = 0x00000010,
NEEDS_MUTE_DISABLED = 0x00000000,
NEEDS_MUTE_ENABLED = 0x00000100,
NEEDS_RESAMPLE_DISABLED = 0x00000000,
NEEDS_RESAMPLE_ENABLED = 0x00001000,
NEEDS_AUX_DISABLED = 0x00000000,
NEEDS_AUX_ENABLED = 0x00010000,
};
static inline int32_t applyVolume(int32_t in, int32_t v) {
return in * v;
}
struct state_t;
struct track_t;
typedef void (*mix_t)(state_t* state);
typedef void (*hook_t)(track_t* t, int32_t* output, size_t numOutFrames, int32_t* temp, int32_t* aux);
static const int BLOCKSIZE = 16; // 4 cache lines
struct track_t {
uint32_t needs;
union {
int16_t volume[2]; // [0]3.12 fixed point
int32_t volumeRL;
};
int32_t prevVolume[2];
int32_t volumeInc[2];
int32_t auxLevel;
int32_t auxInc;
int32_t prevAuxLevel;
uint16_t frameCount;
uint8_t channelCount : 4;
uint8_t enabled : 1;
uint8_t reserved0 : 3;
uint8_t format;
AudioBufferProvider* bufferProvider;
mutable AudioBufferProvider::Buffer buffer;
hook_t hook;
void const* in; // current location in buffer
AudioResampler* resampler;
uint32_t sampleRate;
int32_t* mainBuffer;
int32_t* auxBuffer;
bool setResampler(uint32_t sampleRate, uint32_t devSampleRate);
bool doesResample() const;
void resetResampler();
void adjustVolumeRamp(bool aux);
};
// pad to 32-bytes to fill cache line
struct state_t {
uint32_t enabledTracks;
uint32_t needsChanged;
size_t frameCount;
mix_t hook;
int32_t *outputTemp;
int32_t *resampleTemp;
int32_t reserved[2];
track_t tracks[32]; __attribute__((aligned(32)));
};
int mActiveTrack;
uint32_t mTrackNames;
const uint32_t mSampleRate;
state_t mState __attribute__((aligned(32)));
void invalidateState(uint32_t mask);
static void track__genericResample(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
static void track__nop(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
static void track__16BitsStereo(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
static void track__16BitsMono(track_t* t, int32_t* out, size_t numFrames, int32_t* temp, int32_t* aux);
static void volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux);
static void volumeStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux);
static void process__validate(state_t* state);
static void process__nop(state_t* state);
static void process__genericNoResampling(state_t* state);
static void process__genericResampling(state_t* state);
static void process__OneTrack16BitsStereoNoResampling(state_t* state);
static void process__TwoTracks16BitsStereoNoResampling(state_t* state);
};
// ----------------------------------------------------------------------------
}; // namespace android
#endif // ANDROID_AUDIO_MIXER_H