Browse Source

add audio

master
zhaohe 2 years ago
parent
commit
83a2f88390
  1. 8
      src/iflytop/components/audio/audio.hpp
  2. 86
      src/iflytop/components/audio/audio_clip.cpp
  3. 86
      src/iflytop/components/audio/audio_clip.hpp
  4. 4
      src/iflytop/components/audio/audio_dumper.cpp
  5. 41
      src/iflytop/components/audio/audio_dumper.hpp
  6. 53
      src/iflytop/components/audio/audio_format.hpp
  7. 172
      src/iflytop/components/audio/audio_recoder.cpp
  8. 71
      src/iflytop/components/audio/audio_recoder.hpp
  9. 1
      src/iflytop/core/core.hpp

8
src/iflytop/components/audio/audio.hpp

@ -0,0 +1,8 @@
#pragma once
#include "audio_clip.hpp"
//
#include "audio_dumper.hpp"
//
#include "audio_format.hpp"
//
#include "audio_recoder.hpp"

86
src/iflytop/components/audio/audio_clip.cpp

@ -0,0 +1,86 @@
//
// Created by iflytop
//
#include "audio_clip.hpp"
using namespace std;
using namespace iflytop;
using namespace core;
/**
* @brief
*
* service: zvoice
*/
AudioClip::AudioClip(uint8_t *voice, size_t voicebufsize, int ch, int rate, AudioFormat format) {
m_ch = ch;
m_rate = rate;
m_tp = tu_steady().now();
m_human_readable_tp = tu_sys().now();
m_voice = (uint8_t *)malloc(voicebufsize);
m_voicebufsize = voicebufsize;
m_format = format;
m_bits_per_sample = AudioFormatToBytes(m_format) * 8;
m_number_of_samples = m_voicebufsize / (AudioFormatToBytes(m_format) * ch);
ZCHECK(m_voice != nullptr, "malloc failed");
memcpy(m_voice, voice, voicebufsize);
m_audio_duration_ms = (m_voicebufsize * 1000) / (m_ch * m_rate * (AudioFormatToBytes(m_format)));
}
size_t AudioClip::getAudioDurationMs() { return m_audio_duration_ms; }
size_t AudioClip::getNumberOfSamples() { return m_number_of_samples; }
uint32_t AudioClip::getCh() { return m_ch; }
uint32_t AudioClip::getRate() { return m_rate; }
uint8_t *AudioClip::getVoice() { return m_voice; }
AudioFormat AudioClip::getFormat() { return m_format; }
size_t AudioClip::getVoiceBufSize() { return m_voicebufsize; }
tp_sys AudioClip::getHumanReadableTp() { return m_human_readable_tp; }
uint32_t AudioClip::getBitsPerSample() { return m_bits_per_sample; }
tp_steady AudioClip::getTp() { return m_tp; }
uint8_t *AudioClip::data() { return m_voice; }
size_t AudioClip::size() { return m_voicebufsize; }
void AudioClip::updateTp(tp_steady tp, tp_sys humreadtp) {
m_tp = tp;
m_human_readable_tp = humreadtp;
}
string AudioClip::toString() {
time_t tt = system_clock::to_time_t(m_human_readable_tp);
tm tm = *localtime(&tt);
char buf[64] = {0};
strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &tm);
string str = fmt::format("audio:{} c-{} r-{} f-{} {}:{}", buf, m_ch, m_rate, AudioFormatToStr(m_format),
(void *)m_voice, m_voicebufsize);
return str;
}
void AudioClip::getOneCHVoice(vector<uint8_t> &voice, size_t ch) {
if (ch >= m_ch) {
return;
}
size_t one_ch_size = m_voicebufsize / m_ch;
voice.resize(one_ch_size);
size_t oneframebytes = AudioFormatToBytes(m_format);
for (size_t i = 0; i < one_ch_size / oneframebytes; i++) {
memcpy(&voice[i * oneframebytes], &m_voice[i * m_ch * oneframebytes + ch * oneframebytes], oneframebytes);
}
}

86
src/iflytop/components/audio/audio_clip.hpp

