Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions codesamples/apis.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
"func": "get_end_position",
"args": []
},
"audio_out": {
"client": "AudioOut",
"func": "get_properties",
"args": []
},
"button": {
"client": "Button",
"func": "push",
Expand Down
8 changes: 8 additions & 0 deletions src/viam/api/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,10 @@ if (VIAMCPPSDK_USE_DYNAMIC_PROTOS)
${PROTO_GEN_DIR}/component/arm/v1/arm.grpc.pb.h
${PROTO_GEN_DIR}/component/arm/v1/arm.pb.cc
${PROTO_GEN_DIR}/component/arm/v1/arm.pb.h
${PROTO_GEN_DIR}/component/audioout/v1/audioout.grpc.pb.cc
${PROTO_GEN_DIR}/component/audioout/v1/audioout.grpc.pb.h
${PROTO_GEN_DIR}/component/audioout/v1/audioout.pb.cc
${PROTO_GEN_DIR}/component/audioout/v1/audioout.pb.h
${PROTO_GEN_DIR}/component/base/v1/base.grpc.pb.cc
${PROTO_GEN_DIR}/component/base/v1/base.grpc.pb.h
${PROTO_GEN_DIR}/component/base/v1/base.pb.cc
Expand Down Expand Up @@ -294,6 +298,8 @@ target_sources(viamapi
${PROTO_GEN_DIR}/common/v1/common.pb.cc
${PROTO_GEN_DIR}/component/arm/v1/arm.grpc.pb.cc
${PROTO_GEN_DIR}/component/arm/v1/arm.pb.cc
${PROTO_GEN_DIR}/component/audioout/v1/audioout.grpc.pb.cc
${PROTO_GEN_DIR}/component/audioout/v1/audioout.pb.cc
${PROTO_GEN_DIR}/component/base/v1/base.grpc.pb.cc
${PROTO_GEN_DIR}/component/base/v1/base.pb.cc
${PROTO_GEN_DIR}/component/board/v1/board.grpc.pb.cc
Expand Down Expand Up @@ -358,6 +364,8 @@ target_sources(viamapi
${PROTO_GEN_DIR}/../../viam/api/common/v1/common.pb.h
${PROTO_GEN_DIR}/../../viam/api/component/arm/v1/arm.grpc.pb.h
${PROTO_GEN_DIR}/../../viam/api/component/arm/v1/arm.pb.h
${PROTO_GEN_DIR}/../../viam/api/component/audioout/v1/audioout.grpc.pb.h
${PROTO_GEN_DIR}/../../viam/api/component/audioout/v1/audioout.pb.h
${PROTO_GEN_DIR}/../../viam/api/component/base/v1/base.grpc.pb.h
${PROTO_GEN_DIR}/../../viam/api/component/base/v1/base.pb.h
${PROTO_GEN_DIR}/../../viam/api/component/board/v1/board.grpc.pb.h
Expand Down
6 changes: 6 additions & 0 deletions src/viam/sdk/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ endif()

target_sources(viamsdk
PRIVATE
common/audio.cpp
common/client_helper.cpp
common/exception.cpp
common/instance.cpp
Expand All @@ -71,6 +72,7 @@ target_sources(viamsdk
common/world_state.cpp
common/private/service_helper.cpp
components/arm.cpp
components/audio_out.cpp
components/base.cpp
components/board.cpp
components/button.cpp
Expand All @@ -86,6 +88,8 @@ target_sources(viamsdk
components/power_sensor.cpp
components/private/arm_client.cpp
components/private/arm_server.cpp
components/private/audio_out_client.cpp
components/private/audio_out_server.cpp
components/private/base_client.cpp
components/private/base_server.cpp
components/private/board_client.cpp
Expand Down Expand Up @@ -164,6 +168,7 @@ target_sources(viamsdk
${CMAKE_CURRENT_BINARY_DIR}/../..
FILES
../../viam/sdk/common/client_helper.hpp
../../viam/sdk/common/audio.hpp
../../viam/sdk/common/exception.hpp
../../viam/sdk/common/instance.hpp
../../viam/sdk/common/linear_algebra.hpp
Expand All @@ -174,6 +179,7 @@ target_sources(viamsdk
../../viam/sdk/common/version_metadata.hpp
../../viam/sdk/common/world_state.hpp
../../viam/sdk/components/arm.hpp
../../viam/sdk/components/audio_out.hpp
../../viam/sdk/components/base.hpp
../../viam/sdk/components/board.hpp
../../viam/sdk/components/button.hpp
Expand Down
76 changes: 76 additions & 0 deletions src/viam/sdk/common/audio.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#include <viam/sdk/common/audio.hpp>

#include <fstream>
#include <stdexcept>
#include <tuple>

namespace viam {
namespace sdk {

namespace {
template <typename T>
void write_value(std::ofstream& out, const T& value) {
out.write(reinterpret_cast<const char*>(&value), sizeof(value));
}
} // anonymous namespace

bool operator==(const audio_properties& lhs, const audio_properties& rhs) {
return std::tie(lhs.supported_codecs, lhs.sample_rate_hz, lhs.num_channels) ==
std::tie(rhs.supported_codecs, rhs.sample_rate_hz, rhs.num_channels);
}

bool operator==(const audio_info& lhs, const audio_info& rhs) {
return std::tie(lhs.codec, lhs.sample_rate_hz, lhs.num_channels) ==
std::tie(rhs.codec, rhs.sample_rate_hz, rhs.num_channels);
}

uint16_t get_bits_per_sample(const std::string& codec) {
if (codec == audio_codecs::PCM_16) {
return 16;
}
if (codec == audio_codecs::PCM_32 || codec == audio_codecs::PCM_32_FLOAT) {
return 32;
}
throw std::runtime_error("Unsupported codec for WAV file: " + codec);
}

void write_wav_file(const std::string& filename,
const std::vector<uint8_t>& audio_data,
const std::string& codec,
uint32_t sample_rate_hz,
uint16_t num_channels) {
std::ofstream outfile(filename, std::ios::binary);
if (!outfile.is_open()) {
throw std::runtime_error("Failed to open file for writing: " + filename);
}

const uint16_t bits_per_sample = get_bits_per_sample(codec);
const uint32_t data_size = audio_data.size();
const uint32_t byte_rate = sample_rate_hz * num_channels * (bits_per_sample / 8);
const uint16_t block_align = num_channels * (bits_per_sample / 8);

outfile.write("RIFF", 4);
const uint32_t chunk_size = 36 + data_size;
write_value(outfile, chunk_size);
outfile.write("WAVE", 4);

outfile.write("fmt ", 4);
const uint32_t subchunk1_size = 16;
write_value(outfile, subchunk1_size);
const uint16_t audio_format = 1;
write_value(outfile, audio_format);
write_value(outfile, num_channels);
write_value(outfile, sample_rate_hz);
write_value(outfile, byte_rate);
write_value(outfile, block_align);
write_value(outfile, bits_per_sample);

outfile.write("data", 4);
write_value(outfile, data_size);
outfile.write(reinterpret_cast<const char*>(audio_data.data()), data_size);

outfile.close();
}

} // namespace sdk
} // namespace viam
53 changes: 53 additions & 0 deletions src/viam/sdk/common/audio.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#pragma once

#include <cstddef>
#include <cstdint>
#include <string>
#include <vector>

namespace viam {
namespace sdk {

/// @brief Common audio codecs
namespace audio_codecs {
constexpr const char* PCM_16 = "pcm16";
constexpr const char* PCM_32 = "pcm32";
constexpr const char* PCM_32_FLOAT = "pcm32_float";
constexpr const char* MP3 = "mp3";
constexpr const char* AAC = "aac";
constexpr const char* OPUS = "opus";
constexpr const char* FLAC = "flac";
} // namespace audio_codecs

/// @struct audio_properties
/// @brief Properties of an audio component (input or output)
struct audio_properties {
std::vector<std::string> supported_codecs;
int sample_rate_hz;
int num_channels;
};

/// @struct audio_info
/// @brief Information about a piece of audio data
struct audio_info {
std::string codec;
int sample_rate_hz;
int num_channels;
};

/// @brief Equality operator for properties
bool operator==(const audio_properties& lhs, const audio_properties& rhs);

/// @brief Equality operator for audio_info
bool operator==(const audio_info& lhs, const audio_info& rhs);

uint16_t get_bits_per_sample(const std::string& codec);

void write_wav_file(const std::string& filename,
const std::vector<uint8_t>& audio_data,
const std::string& codec,
uint32_t sample_rate_hz,
uint16_t num_channels);

} // namespace sdk
} // namespace viam
22 changes: 22 additions & 0 deletions src/viam/sdk/components/audio_out.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#include <viam/sdk/components/audio_out.hpp>

#include <viam/sdk/common/audio.hpp>
#include <viam/sdk/common/exception.hpp>
#include <viam/sdk/common/utils.hpp>
#include <viam/sdk/resource/resource.hpp>

namespace viam {
namespace sdk {

API AudioOut::api() const {
return API::get<AudioOut>();
}

API API::traits<AudioOut>::api() {
return {kRDK, kComponent, "audio_out"};
}

AudioOut::AudioOut(std::string name) : Component(std::move(name)) {}

} // namespace sdk
} // namespace viam
86 changes: 86 additions & 0 deletions src/viam/sdk/components/audio_out.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/// @file components/audio_out.hpp
///
/// @brief Defines an `AudioOut` component.
#pragma once

#include <string>

#include <boost/optional/optional.hpp>

#include <viam/sdk/common/audio.hpp>
#include <viam/sdk/common/proto_value.hpp>
#include <viam/sdk/common/utils.hpp>
#include <viam/sdk/config/resource.hpp>

namespace viam {
namespace sdk {

/// @defgroup AudioOut Classes related to the AudioOut component.

/// @class AudioOut audio_out.hpp "components/audio_out.hpp"
/// @brief An `AudioOut` is a device that can output audio.
/// @ingroup AudioOut
///
/// This acts as an abstract parent class to be inherited from by any drivers representing
/// specific AudioOut implementations. This class cannot be used on its own.
class AudioOut : public Component {
public:
using audio_info = viam::sdk::audio_info;

/// @brief Play audio data
/// @param audio_data The audio data to play as bytes
/// @param info Optional info about the audio_data (codec, sample rate, channels). Required for
/// raw PCM data.
inline void play(std::vector<uint8_t> const& audio_data, boost::optional<audio_info> info) {
return play(audio_data, info, {});
}

/// @brief Play audio through the audioout component
/// @param audio_data The audio data to play
/// @param info Optional info about the audio_data (codec, sample rate, channels). Required for
/// raw PCM data.
/// @param extra Any additional arguments to the method
virtual void play(std::vector<uint8_t> const& audio_data,
boost::optional<audio_info> info,
const ProtoStruct& extra) = 0;

/// @brief Returns properties of the audio out device (supported codecs, sample rate, number of
/// channels)
inline audio_properties get_properties() {
return get_properties({});
}

/// @brief Returns properties of the audio out device (supported codecs, sample rate, number of
/// channels)
/// @param extra Any additional arguments to the method
virtual audio_properties get_properties(const ProtoStruct& extra) = 0;

/// @brief Send/receive arbitrary commands to the resource.
/// @param command the command to execute.
/// @return The result of the executed command.
virtual ProtoStruct do_command(const ProtoStruct& command) = 0;

// @brief Returns `GeometryConfig`s associated with the calling audioout.
/// @return The requested `GeometryConfig`s associated with the component.
inline std::vector<GeometryConfig> get_geometries() {
return get_geometries({});
}

/// @brief Returns `GeometryConfig`s associated with the calling audioout.
/// @param extra Any additional arguments to the method.
/// @return The requested `GeometryConfig`s associated with the component.
virtual std::vector<GeometryConfig> get_geometries(const ProtoStruct& extra) = 0;

API api() const override;

protected:
explicit AudioOut(std::string name);
};

template <>
struct API::traits<AudioOut> {
static API api();
};

} // namespace sdk
} // namespace viam
Loading