YobeSDK 0.3.2
 
Loading...
Searching...
No Matches
IDListener_demo.cpp

This is an example of how to use the Yobe IDListener.

#include "util/demo_utils.hpp"
#include "util/client_license.h"
#include <fstream>
#include <iostream>
#include <memory>
#include <vector>
constexpr auto ENV_VAR_LICENSE = "YOBE_LICENSE";
constexpr auto TEMPLATE_01 = AUDIO_FILES_PATH "/IDListener/user_1_template_01.wav";
constexpr auto TEMPLATE_02 = AUDIO_FILES_PATH "/IDListener/user_1_template_02.wav";
// constexpr auto TEMPLATE_03 = AUDIO_FILES_PATH "/IDListener/user_1_template_03.wav";
// These file are in the audio file folder if you want to experiment.
// constexpr auto TEMPLATE_LONG = AUDIO_FILES_PATH "/IDListener/user_1_template_40s.wav";
std::vector<double> YobeProcessing(const std::string& license, std::vector<double> input_buffer);
std::shared_ptr<Yobe::IDTemplate> CreateTemplateFromFile(std::shared_ptr<Yobe::IDListener> id_listener, std::string wav_file_path);
std::ofstream log_stream;
int main(int argc, char* argv[]) {
if (argc != 2) {
std::cout << "cpp demo requires a .wav file as input.\n";
} else {
// Just printing out the setting the IDListener expects
std::cout << "Just checking to see if the Yobe parameters match the audio file.\n";
std::cout << "Expected sampling rate: " << Yobe::Info::SamplingRate() << '\n';
std::cout << "Expected buffer size in seconds: " << Yobe::Info::AudioBufferTime() << '\n';
std::cout << "Number expected input channels: " << Yobe::Info::InputChannels() << '\n';
std::cout << "Number expected output channels: " << Yobe::Info::OutputChannels() << "\n\n";
const std::string file_path(argv[1]);
// Preparing input buffer
const auto input_buffer = DemoUtil::ReadAudioFile(file_path);
std::cout << '\n';
std::vector<double> processed_audio;
try {
// All the Yobe processing happens in this function
processed_audio = YobeProcessing(getLicense(ENV_VAR_LICENSE), input_buffer);
} catch (const std::exception& e) {
std::cerr << e.what() << '\n';
return 1;
}
// Writing the processed data to a .wav file
DemoUtil::WriteAudioFile(file_path, processed_audio);
}
return 0;
}
std::vector<double> YobeProcessing(const std::string& license, std::vector<double> input_buffer) {
auto id_listener = Yobe::Create::NewIDListener();
if (id_listener == nullptr) {
std::cout << "Probably the library you have does not have biometrics." << std::endl;
return {};
}
// Here we set up our logging callback
log_stream.open("IDListener_demo.log");
Yobe::Info::RegisterCallback([](const char* mess) { log_stream << mess << '\n'; });
// Init the IDListener.
#if ASR
auto init_status = id_listener->Init(license.c_str(), INIT_DATA_PATH, Yobe::MicOrientation::BROAD_SIDE, Yobe::OutputBufferType::YOBE_VARIABLE);
#else
auto init_status = id_listener->Init(license.c_str(), INIT_DATA_PATH, Yobe::MicOrientation::BROAD_SIDE, Yobe::OutputBufferType::YOBE_FIXED);
#endif
if (init_status != Yobe::Status::YOBE_OK) {
std::cout << "Init returned: " << Yobe::Info::StdError(init_status) << '\n';
throw std::runtime_error("Initialization error");
}
// Calculate the input buffer size that you are going to pass in to ProcessBuffer.
const auto input_size = Yobe::Info::InputBufferSize();
// Prepare output buffer for collecting the out put from the IDListener.
std::vector<double> output_buffer;
// This is the pre-allocated buffer that will be returned with processed data in it.
std::vector<double> scratch_buffer;
const auto total_input_samples = input_buffer.size();
std::shared_ptr<Yobe::IDTemplate> voice_template;
DemoUtil::ZeroPadSignal(input_buffer);
std::cout << "Yobe has started processing.\n";
bool did_mid_process_enrollment = false;
for (size_t input_index = 0; input_index < total_input_samples; input_index += input_size) {
// This can be used to determine if the selected user was detected in the processed buffer.
bool is_user_verify{};
// Here we are processing the audio a buffer at a time.
status = id_listener->ProcessBuffer(&input_buffer[input_index], scratch_buffer, input_size, is_user_verify);
log_stream << "Yobe::ProcessBuffer: " << Yobe::Info::StdError(status) << " | User detected: " << std::boolalpha << is_user_verify << "\n";
// Process enough data to calibrate, then enroll.
if (!did_mid_process_enrollment && status == Yobe::Status::YOBE_OK) {
// An example of enrolling a user during processing.
// Create two templates and merge them into one.
auto template_1 = CreateTemplateFromFile(id_listener, TEMPLATE_01);
auto template_2 = CreateTemplateFromFile(id_listener, TEMPLATE_02);
voice_template = id_listener->MergeUserTemplates({template_1, template_2});
id_listener->SelectUser(voice_template);
did_mid_process_enrollment = true;
}
// Now we check the status to make sure that the audio got processed.
std::cout << "ProcessBuffer returned: " << Yobe::Info::StdError(status) << '\n';
} else if (!scratch_buffer.empty()) {
// Now we collect our scratch buffer into are output buffer
output_buffer.insert(output_buffer.end(), scratch_buffer.begin(), scratch_buffer.end());
}
}
// Here we are cleaning up and deiniting the IDListener.
auto deinit_status = id_listener->Deinit();
if (deinit_status != Yobe::Status::YOBE_STOPPED) {
std::cout << "There was an error when deinit the IDListener.\n";
throw std::runtime_error("Deinit error");
}
std::cout << "IDListener has finished processing.\n";
// closing the log stream
log_stream.close();
return output_buffer;
}
std::shared_ptr<Yobe::IDTemplate> CreateTemplateFromFile(std::shared_ptr<Yobe::IDListener> id_listener, std::string wav_file_path) {
std::cout << "Now registering a template.\n";
auto template_samples = DemoUtil::ReadAudioFile(wav_file_path);
std::uint32_t sample_idx = 0;
std::vector<double> processed_template_samples{};
id_listener->StartEnrollment();
while (status == Yobe::Status::ENROLLING) {
if (sample_idx + Yobe::Info::InputBufferSize() > template_samples.size()) {
std::cout << "All template samples have been processed, stopping enrollment manually\n";
id_listener->StopEnrollment();
break;
}
std::vector<double> out_samples{};
bool is_user_verify{};
status = id_listener->ProcessBuffer(&template_samples[sample_idx], out_samples, Yobe::Info::InputBufferSize(), is_user_verify);
sample_idx += Yobe::Info::InputBufferSize();
processed_template_samples.insert(processed_template_samples.end(), out_samples.begin(), out_samples.end());
}
return id_listener->RegisterTemplate(processed_template_samples.data(), static_cast<uint32_t>(processed_template_samples.size()));
}
YOBE_SDK_API std::shared_ptr< IDListener > NewIDListener()
Creates a new instance of IDListener.
YOBE_SDK_API int32_t InputChannels()
Returns the number of input channels required for processing.
YOBE_SDK_API double AudioBufferTime()
Returns the processing audio buffer length in seconds.
YOBE_SDK_API void RegisterCallback(std::function< void(const char *)> log_callback)
Registers a callback function to receive Yobe logging information.
YOBE_SDK_API uint32_t InputBufferSize()
Returns the input buffer size in samples.
YOBE_SDK_API uint32_t SamplingRate(bool output_sampling_rate=true)
Returns the expected sampling rate of the input/output buffers.
YOBE_SDK_API const char * StdError(Status status)
Translates a Yobe Status code into a more readable string.
YOBE_SDK_API int32_t OutputChannels()
Returns the number of processing output channels.
Status
Yobe status codes that give information on internal state.
Definition yobe_lib_util.hpp:83
@ YOBE_STOPPED
This means that the engine successfully stopped.
@ YOBE_UNKNOWN
An unknown error has occurred.
@ YOBE_OK
The function executed successfully.
@ NEEDS_MORE_DATA
The algorithm needs more data before it can start processing the audio.
@ ENROLLING
This means the last buffer was processed while the IDListener was configured for enrolling a new Biom...
@ YOBE_VARIABLE
This allow the output buffer to change sizes.
@ YOBE_FIXED
This guarantees that the output buffer will always be the same size.
@ BROAD_SIDE
This orientation has the target voice perpendicular to the line connecting the two microphones.