@ -0,0 +1,86 @@
//
// Created by iflytop
//
#pragma once
#include <fstream>
#include <iostream>
#include <list>
#include <map>
#include <memory>
#include <mutex>
#include <set>
#include <sstream>
#include <string>
#include <vector>
#include "iflytop/core/core.hpp"
#include "audio_format.hpp"
/**
* @brief
*
* service: zvoice
*/
namespace iflytop {
using namespace std;
using namespace iflytop;
using namespace core;
using namespace chrono;
class AudioClip : public enable_shared_from_this<AudioClip> {
ENABLE_LOGGER(AudioClip);
uint8_t *m_voice = nullptr; // 声音数据
size_t m_voicebufsize = 0; // 声音数据长度
uint32_t m_ch = 0; // 声音通道数
uint32_t m_rate = 0; // 声音采样率
AudioFormat m_format = S16_LE; // 声音格式
uint32_t m_bits_per_sample = 0; // 每个采样点的大小
size_t m_audio_duration_ms = 0; // 音频时长
uint32_t m_number_of_samples = 0; // 采样点数
tp_sys m_human_readable_tp = {}; // 人类可读的时间戳,可能由于时间服务器的原因,会发生突变
tp_steady m_tp = {}; // 系统时间戳
mutex m_mutex;
map<string, Any> m_context;
public:
AudioClip(uint8_t *voice, size_t m_voicebufsize, int ch, int rate, AudioFormat format);
uint32_t getCh();
uint32_t getRate();
uint8_t *getVoice();
AudioFormat getFormat();
size_t getVoiceBufSize();
size_t getNumberOfSamples();
tp_sys getHumanReadableTp();
tp_steady getTp();
uint32_t getBitsPerSample();
void getOneCHVoice(vector<uint8_t> &voice, size_t ch);
uint8_t *data();
size_t size();
size_t getAudioDurationMs();
string toString();
void updateTp(tp_steady tp, tp_sys humreadtp);
template <typename T>
void setContext(string key, shared_ptr<T> object) {
lock_guard<mutex> lock(m_mutex);
m_context[key] = Any(object);
}
template <typename T>
shared_ptr<T> getContext(string key) {
lock_guard<mutex> lock(m_mutex);
auto result = m_context.find(key);
if (result == m_context.end()) {
return nullptr;
}
return result->second.Get<std::shared_ptr<T>>();
};
};
} // namespace iflytop

4
src/iflytop/components/audio/audio_dumper.cpp

@ -0,0 +1,4 @@
#include "audio_dumper.hpp"
using namespace iflytop;
using namespace core;

41
src/iflytop/components/audio/audio_dumper.hpp

@ -0,0 +1,41 @@
//
// Created by zwsd
//
#pragma once
#include <fstream>
#include <iostream>
#include <list>
#include <map>
#include <memory>
#include <set>
#include <sstream>
#include <string>
#include <vector>
#include "iflytop/core/core.hpp"
/**
* @brief
*
* service: AudioDumper
*
* :
* :
* :
* :
*
*/
namespace iflytop {
using namespace std;
using namespace core;
class AudioDumper {
ENABLE_LOGGER(AudioDumper);
public:
AudioDumper();
void initialize();
};
} // namespace iflytop

53
src/iflytop/components/audio/audio_format.hpp

@ -0,0 +1,53 @@
//
// Created by iflytop
//
#pragma once
#include <fstream>
#include <iostream>
#include <list>
#include <map>
#include <memory>
#include <set>
#include <sstream>
#include <string>
#include <vector>
#include "iflytop/core/core.hpp"
namespace iflytop {
enum AudioFormat {
S16_LE = 0, // 16位有符号整数,小端模式
S16_BE, // 16位有符号整数,大端模式
S32_LE, // 32位有符号整数,小端模式
S32_BE, // 32位有符号整数,大端模式
};
static inline int AudioFormatToBytes(AudioFormat audioformat) {
switch (audioformat) {
case S16_LE:
case S16_BE:
return 2;
case S32_LE:
case S32_BE:
return 4;
}
return 0;
}
static inline std::string AudioFormatToStr(AudioFormat audioformat) {
switch (audioformat) {
case S16_LE:
return "S16_LE";
case S16_BE:
return "S16_BE";
case S32_LE:
return "S32_LE";
case S32_BE:
return "S32_BE";
}
return "unknown";
}
} // namespace iflytop

172
src/iflytop/components/audio/audio_recoder.cpp

