759 lines
21 KiB
C
Executable File
759 lines
21 KiB
C
Executable File
/*
|
|
* Copyright (c) 2008-2016 Allwinner Technology Co. Ltd.
|
|
* All rights reserved.
|
|
*
|
|
* File : soundControl_alsa.c
|
|
* Description : soundControl for alsa
|
|
* History :
|
|
*
|
|
*/
|
|
|
|
#include <unistd.h>
|
|
#include <stdio.h>
|
|
#include <fcntl.h>
|
|
#include <stdlib.h>
|
|
#include <sys/ioctl.h>
|
|
#include <pthread.h>
|
|
#include <cdx_log.h>
|
|
#include "soundControl.h"
|
|
#include <pthread.h>
|
|
|
|
#include <alsa/asoundlib.h>
|
|
|
|
enum SDSTATUS
|
|
{
|
|
SD_STATUS_STOPPED = 0,
|
|
SD_STATUS_STARTED,
|
|
SD_STATUS_PAUSED
|
|
};
|
|
|
|
|
|
typedef struct SoundCtrlContext
|
|
{
|
|
SoundCtrl base;
|
|
snd_pcm_uframes_t chunk_size;
|
|
snd_pcm_format_t alsa_format;
|
|
snd_pcm_hw_params_t *alsa_hwparams;
|
|
snd_pcm_sw_params_t *alsa_swparams;
|
|
snd_pcm_t *alsa_handler;
|
|
|
|
unsigned int nSampleRate;
|
|
unsigned int nChannelNum;
|
|
int alsa_fragcount;
|
|
int ao_noblock;
|
|
int open_mode;
|
|
int alsa_can_pause;
|
|
size_t bytes_per_sample;
|
|
|
|
enum SDSTATUS eStatus;
|
|
pthread_mutex_t mutex;
|
|
} SoundCtrlContext;
|
|
|
|
#define OUTBURST 512
|
|
#define ALSA_DEVICE_SIZE 256
|
|
|
|
typedef struct pcm_hw_params_t
|
|
{
|
|
unsigned int samplerate;
|
|
unsigned int channels;
|
|
int format;
|
|
int bps;
|
|
int outburst;
|
|
int buffersize;
|
|
int pts;
|
|
} pcm_hw_params;
|
|
pcm_hw_params pcm_params = {0, \
|
|
0, \
|
|
0, \
|
|
0, \
|
|
OUTBURST,\
|
|
-1, \
|
|
0
|
|
};
|
|
|
|
typedef struct strarg_s
|
|
{
|
|
int len;
|
|
char const *str;
|
|
} strarg_t;
|
|
|
|
static int try_open_device(SoundCtrlContext* sc, const char *device, int open_mode, int try_ac3)
|
|
{
|
|
int err;
|
|
|
|
err = snd_pcm_open(&sc->alsa_handler, device, SND_PCM_STREAM_PLAYBACK,
|
|
open_mode);
|
|
return err;
|
|
}
|
|
|
|
static void parse_device(char *dest, const char *src, int len)
|
|
{
|
|
char *tmp;
|
|
memmove(dest, src, len);
|
|
dest[len] = 0;
|
|
while ((tmp = strrchr(dest, '.')))
|
|
tmp[0] = ',';
|
|
while ((tmp = strrchr(dest, '=')))
|
|
tmp[0] = ':';
|
|
}
|
|
|
|
static int SoundDeviceStop_l(SoundCtrlContext* sc);
|
|
|
|
static void __Release(SoundCtrl* s)
|
|
{
|
|
int ret;
|
|
SoundCtrlContext* sc;
|
|
|
|
sc = (SoundCtrlContext*)s;
|
|
pthread_mutex_lock(&sc->mutex);
|
|
if(sc->eStatus != SD_STATUS_STOPPED)
|
|
{
|
|
if (sc->alsa_handler)
|
|
{
|
|
int err;
|
|
if ((err = snd_pcm_close(sc->alsa_handler)) < 0)
|
|
{
|
|
logw("MSGTR_AO_ALSA_PcmCloseError");
|
|
}
|
|
else
|
|
{
|
|
sc->alsa_handler = NULL;
|
|
logd("alsa-uninit: pcm closed\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
logw("MSGTR_AO_ALSA_NoHandlerDefined");
|
|
}
|
|
}
|
|
pthread_mutex_unlock(&sc->mutex);
|
|
|
|
pthread_mutex_destroy(&sc->mutex);
|
|
|
|
free(sc);
|
|
sc = NULL;
|
|
|
|
return;
|
|
}
|
|
|
|
static void __SetFormat(SoundCtrl* s, unsigned int nSampleRate, unsigned int nChannelNum)
|
|
{
|
|
int ret;
|
|
SoundCtrlContext* sc;
|
|
|
|
sc = (SoundCtrlContext*)s;
|
|
|
|
pthread_mutex_lock(&sc->mutex);
|
|
|
|
if(sc->eStatus != SD_STATUS_STOPPED)
|
|
{
|
|
logd("Sound device not int stop status, can not set audio params.");
|
|
abort();
|
|
}
|
|
|
|
pcm_params.samplerate = (nSampleRate < 32000) ? 44100 : nSampleRate;
|
|
pcm_params.channels = nChannelNum;
|
|
pcm_params.format = 1;
|
|
|
|
pthread_mutex_unlock(&sc->mutex);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
static int __Start(SoundCtrl* s)
|
|
{
|
|
int ret;
|
|
SoundCtrlContext* sc;
|
|
|
|
int err;
|
|
int block;
|
|
strarg_t device;
|
|
snd_pcm_uframes_t bufsize;
|
|
snd_pcm_uframes_t boundary;
|
|
|
|
sc = (SoundCtrlContext*)s;
|
|
|
|
pthread_mutex_lock(&sc->mutex);
|
|
if(sc->eStatus == SD_STATUS_STARTED)
|
|
{
|
|
logw("Sound device already started.");
|
|
pthread_mutex_unlock(&sc->mutex);
|
|
return -1;
|
|
}
|
|
|
|
if(sc->eStatus == SD_STATUS_STOPPED)
|
|
{
|
|
char alsa_device[ALSA_DEVICE_SIZE + 1];
|
|
// make sure alsa_device is null-terminated even when using strncpy etc.
|
|
memset(alsa_device, 0, ALSA_DEVICE_SIZE + 1);
|
|
|
|
logd("alsa-init: requested format: %d Hz, %d channels, %x\n", pcm_params.samplerate,
|
|
pcm_params.channels, pcm_params.format);
|
|
|
|
sc->alsa_handler = NULL;
|
|
sc->alsa_format = SND_PCM_FORMAT_S16_LE; //TODO: is it right??
|
|
block = 1;
|
|
switch (pcm_params.channels)
|
|
{
|
|
case 1:
|
|
case 2:
|
|
device.str = "default";
|
|
break;
|
|
case 4:
|
|
if (sc->alsa_format == SND_PCM_FORMAT_FLOAT_LE)
|
|
device.str = "plug:surround40";
|
|
else
|
|
device.str = "surround40";
|
|
break;
|
|
case 6:
|
|
if (sc->alsa_format == SND_PCM_FORMAT_FLOAT_LE)
|
|
device.str = "plug:surround51";
|
|
else
|
|
device.str = "surround51";
|
|
break;
|
|
default:
|
|
device.str = "default";
|
|
|
|
}
|
|
device.len = strlen(device.str);
|
|
|
|
sc->ao_noblock = !block;
|
|
parse_device(alsa_device, device.str, device.len);
|
|
|
|
if(pcm_params.format == 1)
|
|
strcpy(alsa_device, "hw:1");
|
|
else if(pcm_params.format == 2)
|
|
strcpy(alsa_device, "hw:2");
|
|
else
|
|
strcpy(alsa_device, "hw:0");
|
|
|
|
logd("alsa-init: using device %s\n", alsa_device);
|
|
|
|
//setting modes for block or nonblock-mode
|
|
if (sc->ao_noblock)
|
|
{
|
|
sc->open_mode = SND_PCM_NONBLOCK;
|
|
}
|
|
else
|
|
{
|
|
sc->open_mode = 0;
|
|
}
|
|
|
|
//sets buff/chunksize if its set manually
|
|
if (pcm_params.buffersize)
|
|
{
|
|
switch (pcm_params.buffersize)
|
|
{
|
|
case 1:
|
|
sc->alsa_fragcount = 16;
|
|
sc->chunk_size = 512;
|
|
break;
|
|
case 2:
|
|
sc->alsa_fragcount = 8;
|
|
sc->chunk_size = 1024;
|
|
break;
|
|
case 3:
|
|
sc->alsa_fragcount = 32;
|
|
sc->chunk_size = 512;
|
|
break;
|
|
case 4:
|
|
sc->alsa_fragcount = 16;
|
|
sc->chunk_size = 1024;
|
|
break;
|
|
default:
|
|
sc->alsa_fragcount = 16;
|
|
sc->chunk_size = 1024;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!sc->alsa_handler)
|
|
{
|
|
if ((err = try_open_device(sc, alsa_device, sc->open_mode, 0)) < 0)
|
|
{
|
|
if (err != -EBUSY && sc->ao_noblock)
|
|
{
|
|
loge("MSGTR_AO_ALSA_OpenInNonblockModeFailed");
|
|
if ((err = try_open_device(sc, alsa_device, 0, 0)) < 0)
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
loge("Playback open error: %s", snd_strerror(err));
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if ((err = snd_pcm_nonblock(sc->alsa_handler, 0)) < 0)
|
|
{
|
|
logw("MSGTR_AO_ALSA_ErrorSetBlockMode");
|
|
}
|
|
else
|
|
{
|
|
logv("alsa-init: pcm opened in blocking mode\n");
|
|
}
|
|
|
|
snd_pcm_hw_params_alloca(&sc->alsa_hwparams);
|
|
snd_pcm_sw_params_alloca(&sc->alsa_swparams);
|
|
|
|
// setting hw-parameters
|
|
if ((err = snd_pcm_hw_params_any(sc->alsa_handler, sc->alsa_hwparams)) < 0)
|
|
{
|
|
logw("MSGTR_AO_ALSA_UnableToGetInitialParameters");
|
|
return 0;
|
|
}
|
|
|
|
err = snd_pcm_hw_params_set_access(sc->alsa_handler, sc->alsa_hwparams,
|
|
SND_PCM_ACCESS_RW_INTERLEAVED);
|
|
if (err < 0)
|
|
{
|
|
logw("MSGTR_AO_ALSA_UnableToSetAccessType");
|
|
return 0;
|
|
}
|
|
|
|
/* workaround for nonsupported formats
|
|
sets default format to S16_LE if the given formats aren't supported */
|
|
if ((err = snd_pcm_hw_params_test_format(sc->alsa_handler, sc->alsa_hwparams,
|
|
sc->alsa_format)) < 0)
|
|
{
|
|
logw("MSGTR_AO_ALSA_FormatNotSupportedByHardware");
|
|
sc->alsa_format = SND_PCM_FORMAT_S16_LE;
|
|
}
|
|
|
|
if ((err = snd_pcm_hw_params_set_format(sc->alsa_handler, sc->alsa_hwparams,
|
|
sc->alsa_format)) < 0)
|
|
{
|
|
logw("MSGTR_AO_ALSA_UnableToSetFormat");
|
|
return 0;
|
|
}
|
|
|
|
if ((err = snd_pcm_hw_params_set_channels_near(sc->alsa_handler,
|
|
sc->alsa_hwparams, &pcm_params.channels)) < 0)
|
|
{
|
|
logw("MSGTR_AO_ALSA_UnableToSetChannels");
|
|
return 0;
|
|
}
|
|
|
|
/* workaround for buggy rate plugin (should be fixed in ALSA 1.0.11)
|
|
prefer our own resampler */
|
|
#if SND_LIB_VERSION >= 0x010009
|
|
if ((err = snd_pcm_hw_params_set_rate_resample(sc->alsa_handler, sc->alsa_hwparams,
|
|
0)) < 0)
|
|
{
|
|
logw("MSGTR_AO_ALSA_UnableToDisableResampling");
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
if ((err = snd_pcm_hw_params_set_rate_near(sc->alsa_handler, sc->alsa_hwparams,
|
|
&pcm_params.samplerate, NULL)) < 0)
|
|
{
|
|
logw("MSGTR_AO_ALSA_UnableToSetSamplerate2");
|
|
return 0;
|
|
}
|
|
|
|
sc->bytes_per_sample = snd_pcm_format_physical_width(sc->alsa_format) / 8;
|
|
sc->bytes_per_sample *= pcm_params.channels;
|
|
pcm_params.bps = pcm_params.samplerate * sc->bytes_per_sample;
|
|
|
|
#ifdef BUFFERTIME
|
|
{
|
|
int alsa_buffer_time = 500000; /* original 60 */
|
|
int alsa_period_time;
|
|
alsa_period_time = alsa_buffer_time/4;
|
|
if ((err = snd_pcm_hw_params_set_buffer_time_near(sc->alsa_handler,
|
|
sc->alsa_hwparams, &alsa_buffer_time, NULL)) < 0)
|
|
{
|
|
logw("MSGTR_AO_ALSA_UnableToSetBufferTimeNear");
|
|
return 0;
|
|
}
|
|
else
|
|
alsa_buffer_time = err;
|
|
|
|
if ((err = snd_pcm_hw_params_set_period_time_near(sc->alsa_handler,
|
|
sc->alsa_hwparams, &alsa_period_time, NULL)) < 0)
|
|
/* original: alsa_buffer_time/pcm_params.bps */
|
|
{
|
|
logw("MSGTR_AO_ALSA_UnableToSetBufferTimeNear");
|
|
return 0;
|
|
}
|
|
}
|
|
#endif//end SET_BUFFERTIME
|
|
#ifdef SET_CHUNKSIZE
|
|
{
|
|
//set chunksize
|
|
if ((err = snd_pcm_hw_params_set_period_size_near(sc->alsa_handler,
|
|
sc->alsa_hwparams, &sc->chunk_size, NULL)) < 0)
|
|
{
|
|
logw("MSGTR_AO_ALSA_UnableToSetPeriodSize");
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
logv("alsa-init: chunksize set to %li\n", sc->chunk_size);
|
|
}
|
|
|
|
if ((err = snd_pcm_hw_params_set_periods_near(sc->alsa_handler,
|
|
sc->alsa_hwparams, (unsigned int*)&sc->alsa_fragcount, NULL)) < 0)
|
|
{
|
|
logw("MSGTR_AO_ALSA_UnableToSetPeriods");
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
logv("alsa-init: fragcount=%i\n", sc->alsa_fragcount);
|
|
}
|
|
}
|
|
#endif//end SET_CHUNKSIZE
|
|
|
|
if ((err = snd_pcm_hw_params(sc->alsa_handler, sc->alsa_hwparams)) < 0)
|
|
{
|
|
logw("MSGTR_AO_ALSA_UnableToSetHwParameters");
|
|
return 0;
|
|
}
|
|
|
|
// gets buffersize for control
|
|
if ((err = snd_pcm_hw_params_get_buffer_size(sc->alsa_hwparams, &bufsize))
|
|
< 0)
|
|
{
|
|
logw("MSGTR_AO_ALSA_UnableToGetBufferSize");
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
pcm_params.buffersize = bufsize * sc->bytes_per_sample;
|
|
logv("alsa-init: got buffersize=%i\n", pcm_params.buffersize);
|
|
}
|
|
|
|
if ((err = snd_pcm_hw_params_get_period_size(sc->alsa_hwparams,
|
|
&sc->chunk_size, NULL)) < 0)
|
|
{
|
|
logw("MSGTR_AO_ALSA_UnableToGetPeriodSize");
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
logv("alsa-init: got period size %li\n", sc->chunk_size);
|
|
}
|
|
pcm_params.outburst = sc->chunk_size * sc->bytes_per_sample;
|
|
|
|
/* setting software parameters */
|
|
if ((err = snd_pcm_sw_params_current(sc->alsa_handler, sc->alsa_swparams)) < 0)
|
|
{
|
|
logw("MSGTR_AO_ALSA_UnableToGetSwParameters");
|
|
return 0;
|
|
}
|
|
#if SND_LIB_VERSION >= 0x000901
|
|
if ((err = snd_pcm_sw_params_get_boundary(sc->alsa_swparams, &boundary)) < 0)
|
|
{
|
|
logw("MSGTR_AO_ALSA_UnableToGetBoundary");
|
|
return 0;
|
|
}
|
|
#else
|
|
boundary = 0x7fffffff;
|
|
#endif
|
|
/* start playing when one period has been written */
|
|
if ((err = snd_pcm_sw_params_set_start_threshold(sc->alsa_handler,
|
|
sc->alsa_swparams, sc->chunk_size)) < 0)
|
|
{
|
|
logw("MSGTR_AO_ALSA_UnableToSetStartThreshold");
|
|
return 0;
|
|
}
|
|
/* disable underrun reporting */
|
|
if ((err = snd_pcm_sw_params_set_stop_threshold(sc->alsa_handler,
|
|
sc->alsa_swparams, boundary)) < 0)
|
|
{
|
|
logw("MSGTR_AO_ALSA_UnableToSetStopThreshold");
|
|
return 0;
|
|
}
|
|
#if SND_LIB_VERSION >= 0x000901
|
|
/* play silence when there is an underrun */
|
|
if ((err = snd_pcm_sw_params_set_silence_size(sc->alsa_handler,
|
|
sc->alsa_swparams, boundary)) < 0)
|
|
{
|
|
logw("MSGTR_AO_ALSA_UnableToSetSilenceSize");
|
|
return 0;
|
|
}
|
|
#endif
|
|
if ((err = snd_pcm_sw_params(sc->alsa_handler, sc->alsa_swparams)) < 0)
|
|
{
|
|
logw("MSGTR_AO_ALSA_UnableToGetSwParameters");
|
|
return 0;
|
|
}
|
|
/* end setting sw-params */
|
|
logv("alsa: %d Hz/%d channels/%d bpf/%d bytes buffer\n",
|
|
pcm_params.samplerate, pcm_params.channels,
|
|
(int)sc->bytes_per_sample, pcm_params.buffersize);
|
|
|
|
} // end switch alsa_handler (spdif)
|
|
sc->alsa_can_pause = snd_pcm_hw_params_can_pause(sc->alsa_hwparams);
|
|
|
|
}
|
|
else if(sc->eStatus == SD_STATUS_PAUSED)
|
|
{
|
|
if(snd_pcm_state(sc->alsa_handler) == SND_PCM_STATE_SUSPENDED) //it is right?
|
|
{
|
|
logw("MSGTR_AO_ALSA_PcmInSuspendModeTryingResume");
|
|
while((err = snd_pcm_resume(sc->alsa_handler)) == -EAGAIN)
|
|
sleep(1);
|
|
}
|
|
|
|
if(0 && sc->alsa_can_pause) //TODO fix
|
|
{
|
|
if((err = snd_pcm_pause(sc->alsa_handler, 0)) < 0)
|
|
{
|
|
logw("MSGTR_AO_ALSA_PcmResumeError");
|
|
return -1;
|
|
}
|
|
logw("alsa-resume: resume supported by hardware\n");
|
|
}
|
|
else
|
|
{
|
|
if((err = snd_pcm_prepare(sc->alsa_handler)) < 0)
|
|
{
|
|
logw("MSGTR_AO_ALSA_PcmPrepareError");
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
sc->eStatus = SD_STATUS_STARTED;
|
|
pthread_mutex_unlock(&sc->mutex);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int __Stop(SoundCtrl* s)
|
|
{
|
|
int ret;
|
|
SoundCtrlContext* sc;
|
|
|
|
sc = (SoundCtrlContext*)s;
|
|
|
|
ret = SoundDeviceStop_l(sc);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int SoundDeviceStop_l(SoundCtrlContext* sc)
|
|
{
|
|
int err = 0;
|
|
|
|
if(sc->eStatus == SD_STATUS_STOPPED)
|
|
{
|
|
logw("Sound device already stopped.");
|
|
return 0;
|
|
}
|
|
|
|
if ((err = snd_pcm_drop(sc->alsa_handler)) < 0)
|
|
{
|
|
logw("MSGTR_AO_ALSA_PcmPrepareError");
|
|
return err;
|
|
}
|
|
if ((err = snd_pcm_prepare(sc->alsa_handler)) < 0)
|
|
{
|
|
logw("MSGTR_AO_ALSA_PcmPrepareError");
|
|
return err;
|
|
}
|
|
|
|
if(sc->eStatus != SD_STATUS_STOPPED)
|
|
{
|
|
if (sc->alsa_handler)
|
|
{
|
|
int err;
|
|
if ((err = snd_pcm_close(sc->alsa_handler)) < 0)
|
|
{
|
|
logw("MSGTR_AO_ALSA_PcmCloseError");
|
|
}
|
|
else
|
|
{
|
|
sc->alsa_handler = NULL;
|
|
logd("alsa-uninit: pcm closed\n");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
logw("MSGTR_AO_ALSA_NoHandlerDefined");
|
|
}
|
|
|
|
sc->eStatus = SD_STATUS_STOPPED;
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static int __Pause(SoundCtrl* s)
|
|
{
|
|
int ret;
|
|
SoundCtrlContext* sc;
|
|
|
|
sc = (SoundCtrlContext*)s;
|
|
int err;
|
|
|
|
if (0 && sc->alsa_can_pause) //TODO fix
|
|
{
|
|
if ((err = snd_pcm_pause(sc->alsa_handler, 1)) < 0)
|
|
{
|
|
logw("MSGTR_AO_ALSA_PcmPauseError");
|
|
return -1;
|
|
}
|
|
logw("alsa-pause: pause supported by hardware\n");
|
|
|
|
}
|
|
else
|
|
{
|
|
if ((err = snd_pcm_drop(sc->alsa_handler)) < 0)
|
|
{
|
|
logw("MSGTR_AO_ALSA_PcmDropError");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
sc->eStatus = SD_STATUS_PAUSED;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int __Write(SoundCtrl* s, void* pData, int nDataSize)
|
|
{
|
|
int ret;
|
|
SoundCtrlContext* sc;
|
|
|
|
sc = (SoundCtrlContext*)s;
|
|
|
|
if(sc->eStatus == SD_STATUS_STOPPED || sc->eStatus == SD_STATUS_PAUSED)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int num_frames = nDataSize / sc->bytes_per_sample;
|
|
snd_pcm_sframes_t res = 0;
|
|
|
|
logv("alsa-play: frames=%i, len=%i\n",num_frames,nDataSize);
|
|
if (!sc->alsa_handler)
|
|
{
|
|
logw("MSGTR_AO_ALSA_DeviceConfigurationError");
|
|
return 0;
|
|
}
|
|
|
|
if (num_frames == 0)
|
|
return 0;
|
|
|
|
do
|
|
{
|
|
res = snd_pcm_writei(sc->alsa_handler, pData, num_frames);
|
|
if (res == -EINTR)
|
|
{
|
|
/* nothing to do */
|
|
res = 0;
|
|
}
|
|
else if (res == -ESTRPIPE)
|
|
{
|
|
/* suspend */
|
|
logw("MSGTR_AO_ALSA_PcmInSuspendModeTryingResume");
|
|
while ((res = snd_pcm_resume(sc->alsa_handler)) == -EAGAIN)
|
|
sleep(1);
|
|
}
|
|
if (res < 0)
|
|
{
|
|
logw("MSGTR_AO_ALSA_WriteError");
|
|
if ((res = snd_pcm_prepare(sc->alsa_handler)) < 0)
|
|
{
|
|
logw("MSGTR_AO_ALSA_PcmPrepareError");
|
|
return 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
while (res == 0);
|
|
|
|
return res < 0 ? res : res * sc->bytes_per_sample;
|
|
}
|
|
|
|
|
|
//* called at player seek operation.
|
|
static int __Reset(SoundCtrl* s)
|
|
{
|
|
return SoundDeviceStop(s);
|
|
}
|
|
|
|
|
|
static int __GetCachedTime(SoundCtrl* s)
|
|
{
|
|
int ret;
|
|
SoundCtrlContext* sc;
|
|
|
|
sc = (SoundCtrlContext*)s;
|
|
|
|
if (sc->alsa_handler)
|
|
{
|
|
snd_pcm_sframes_t delay;
|
|
|
|
if (snd_pcm_delay(sc->alsa_handler, &delay) < 0)
|
|
return 0;
|
|
|
|
if (delay < 0)
|
|
{
|
|
/* underrun - move the application pointer forward to catch up */
|
|
#if SND_LIB_VERSION >= 0x000901 /* snd_pcm_forward() exists since 0.9.0rc8 */
|
|
snd_pcm_forward(sc->alsa_handler, -delay);
|
|
#endif
|
|
delay = 0;
|
|
}
|
|
return ((int)((float) delay * 1000000 / (float) pcm_params.samplerate));
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
}
|
|
|
|
static int __GetFrameCount(SoundCtrl* s)
|
|
{
|
|
SoundCtrlContext* sc;
|
|
sc = (SoundCtrlContext*)s;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static SoundControlOpsT mSoundControlOps =
|
|
{
|
|
.destroy = __Release,
|
|
.setFormat = __SetFormat,
|
|
.start = __Start,
|
|
.stop = __Stop,
|
|
.pause = __Pause,
|
|
.write = __Write,
|
|
.reset = __Reset,
|
|
.getCachedTime = __GetCachedTime,
|
|
.getFrameCount = __GetFrameCount,
|
|
};
|
|
|
|
SoundCtrl* SoundDeviceCreate()
|
|
{
|
|
SoundCtrlContext* s;
|
|
logd("== SoundDeviceInit");
|
|
s = (SoundCtrlContext*)malloc(sizeof(SoundCtrlContext));
|
|
if(s == NULL)
|
|
{
|
|
loge("malloc memory fail.");
|
|
return NULL;
|
|
}
|
|
memset(s, 0, sizeof(SoundCtrlContext));
|
|
|
|
s->base.ops = &mSoundControlOps;
|
|
|
|
s->eStatus = SD_STATUS_STOPPED;
|
|
s->alsa_fragcount = 16;
|
|
|
|
pthread_mutex_init(&s->mutex, NULL);
|
|
return (SoundCtrl*)s;
|
|
}
|