Using DirectSound to Play Audio Stream Data

本文介绍如何使用DirectSound灵活地播放音频流数据,并提供控制方法如播放、暂停、停止等。通过设置回调函数获取音频数据,实现精确的音频位置控制。

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

Introduction

This article with its code shows how to play audio stream data with DirectSound. It gives a more flexible method to control the stream data. The demo shows how to play, pause, stop, seek a small or a big WAV file.

Background

Before you read the code, you should know something about DirectSound. You can find the related material in:

MSDN\Graphics and Multimedia\DirectX\SDK Documentation\DiretX8.1(C++)\DirectX Audio

Using the code

The CMyDirectSound has the following public method:

HRESULT SetFormat(const WAVEFORMATEX WFE);
HRESULT SetCallback(LPGETAUDIOSAMPLES_PROGRESS Function_Callback, LPVOID lpData);
typedef HRESULT  (WINAPI *LPGETAUDIOSAMPLES_PROGRESS)(int iAudioChannel, 
                                                      LPBYTE lpDesBuf,
                                                      const DWORD dwRequiredSamples, 
                                                      DWORD &dwRetSamples, 
                                                      LPVOID lpData);
void Play();
void Pause();
void Stop();
void Release();
DWORD GetSamplesPlayed();

First, you should give the information of the audio data you want to play, with "SetFormat":

WAVEFORMATEX formatWav;
m_pWavFile->Open((LPTSTR)(LPCTSTR)m_strFileName, &formatWav, WAVEFILE_READ);
formatWav = *m_pWavFile->GetFormat();

if (NULL == m_pDYDS) {

  m_pDYDS = new CDYDirectSound;
}
m_pDYDS->SetFormat(formatWav);

Second, set the callback function which is to get the audio stream data:

m_pMyDS->SetCallback(GetSamples, this);

Certainly, the body of the callback function should be written by yourself. Then you can play, pause, stop the audio data.

The "GetSamplesPlayed" method will give the number of audio samples played after you begin play. You can use this method to give the playing position.

Points of Interest

I think there're two key points in this class.

1. How to control the DirectSound buffer circle?

I create a second DirectSound buffer with two seconds duration:

//Create Second Sound Buffer
dsbd.dwFlags = DSBCAPS_CTRLPOSITIONNOTIFY;
dsbd.dwBufferBytes = 2*m_WFE.nAvgBytesPerSec; //2-Second Buffer 
dsbd.lpwfxFormat = &m_WFE;

if ( FAILED(m_lpDS->CreateSoundBuffer(&dsbd, &m_lpDSB, NULL)) ) {

  OutputDebugString(_T("Create Second Sound Buffer Failed!"));
  m_strLastError = _T("MyDirectSound SetFormat Failed!");
  return;
}

And I set two DirectSound notify at 0.5 second and 1.5 second:

               (1st Notify)                          (2nd Notify)
0              0.5Second        1Second              1.5Second         2Second
|                  |               |                     |                 |
---------------------------------------------------------------------------
|                                  |                                       |
|--------------------------------------------------------------------------|
//Set Direct Sound Buffer Notify Position
DSBPOSITIONNOTIFY pPosNotify[2];
pPosNotify[0].dwOffset = m_WFE.nAvgBytesPerSec/2 - 1;
pPosNotify[1].dwOffset = 3*m_WFE.nAvgBytesPerSec/2 - 1;  
pPosNotify[0].hEventNotify = m_pHEvent[0];
pPosNotify[1].hEventNotify = m_pHEvent[1];

if ( FAILED(lpDSBNotify->SetNotificationPositions(2, pPosNotify)) ) {

  OutputDebugString(_T("Set NotificationPosition Failed!"));
  m_strLastError = _T("MyDirectSound SetFormat Failed!");
  return;
}

When you call the Play method, a timer will be triggered. The "TimerProcess" function will be called every 300 milliseconds.

//Beging Play
m_lpDSB->Play(0, 0, DSBPLAY_LOOPING);

//timeSetEvent
m_timerID = timeSetEvent(300, 100, TimerProcess, 
    (DWORD)this, TIME_PERIODIC | TIME_CALLBACK_FUNCTION);

In the "TimerProcess" function, the next second audio stream data will be gotten when the current play cursor arrives the 1st or 2nd notify point.

void CALLBACK TimerProcess(UINT uTimerID, UINT uMsg, 
                           DWORD dwUser, DWORD dw1, DWORD dw2)
{
  CMyDirectSound *pDDS = (CMyDirectSound *)dwUser;
  pDDS->TimerCallback();  
}
//<\TimerProcess>