@ -0,0 +1,172 @@
#include "audio_recoder.hpp"
//
using namespace std;
using namespace iflytop;
using namespace core;
AudioFormat AudioRecoder::snd_pcm_format_t2AudioFormat(snd_pcm_format_t format) {
switch (format) {
case SND_PCM_FORMAT_S16_LE:
return AudioFormat::S16_LE;
case SND_PCM_FORMAT_S16_BE:
return AudioFormat::S16_BE;
case SND_PCM_FORMAT_S32_LE:
return AudioFormat::S32_LE;
case SND_PCM_FORMAT_S32_BE:
return AudioFormat::S32_BE;
default:
logger->error("snd_pcm_format_t2AudioFormat fail,unsupport format:{}", format);
exit(1);
}
}
int AudioRecoder::getBitsPerSample(snd_pcm_format_t format) {
switch (format) {
case SND_PCM_FORMAT_S16_LE:
case SND_PCM_FORMAT_S16_BE:
return 16;
case SND_PCM_FORMAT_S32_LE:
case SND_PCM_FORMAT_S32_BE:
return 32;
default:
logger->error("getBitsPerSample fail,unsupport format:{}", format);
exit(1);
}
}
#define CALL(exptr, errorinfo) \
{ \
ret = exptr; \
if (ret < 0) { \
logger->error("call {},failed: {},{}", #exptr, snd_strerror(ret), errorinfo); \
goto err; \
} \
}
bool AudioRecoder::initialize(const char *pcmName, unsigned int channels, unsigned int sample_rate,
snd_pcm_format_t format,
int rec_period_time_ms) { //
logger->info("AudioRecoder::initialize pcmName:{},channels:{},sample_rate:{},format:{},rec_period_time_ms:{}",
pcmName, channels, sample_rate, format, rec_period_time_ms);
/**
* @brief
*/
int ret = 0;
snd_pcm_hw_params_t *params = NULL;
snd_pcm_sw_params_t *swparams = NULL;
m_sample_rate = sample_rate;
m_channels = channels;
m_format = format;
unsigned int alsa_rec_period_time = rec_period_time_ms * 1000;
unsigned int alsa_rec_buffer_time = rec_period_time_ms * 1000 * 4;
size_t sz = 0;
m_pcmName = pcmName;
CALL(snd_pcm_open(&m_pcmhandle, m_pcmName, SND_PCM_STREAM_CAPTURE, 0), "open pcm failed");
/**
* @brief
*/
snd_pcm_hw_params_alloca(&params);
if (!params) {
logger->error("alloc params failed");
goto err;
}
CALL(snd_pcm_hw_params_any(m_pcmhandle, params), "Broken configuration for this PCM");
CALL(snd_pcm_hw_params_set_access(m_pcmhandle, params, SND_PCM_ACCESS_RW_INTERLEAVED), "Access type not available");
// set audio format
CALL(snd_pcm_hw_params_set_format(m_pcmhandle, params, m_format), "Sample format non available");
// set channel
CALL(snd_pcm_hw_params_set_channels(m_pcmhandle, params, m_channels), "Channels count non available");
// set rate
CALL(snd_pcm_hw_params_set_rate_near(m_pcmhandle, params, &m_sample_rate, 0), "Set rate failed");
// set rec period time
CALL(snd_pcm_hw_params_set_period_time_near(m_pcmhandle, params, &alsa_rec_period_time, 0), "set period time fail");
// set buffer time
CALL(snd_pcm_hw_params_set_buffer_time_near(m_pcmhandle, params, &alsa_rec_buffer_time, 0), "set buffer time failed");
CALL(snd_pcm_hw_params_get_period_size(params, &m_period_frames, 0), "get period size failed");
CALL(snd_pcm_hw_params_get_buffer_size(params, &m_buffer_frames), "get buffer size failed");
/**
* @brief
*/
CALL(snd_pcm_hw_params(m_pcmhandle, params), "set params failed");
//
snd_pcm_sw_params_alloca(&swparams);
if (!swparams) {
logger->error("alloc sw params failed");
goto err;
}
CALL(snd_pcm_sw_params_current(m_pcmhandle, swparams), "get current sw para fail");
CALL(snd_pcm_sw_params_set_avail_min(m_pcmhandle, swparams, m_period_frames), "set avail min failed");
CALL(snd_pcm_sw_params(m_pcmhandle, swparams), "unable to install sw params");
/**
* @brief Buffer
*/
sz = (m_period_frames * getBitsPerSample(m_format) / 8) * m_channels;
m_recordbuf = (uint8_t *)malloc(sz);
if (m_recordbuf == NULL) {
logger->error("malloc record buffer failed");
goto err;
}
m_recordbuf_size = sz;
// snd_pcm_hw_params_free(params);
// snd_pcm_sw_params_free(swparams);
return true;
err:
if (m_pcmhandle) snd_pcm_close(m_pcmhandle);
// if (params) snd_pcm_hw_params_free(params);
// if (swparams) snd_pcm_sw_params_free(swparams);
if (m_recordbuf) {
free(m_recordbuf);
m_recordbuf = nullptr;
}
return false;
}
void AudioRecoder::onAudio(uint8_t *voice, int size) {
shared_ptr<AudioClip> audioClip;
audioClip.reset(new AudioClip(voice, // voice
size, // voice len
m_channels, // ch
m_sample_rate, // rate
snd_pcm_format_t2AudioFormat(m_format) // format
));
onRecordData(audioClip);
}
void AudioRecoder::startRecord() {
m_recordThread.reset(new Thread("AudioRecoder", [this]() {
ThisThread thisThread;
int err = 0;
if ((err = snd_pcm_prepare(m_pcmhandle)) < 0) {
logger->error("cannot prepare audio interface for use ({})", snd_strerror(err));
exit(1);
}
while (!thisThread.getExitFlag()) {
int readframes = 0;
// int bytes = 0;
readframes = snd_pcm_readi(m_pcmhandle, m_recordbuf, m_period_frames);
if (readframes == -EINTR || readframes == -EPIPE || readframes == -ESTRPIPE) {
snd_pcm_recover(m_pcmhandle, readframes, 1);
continue;
} else if (readframes < 0) {
logger->error("read from audio interface failed ({})", snd_strerror(readframes));
exit(1);
}
int readbytes = readframes * getBitsPerSample(m_format) / 8 * m_channels;
onAudio(m_recordbuf, readbytes);
}
}));
}

71
src/iflytop/components/audio/audio_recoder.hpp

@ -0,0 +1,71 @@
//
// Created by zwsd
//
#pragma once
#include <alsa/asoundlib.h>
#include <fstream>
#include <iostream>
#include <list>
#include <map>
#include <memory>
#include <set>
#include <sstream>
#include <string>
#include <vector>
#include "iflytop/core/core.hpp"
#include "audio_clip.hpp"
/**
* @brief
*
* service: AudioRecoder
*
* :
* :
* :
* :
*
*/
namespace iflytop {
using namespace std;
using namespace iflytop;
using namespace core;
class AudioRecoder : public enable_shared_from_this<AudioRecoder> {
ENABLE_LOGGER(AudioRecoder);
snd_pcm_t *m_pcmhandle = nullptr;
const char *m_pcmName = nullptr;
snd_pcm_stream_t stream;
int mode;
unsigned int m_channels = 0;
unsigned int m_sample_rate = 0;
// int m_sample_real_rate = 0;
snd_pcm_format_t m_format = SND_PCM_FORMAT_S16_LE;
size_t m_period_frames = 0;
size_t m_buffer_frames = 0;
uint8_t *m_recordbuf = nullptr;
size_t m_recordbuf_size = 0;
unique_ptr<Thread> m_recordThread;
public:
bool initialize(const char *pcmName, unsigned int channels, unsigned int sample_rate, snd_pcm_format_t format,
int rec_period_time_ms);
void startRecord();
nod::signal<void(shared_ptr<AudioClip> audioclip)> onRecordData;
private:
AudioFormat snd_pcm_format_t2AudioFormat(snd_pcm_format_t format);
int getBitsPerSample(snd_pcm_format_t format);
void onAudio(uint8_t *voice, int size);
};
} // namespace iflytop

1
src/iflytop/core/core.hpp

@ -19,5 +19,6 @@
#include "iflytop/core/error/error_code.hpp"
#include "iflytop/core/spdlogfactory/logger.hpp"
#include "iflytop/core/thread/thread.hpp"
#include "iflytop/core/basic/any.hpp"
#define ZARRAYSIZE(a) (sizeof(a) / sizeof(a[0]))
Loading…
Cancel
Save