| // 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 kInt16FormatSwitch[] = "int16"; |
| constexpr char kInt24FormatSwitch[] = "int24"; |
| constexpr char kFrameRateSwitch[] = "rate"; |
| constexpr char kFrameRateDefaultHz[] = "48000"; |
| |
| constexpr char kSineWaveSwitch[] = "sine"; |
| constexpr char kSquareWaveSwitch[] = "square"; |
| constexpr char kSawtoothWaveSwitch[] = "saw"; |
| constexpr char kWhiteNoiseSwitch[] = "noise"; |
| constexpr char kFrequencyDefaultHz[] = "440.0"; |
| |
| constexpr char kDurationSwitch[] = "dur"; |
| constexpr char kDurationDefaultSecs[] = "2.0"; |
| constexpr char kAmplitudeSwitch[] = "amp"; |
| constexpr char kAmplitudeDefaultScale[] = "0.25"; |
| |
| constexpr char kSaveToFileSwitch[] = "wav"; |
| constexpr char kSaveToFileDefaultName[] = "/tmp/signal_generator.wav"; |
| |
| constexpr char kFramesPerPayloadSwitch[] = "frames"; |
| constexpr char kFramesPerPayloadDefault[] = "480"; |
| |
| constexpr char kStreamGainSwitch[] = "gain"; |
| constexpr char kStreamGainDefaultDb[] = "0.0"; |
| constexpr char kStreamMuteSwitch[] = "mute"; |
| constexpr char kStreamMuteDefault[] = "1"; |
| |
| 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("\n\t By default, set stream format to %s-channel float32 at %s Hz\n", |
| kNumChannelsDefault, kFrameRateDefaultHz); |
| printf("\t--%s=<NUM_CHANS>\tSpecify number of channels\n", |
| kNumChannelsSwitch); |
| printf("\t--%s\t\t\tUse 16-bit integer samples\n", kInt16FormatSwitch); |
| printf( |
| "\t--%s\t\t\tUse 24-in-32-bit integer samples (left-justified " |
| "'padded-24')\n", |
| kInt24FormatSwitch); |
| printf("\t--%s=<FRAME_RATE>\tSet frame rate in Hz\n", kFrameRateSwitch); |
| |
| printf("\n\t By default, signal is a %s Hz sine wave\n", |
| kFrequencyDefaultHz); |
| printf("\t--%s[=<FREQ>] \tPlay sine wave at given frequency (Hz)\n", |
| kSineWaveSwitch); |
| printf("\t--%s[=<FREQ>] \tPlay square wave at given frequency\n", |
| kSquareWaveSwitch); |
| printf("\t--%s[=<FREQ>] \tPlay rising sawtooth wave at given frequency\n", |
| kSawtoothWaveSwitch); |
| printf("\t--%s \t\tPlay pseudo-random 'white' noise\n", kWhiteNoiseSwitch); |
| printf("\t If no frequency is provided (e.g. '--%s'), %s Hz is used\n", |
| kSquareWaveSwitch, kFrequencyDefaultHz); |
| |
| printf("\n\t By default, signal plays for %s seconds, at amplitude %s\n", |
| kDurationDefaultSecs, kAmplitudeDefaultScale); |
| printf("\t--%s=<DURATION_SEC>\tSet playback length in seconds\n", |
| kDurationSwitch); |
| printf("\t--%s=<AMPL>\t\tSet amplitude (full-scale=1.0, silence=0.0)\n", |
| kAmplitudeSwitch); |
| |
| printf( |
| "\n\t--%s[=<FILEPATH>]\tSave to .wav file ('%s' if only '--%s' is " |
| "provided)\n", |
| kSaveToFileSwitch, kSaveToFileDefaultName, kSaveToFileSwitch); |
| printf( |
| "\t Subsequent settings (e.g. gain) do not affect .wav file contents\n"); |
| |
| printf( |
| "\n\t By default, submit data to the renderer using buffers of %s " |
| "frames\n", |
| kFramesPerPayloadDefault); |
| printf("\t--%s=<FRAMES>\tSet data buffer size in frames \n", |
| kFramesPerPayloadSwitch); |
| |
| printf( |
| "\n\t By default, AudioRenderer gain and mute are not set (unity 0 dB " |
| "unmuted, no ramping)\n"); |
| printf( |
| "\t--%s[=<GAIN_DB>]\tSet stream gain (dB in [%.1f, %.1f]; %s if only " |
| "'--%s' is provided)\n", |
| kStreamGainSwitch, fuchsia::media::MUTED_GAIN_DB, |
| fuchsia::media::MAX_GAIN_DB, kStreamGainDefaultDb, kStreamGainSwitch); |
| printf( |
| "\t--%s[=<0|1>]\t\tSet stream mute (0=Unmute or 1=Mute; Mute if only " |
| "'--%s' is provided)\n", |
| kStreamMuteSwitch, kStreamMuteSwitch); |
| printf( |
| "\t--%s\t\t\tSmoothly ramp gain from initial value to a target %s dB " |
| "by end-of-signal\n", |
| kStreamRampSwitch, kStreamRampTargetGainDefaultDb); |
| printf("\t\t\t\tIf '--%s' is not provided, ramping starts at unity gain\n", |
| kStreamGainSwitch); |
| printf( |
| "\t--%s=<GAIN_DB>\tSet a different ramp target gain (dB). Implies " |
| "'--%s'\n", |
| kStreamRampTargetGainSwitch, kStreamRampSwitch); |
| printf( |
| "\t--%s=<DURATION_MS>\tSet a specific ramp duration in milliseconds. " |
| "Implies '--%s'\n", |
| kStreamRampDurationSwitch, kStreamRampSwitch); |
| |
| printf("\n\t By default, System Gain and Mute are unchanged\n"); |
| printf( |
| "\t--%s[=<GAIN_DB>]\tSet System Gain (dB in [%.1f, 0.0]; %s if only " |
| "'--%s' is provided)\n", |
| kSystemGainSwitch, fuchsia::media::MUTED_GAIN_DB, kSystemGainDefaultDb, |
| kSystemGainSwitch); |
| printf( |
| "\t--%s[=<0|1>]\t\tSet System Mute (0=Unmute or 1=Mute; Mute if only " |
| "'--%s' is provided)\n", |
| kSystemMuteSwitch, kSystemMuteSwitch); |
| printf("\t Note: changes to System Gain/Mute persist after playback\n"); |
| |
| printf("\n\t By default, system audio output routing policy is unchanged\n"); |
| printf("\t--%s\t\t\tSet 'Play to Most-Recently-Plugged' routing policy\n", |
| kPlayToLastSwitch); |
| printf("\t--%s\t\t\tSet 'Play to All' routing policy\n", kPlayToAllSwitch); |
| printf("\t\t\t\tNote: changes to routing 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_stream_gain(std::stof(stream_gain_str)); |
| } |
| |
| if (command_line.HasOption(kStreamMuteSwitch)) { |
| std::string stream_mute_str; |
| command_line.GetOptionValue(kStreamMuteSwitch, &stream_mute_str); |
| if (stream_mute_str == "") { |
| stream_mute_str = kStreamMuteDefault; |
| } |
| |
| media_app.set_stream_mute(fxl::StringToNumber<uint32_t>(stream_mute_str) != |
| 0); |
| } |
| |
| // 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. |
| auto 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)) { |
| 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; |
| } |
| |
| media_app.set_system_mute(fxl::StringToNumber<uint32_t>(system_mute_str) != |
| 0); |
| } |
| |
| // 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_audio_policy( |
| fuchsia::media::AudioOutputRoutingPolicy::LAST_PLUGGED_OUTPUT); |
| } |
| if (command_line.HasOption(kPlayToAllSwitch)) { |
| 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; |
| } |