void CMyDirectSound::TimerCallback()
{
  LPVOID lpvAudio1 = NULL, lpvAudio2 = NULL;
  DWORD dwBytesAudio1 = 0, dwBytesAudio2 = 0;
  DWORD dwRetSamples = 0, dwRetBytes = 0;

  HRESULT hr = WaitForMultipleObjects(2, m_pHEvent, FALSE, 0);
  if(WAIT_OBJECT_0 == hr) {

    m_dwCircles1++;

    //Lock DirectSoundBuffer Second Part
    HRESULT hr = m_lpDSB->Lock(m_WFE.nAvgBytesPerSec, m_WFE.nAvgBytesPerSec,
    &lpvAudio1, &dwBytesAudio1,&lpvAudio2, &dwBytesAudio2, 0);
    if ( FAILED(hr) ) {

      m_strLastError = _T("Lock DirectSoundBuffer Failed!");
      OutputDebugString(m_strLastError);
      return;
    }    
  }
  else if (WAIT_OBJECT_0 + 1 == hr) {    

    m_dwCircles2++;

    //Lock DirectSoundBuffer First Part
    HRESULT hr = m_lpDSB->Lock(0, m_WFE.nAvgBytesPerSec, 
    &lpvAudio1, &dwBytesAudio1, &lpvAudio2, &dwBytesAudio2, 0);
    if ( FAILED(hr) ) {

      m_strLastError = _T("Lock DirectSoundBuffer Failed!");
      OutputDebugString(m_strLastError);
      return;
    }    
  }
  else {

    return;
  }

  //Get 1 Second Audio Buffer 
  m_lpGETAUDIOSAMPLES(m_lpAudioBuf, m_WFE.nSamplesPerSec, dwRetSamples, m_lpData);
  dwRetBytes = dwRetSamples*m_WFE.nBlockAlign;
  
  //If near the end of the audio data
  if (dwRetSamples < m_WFE.nSamplesPerSec) {

    DWORD dwRetBytes = dwRetSamples*m_WFE.nBlockAlign;
    memset(m_lpAudioBuf+dwRetBytes, 0, m_WFE.nAvgBytesPerSec - dwRetBytes);        
  }
  
  //Copy AudioBuffer to DirectSoundBuffer
  if (NULL == lpvAudio2) {

    memcpy(lpvAudio1, m_lpAudioBuf, dwBytesAudio1);
  }
  else {

    memcpy(lpvAudio1, m_lpAudioBuf, dwBytesAudio1);
    memcpy(lpvAudio2, m_lpAudioBuf + dwBytesAudio1, dwBytesAudio2);
  }
  
  //Unlock DirectSoundBuffer
  m_lpDSB->Unlock(lpvAudio1, dwBytesAudio1, lpvAudio2, dwBytesAudio2);
}
//<\TimerCallback>

2. How the following callback function works?

//Get Audio Buffer
  m_lpGETAUDIOSAMPLES(m_lpAudioBuf, m_WFE.nSamplesPerSec, dwRetSamples, m_lpData);

Do you remember what you have done when you set the callback function?

void CDYDirectSound::SetCallback(LPGETAUDIOSAMPLES_PROGRESS Function_Callback, 
                                 LPVOID lpData)
{
  m_lpGETAUDIOSAMPLES = Function_Callback;
  m_lpData = lpData;
}
//<\SetCallback>

Yes, you transfer the GETAUDIOSAMPLES_PROGRESS function's pointer to m_lpGETAUDIOSAMPLES.

m_pMyDS->SetCallback(GetSamples, this);

And the GetSamples is defined as:

HRESULT CALLBACK GetSamples(LPBYTE lpDesBuf, 
                            const DWORD dwRequiredSamples, 
                            DWORD &dwRetSamples, 
                            LPVOID lpData)
{
  CDirectSoundTestDlg *pDlg = (CDirectSoundTestDlg *)lpData;
  pDlg->GetAudioSamples(lpDesBuf, dwRequiredSamples, dwRetSamples);
  return 0;
}
//<\GetSamples>

HRESULT CDirectSoundTestDlg::GetAudioSamples(LPBYTE lpDesBuf,
                                             const DWORD dwRequiredSamples,
                                             DWORD &dwRetSamples)
{
  DWORD dwRequiredBytes = 0, dwRetBytes = 0;
  WAVEFORMATEX *pWFE = m_pWavFile->GetFormat();
  dwRequiredBytes = dwRequiredSamples*pWFE->nBlockAlign;
  m_pWavFile->Read(lpDesBuf, dwRequiredBytes, &dwRetBytes);
  dwRetSamples = dwRetBytes/pWFE->nBlockAlign;
  return dwRetBytes;
}
//<\GetAudioSamples>

