PulseAudio 异步方式播放音频,并控制流的软件音量

本文介绍了如何使用PulseAudio进行音频流处理,包括设置回调函数、处理延迟、连接到alsa_output设备,以及状态管理和流操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

• pulseaudio官网

https://www.freedesktop.org/wiki/Software/PulseAudio/

•  pulseaudio-under-the-hood

https://gavv.github.io/articles/pulseaudio-under-the-hood/

• Developer Documentation

https://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/

• PulseAudio Documentation

https://freedesktop.org/software/pulseaudio/doxygen/

过了压力测试

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include <stdio.h>
#include <string.h>
#include <pulse/pulseaudio.h>
#include <math.h>
#include "err_code.hpp"
static int latency = 20000; // start latency in micro seconds
static int sampleoffs = 0;
static uint8_t sampledata[98382];
static pa_buffer_attr bufattr;
static int underflows = 0;
static pa_sample_spec ss;

// This callback gets called when our context changes state.  We really only
// care about when it's ready or if it has failed
void pa_state_cb(pa_context *c, void *userdata) {
  pa_context_state_t state;
  int *pa_ready = userdata;
  state = pa_context_get_state(c);
  LOGI(TAG,"ctx state is %d\n",state);
  switch  (state) {
    // These are just here for reference
  case PA_CONTEXT_UNCONNECTED:
  case PA_CONTEXT_CONNECTING:
  case PA_CONTEXT_AUTHORIZING:
  case PA_CONTEXT_SETTING_NAME:
  default:
    break;
  case PA_CONTEXT_FAILED:
  case PA_CONTEXT_TERMINATED:
    *pa_ready = 2;
    break;
  case PA_CONTEXT_READY:
    *pa_ready = 1;
    break;
  }
}
static pa_stream *playstream;
static pa_context *pa_ctx;
static pa_threaded_mainloop *pa_ml;
static pa_mainloop_api *pa_mlapi;
//#include "./src/MiscUtils.cpp"
#include <mutex>
#include <condition_variable>
typedef enum PLAY_STATE {
    UNKNOWN_YFCF,
    STOPPED_YFCF,  /**< The stream is DISCONNECT */
    PAUSED_YFCF,     /***/
    PLAYING_YFCF,
    STOPPING_YFCF,
    STARTING_YFCF
} play_state_t;
static std::mutex gLock;
static std::condition_variable gCond;
static volatile play_state_t gPlayState = UNKNOWN_YFCF; //-1 unknow;0 end;1 playing
static void context_drain_complete(pa_context*c, void *userdata) {
    pa_context_disconnect(c);
    LOGI(TAG,"DISCONNECT ctx ");
}
static void stream_drain_complete(pa_stream*s, int success, void *userdata) {
    if (!success) {
        LOGE(TAG,"Failed to drain stream: %s", pa_strerror(pa_context_errno(pa_ctx)));
        
    }
    int r;
    pa_operation *o = NULL;
    r = pa_stream_disconnect(playstream);
    LOGI(TAG,"DISCONNECT S %d",r);
    pa_stream_unref(playstream);
    playstream = NULL;
    if (!(o = pa_context_drain(pa_ctx, context_drain_complete, NULL))){
        pa_context_disconnect(pa_ctx);
        LOGI(TAG,"DISCONNECT ctx %d",r);
    }
    else {
        pa_operation_unref(o);
        
    }
   
}
static void start_drain(void) {

    if (playstream) {
        pa_operation *o;

        pa_stream_set_write_callback(playstream, NULL, NULL);

        if (!(o = pa_stream_drain(playstream, stream_drain_complete, NULL))) {
            LOGW(TAG,"pa_stream_drain(): %s", pa_strerror(pa_context_errno(pa_ctx)));
            
        }

        pa_operation_unref(o);
    } 
}
static uint32_t cork_requests = 0;
static void stream_event_callback(pa_stream *s, const char *name, pa_proplist *pl, void *userdata) {
  
   LOGI(TAG,"Cork ev %s",name);

    
}
static void stream_request_cb(pa_stream *s, size_t length, void *userdata) {

  
  
  if (length > 98382-sampleoffs) {
    length = 98382-sampleoffs;
  }
  //printf("off=%d len=%d\n",sampleoffs,length);
  pa_stream_write(s, &sampledata[sampleoffs], length, NULL, 0LL, PA_SEEK_RELATIVE);
  sampleoffs += length;
  //printf("----------off=%d len=%d\n",sampleoffs,length);
  if (sampleoffs + length >= 98382) {
    sampleoffs = 0;
   
  }
  
}

