blob: 7c062a7ea12a307f026c0dd4340b7aecf7e2f107 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <lib/async-loop/cpp/loop.h>
#include <lib/async/cpp/task.h>
#include "garnet/bin/media/signal_generator/signal_generator.h"
#include "lib/component/cpp/startup_context.h"
#include "lib/fxl/command_line.h"
#include "lib/fxl/strings/string_number_conversions.h"
namespace {
constexpr char kNumChannelsSwitch[] = "chans";
constexpr char kNumChannelsDefault[] = "2";
constexpr char kFrameRateSwitch[] = "rate";
constexpr char kFrameRateDefaultHz[] = "48000";
constexpr char kInt16FormatSwitch[] = "int16";
constexpr char kInt24FormatSwitch[] = "int24";
constexpr char kSineWaveSwitch[] = "sine";
constexpr char kSquareWaveSwitch[] = "square";
constexpr char kSawtoothWaveSwitch[] = "saw";
constexpr char kWhiteNoiseSwitch[] = "noise";
constexpr char kFrequencyDefaultHz[] = "440.0";
constexpr char kAmplitudeSwitch[] = "amp";
constexpr char kAmplitudeDefaultScale[] = "0.5";
constexpr char kDurationSwitch[] = "dur";
constexpr char kDurationDefaultSecs[] = "2.0";
constexpr char kFramesPerPayloadSwitch[] = "frames";
constexpr char kFramesPerPayloadDefault[] = "480";
constexpr char kSaveToFileSwitch[] = "wav";
constexpr char kSaveToFileDefaultName[] = "/tmp/signal_generator.wav";
constexpr char kStreamGainSwitch[] = "gain";
constexpr char kStreamGainDefaultDb[] = "0.0";
constexpr char kStreamRampSwitch[] = "ramp";
constexpr char kStreamRampDurationSwitch[] = "rampdur";
constexpr char kStreamRampTargetGainSwitch[] = "endgain";
constexpr char kStreamRampTargetGainDefaultDb[] = "-75.0";
constexpr char kSystemGainSwitch[] = "sgain";
constexpr char kSystemGainDefaultDb[] = "-12.0";
constexpr char kSystemMuteSwitch[] = "smute";
constexpr char kSystemMuteDefault[] = "1";
constexpr char kPlayToLastSwitch[] = "last";
constexpr char kPlayToAllSwitch[] = "all";
constexpr char kHelpSwitch[] = "help";
} // namespace
void usage(const char* prog_name) {
printf("\nUsage: %s [--option] [...]\n", prog_name);
printf("Generate and play an audio signal to the preferred output device.\n");
printf("\nAdditional optional settings include:\n");
printf("\t--%s=<NUM_CHANS>\tSpecify number of output channels (default %s)\n",
kNumChannelsSwitch, kNumChannelsDefault);
printf("\t--%s=<FRAME_RATE>\tSet output frame rate in Hertz (default %s)\n",
kFrameRateSwitch, kFrameRateDefaultHz);
printf("\t--%s\t\t\tEmit signal as 16-bit integer (default float32)\n",
kInt16FormatSwitch);
printf("\t--%s\t\t\tEmit signal as 24-in-32-bit integer (default float32)\n",
kInt24FormatSwitch);
printf(
"\n\t--%s[=<FREQ>] \tPlay sine of given frequency, in Hz (default %s)\n",
kSineWaveSwitch, kFrequencyDefaultHz);
printf("\t--%s[=<FREQ>] \tPlay square wave (default %s Hz)\n",
kSquareWaveSwitch, kFrequencyDefaultHz);
printf("\t--%s[=<FREQ>] \tPlay rising sawtooth wave (default %s Hz)\n",
kSawtoothWaveSwitch, kFrequencyDefaultHz);
printf("\t--%s \t\tPlay pseudo-random 'white' noise\n", kWhiteNoiseSwitch);
printf("\t\t\t\tIn the absence of any of the above, a sine is played.\n");
printf(
"\n\t--%s=<AMPL>\t\tSet signal amplitude (full-scale=1.0, default %s)\n",
kAmplitudeSwitch, kAmplitudeDefaultScale);
printf(
"\n\t--%s=<DURATION_SEC>\tSet playback length, in seconds (default %s)\n",
kDurationSwitch, kDurationDefaultSecs);
printf("\t--%s=<FRAMES>\tSet data buffer size, in frames (default %s)\n",
kFramesPerPayloadSwitch, kFramesPerPayloadDefault);
printf("\n\t--%s[=<FILEPATH>]\tSave signal to .wav file (default %s)\n",
kSaveToFileSwitch, kSaveToFileDefaultName);
printf("\t\t\t\tGain/mute/ramp settings do not affect .wav file contents.");
printf("\n\t\t\t\t24-bit signals are saved left-justified in 32-bit ints.\n");
printf(
"\n\t--%s[=<GAIN_DB>]\tSet AudioRenderer gain, in dB (default %s, "
"range %.1f, %.1f])\n",
kStreamGainSwitch, kStreamGainDefaultDb, fuchsia::media::MUTED_GAIN_DB,
fuchsia::media::MAX_GAIN_DB);
printf("\t\t\t\tIf '--%s' is not specified, stream plays at unity gain.\n",
kStreamGainSwitch);
printf(
"\n\t--%s\t\t\tSmoothly ramp gain to %s dB, over the signal duration.\n",
kStreamRampSwitch, kStreamRampTargetGainDefaultDb);
printf(
"\t\t\t\tValues for end-of-ramp gain and ramp duration are overridden\n"
"\t\t\t\tby '--%s' and '--%s', respectively.\n",
kStreamRampTargetGainSwitch, kStreamRampDurationSwitch);
printf("\t\t\t\tIf '--%s' is not specified, ramping starts at unity gain.\n",
kStreamGainSwitch);
printf(
"\t--%s=<GAIN_DB>\tRamp stream gain to the specified value (in dB),\n"
"\t\t\t\tinstead of the default %s dB. (implies '--%s')\n",
kStreamRampTargetGainSwitch, kStreamRampTargetGainDefaultDb,
kStreamRampSwitch);
printf(
"\t--%s=<DURATION_MS>\tRamp stream gain over the specified duration "
"(in milliseconds),\n",
kStreamRampDurationSwitch);
printf("\t\t\t\trather than the signal's entire length. (implies '--%s')\n",
kStreamRampSwitch);
printf("\n\t--%s=<GAIN>\t\tSet System Gain to [%.1f, 0.0] dB (default %s)\n",
kSystemGainSwitch, fuchsia::media::MUTED_GAIN_DB,
kSystemGainDefaultDb);
printf("\t--%s[=<0|1>]\t\tSet System Mute (1=mute, 0=unmute, default %s)\n",
kSystemMuteSwitch, kSystemMuteDefault);
printf("\t\t\t\tNote: changes to System Gain/Mute persist after playback.\n");
printf("\n\t--%s\t\t\tSet 'Play to Most-Recently-Plugged' policy\n",
kPlayToLastSwitch);
printf("\t--%s\t\t\tSet 'Play to All' policy\n", kPlayToAllSwitch);
printf("\t\t\t\tNote: changes to audio policy persist after playback.\n");
printf("\n\t--%s, --?\t\tShow this message\n\n", kHelpSwitch);
}
int main(int argc, const char** argv) {
const auto command_line = fxl::CommandLineFromArgcArgv(argc, argv);
if (command_line.HasOption("?") || command_line.HasOption(kHelpSwitch)) {
usage(argv[0]);
return 0;
}
async::Loop loop(&kAsyncLoopConfigAttachToThread);
auto startup_context = component::StartupContext::CreateFromStartupInfo();
media::tools::MediaApp media_app([&loop]() {
async::PostTask(loop.dispatcher(), [&loop]() { loop.Quit(); });
});
// Handle channels and frame-rate
std::string num_channels_str = command_line.GetOptionValueWithDefault(
kNumChannelsSwitch, kNumChannelsDefault);
media_app.set_num_channels(fxl::StringToNumber<uint32_t>(num_channels_str));
std::string frame_rate_str = command_line.GetOptionValueWithDefault(
kFrameRateSwitch, kFrameRateDefaultHz);
media_app.set_frame_rate(fxl::StringToNumber<uint32_t>(frame_rate_str));
// Handle signal format
if (command_line.HasOption(kInt16FormatSwitch)) {
// Don't allow the user to specify more than one container format
if (command_line.HasOption(kInt24FormatSwitch)) {
usage(argv[0]);
return 0;
}
media_app.set_int16_format(true);
}
if (command_line.HasOption(kInt24FormatSwitch)) {
media_app.set_int24_format(true);
}
// Handle signal type and frequency specifications.
// If >1 type is specified, obey usage order: sine, square, saw, noise.
std::string frequency_str = "";
if (command_line.HasOption(kSineWaveSwitch)) {
media_app.set_output_type(media::tools::kOutputTypeSine);
command_line.GetOptionValue(kSineWaveSwitch, &frequency_str);
} else if (command_line.HasOption(kSquareWaveSwitch)) {
media_app.set_output_type(media::tools::kOutputTypeSquare);
command_line.GetOptionValue(kSquareWaveSwitch, &frequency_str);
} else if (command_line.HasOption(kSawtoothWaveSwitch)) {
media_app.set_output_type(media::tools::kOutputTypeSawtooth);
command_line.GetOptionValue(kSawtoothWaveSwitch, &frequency_str);
} else if (command_line.HasOption(kWhiteNoiseSwitch)) {
media_app.set_output_type(media::tools::kOutputTypeNoise);
} else {
media_app.set_output_type(media::tools::kOutputTypeSine);
}
if (frequency_str == "") {
frequency_str = kFrequencyDefaultHz;
}
media_app.set_frequency(std::stod(frequency_str));
// Handle duration and amplitude of generated signal
std::string amplitude_str = command_line.GetOptionValueWithDefault(
kAmplitudeSwitch, kAmplitudeDefaultScale);
if (amplitude_str != "") {
media_app.set_amplitude(std::stof(amplitude_str));
}
std::string duration_str = command_line.GetOptionValueWithDefault(
kDurationSwitch, kDurationDefaultSecs);
if (duration_str != "") {
media_app.set_duration(std::stod(duration_str));
}
// Handle payload buffer size
std::string frames_per_payload_str = command_line.GetOptionValueWithDefault(
kFramesPerPayloadSwitch, kFramesPerPayloadDefault);
media_app.set_frames_per_payload(
fxl::StringToNumber<uint32_t>(frames_per_payload_str));
// Handle stream gain
if (command_line.HasOption(kStreamGainSwitch)) {
std::string stream_gain_str;
command_line.GetOptionValue(kStreamGainSwitch, &stream_gain_str);
if (stream_gain_str == "") {
stream_gain_str = kStreamGainDefaultDb;
}
media_app.set_will_set_stream_gain(true);
media_app.set_stream_gain(std::stof(stream_gain_str));
}
// Handle stream gain ramping, target gain and ramp duration.
if (command_line.HasOption(kStreamRampSwitch) ||
command_line.HasOption(kStreamRampTargetGainSwitch) ||
command_line.HasOption(kStreamRampDurationSwitch)) {
media_app.set_will_ramp_stream_gain();
std::string target_gain_db_str = command_line.GetOptionValueWithDefault(
kStreamRampTargetGainSwitch, kStreamRampTargetGainDefaultDb);
if (target_gain_db_str == "") {
target_gain_db_str = kStreamRampTargetGainDefaultDb;
}
media_app.set_ramp_target_gain_db(std::stof(target_gain_db_str));
// Convert signal duration of doublefloat seconds, to int64 nanoseconds.
zx_duration_t ramp_duration_nsec =
static_cast<zx_duration_t>(media_app.get_duration() * 1000000000.0);
if (command_line.HasOption(kStreamRampDurationSwitch)) {
std::string ramp_duration_str = "";
command_line.GetOptionValue(kStreamRampDurationSwitch,
&ramp_duration_str);
if (ramp_duration_str != "") {
// Convert input of doublefloat milliseconds, to int64 nanoseconds.
ramp_duration_nsec = static_cast<zx_duration_t>(
std::stod(ramp_duration_str) * 1000000.0);
}
}
media_app.set_ramp_duration_nsec(ramp_duration_nsec);
}
// Handle system gain and system mute
if (command_line.HasOption(kSystemGainSwitch)) {
media_app.set_will_set_system_gain();
std::string system_gain_str;
command_line.GetOptionValue(kSystemGainSwitch, &system_gain_str);
if (system_gain_str == "") {
system_gain_str = kSystemGainDefaultDb;
}
media_app.set_system_gain(std::stof(system_gain_str));
}
if (command_line.HasOption(kSystemMuteSwitch)) {
std::string system_mute_str;
command_line.GetOptionValue(kSystemMuteSwitch, &system_mute_str);
if (system_mute_str == "") {
system_mute_str = kSystemMuteDefault;
}
if (fxl::StringToNumber<uint32_t>(system_mute_str) != 0) {
media_app.set_system_mute();
} else {
media_app.set_system_unmute();
}
}
// Handle output routing policy
if (command_line.HasOption(kPlayToLastSwitch)) {
// Don't allow the user to specify both policies
if (command_line.HasOption(kPlayToAllSwitch)) {
usage(argv[0]);
return 0;
}
media_app.set_will_set_audio_policy(true);
media_app.set_audio_policy(
fuchsia::media::AudioOutputRoutingPolicy::LAST_PLUGGED_OUTPUT);
}
if (command_line.HasOption(kPlayToAllSwitch)) {
media_app.set_will_set_audio_policy(true);
media_app.set_audio_policy(
fuchsia::media::AudioOutputRoutingPolicy::ALL_PLUGGED_OUTPUTS);
}
// Handle "generate to file"
if (command_line.HasOption(kSaveToFileSwitch)) {
std::string save_file_str;
command_line.GetOptionValue(kSaveToFileSwitch, &save_file_str);
// If just '--wav' is specified, use the default file name.
if (save_file_str == "") {
save_file_str = kSaveToFileDefaultName;
}
media_app.set_save_to_file(true);
media_app.set_save_file_name(save_file_str);
}
media_app.Run(startup_context.get());
// We've set everything going. Wait for our message loop to return.
loop.Run();
return 0;
}