You can write your own "GetAudioSamples" to get the audio stream data.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Audio Stream.h class AudioDevice; typedef unsigned int app_type_t; class StreamPrimary { public: StreamPrimary(audio_io_handle_t handle, const std::set<audio_devices_t> &devices, struct audio_config *config); virtual ~StreamPrimary(); uint32_t GetSampleRate(); uint32_t GetBufferSize(); audio_format_t GetFormat(); audio_channel_mask_t GetChannelMask(); int getPalDeviceIds(const std::set<audio_devices_t> &halDeviceIds, pal_device_id_t* palOutDeviceIds); audio_io_handle_t GetHandle(); int GetUseCase(); std::mutex write_wait_mutex_; std::condition_variable write_condition_; std::mutex stream_mutex_; bool write_ready_; std::mutex drain_wait_mutex_; std::condition_variable drain_condition_; bool drain_ready_; stream_callback_t client_callback; void *client_cookie; static int GetDeviceAddress(struct str_parms *parms, int *card_id, int *device_num); int GetLookupTableIndex(const struct string_to_enum *table, const int table_size, int value); bool GetSupportedConfig(bool isOutStream, struct str_parms *query, struct str_parms *reply); virtual int RouteStream(const std::set<audio_devices_t>&, bool force_device_switch = false) = 0; bool isStarted() { return stream_started_; }; protected: struct pal_stream_attributes streamAttributes_; pal_stream_handle_t* pal_stream_handle_; audio_io_handle_t handle_; pal_device_id_t pal_device_id_; struct audio_config config_; char address_[AUDIO_DEVICE_MAX_ADDRESS_LEN]; bool stream_started_ = false; bool stream_paused_ = false; bool stream_flushed_ = false; int usecase_; struct pal_volume_data *volume_; /* used to cache volume */ std::map <audio_devices_t, pal_device_id_t> mAndroidDeviceMap; int mmap_shared_memory_fd; app_type_t app_types_; pal_param_device_capability_t *device_cap_query_; app_type_t audio_power_app_types_;/* Audio PowerSave */ }; class StreamOutPrimary : public StreamPrimary { private: // Helper function for write to open pal stream & configure. ssize_t configurePalOutputStream(); //Helper method to standby streams upon write failures and sleep for buffer duration. ssize_t onWriteError(size_t bytes, ssize_t ret); protected: struct pal_device* mPalOutDevice; private: pal_device_id_t* mPalOutDeviceIds; std::set<audio_devices_t> mAndroidOutDevices; bool mInitialized; /* fixed ear_out aux_out stereo start */ bool mIsKaraokeMuteOnCombo; /* fixed ear_out aux_out stereo end */ // [offload playspeed bool isOffloadUsecase() { return GetUseCase() == USECASE_AUDIO_PLAYBACK_OFFLOAD; } bool isOffloadSpeedSupported(); bool isValidPlaybackRate(const audio_playback_rate_t *playbackRate); bool isValidStretchMode(audio_timestretch_stretch_mode_t stretchMode); bool isValidFallbackMode(audio_timestretch_fallback_mode_t fallbackMode); int setPlaybackRateToPal(const audio_playback_rate_t *playbackRate); audio_playback_rate_t mPlaybackRate = AUDIO_PLAYBACK_RATE_INITIALIZER; // offload Playspeed] public: StreamOutPrimary(audio_io_handle_t handle, const std::set<audio_devices_t>& devices, audio_output_flags_t flags, struct audio_config *config, const char *address, offload_effects_start_output fnp_start_offload_effect, offload_effects_stop_output fnp_stop_offload_effect, visualizer_hal_start_output fnp_visualizer_start_output_, visualizer_hal_stop_output fnp_visualizer_stop_output_); ~StreamOutPrimary(); bool sendGaplessMetadata = true; bool isCompressMetadataAvail = false; void UpdatemCachedPosition(uint64_t val); virtual int Standby(); int SetVolume(float left, float right); int refactorVolumeData(float left, float right); uint64_t GetFramesWritten(struct timespec *timestamp); virtual int SetParameters(struct str_parms *parms); int Pause(); int Resume(); int Drain(audio_drain_type_t type); int Flush(); virtual int Start(); int Stop(); virtual ssize_t write(const void *buffer, size_t bytes); virtual int Open(); void GetStreamHandle(audio_stream_out** stream); uint32_t GetBufferSize(); uint32_t GetBufferSizeForLowLatency(); int GetFrames(uint64_t *frames); static pal_stream_type_t GetPalStreamType(audio_output_flags_t halStreamFlags, uint32_t sample_rate, bool isDeviceAvail); static int64_t GetRenderLatency(audio_output_flags_t halStreamFlags); int GetOutputUseCase(audio_output_flags_t halStreamFlags); int StartOffloadEffects(audio_io_handle_t, pal_stream_handle_t*); int StopOffloadEffects(audio_io_handle_t, pal_stream_handle_t*); bool CheckOffloadEffectsType(pal_stream_type_t pal_stream_type); int StartOffloadVisualizer(audio_io_handle_t, pal_stream_handle_t*); int StopOffloadVisualizer(audio_io_handle_t, pal_stream_handle_t*); audio_output_flags_t flags_; int CreateMmapBuffer(int32_t min_size_frames, struct audio_mmap_buffer_info *info); int GetMmapPosition(struct audio_mmap_position *position); bool isDeviceAvailable(pal_device_id_t deviceId); int RouteStream(const std::set<audio_devices_t>&, bool force_device_switch = false); virtual void SetMode(audio_mode_t mode) = 0; ssize_t splitAndWriteAudioHapticsStream(const void *buffer, size_t bytes); bool period_size_is_plausible_for_low_latency(int period_size); source_metadata_t btSourceMetadata; std::vector<playback_track_metadata_t> tracks; int SetAggregateSourceMetadata(bool voice_active); static std::mutex sourceMetadata_mutex_; // [offload playback speed int getPlaybackRateParameters(audio_playback_rate_t *playbackRate); int setPlaybackRateParameters(const audio_playback_rate_t *playbackRate); // offload playback speed] protected: struct timespec writeAt; int get_compressed_buffer_size(); int get_pcm_buffer_size(); int is_direct(); audio_format_t halInputFormat = AUDIO_FORMAT_DEFAULT; audio_format_t halOutputFormat = AUDIO_FORMAT_DEFAULT; uint32_t convertBufSize; uint32_t fragments_ = 0; uint32_t fragment_size_ = 0; pal_snd_dec_t palSndDec; struct pal_compr_gapless_mdata gaplessMeta = {0, 0}; uint32_t msample_rate; uint16_t mchannels; std::shared_ptr<audio_stream_out> stream_; uint64_t mBytesWritten; /* total bytes written, not cleared when entering standby */ uint64_t mCachedPosition = 0; /* cache pcm offload position when entering standby */ offload_effects_start_output fnp_offload_effect_start_output_ = nullptr; offload_effects_stop_output fnp_offload_effect_stop_output_ = nullptr; visualizer_hal_start_output fnp_visualizer_start_output_ = nullptr; visualizer_hal_stop_output fnp_visualizer_stop_output_ = nullptr; void *convertBuffer; //Haptics Usecase struct pal_stream_attributes hapticsStreamAttributes; pal_stream_handle_t* pal_haptics_stream_handle; AudioExtn AudExtn; struct pal_device* hapticsDevice; uint8_t* hapticBuffer; size_t hapticsBufSize; audio_mode_t _mode; int FillHalFnPtrs(); friend class AudioDevice; struct timespec ts_first_write = {0, 0}; }; class StreamInPrimary : public StreamPrimary{ protected: struct pal_device* mPalInDevice; private: pal_device_id_t* mPalInDeviceIds; std::set<audio_devices_t> mAndroidInDevices; bool mInitialized; //Helper method to standby streams upon read failures and sleep for buffer duration. ssize_t onReadError(size_t bytes, size_t ret); public: StreamInPrimary(audio_io_handle_t handle, const std::set<audio_devices_t> &devices, audio_input_flags_t flags, struct audio_config *config, const char *address, audio_source_t source); ~StreamInPrimary(); int Standby(); int SetGain(float gain); void GetStreamHandle(audio_stream_in** stream); virtual int Open(); int Start(); int Stop(); int SetMicMute(bool mute); ssize_t read(const void *buffer, size_t bytes); uint32_t GetBufferSize(); uint32_t GetBufferSizeForLowLatencyRecord(); pal_stream_type_t GetPalStreamType(audio_input_flags_t halStreamFlags, uint32_t sample_rate); int GetInputUseCase(audio_input_flags_t halStreamFlags, audio_source_t source); int addRemoveAudioEffect(const struct audio_stream *stream, effect_handle_t effect,bool enable); virtual int SetParameters(const char *kvpairs); bool getParameters(struct str_parms *query, struct str_parms *reply); bool is_st_session; audio_input_flags_t flags_; int CreateMmapBuffer(int32_t min_size_frames, struct audio_mmap_buffer_info *info); int GetMmapPosition(struct audio_mmap_position *position); bool isDeviceAvailable(pal_device_id_t deviceId); int RouteStream(const std::set<audio_devices_t>& new_devices, bool force_device_switch = false); int64_t GetSourceLatency(audio_input_flags_t halStreamFlags); uint64_t GetFramesRead(int64_t *time); int GetPalDeviceIds(pal_device_id_t *palDevIds, int *numPalDevs); sink_metadata_t btSinkMetadata; std::vector<record_track_metadata_t> tracks; int SetAggregateSinkMetadata(bool voice_active); static std::mutex sinkMetadata_mutex_; pal_stream_handle_t *pal_vui_handle_; protected: struct timespec readAt; uint32_t fragments_ = 0; uint32_t fragment_size_ = 0; int FillHalFnPtrs(); std::shared_ptr<audio_stream_in> stream_; audio_source_t source_; friend class AudioDevice; uint64_t mBytesRead = 0; /* total bytes read, not cleared when entering standby */ // for compress capture usecase std::unique_ptr<CompressCapture::CompressAAC> mCompressEncoder; bool isECEnabled = false; bool isNSEnabled = false; bool effects_applied_ = true; //ADD: KARAOKE bool is_karaoke_on = false; int is_karaoke_status = 0; bool is_cts_stream = false; std::mutex activeStreamMutex; //END KARAOKE // MIUI ADD: Audio_XiaoAi bool is_map_switch = false; // END Audio_XiaoAi }; AudioStream.cpp int StreamOutPrimary::Standby() { int ret = 0; /* fixed ear_out aux_out stereo start */ std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance(); std::set<audio_devices_t> new_devices; /* fixed ear_out aux_out stereo end */ AHAL_DBG("Enter"); if (adevice->is_earout_hphl_conflict && mIsKaraokeMuteOnCombo) { AHAL_DBG("routestream from combo whs to whs before standby"); mAndroidOutDevices.erase(AUDIO_DEVICE_OUT_SPEAKER); new_devices = mAndroidOutDevices; StreamOutPrimary::RouteStream(new_devices, true); } stream_mutex_.lock(); if (pal_stream_handle_) { if (streamAttributes_.type == PAL_STREAM_PCM_OFFLOAD) { /* * when ssr happens, dsp position for pcm offload could be 0, * so get written frames. Else, get frames. */ if (PAL_CARD_STATUS_DOWN(AudioDevice::sndCardState)) { struct timespec ts; // release stream lock as GetFramesWritten will lock/unlock stream mutex stream_mutex_.unlock(); mCachedPosition = GetFramesWritten(&ts); stream_mutex_.lock(); AHAL_DBG("card is offline, return written frames %lld", (long long)mCachedPosition); } else { GetFrames(&mCachedPosition); } } ret = pal_stream_stop(pal_stream_handle_); if (ret) { AHAL_ERR("failed to stop stream."); ret = -EINVAL; } if (usecase_ == USECASE_AUDIO_PLAYBACK_WITH_HAPTICS && pal_haptics_stream_handle) { ret = pal_stream_stop(pal_haptics_stream_handle); if (ret) { AHAL_ERR("failed to stop haptics stream."); } } } stream_started_ = false; stream_paused_ = false; sendGaplessMetadata = true; if (CheckOffloadEffectsType(streamAttributes_.type)) { ret = StopOffloadEffects(handle_, pal_stream_handle_); ret = StopOffloadVisualizer(handle_, pal_stream_handle_); } if (pal_stream_handle_) { ret = pal_stream_close(pal_stream_handle_); pal_stream_handle_ = NULL; if (usecase_ == USECASE_AUDIO_PLAYBACK_WITH_HAPTICS && pal_haptics_stream_handle) { ret = pal_stream_close(pal_haptics_stream_handle); pal_haptics_stream_handle = NULL; if (hapticBuffer) { free (hapticBuffer); hapticBuffer = NULL; } hapticsBufSize = 0; if (hapticsDevice) { free(hapticsDevice); hapticsDevice = NULL; } } } if (karaoke) { ret = AudExtn.karaoke_stop(); if (ret) { AHAL_ERR("failed to stop karaoke path."); ret = 0; } else { ret = AudExtn.karaoke_close(); if (ret) { AHAL_ERR("failed to close karaoke path."); ret = 0; } } } if (mmap_shared_memory_fd >= 0) { close(mmap_shared_memory_fd); mmap_shared_memory_fd = -1; } /* fixed ear_out aux_out stereo start */ if (adevice->is_earout_hphl_conflict && mIsKaraokeMuteOnCombo) { const char kvp[] = "audio_karaoke_mute=0"; struct str_parms *parms = str_parms_create_str(kvp); if (parms) { AudioExtn::audio_extn_set_parameters(adevice, parms); mIsKaraokeMuteOnCombo = false; str_parms_destroy(parms); } else { AHAL_ERR("Error in str_parms_create_str"); } } /* fixed ear_out aux_out stereo end */ if (ret) ret = -EINVAL; exit: stream_mutex_.unlock(); AHAL_DBG("Exit ret: %d", ret); return ret; } StreamOutPrimary::StreamOutPrimary( audio_io_handle_t handle, const std::set<audio_devices_t> &devices, audio_output_flags_t flags, struct audio_config *config, const char *address __unused, offload_effects_start_output start_offload_effect, offload_effects_stop_output stop_offload_effect, visualizer_hal_start_output visualizer_start_output, visualizer_hal_stop_output visualizer_stop_output): StreamPrimary(handle, devices, config), mAndroidOutDevices(devices), flags_(flags), btSourceMetadata{0, nullptr} { stream_ = std::shared_ptr<audio_stream_out> (new audio_stream_out()); std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance(); mInitialized = false; /* fixed ear_out aux_out stereo start */ mIsKaraokeMuteOnCombo = false; bool isCombo = false; audio_devices_t OutDevices = AudioExtn::get_device_types(mAndroidOutDevices); /* fixed ear_out aux_out stereo end */ pal_stream_handle_ = nullptr; pal_haptics_stream_handle = nullptr; mPalOutDeviceIds = nullptr; mPalOutDevice = nullptr; convertBuffer = NULL; hapticsDevice = NULL; hapticBuffer = NULL; hapticsBufSize = 0; writeAt.tv_sec = 0; writeAt.tv_nsec = 0; mBytesWritten = 0; int noPalDevices = 0; int ret = 0; /*Initialize the gaplessMeta value with 0*/ memset(&gaplessMeta,0,sizeof(struct pal_compr_gapless_mdata)); if (!stream_) { AHAL_ERR("No memory allocated for stream_"); throw std::runtime_error("No memory allocated for stream_"); } AHAL_DBG("enter: handle (%x) format(%#x) sample_rate(%d) channel_mask(%#x) devices(%zu) flags(%#x)\ address(%s)", handle, config->format, config->sample_rate, config->channel_mask, mAndroidOutDevices.size(), flags, address); //TODO: check if USB device is connected or not if (AudioExtn::audio_devices_cmp(mAndroidOutDevices, audio_is_usb_out_device)){ // get capability from device of USB device_cap_query_ = (pal_param_device_capability_t *) calloc(1, sizeof(pal_param_device_capability_t)); if (!device_cap_query_) { AHAL_ERR("Failed to allocate mem for device_cap_query_"); goto error; } dynamic_media_config_t *dynamic_media_config = (dynamic_media_config_t *) calloc(1, sizeof(dynamic_media_config_t)); if (!dynamic_media_config) { free(device_cap_query_); AHAL_ERR("Failed to allocate mem for dynamic_media_config"); goto error; } size_t payload_size = 0; device_cap_query_->id = PAL_DEVICE_OUT_USB_DEVICE; device_cap_query_->addr.card_id = adevice->usb_card_id_; device_cap_query_->addr.device_num = adevice->usb_dev_num_; device_cap_query_->config = dynamic_media_config; device_cap_query_->is_playback = true; ret = pal_get_param(PAL_PARAM_ID_DEVICE_CAPABILITY, (void **)&device_cap_query_, &payload_size, nullptr); if (ret < 0) { AHAL_ERR("Error usb device is not connected"); free(dynamic_media_config); free(device_cap_query_); dynamic_media_config = NULL; device_cap_query_ = NULL; } else if (audio_is_linear_pcm(config->format) && AUDIO_OUTPUT_FLAG_NONE == flags) { // HIFI output port AHAL_DBG("use deep buffer for HIFI output on USBC hs"); flags_ = AUDIO_OUTPUT_FLAG_DEEP_BUFFER; } if (!config->sample_rate || !config->format || !config->channel_mask) { if (dynamic_media_config) { config->sample_rate = dynamic_media_config->sample_rate[0]; config->channel_mask = (audio_channel_mask_t) dynamic_media_config->mask[0]; config->format = (audio_format_t)dynamic_media_config->format[0]; } if (config->sample_rate == 0) config->sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE; if (config->channel_mask == AUDIO_CHANNEL_NONE) config->channel_mask = AUDIO_CHANNEL_OUT_STEREO; if (config->format == AUDIO_FORMAT_DEFAULT) config->format = AUDIO_FORMAT_PCM_16_BIT; memcpy(&config_, config, sizeof(struct audio_config)); AHAL_INFO("sample rate = %d channel_mask = %#x fmt = %#x", config->sample_rate, config->channel_mask, config->format); } } if (AudioExtn::audio_devices_cmp(mAndroidOutDevices, AUDIO_DEVICE_OUT_AUX_DIGITAL)){ AHAL_DBG("AUDIO_DEVICE_OUT_AUX_DIGITAL and DIRECT | OFFLOAD, check hdmi caps"); if (config->sample_rate == 0) { config->sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE; config_.sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE; } if (config->channel_mask == AUDIO_CHANNEL_NONE) { config->channel_mask = AUDIO_CHANNEL_OUT_5POINT1; config_.channel_mask = AUDIO_CHANNEL_OUT_5POINT1; } if (config->format == AUDIO_FORMAT_DEFAULT) { config->format = AUDIO_FORMAT_PCM_16_BIT; config_.format = AUDIO_FORMAT_PCM_16_BIT; } } usecase_ = GetOutputUseCase(flags_); if (address) { strlcpy((char *)&address_, address, AUDIO_DEVICE_MAX_ADDRESS_LEN); } else { AHAL_DBG("invalid address"); } fnp_offload_effect_start_output_ = start_offload_effect; fnp_offload_effect_stop_output_ = stop_offload_effect; fnp_visualizer_start_output_ = visualizer_start_output; fnp_visualizer_stop_output_ = visualizer_stop_output; if (mAndroidOutDevices.empty()) mAndroidOutDevices.insert(AUDIO_DEVICE_OUT_DEFAULT); AHAL_DBG("No of Android devices %zu", mAndroidOutDevices.size()); mPalOutDeviceIds = (pal_device_id_t*) calloc(mAndroidOutDevices.size(), sizeof(pal_device_id_t)); if (!mPalOutDeviceIds) { goto error; } noPalDevices = getPalDeviceIds(mAndroidOutDevices, mPalOutDeviceIds); if (noPalDevices != mAndroidOutDevices.size()) { AHAL_ERR("mismatched pal no of devices %d and hal devices %zu", noPalDevices, mAndroidOutDevices.size()); goto error; } mPalOutDevice = (struct pal_device*) calloc(mAndroidOutDevices.size(), sizeof(struct pal_device)); if (!mPalOutDevice) { goto error; } /* fixed ear_out aux_out stereo start */ if (adevice->is_earout_hphl_conflict) { if ((OutDevices == (AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_OUT_WIRED_HEADSET)) || (OutDevices == (AUDIO_DEVICE_OUT_SPEAKER | AUDIO_DEVICE_OUT_WIRED_HEADPHONE))) { isCombo = true; } } /* fixed ear_out aux_out stereo end */ /* TODO: how to update based on stream parameters and see if device is supported */ for (int i = 0; i < mAndroidOutDevices.size(); i++) { memset(mPalOutDevice[i].custom_config.custom_key, 0, sizeof(mPalOutDevice[i].custom_config.custom_key)); mPalOutDevice[i].id = mPalOutDeviceIds[i]; if (AudioExtn::audio_devices_cmp(mAndroidOutDevices, audio_is_usb_out_device)) mPalOutDevice[i].config.sample_rate = config_.sample_rate; else mPalOutDevice[i].config.sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE; mPalOutDevice[i].config.bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH; mPalOutDevice[i].config.aud_fmt_id = PAL_AUDIO_FMT_PCM_S16_LE; // TODO: need to convert this from output format AHAL_INFO("device rate = %d width = %#x fmt = %#x", mPalOutDevice[i].config.sample_rate, mPalOutDevice[i].config.bit_width, mPalOutDevice[i].config.aud_fmt_id); mPalOutDevice[i].config.ch_info = {0, {0}}; if ((mPalOutDeviceIds[i] == PAL_DEVICE_OUT_USB_DEVICE) || (mPalOutDeviceIds[i] == PAL_DEVICE_OUT_USB_HEADSET)) { mPalOutDevice[i].address.card_id = adevice->usb_card_id_; mPalOutDevice[i].address.device_num = adevice->usb_dev_num_; } strlcpy(mPalOutDevice[i].custom_config.custom_key, "", sizeof(mPalOutDevice[i].custom_config.custom_key)); if ((AudioExtn::audio_devices_cmp(mAndroidOutDevices, AUDIO_DEVICE_OUT_SPEAKER_SAFE)) && (mPalOutDeviceIds[i] == PAL_DEVICE_OUT_SPEAKER)) { strlcpy(mPalOutDevice[i].custom_config.custom_key, "speaker-safe", sizeof(mPalOutDevice[i].custom_config.custom_key)); AHAL_INFO("Setting custom key as %s", mPalOutDevice[i].custom_config.custom_key); } /* fixed ear_out aux_out stereo start */ if (adevice->is_earout_hphl_conflict) { if (isCombo && (mPalOutDevice[i].id == PAL_DEVICE_OUT_WIRED_HEADSET || mPalOutDevice[i].id == PAL_DEVICE_OUT_WIRED_HEADPHONE)) { AHAL_DBG("copy combo custom key"); strlcpy(mPalOutDevice[i].custom_config.custom_key, "headphones-combo_custom_key", sizeof(mPalOutDevice[i].custom_config.custom_key)); } } /* fixed ear_out aux_out stereo end */ if (((AudioExtn::audio_devices_cmp(mAndroidOutDevices, AUDIO_DEVICE_OUT_SPEAKER)) && (mPalOutDeviceIds[i] == PAL_DEVICE_OUT_SPEAKER)) && property_get_bool("vendor.audio.mspp.enable", false)) { strlcpy(mPalOutDevice[i].custom_config.custom_key, "mspp", sizeof(mPalOutDevice[i].custom_config.custom_key)); AHAL_INFO("Setting custom key as %s", mPalOutDevice[i].custom_config.custom_key); } } /* fixed ear_out aux_out stereo start */ if (adevice->is_earout_hphl_conflict) { AHAL_DBG("sjn: copied above?"); } /* fixed ear_out aux_out stereo end */ if (flags & AUDIO_OUTPUT_FLAG_MMAP_NOIRQ) { stream_.get()->start = astream_out_mmap_noirq_start; stream_.get()->stop = astream_out_mmap_noirq_stop; stream_.get()->create_mmap_buffer = astream_out_create_mmap_buffer; stream_.get()->get_mmap_position = astream_out_get_mmap_position; } if (isOffloadSpeedSupported() && isOffloadUsecase()) { stream_.get()->set_playback_rate_parameters = out_set_playback_rate_parameters; stream_.get()->get_playback_rate_parameters = out_get_playback_rate_parameters; } if (usecase_ == USECASE_AUDIO_PLAYBACK_WITH_HAPTICS) { AHAL_INFO("Haptics Usecase"); /* Setting flag here as no flag is being set for haptics from AudioPolicyManager * so that audio stream runs as low latency stream. */ flags_ = AUDIO_OUTPUT_FLAG_FAST; } mInitialized = true; for(auto dev : mAndroidOutDevices) audio_extn_gef_notify_device_config(dev, config_.channel_mask, config_.sample_rate, flags_, 0 /* MISOUND */); error: (void)FillHalFnPtrs(); AHAL_DBG("Exit"); return; } StreamOutPrimary::~StreamOutPrimary() { AHAL_DBG("close stream, handle(%x), pal_stream_handle (%p)", handle_, pal_stream_handle_); stream_mutex_.lock(); if (pal_stream_handle_) { if (CheckOffloadEffectsType(streamAttributes_.type)) { StopOffloadEffects(handle_, pal_stream_handle_); StopOffloadVisualizer(handle_, pal_stream_handle_); } pal_stream_close(pal_stream_handle_); pal_stream_handle_ = nullptr; } if (pal_haptics_stream_handle) { pal_stream_close(pal_haptics_stream_handle); pal_haptics_stream_handle = NULL; if (hapticBuffer) { free (hapticBuffer); hapticBuffer = NULL; } hapticsBufSize = 0; } if (convertBuffer) free(convertBuffer); if (mPalOutDeviceIds) { free(mPalOutDeviceIds); mPalOutDeviceIds = NULL; } if (mPalOutDevice) { free(mPalOutDevice); mPalOutDevice = NULL; } if (hapticsDevice) { free(hapticsDevice); hapticsDevice = NULL; } stream_mutex_.unlock(); } StreamInPrimary::StreamInPrimary(audio_io_handle_t handle, const std::set<audio_devices_t> &devices, audio_input_flags_t flags, struct audio_config *config, const char *address __unused, audio_source_t source) : StreamPrimary(handle, devices, config), mAndroidInDevices(devices), flags_(flags), btSinkMetadata{0, nullptr}, pal_vui_handle_(nullptr), mCompressEncoder(nullptr) { stream_ = std::shared_ptr<audio_stream_in> (new audio_stream_in()); std::shared_ptr<AudioDevice> adevice = AudioDevice::GetInstance(); pal_stream_handle_ = NULL; mInitialized = false; int noPalDevices = 0; int ret = 0; readAt.tv_sec = 0; readAt.tv_nsec = 0; void *st_handle = nullptr; pal_param_payload *payload = nullptr; AHAL_DBG("enter: handle (%x) format(%#x) sample_rate(%d) channel_mask(%#x) devices(%zu) flags(%#x)"\ , handle, config->format, config->sample_rate, config->channel_mask, mAndroidInDevices.size(), flags); if (!(stream_.get())) { AHAL_ERR("stream_ new allocation failed"); goto error; } if (AudioExtn::audio_devices_cmp(mAndroidInDevices, audio_is_usb_in_device)) { // get capability from device of USB device_cap_query_ = (pal_param_device_capability_t *) calloc(1, sizeof(pal_param_device_capability_t)); if (!device_cap_query_) { AHAL_ERR("Failed to allocate mem for device_cap_query_"); goto error; } dynamic_media_config_t *dynamic_media_config = (dynamic_media_config_t *) calloc(1, sizeof(dynamic_media_config_t)); if (!dynamic_media_config) { free(device_cap_query_); AHAL_ERR("Failed to allocate mem for dynamic_media_config"); goto error; } size_t payload_size = 0; device_cap_query_->id = PAL_DEVICE_IN_USB_HEADSET; device_cap_query_->addr.card_id = adevice->usb_card_id_; device_cap_query_->addr.device_num = adevice->usb_dev_num_; device_cap_query_->config = dynamic_media_config; device_cap_query_->is_playback = false; ret = pal_get_param(PAL_PARAM_ID_DEVICE_CAPABILITY, (void **)&device_cap_query_, &payload_size, nullptr); if (ret < 0) { AHAL_ERR("Error usb device is not connected"); free(dynamic_media_config); free(device_cap_query_); dynamic_media_config = NULL; device_cap_query_ = NULL; } if (dynamic_media_config) { AHAL_DBG("usb fs=%d format=%d mask=%x", dynamic_media_config->sample_rate[0], dynamic_media_config->format[0], dynamic_media_config->mask[0]); if (!config->sample_rate) { config->sample_rate = dynamic_media_config->sample_rate[0]; config->channel_mask = (audio_channel_mask_t) dynamic_media_config->mask[0]; if (flags == AUDIO_INPUT_FLAG_DIRECT) { config_.format = AUDIO_FORMAT_AAC_LC; } else { config->format = (audio_format_t)dynamic_media_config->format[0]; } memcpy(&config_, config, sizeof(struct audio_config)); } } } /* this is required for USB otherwise adev_open_input_stream is failed */ if (!config_.sample_rate) { config_.sample_rate = DEFAULT_OUTPUT_SAMPLING_RATE; } if (!config_.channel_mask) { config_.channel_mask = AUDIO_CHANNEL_IN_MONO; } if(!config_.format && flags == AUDIO_INPUT_FLAG_DIRECT) { // input direct flag is used for compress capture config_.format = AUDIO_FORMAT_AAC_LC; } else if (!config_.format) { config_.format = AUDIO_FORMAT_PCM_16_BIT; } /* * Audio config set from client may not be same as config used in pal, * update audio config here so that AudioFlinger can acquire correct * config used in pal/hal and configure record buffer converter properly. */ st_handle = audio_extn_sound_trigger_check_and_get_session(this); if (st_handle) { AHAL_VERBOSE("Found existing pal stream handle associated with capture handle"); pal_stream_handle_ = (pal_stream_handle_t *)st_handle; payload = (pal_param_payload *)calloc(1, sizeof(pal_param_payload) + sizeof(struct pal_stream_attributes)); if (!payload) { AHAL_ERR("Failed to allocate memory for stream attributes"); goto error; } payload->payload_size = sizeof(struct pal_stream_attributes); ret = pal_stream_get_param(pal_stream_handle_, PAL_PARAM_ID_STREAM_ATTRIBUTES, &payload); if (ret) { AHAL_ERR("Failed to get pal stream attributes, ret = %d", ret); if (payload) free(payload); goto error; } memcpy(&streamAttributes_, payload->payload, payload->payload_size); if (streamAttributes_.in_media_config.ch_info.channels == 1) config_.channel_mask = AUDIO_CHANNEL_IN_MONO; else if (streamAttributes_.in_media_config.ch_info.channels == 2) config_.channel_mask = AUDIO_CHANNEL_IN_STEREO; config_.format = AUDIO_FORMAT_PCM_16_BIT; config_.sample_rate = streamAttributes_.in_media_config.sample_rate; /* * reset pal_stream_handle in case standby come before * read as anyway it will be updated in StreamInPrimary::Open */ if (payload) free(payload); pal_stream_handle_ = nullptr; } AHAL_DBG("local : handle (%x) format(%#x) sample_rate(%d) channel_mask(%#x) devices(%#x) flags(%#x)"\ , handle, config_.format, config_.sample_rate, config_.channel_mask, AudioExtn::get_device_types(devices), flags); source_ = source; mAndroidInDevices = devices; if(mAndroidInDevices.empty()) mAndroidInDevices.insert(AUDIO_DEVICE_IN_DEFAULT); AHAL_DBG("No of devices %zu", mAndroidInDevices.size()); mPalInDeviceIds = (pal_device_id_t*) calloc(mAndroidInDevices.size(), sizeof(pal_device_id_t)); if (!mPalInDeviceIds) { goto error; } noPalDevices = getPalDeviceIds(devices, mPalInDeviceIds); if (noPalDevices != mAndroidInDevices.size()) { AHAL_ERR("mismatched pal %d and hal devices %zu", noPalDevices, mAndroidInDevices.size()); goto error; } mPalInDevice = (struct pal_device*) calloc(mAndroidInDevices.size(), sizeof(struct pal_device)); if (!mPalInDevice) { goto error; } for (int i = 0; i < mAndroidInDevices.size(); i++) { mPalInDevice[i].id = mPalInDeviceIds[i]; mPalInDevice[i].config.sample_rate = config->sample_rate; mPalInDevice[i].config.bit_width = CODEC_BACKEND_DEFAULT_BIT_WIDTH; // ch_info memory is allocated at resource manager:getdeviceconfig mPalInDevice[i].config.ch_info = {0, {0}}; mPalInDevice[i].config.aud_fmt_id = PAL_AUDIO_FMT_PCM_S16_LE; // TODO: need to convert this from output format if ((mPalInDeviceIds[i] == PAL_DEVICE_IN_USB_DEVICE) || (mPalInDeviceIds[i] == PAL_DEVICE_IN_USB_HEADSET)) { mPalInDevice[i].address.card_id = adevice->usb_card_id_; mPalInDevice[i].address.device_num = adevice->usb_dev_num_; } strlcpy(mPalInDevice[i].custom_config.custom_key, "", sizeof(mPalInDevice[i].custom_config.custom_key)); /* HDR use case check */ if ((source_ == AUDIO_SOURCE_UNPROCESSED) && (config_.sample_rate == 48000)) { uint8_t channels = audio_channel_count_from_in_mask(config_.channel_mask); if (channels == 4) { if (get_hdr_mode() == AUDIO_RECORD_ARM_HDR) { flags = flags_ = AUDIO_INPUT_FLAG_RAW; setup_hdr_usecase(&mPalInDevice[i]); } } } if (source_ == AUDIO_SOURCE_CAMCORDER && adevice->cameraOrientation == CAMERA_DEFAULT) { strlcpy(mPalInDevice[i].custom_config.custom_key, "camcorder_landscape", sizeof(mPalInDevice[i].custom_config.custom_key)); AHAL_INFO("Setting custom key as %s", mPalInDevice[i].custom_config.custom_key); } usecase_ = GetInputUseCase(flags, source); if (usecase_ == USECASE_AUDIO_RECORD_LOW_LATENCY || usecase_ == USECASE_AUDIO_RECORD_MMAP) { uint8_t channels = audio_channel_count_from_in_mask(config_.channel_mask); if (channels == 2) { strlcpy(mPalInDevice[i].custom_config.custom_key, "dual-mic", sizeof(mPalInDevice[i].custom_config.custom_key)); AHAL_INFO("Setting custom key as %s", mPalInDevice[i].custom_config.custom_key); } } if ((get_hdr_mode() == AUDIO_RECORD_SPF_HDR) && (source_ == AUDIO_SOURCE_CAMCORDER || source_ == AUDIO_SOURCE_MIC)) { setup_hdr_usecase(&mPalInDevice[i]); } } usecase_ = GetInputUseCase(flags, source); mInitialized = true; // compress capture using CompressAAC = CompressCapture::CompressAAC; if (usecase_ == USECASE_AUDIO_RECORD_COMPRESS) { if (config_.format == AUDIO_FORMAT_AAC_LC || config_.format == AUDIO_FORMAT_AAC_ADTS_HE_V1 || config_.format == AUDIO_FORMAT_AAC_ADTS_HE_V2) { mCompressEncoder = std::make_unique<CompressAAC>( config_.format, config_.sample_rate, audio_channel_count_from_in_mask(config_.channel_mask)); if (!mCompressEncoder) { usecase_ = USECASE_INVALID; AHAL_ERR("memory allocation failed"); mInitialized = false; } } else { usecase_ = USECASE_INVALID; AHAL_ERR("invalid usecase detected"); mInitialized = false; } } if (flags & AUDIO_INPUT_FLAG_MMAP_NOIRQ) { stream_.get()->start = astream_in_mmap_noirq_start; stream_.get()->stop = astream_in_mmap_noirq_stop; stream_.get()->create_mmap_buffer = astream_in_create_mmap_buffer; stream_.get()->get_mmap_position = astream_in_get_mmap_position; } //ADD: KARAOKE if (usecase_ == USECASE_AUDIO_RECORD_LOW_LATENCY) { adevice->is_karaoke_fastcapture = true; } //END KARAOKE error: (void)FillHalFnPtrs(); AHAL_DBG("Exit"); return; } 请修改上面的代码在StreamOutPrimary::Standby中需要获取is_karaoke_status的值。
最新发布
08-08
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值