static void pa_stream_st_cb(pa_stream *p, void *userdata){
  pa_stream_state_t  st = pa_stream_get_state(p);
  LOGI(TAG,"stream state is %d\n",st);
 
  
}
static void stream_underflow_cb(pa_stream *s, void *userdata) {
  // We increase the latency by 50% if we get 6 underflows and latency is under 2s
  // This is very useful for over the network playback that can't handle low latencies
  #if 0
  printf("underflow\n");
  underflows++;
  if (underflows >= 6 && latency < 2000000) {
    latency = (latency*3)/2;
    bufattr.maxlength = pa_usec_to_bytes(latency,&ss);
    bufattr.tlength = pa_usec_to_bytes(latency,&ss);  
    pa_stream_set_buffer_attr(s, &bufattr, NULL, NULL);
    underflows = 0;
    printf("latency increased to %d\n", latency);
  }
  #endif
}

void pauseStream(){
 
}
void stopStream(){
  
        pa_threaded_mainloop_stop(pa_ml);

    
        pa_stream_disconnect(playstream);
        pa_stream_unref(playstream);
    

    
        pa_context_disconnect(pa_ctx);
        pa_context_unref(pa_ctx);
    

    
        pa_threaded_mainloop_free(pa_ml);
}
int startStream(float vol) {
  
  
  
  
  int r;
  int pa_ready = 0;
  int retval = 0;
  unsigned int a;
  double amp;

  // Create some data to play
  #if 0
  for (a=0; a<sizeof(sampledata)/2; a++) {
    amp = cos(5000*(double)a/44100.0);
    sampledata[a] = amp * 32000.0;
  }
  #endif
  ///usr/yf/Game/etc
  FILE * sFp = fopen("/home/cf/myprj/shock_0_p5s_45hz.wav","r");
  if(sFp == NULL)
  {
        LOGE(TAG, "fopen  err:%s", strerror(errno));
        return -1;
  }
  size_t readed = 0;
  size_t wed = 0;
  int32_t ret = 0;
  while( (readed += fread(sampledata,1,98382-readed,sFp)) != 98382 ){
      
  }
  fclose(sFp);
  pa_ready = 0;
  // Create a mainloop API and connection to the default server
  pa_ml = pa_threaded_mainloop_new();
  pa_mlapi = pa_threaded_mainloop_get_api(pa_ml);
  pa_ctx = pa_context_new(pa_mlapi, "yfcf_player ctx");
  pa_context_connect(pa_ctx, NULL, 0, NULL);

  // This function defines a callback so the server will tell us it's state.
  // Our callback will wait for the state to be ready.  The callback will
  // modify the variable to 1 so we know when we have a connection and it's
  // ready.
  // If there's an error, the callback will set pa_ready to 2
  pa_context_set_state_callback(pa_ctx, pa_state_cb, &pa_ready);

  // We can't do anything until PA is ready, so just iterate the mainloop
  // and continue
  pa_threaded_mainloop_start(pa_ml);
  
  ss.rate = 48000;
  ss.channels = 2;
  ss.format = PA_SAMPLE_S16LE;
  playstream = pa_stream_new(pa_ctx, "yfcf_stream", &ss, NULL);
  if (!playstream) {
    LOGE(TAG,"pa_stream_new failed\n");
  }

pa_threaded_mainloop_lock(pa_ml);
  pa_stream_set_write_callback(playstream, stream_request_cb, NULL);
  pa_stream_set_underflow_callback(playstream, stream_underflow_cb, NULL);
  pa_stream_set_state_callback(playstream,pa_stream_st_cb,NULL);
  pa_stream_set_event_callback(playstream, stream_event_callback, NULL);
  bufattr.fragsize = (uint32_t)-1;
  bufattr.maxlength = pa_usec_to_bytes(latency,&ss);
  bufattr.minreq = pa_usec_to_bytes(0,&ss);
  bufattr.prebuf = (uint32_t)-1;
  bufattr.tlength = pa_usec_to_bytes(latency,&ss);
//alsa_output.pci-0000_07_00.6.analog-stereo
  r = pa_stream_connect_playback(playstream, "alsa_output.pci-0000_02_02.0.analog-stereo", &bufattr,
                                 PA_STREAM_INTERPOLATE_TIMING
                                 |PA_STREAM_ADJUST_LATENCY
                                 |PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL);
  if (r < 0) {
    // Old pulse audio servers don't like the ADJUST_LATENCY flag, so retry without that
    r = pa_stream_connect_playback(playstream, "alsa_output.pci-0000_07_00.6.analog-stereo", &bufattr,
                                   PA_STREAM_INTERPOLATE_TIMING|
                                   PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL);
  }
  
 // pa_operation_unref(pa_context_set_sink_input_volume(c, pa_stream_get_index(playstream), &cv, simple_callback, NULL));
  if (r < 0) {
    LOGE(TAG,"pa_stream_connect_playback failed\n");
    retval = -1;
    
  }

pa_threaded_mainloop_unlock(pa_ml);

  // Run the mainloop until pa_mainloop_quit() is called
  // (this example never calls it, so the mainloop runs forever).
 
 gPlayState=PLAYING_YFCF;
 return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

无v邪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值