mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1027713 - Part 1 - Add a volume API in cubeb and use it instead of doing our own soft gain. r=kinetik
--HG-- extra : rebase_source : c94c9f6792c002d515f2fee0cf708928e76f91a8
This commit is contained in:
parent
c833d9608c
commit
25e8004fd3
@ -246,7 +246,6 @@ AudioStream::AudioStream()
|
||||
, mLatencyRequest(HighLatency)
|
||||
, mReadPoint(0)
|
||||
, mDumpFile(nullptr)
|
||||
, mVolume(1.0)
|
||||
, mBytesPerFrame(0)
|
||||
, mState(INITIALIZED)
|
||||
, mNeedsStart(false)
|
||||
@ -751,9 +750,11 @@ AudioStream::Available()
|
||||
void
|
||||
AudioStream::SetVolume(double aVolume)
|
||||
{
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
NS_ABORT_IF_FALSE(aVolume >= 0.0 && aVolume <= 1.0, "Invalid volume");
|
||||
mVolume = aVolume;
|
||||
|
||||
if (cubeb_stream_set_volume(mCubebStream, aVolume * GetVolumeScale()) != CUBEB_OK) {
|
||||
NS_WARNING("Could not change volume on cubeb stream.");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -1099,9 +1100,6 @@ AudioStream::DataCallback(void* aBuffer, long aFrames)
|
||||
} else {
|
||||
servicedFrames = GetTimeStretched(output, aFrames, insertTime);
|
||||
}
|
||||
float scaled_volume = float(GetVolumeScale() * mVolume);
|
||||
|
||||
ScaleAudioSamples(output, aFrames * mOutChannels, scaled_volume);
|
||||
|
||||
NS_ABORT_IF_FALSE(mBuffer.Length() % mBytesPerFrame == 0, "Must copy complete frames");
|
||||
|
||||
|
@ -361,9 +361,6 @@ private:
|
||||
// frames.
|
||||
CircularByteBuffer mBuffer;
|
||||
|
||||
// Software volume level. Applied during the servicing of DataCallback().
|
||||
double mVolume;
|
||||
|
||||
// Owning reference to a cubeb_stream. cubeb_stream_destroy is called by
|
||||
// nsAutoRef's destructor.
|
||||
nsAutoRef<cubeb_stream> mCubebStream;
|
||||
|
@ -135,6 +135,8 @@ cubeb_stream_init
|
||||
cubeb_stream_start
|
||||
cubeb_stream_stop
|
||||
cubeb_stream_get_latency
|
||||
cubeb_stream_set_volume
|
||||
cubeb_stream_set_panning
|
||||
th_comment_clear
|
||||
th_comment_init
|
||||
th_decode_alloc
|
||||
|
@ -261,6 +261,30 @@ int cubeb_stream_get_position(cubeb_stream * stream, uint64_t * position);
|
||||
@retval CUBEB_ERROR */
|
||||
int cubeb_stream_get_latency(cubeb_stream * stream, uint32_t * latency);
|
||||
|
||||
/**
|
||||
* Set the volume for a stream.
|
||||
* @param stream the stream for which to adjust the volume.
|
||||
* @param volumes a float between 0.0 (muted) and 1.0 (maximum volumes)
|
||||
* @return CUBEB_ERROR_INVALID_PARAMETER if volume is outside [0.0; 1.0]
|
||||
* @return CUBEB_ERROR_INVALID_PARAMETER if stream is an invalid pointer
|
||||
* @return CUBEB_OK otherwise
|
||||
*/
|
||||
int cubeb_stream_set_volume(cubeb_stream * stream, float volume);
|
||||
|
||||
/**
|
||||
* If the stream is stereo, set the left/right panning. If the stream is mono,
|
||||
* this has no effect.
|
||||
* @param stream the stream for which to change the panning
|
||||
* @param panning a number from -1.0 to 1.0. -1.0 means that the stream is fully
|
||||
* mixed in the left channel, 1.0 means the stream is fully mixed in the right
|
||||
* channel. 0.0 is equal power in the right and left channel (default).
|
||||
* @return CUBEB_ERROR_INVALID_PARAMETER if stream is null or if panning is outside
|
||||
* the [-1.0; 1.0] range.
|
||||
* @return CUBEB_ERROR if this stream is not mono nor stereo.
|
||||
* @return CUBEB_OK otherwise.
|
||||
*/
|
||||
int cubeb_stream_set_panning(cubeb_stream * stream, float panning);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
@ -28,6 +28,8 @@ struct cubeb_ops {
|
||||
int (* stream_stop)(cubeb_stream * stream);
|
||||
int (* stream_get_position)(cubeb_stream * stream, uint64_t * position);
|
||||
int (* stream_get_latency)(cubeb_stream * stream, uint32_t * latency);
|
||||
int (* stream_set_volume)(cubeb_stream * stream, float volumes);
|
||||
int (* stream_set_panning)(cubeb_stream * stream, float panning);
|
||||
};
|
||||
|
||||
#endif /* CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5 */
|
||||
|
@ -258,3 +258,22 @@ cubeb_stream_get_latency(cubeb_stream * stream, uint32_t * latency)
|
||||
|
||||
return stream->context->ops->stream_get_latency(stream, latency);
|
||||
}
|
||||
|
||||
int
|
||||
cubeb_stream_set_volume(cubeb_stream * stream, float volume)
|
||||
{
|
||||
if (!stream || volume > 1.0 || volume < 0.0) {
|
||||
return CUBEB_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
return stream->context->ops->stream_set_volume(stream, volume);
|
||||
}
|
||||
|
||||
int cubeb_stream_set_panning(cubeb_stream * stream, float panning)
|
||||
{
|
||||
if (!stream || panning < -1.0 || panning > 1.0) {
|
||||
return CUBEB_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
return stream->context->ops->stream_set_panning(stream, panning);
|
||||
}
|
||||
|
@ -106,6 +106,7 @@ struct cubeb_stream {
|
||||
PulseAudio where streams would stop requesting new data despite still
|
||||
being logically active and playing. */
|
||||
struct timeval last_activity;
|
||||
float volume;
|
||||
};
|
||||
|
||||
static int
|
||||
@ -313,7 +314,20 @@ alsa_refill_stream(cubeb_stream * stm)
|
||||
return ERROR;
|
||||
}
|
||||
if (got > 0) {
|
||||
snd_pcm_sframes_t wrote = snd_pcm_writei(stm->pcm, p, got);
|
||||
snd_pcm_sframes_t wrote;
|
||||
|
||||
if (stm->params.format == CUBEB_SAMPLE_FLOAT32NE) {
|
||||
float * b = (float *) p;
|
||||
for (uint32_t i = 0; i < got * stm->params.channels; i++) {
|
||||
b[i] *= stm->volume;
|
||||
}
|
||||
} else {
|
||||
short * b = (short *) p;
|
||||
for (uint32_t i = 0; i < got * stm->params.channels; i++) {
|
||||
b[i] *= stm->volume;
|
||||
}
|
||||
}
|
||||
wrote = snd_pcm_writei(stm->pcm, p, got);
|
||||
if (wrote == -EPIPE) {
|
||||
snd_pcm_recover(stm->pcm, wrote, 1);
|
||||
wrote = snd_pcm_writei(stm->pcm, p, got);
|
||||
@ -813,6 +827,7 @@ alsa_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
|
||||
stm->user_ptr = user_ptr;
|
||||
stm->params = stream_params;
|
||||
stm->state = INACTIVE;
|
||||
stm->volume = 1.0;
|
||||
|
||||
r = pthread_mutex_init(&stm->mutex, NULL);
|
||||
assert(r == 0);
|
||||
@ -1083,6 +1098,24 @@ alsa_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
int
|
||||
alsa_stream_set_volume(cubeb_stream * stm, float volume)
|
||||
{
|
||||
/* setting the volume using an API call does not seem very stable/supported */
|
||||
pthread_mutex_lock(&stm->mutex);
|
||||
stm->volume = volume;
|
||||
pthread_mutex_unlock(&stm->mutex);
|
||||
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
int
|
||||
alsa_stream_set_panning(cubeb_stream * stream, float panning)
|
||||
{
|
||||
assert(0 && "not implemented");
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
static struct cubeb_ops const alsa_ops = {
|
||||
.init = alsa_init,
|
||||
.get_backend_id = alsa_get_backend_id,
|
||||
@ -1095,5 +1128,7 @@ static struct cubeb_ops const alsa_ops = {
|
||||
.stream_start = alsa_stream_start,
|
||||
.stream_stop = alsa_stream_stop,
|
||||
.stream_get_position = alsa_stream_get_position,
|
||||
.stream_get_latency = alsa_stream_get_latency
|
||||
.stream_get_latency = alsa_stream_get_latency,
|
||||
.stream_set_volume = alsa_stream_set_volume,
|
||||
.stream_set_panning = alsa_stream_set_panning
|
||||
};
|
||||
|
@ -71,7 +71,7 @@ struct AudioTrack {
|
||||
/* static */ int (*get_output_latency)(uint32_t* latency, int stream);
|
||||
/* static */ int (*get_output_samplingrate)(int* samplerate, int stream);
|
||||
status_t (*set_marker_position)(void* instance, unsigned int);
|
||||
|
||||
status_t (*set_volume)(void* instance, float left, float right);
|
||||
};
|
||||
|
||||
struct cubeb {
|
||||
@ -251,6 +251,7 @@ audiotrack_init(cubeb ** context, char const * context_name)
|
||||
DLSYM_DLERROR("_ZN7android10AudioTrack5pauseEv", ctx->klass.pause, ctx->library);
|
||||
DLSYM_DLERROR("_ZN7android10AudioTrack11getPositionEPj", ctx->klass.get_position, ctx->library);
|
||||
DLSYM_DLERROR("_ZN7android10AudioTrack17setMarkerPositionEj", ctx->klass.set_marker_position, ctx->library);
|
||||
DLSYM_DLERROR("_ZN7android10AudioTrack9setVolumeEff", ctx->klass.set_volume, ctx->library);
|
||||
|
||||
/* check that we have a combination of symbol that makes sense */
|
||||
c = &ctx->klass;
|
||||
@ -468,6 +469,27 @@ audiotrack_stream_get_latency(cubeb_stream * stream, uint32_t * latency)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
audiotrack_stream_set_volume(cubeb_stream * stream, float volume)
|
||||
{
|
||||
status_t status;
|
||||
|
||||
status = stream->context->klass.set_volume(stream->instance, volume, volume);
|
||||
|
||||
if (status) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
int
|
||||
audiotrack_stream_set_panning(cubeb_stream * stream, float panning)
|
||||
{
|
||||
assert(false && "not implemented.");
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
static struct cubeb_ops const audiotrack_ops = {
|
||||
.init = audiotrack_init,
|
||||
.get_backend_id = audiotrack_get_backend_id,
|
||||
@ -480,5 +502,7 @@ static struct cubeb_ops const audiotrack_ops = {
|
||||
.stream_start = audiotrack_stream_start,
|
||||
.stream_stop = audiotrack_stream_stop,
|
||||
.stream_get_position = audiotrack_stream_get_position,
|
||||
.stream_get_latency = audiotrack_stream_get_latency
|
||||
.stream_get_latency = audiotrack_stream_get_latency,
|
||||
.stream_set_volume = audiotrack_stream_set_volume,
|
||||
.stream_set_panning = audiotrack_stream_set_panning
|
||||
};
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include "cubeb/cubeb.h"
|
||||
#include "cubeb-internal.h"
|
||||
#include "cubeb_panner.h"
|
||||
|
||||
#if !defined(kCFCoreFoundationVersionNumber10_7)
|
||||
/* From CoreFoundation CFBase.h */
|
||||
@ -46,6 +47,7 @@ struct cubeb_stream {
|
||||
int draining;
|
||||
uint64_t current_latency_frames;
|
||||
uint64_t hw_latency_frames;
|
||||
float panning;
|
||||
};
|
||||
|
||||
static int64_t
|
||||
@ -70,6 +72,7 @@ audiounit_output_callback(void * user_ptr, AudioUnitRenderActionFlags * flags,
|
||||
unsigned char * buf;
|
||||
long got;
|
||||
OSStatus r;
|
||||
float panning;
|
||||
|
||||
assert(bufs->mNumberBuffers == 1);
|
||||
buf = bufs->mBuffers[0].mData;
|
||||
@ -79,6 +82,7 @@ audiounit_output_callback(void * user_ptr, AudioUnitRenderActionFlags * flags,
|
||||
pthread_mutex_lock(&stm->mutex);
|
||||
|
||||
stm->current_latency_frames = audiotimestamp_to_latency(tstamp, stm);
|
||||
panning = stm->panning;
|
||||
|
||||
if (stm->draining || stm->shutdown) {
|
||||
pthread_mutex_unlock(&stm->mutex);
|
||||
@ -113,6 +117,10 @@ audiounit_output_callback(void * user_ptr, AudioUnitRenderActionFlags * flags,
|
||||
stm->frames_queued += got;
|
||||
pthread_mutex_unlock(&stm->mutex);
|
||||
|
||||
if (stm->sample_spec.mChannelsPerFrame == 2) {
|
||||
cubeb_pan_stereo_buffer_float((float*)buf, got, panning);
|
||||
}
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
@ -617,6 +625,35 @@ audiounit_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
int audiounit_stream_set_volume(cubeb_stream * stm, float volume)
|
||||
{
|
||||
AudioDeviceID id;
|
||||
OSStatus r;
|
||||
|
||||
r = AudioUnitSetParameter(stm->unit,
|
||||
kHALOutputParam_Volume,
|
||||
kAudioUnitScope_Global,
|
||||
0, volume, 0);
|
||||
|
||||
if (r != noErr) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
int audiounit_stream_set_panning(cubeb_stream * stm, float panning)
|
||||
{
|
||||
if (stm->sample_spec.mChannelsPerFrame > 2) {
|
||||
return CUBEB_ERROR_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&stm->mutex);
|
||||
stm->panning = panning;
|
||||
pthread_mutex_unlock(&stm->mutex);
|
||||
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
static struct cubeb_ops const audiounit_ops = {
|
||||
.init = audiounit_init,
|
||||
.get_backend_id = audiounit_get_backend_id,
|
||||
@ -629,5 +666,7 @@ static struct cubeb_ops const audiounit_ops = {
|
||||
.stream_start = audiounit_stream_start,
|
||||
.stream_stop = audiounit_stream_stop,
|
||||
.stream_get_position = audiounit_stream_get_position,
|
||||
.stream_get_latency = audiounit_stream_get_latency
|
||||
.stream_get_latency = audiounit_stream_get_latency,
|
||||
.stream_set_volume = audiounit_stream_set_volume,
|
||||
.stream_set_panning = audiounit_stream_set_panning
|
||||
};
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
||||
#include <SLES/OpenSLES.h>
|
||||
#include <math.h>
|
||||
#if defined(__ANDROID__)
|
||||
#include <sys/system_properties.h>
|
||||
#include "android/sles_definitions.h"
|
||||
@ -34,6 +35,7 @@ struct cubeb {
|
||||
#if defined(__ANDROID__)
|
||||
SLInterfaceID SL_IID_ANDROIDCONFIGURATION;
|
||||
#endif
|
||||
SLInterfaceID SL_IID_VOLUME;
|
||||
SLObjectItf engObj;
|
||||
SLEngineItf eng;
|
||||
SLObjectItf outmixObj;
|
||||
@ -49,6 +51,7 @@ struct cubeb_stream {
|
||||
SLObjectItf playerObj;
|
||||
SLPlayItf play;
|
||||
SLBufferQueueItf bufq;
|
||||
SLVolumeItf volume;
|
||||
uint8_t *queuebuf[NBUFS];
|
||||
int queuebuf_idx;
|
||||
long queuebuf_len;
|
||||
@ -243,6 +246,7 @@ opensl_init(cubeb ** context, char const * context_name)
|
||||
(slCreateEngine_t)dlsym(ctx->lib, "slCreateEngine");
|
||||
SLInterfaceID SL_IID_ENGINE = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_ENGINE");
|
||||
SLInterfaceID SL_IID_OUTPUTMIX = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_OUTPUTMIX");
|
||||
ctx->SL_IID_VOLUME = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_VOLUME");
|
||||
ctx->SL_IID_BUFFERQUEUE = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_BUFFERQUEUE");
|
||||
#if defined(__ANDROID__)
|
||||
ctx->SL_IID_ANDROIDCONFIGURATION = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_ANDROIDCONFIGURATION");
|
||||
@ -521,11 +525,13 @@ opensl_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name
|
||||
sink.pFormat = NULL;
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
const SLInterfaceID ids[] = {ctx->SL_IID_BUFFERQUEUE, ctx->SL_IID_ANDROIDCONFIGURATION};
|
||||
const SLboolean req[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
|
||||
const SLInterfaceID ids[] = {ctx->SL_IID_BUFFERQUEUE,
|
||||
ctx->SL_IID_VOLUME,
|
||||
ctx->SL_IID_ANDROIDCONFIGURATION};
|
||||
const SLboolean req[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
|
||||
#else
|
||||
const SLInterfaceID ids[] = {ctx->SL_IID_BUFFERQUEUE};
|
||||
const SLboolean req[] = {SL_BOOLEAN_TRUE};
|
||||
const SLInterfaceID ids[] = {ctx->SL_IID_BUFFERQUEUE, ctx->SL_IID_VOLUME};
|
||||
const SLboolean req[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
|
||||
#endif
|
||||
assert(NELEMS(ids) == NELEMS(req));
|
||||
SLresult res = (*ctx->eng)->CreateAudioPlayer(ctx->eng, &stm->playerObj,
|
||||
@ -609,6 +615,14 @@ opensl_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
res = (*stm->playerObj)->GetInterface(stm->playerObj, ctx->SL_IID_VOLUME,
|
||||
&stm->volume);
|
||||
|
||||
if (res != SL_RESULT_SUCCESS) {
|
||||
opensl_stream_destroy(stm);
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
res = (*stm->play)->RegisterCallback(stm->play, play_callback, stm);
|
||||
if (res != SL_RESULT_SUCCESS) {
|
||||
opensl_stream_destroy(stm);
|
||||
@ -724,6 +738,41 @@ opensl_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
int
|
||||
opensl_stream_set_volume(cubeb_stream * stm, float volume)
|
||||
{
|
||||
SLresult res;
|
||||
SLmillibel max_level, millibels;
|
||||
|
||||
res = (*stm->volume)->GetMaxVolumeLevel(stm->volume, &max_level);
|
||||
|
||||
if (res != SL_RESULT_SUCCESS) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
/* convert to millibels */
|
||||
millibels = lroundf(2000.f * log10f(volume));
|
||||
/* clamp to supported range */
|
||||
if (millibels > max_level) {
|
||||
millibels = max_level;
|
||||
}
|
||||
|
||||
res = (*stm->volume)->SetVolumeLevel(stm->volume, millibels);
|
||||
|
||||
if (res != SL_RESULT_SUCCESS) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
int
|
||||
opensl_stream_set_panning(cubeb_stream * stream, float panning)
|
||||
{
|
||||
assert(0 && "not implemented.");
|
||||
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
static struct cubeb_ops const opensl_ops = {
|
||||
.init = opensl_init,
|
||||
.get_backend_id = opensl_get_backend_id,
|
||||
@ -736,5 +785,7 @@ static struct cubeb_ops const opensl_ops = {
|
||||
.stream_start = opensl_stream_start,
|
||||
.stream_stop = opensl_stream_stop,
|
||||
.stream_get_position = opensl_stream_get_position,
|
||||
.stream_get_latency = opensl_stream_get_latency
|
||||
.stream_get_latency = opensl_stream_get_latency,
|
||||
.stream_set_volume = opensl_stream_set_volume,
|
||||
.stream_set_panning = opensl_stream_set_panning
|
||||
};
|
||||
|
56
media/libcubeb/src/cubeb_panner.cpp
Normal file
56
media/libcubeb/src/cubeb_panner.cpp
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright © 2014 Mozilla Foundation
|
||||
*
|
||||
* This program is made available under an ISC-style license. See the
|
||||
* accompanying file LICENSE for details.
|
||||
*/
|
||||
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <math.h>
|
||||
#include <cubeb/cubeb-stdint.h>
|
||||
|
||||
#include "cubeb_panner.h"
|
||||
|
||||
/**
|
||||
* We use a cos/sin law.
|
||||
*/
|
||||
|
||||
namespace {
|
||||
template<typename T>
|
||||
void cubeb_pan_stereo_buffer(T * buf, uint32_t frames, float pan)
|
||||
{
|
||||
if (pan == 0.0) {
|
||||
return;
|
||||
}
|
||||
/* rescale in [0; 1] */
|
||||
pan += 1;
|
||||
pan /= 2;
|
||||
float left_gain = cos(pan * M_PI * 0.5);
|
||||
float right_gain = sin(pan * M_PI * 0.5);
|
||||
|
||||
/* In we are panning on the left, pan the right channel into the left one and
|
||||
* vice-versa. */
|
||||
if (pan < 0.5) {
|
||||
for (uint32_t i = 0; i < frames * 2; i+=2) {
|
||||
buf[i] = buf[i] + buf[i + 1] * left_gain;
|
||||
buf[i + 1] = buf[i + 1] * right_gain;
|
||||
}
|
||||
} else {
|
||||
for (uint32_t i = 0; i < frames * 2; i+=2) {
|
||||
buf[i] = buf[i] * left_gain;
|
||||
buf[i + 1] = buf[i + 1] + buf[i] * right_gain;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cubeb_pan_stereo_buffer_float(float * buf, uint32_t frames, float pan)
|
||||
{
|
||||
cubeb_pan_stereo_buffer(reinterpret_cast<float*>(buf), frames, pan);
|
||||
}
|
||||
|
||||
void cubeb_pan_stereo_buffer_int(short * buf, uint32_t frames, float pan)
|
||||
{
|
||||
cubeb_pan_stereo_buffer(reinterpret_cast<short*>(buf), frames, pan);
|
||||
}
|
||||
|
28
media/libcubeb/src/cubeb_panner.h
Normal file
28
media/libcubeb/src/cubeb_panner.h
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright © 2014 Mozilla Foundation
|
||||
*
|
||||
* This program is made available under an ISC-style license. See the
|
||||
* accompanying file LICENSE for details.
|
||||
*/
|
||||
|
||||
#if !defined(CUBEB_PANNER)
|
||||
#define CUBEB_PANNER
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Pan an integer or an float stereo buffer according to a cos/sin pan law
|
||||
* @param buf the buffer to pan
|
||||
* @param frames the number of frames in `buf`
|
||||
* @param pan a float in [-1.0; 1.0]
|
||||
*/
|
||||
void cubeb_pan_stereo_buffer_float(float * buf, uint32_t frames, float pan);
|
||||
void cubeb_pan_stereo_buffer_int(short* buf, uint32_t frames, float pan);
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -12,23 +12,28 @@
|
||||
#include <string.h>
|
||||
#include "cubeb/cubeb.h"
|
||||
#include "cubeb-internal.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef DISABLE_LIBPULSE_DLOPEN
|
||||
#define WRAP(x) x
|
||||
#else
|
||||
#define WRAP(x) cubeb_##x
|
||||
#define MAKE_TYPEDEF(x) static typeof(x) * cubeb_##x
|
||||
MAKE_TYPEDEF(pa_channel_map_can_balance);
|
||||
MAKE_TYPEDEF(pa_channel_map_init_auto);
|
||||
MAKE_TYPEDEF(pa_context_connect);
|
||||
MAKE_TYPEDEF(pa_context_disconnect);
|
||||
MAKE_TYPEDEF(pa_context_drain);
|
||||
MAKE_TYPEDEF(pa_context_get_server_info);
|
||||
MAKE_TYPEDEF(pa_context_get_sink_info_by_name);
|
||||
MAKE_TYPEDEF(pa_context_get_state);
|
||||
MAKE_TYPEDEF(pa_context_new);
|
||||
MAKE_TYPEDEF(pa_context_rttime_new);
|
||||
MAKE_TYPEDEF(pa_context_set_sink_input_volume);
|
||||
MAKE_TYPEDEF(pa_context_set_state_callback);
|
||||
MAKE_TYPEDEF(pa_context_unref);
|
||||
MAKE_TYPEDEF(pa_context_get_sink_info_by_name);
|
||||
MAKE_TYPEDEF(pa_context_get_server_info);
|
||||
MAKE_TYPEDEF(pa_cvolume_set);
|
||||
MAKE_TYPEDEF(pa_cvolume_set_balance);
|
||||
MAKE_TYPEDEF(pa_frame_size);
|
||||
MAKE_TYPEDEF(pa_operation_get_state);
|
||||
MAKE_TYPEDEF(pa_operation_unref);
|
||||
@ -38,7 +43,10 @@ MAKE_TYPEDEF(pa_stream_cancel_write);
|
||||
MAKE_TYPEDEF(pa_stream_connect_playback);
|
||||
MAKE_TYPEDEF(pa_stream_cork);
|
||||
MAKE_TYPEDEF(pa_stream_disconnect);
|
||||
MAKE_TYPEDEF(pa_stream_get_channel_map);
|
||||
MAKE_TYPEDEF(pa_stream_get_index);
|
||||
MAKE_TYPEDEF(pa_stream_get_latency);
|
||||
MAKE_TYPEDEF(pa_stream_get_sample_spec);
|
||||
MAKE_TYPEDEF(pa_stream_get_state);
|
||||
MAKE_TYPEDEF(pa_stream_get_time);
|
||||
MAKE_TYPEDEF(pa_stream_new);
|
||||
@ -47,10 +55,11 @@ MAKE_TYPEDEF(pa_stream_set_write_callback);
|
||||
MAKE_TYPEDEF(pa_stream_unref);
|
||||
MAKE_TYPEDEF(pa_stream_update_timing_info);
|
||||
MAKE_TYPEDEF(pa_stream_write);
|
||||
MAKE_TYPEDEF(pa_sw_volume_from_linear);
|
||||
MAKE_TYPEDEF(pa_threaded_mainloop_free);
|
||||
MAKE_TYPEDEF(pa_threaded_mainloop_get_api);
|
||||
MAKE_TYPEDEF(pa_threaded_mainloop_lock);
|
||||
MAKE_TYPEDEF(pa_threaded_mainloop_in_thread);
|
||||
MAKE_TYPEDEF(pa_threaded_mainloop_lock);
|
||||
MAKE_TYPEDEF(pa_threaded_mainloop_new);
|
||||
MAKE_TYPEDEF(pa_threaded_mainloop_signal);
|
||||
MAKE_TYPEDEF(pa_threaded_mainloop_start);
|
||||
@ -244,10 +253,12 @@ operation_wait(cubeb * ctx, pa_stream * stream, pa_operation * o)
|
||||
{
|
||||
while (WRAP(pa_operation_get_state)(o) == PA_OPERATION_RUNNING) {
|
||||
WRAP(pa_threaded_mainloop_wait)(ctx->mainloop);
|
||||
if (!PA_CONTEXT_IS_GOOD(WRAP(pa_context_get_state)(ctx->context)))
|
||||
if (!PA_CONTEXT_IS_GOOD(WRAP(pa_context_get_state)(ctx->context))) {
|
||||
return -1;
|
||||
if (stream && !PA_STREAM_IS_GOOD(WRAP(pa_stream_get_state)(stream)))
|
||||
}
|
||||
if (stream && !PA_STREAM_IS_GOOD(WRAP(pa_stream_get_state)(stream))) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -324,17 +335,21 @@ pulse_init(cubeb ** context, char const * context_name)
|
||||
return CUBEB_ERROR; \
|
||||
} \
|
||||
} while(0)
|
||||
LOAD(pa_channel_map_can_balance);
|
||||
LOAD(pa_channel_map_init_auto);
|
||||
LOAD(pa_context_connect);
|
||||
LOAD(pa_context_disconnect);
|
||||
LOAD(pa_context_drain);
|
||||
LOAD(pa_context_get_server_info);
|
||||
LOAD(pa_context_get_sink_info_by_name);
|
||||
LOAD(pa_context_get_state);
|
||||
LOAD(pa_context_new);
|
||||
LOAD(pa_context_rttime_new);
|
||||
LOAD(pa_context_set_sink_input_volume);
|
||||
LOAD(pa_context_set_state_callback);
|
||||
LOAD(pa_context_get_sink_info_by_name);
|
||||
LOAD(pa_context_get_server_info);
|
||||
LOAD(pa_context_unref);
|
||||
LOAD(pa_cvolume_set);
|
||||
LOAD(pa_cvolume_set_balance);
|
||||
LOAD(pa_frame_size);
|
||||
LOAD(pa_operation_get_state);
|
||||
LOAD(pa_operation_unref);
|
||||
@ -344,7 +359,10 @@ pulse_init(cubeb ** context, char const * context_name)
|
||||
LOAD(pa_stream_connect_playback);
|
||||
LOAD(pa_stream_cork);
|
||||
LOAD(pa_stream_disconnect);
|
||||
LOAD(pa_stream_get_channel_map);
|
||||
LOAD(pa_stream_get_index);
|
||||
LOAD(pa_stream_get_latency);
|
||||
LOAD(pa_stream_get_sample_spec);
|
||||
LOAD(pa_stream_get_state);
|
||||
LOAD(pa_stream_get_time);
|
||||
LOAD(pa_stream_new);
|
||||
@ -353,10 +371,11 @@ pulse_init(cubeb ** context, char const * context_name)
|
||||
LOAD(pa_stream_unref);
|
||||
LOAD(pa_stream_update_timing_info);
|
||||
LOAD(pa_stream_write);
|
||||
LOAD(pa_sw_volume_from_linear);
|
||||
LOAD(pa_threaded_mainloop_free);
|
||||
LOAD(pa_threaded_mainloop_get_api);
|
||||
LOAD(pa_threaded_mainloop_lock);
|
||||
LOAD(pa_threaded_mainloop_in_thread);
|
||||
LOAD(pa_threaded_mainloop_lock);
|
||||
LOAD(pa_threaded_mainloop_new);
|
||||
LOAD(pa_threaded_mainloop_signal);
|
||||
LOAD(pa_threaded_mainloop_start);
|
||||
@ -661,6 +680,60 @@ pulse_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
void volume_success(pa_context *c, int success, void *userdata)
|
||||
{
|
||||
cubeb_stream * stream = userdata;
|
||||
assert(success);
|
||||
WRAP(pa_threaded_mainloop_signal)(stream->context->mainloop, 0);
|
||||
}
|
||||
|
||||
int
|
||||
pulse_stream_set_volume(cubeb_stream * stm, float volume)
|
||||
{
|
||||
uint32_t index;
|
||||
pa_operation * op;
|
||||
pa_volume_t vol;
|
||||
pa_cvolume cvol;
|
||||
const pa_sample_spec * ss;
|
||||
|
||||
WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop);
|
||||
|
||||
ss = WRAP(pa_stream_get_sample_spec)(stm->stream);
|
||||
|
||||
vol = WRAP(pa_sw_volume_from_linear)(volume);
|
||||
WRAP(pa_cvolume_set)(&cvol, ss->channels, vol);
|
||||
|
||||
index = WRAP(pa_stream_get_index)(stm->stream);
|
||||
|
||||
op = WRAP(pa_context_set_sink_input_volume)(stm->context->context,
|
||||
index, &cvol, volume_success,
|
||||
stm);
|
||||
if (op) {
|
||||
operation_wait(stm->context, stm->stream, op);
|
||||
WRAP(pa_operation_unref)(op);
|
||||
}
|
||||
WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
|
||||
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
int
|
||||
pulse_stream_set_panning(cubeb_stream * stream, float panning)
|
||||
{
|
||||
const pa_channel_map * map;
|
||||
pa_cvolume vol;
|
||||
|
||||
map = WRAP(pa_stream_get_channel_map)(stream->stream);
|
||||
|
||||
if (!WRAP(pa_channel_map_can_balance)(map)) {
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
WRAP(pa_cvolume_set_balance)(&vol, map, panning);
|
||||
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
static struct cubeb_ops const pulse_ops = {
|
||||
.init = pulse_init,
|
||||
.get_backend_id = pulse_get_backend_id,
|
||||
@ -673,5 +746,7 @@ static struct cubeb_ops const pulse_ops = {
|
||||
.stream_start = pulse_stream_start,
|
||||
.stream_stop = pulse_stream_stop,
|
||||
.stream_get_position = pulse_stream_get_position,
|
||||
.stream_get_latency = pulse_stream_get_latency
|
||||
.stream_get_latency = pulse_stream_get_latency,
|
||||
.stream_set_volume = pulse_stream_set_volume,
|
||||
.stream_set_panning = pulse_stream_set_panning
|
||||
};
|
||||
|
@ -341,6 +341,12 @@ sndio_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
int
|
||||
sndio_stream_set_volume(cubeb_stream * stm, float panning)
|
||||
{
|
||||
assert(0 && "not implemented");
|
||||
}
|
||||
|
||||
static struct cubeb_ops const sndio_ops = {
|
||||
.init = sndio_init,
|
||||
.get_backend_id = sndio_get_backend_id,
|
||||
@ -353,5 +359,7 @@ static struct cubeb_ops const sndio_ops = {
|
||||
.stream_start = sndio_stream_start,
|
||||
.stream_stop = sndio_stream_stop,
|
||||
.stream_get_position = sndio_stream_get_position,
|
||||
.stream_get_latency = sndio_stream_get_latency
|
||||
.stream_get_latency = sndio_stream_get_latency,
|
||||
.stream_set_volume = sndio_stream_set_volume,
|
||||
.stream_set_panning = sndio_stream_set_panning
|
||||
};
|
||||
|
@ -102,6 +102,8 @@ struct cubeb_stream
|
||||
IAudioRenderClient * render_client;
|
||||
/* Interface pointer to use the clock facilities. */
|
||||
IAudioClock * audio_clock;
|
||||
/* Interface pointer to use the volume facilities. */
|
||||
IAudioStreamVolume * audio_stream_volume;
|
||||
/* This event is set by the stream_stop and stream_destroy
|
||||
* function, so the render loop can exit properly. */
|
||||
HANDLE shutdown_event;
|
||||
@ -748,6 +750,14 @@ wasapi_stream_init(cubeb * context, cubeb_stream ** stream,
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
hr = stm->client->GetService(__uuidof(IAudioStreamVolume),
|
||||
(void **)&stm->audio_stream_volume);
|
||||
if (FAILED(hr)) {
|
||||
LOG("Could not get the IAudioStreamVolume %x.", hr);
|
||||
wasapi_stream_destroy(stm);
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
hr = stm->audio_clock->GetFrequency(&stm->clock_freq);
|
||||
if (FAILED(hr)) {
|
||||
LOG("failed to get audio clock frequency, %x", hr);
|
||||
@ -882,6 +892,39 @@ int wasapi_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
int wasapi_stream_set_volume(cubeb_stream * stm, float volume)
|
||||
{
|
||||
HRESULT hr;
|
||||
uint32_t channels;
|
||||
/* up to 9.1 for now */
|
||||
float volumes[10];
|
||||
|
||||
hr = stm->audio_stream_volume->GetChannelCount(&channels);
|
||||
if (hr != S_OK) {
|
||||
LOG("could not get the channel count: %x", hr);
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
assert(channels <= 10 && "bump the array size");
|
||||
|
||||
for (uint32_t i = 0; i < channels; i++) {
|
||||
volumes[i] = volume;
|
||||
}
|
||||
|
||||
hr = stm->audio_stream_volume->SetAllVolumes(channels, volumes);
|
||||
if (hr != S_OK) {
|
||||
LOG("coult not set the channels volume: %x", hr);
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
int wasapi_stream_set_panning(cubeb_stream * stream, float panning)
|
||||
{
|
||||
assert(false && "not implemented");
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
cubeb_ops const wasapi_ops = {
|
||||
/*.init =*/ wasapi_init,
|
||||
/*.get_backend_id =*/ wasapi_get_backend_id,
|
||||
@ -894,7 +937,9 @@ cubeb_ops const wasapi_ops = {
|
||||
/*.stream_start =*/ wasapi_stream_start,
|
||||
/*.stream_stop =*/ wasapi_stream_stop,
|
||||
/*.stream_get_position =*/ wasapi_stream_get_position,
|
||||
/*.stream_get_latency =*/ wasapi_stream_get_latency
|
||||
/*.stream_get_latency =*/ wasapi_stream_get_latency,
|
||||
/*.stream_set_volume =*/ wasapi_stream_set_volume,
|
||||
/*.stream_set_panning =*/ wasapi_stream_set_panning
|
||||
};
|
||||
} // namespace anonymous
|
||||
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <mmsystem.h>
|
||||
#include <process.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "cubeb/cubeb.h"
|
||||
#include "cubeb-internal.h"
|
||||
|
||||
@ -28,6 +29,9 @@
|
||||
|
||||
#define CUBEB_STREAM_MAX 32
|
||||
#define NBUFS 4
|
||||
/* When cubeb_stream.soft_volume is set to this value, the device supports
|
||||
* setting the volume. Otherwise, a gain will be applied manually. */
|
||||
#define SETTING_VOLUME_SUPPORTED -1.0
|
||||
|
||||
const GUID KSDATAFORMAT_SUBTYPE_PCM =
|
||||
{ 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
|
||||
@ -68,6 +72,7 @@ struct cubeb_stream {
|
||||
HWAVEOUT waveout;
|
||||
CRITICAL_SECTION lock;
|
||||
uint64_t written;
|
||||
float soft_volume;
|
||||
};
|
||||
|
||||
static size_t
|
||||
@ -155,6 +160,22 @@ winmm_refill_stream(cubeb_stream * stm)
|
||||
hdr->dwBufferLength = got * bytes_per_frame(stm->params);
|
||||
assert(hdr->dwBufferLength <= stm->buffer_size);
|
||||
|
||||
if (stm->soft_volume != SETTING_VOLUME_SUPPORTED) {
|
||||
if (stm->params.format == CUBEB_SAMPLE_FLOAT32NE) {
|
||||
short * b = (short *) hdr->lpData;
|
||||
uint32_t i;
|
||||
for (i = 0; i < got * stm->params.channels; i++) {
|
||||
b[i] *= stm->soft_volume;
|
||||
}
|
||||
} else {
|
||||
short * b = (short *) hdr->lpData;
|
||||
uint32_t i;
|
||||
for (i = 0; i < got * stm->params.channels; i++) {
|
||||
b[i] *= stm->soft_volume;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r = waveOutWrite(stm->waveout, hdr, sizeof(*hdr));
|
||||
if (r != MMSYSERR_NOERROR) {
|
||||
LeaveCriticalSection(&stm->lock);
|
||||
@ -330,6 +351,7 @@ winmm_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_n
|
||||
{
|
||||
MMRESULT r;
|
||||
WAVEFORMATEXTENSIBLE wfx;
|
||||
WAVEOUTCAPS waveoutcaps;
|
||||
cubeb_stream * stm;
|
||||
int i;
|
||||
size_t bufsz;
|
||||
@ -416,6 +438,20 @@ winmm_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_n
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
r = waveOutGetDevCaps(WAVE_MAPPER, &waveoutcaps, sizeof(WAVEOUTCAPS));
|
||||
if(r != MMSYSERR_NOERROR) {
|
||||
winmm_stream_destroy(stm);
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
stm->soft_volume = SETTING_VOLUME_SUPPORTED;
|
||||
|
||||
/* if this device does not support setting the volume, do it manually. */
|
||||
if(!(waveoutcaps.dwSupport & WAVECAPS_VOLUME)) {
|
||||
stm->soft_volume = 1.0;
|
||||
}
|
||||
|
||||
|
||||
/* winmm_buffer_callback will be called during waveOutOpen, so all
|
||||
other initialization must be complete before calling it. */
|
||||
r = waveOutOpen(&stm->waveout, WAVE_MAPPER, &wfx.Format,
|
||||
@ -432,6 +468,7 @@ winmm_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_n
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
|
||||
|
||||
for (i = 0; i < NBUFS; ++i) {
|
||||
WAVEHDR * hdr = &stm->buffers[i];
|
||||
|
||||
@ -628,6 +665,41 @@ winmm_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
winmm_stream_set_volume(cubeb_stream * stm, float volume)
|
||||
{
|
||||
MMRESULT r;
|
||||
DWORD vol;
|
||||
|
||||
if (stm->soft_volume != SETTING_VOLUME_SUPPORTED) {
|
||||
stm->soft_volume = volume;
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
// lower order word is the left channel, higher order
|
||||
// word is the right channel. Full volume on a channel is 0xffff.
|
||||
vol = volume * 0xffff;
|
||||
vol |= vol << 16;
|
||||
|
||||
EnterCriticalSection(&stm->lock);
|
||||
r = waveOutSetVolume(stm->waveout, vol);
|
||||
if (r != MMSYSERR_NOERROR) {
|
||||
stm->soft_volume = volume;
|
||||
LeaveCriticalSection(&stm->lock);
|
||||
return CUBEB_ERROR;
|
||||
}
|
||||
LeaveCriticalSection(&stm->lock);
|
||||
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
static int
|
||||
winmm_stream_set_panning(cubeb_stream * stream, float panning)
|
||||
{
|
||||
assert(0 && "not implemented");
|
||||
return CUBEB_OK;
|
||||
}
|
||||
|
||||
static struct cubeb_ops const winmm_ops = {
|
||||
/*.init =*/ winmm_init,
|
||||
/*.get_backend_id =*/ winmm_get_backend_id,
|
||||
@ -640,5 +712,7 @@ static struct cubeb_ops const winmm_ops = {
|
||||
/*.stream_start =*/ winmm_stream_start,
|
||||
/*.stream_stop =*/ winmm_stream_stop,
|
||||
/*.stream_get_position =*/ winmm_stream_get_position,
|
||||
/*.stream_get_latency = */ winmm_stream_get_latency
|
||||
/*.stream_get_latency = */ winmm_stream_get_latency,
|
||||
/*.stream_set_volume =*/ winmm_stream_set_volume,
|
||||
/*.stream_set_panning =*/ winmm_stream_set_panning
|
||||
};
|
||||
|
@ -8,6 +8,7 @@ LIBRARY_NAME = 'cubeb'
|
||||
|
||||
SOURCES += [
|
||||
'cubeb.c',
|
||||
'cubeb_panner.cpp'
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_ALSA']:
|
||||
|
Loading…
Reference in New Issue
Block a user