3 # Copyright (C) 2011--2013 Kipp Cannon, Chad Hanna, Drew Keppel
5 # This program is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by the
7 # Free Software Foundation; either version 2 of the License, or (at your
8 # option) any later version.
10 # This program is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
13 # Public License for more details.
15 # You should have received a copy of the GNU General Public License along
16 # with this program; if not, write to the Free Software Foundation, Inc.,
17 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 from optparse
import OptionParser
26 gobject.threads_init()
33 from pylal
import series as lalseries
34 from glue.ligolw
import ligolw
35 from glue.ligolw
import array
36 from glue.ligolw
import param
37 from glue.ligolw
import lsctables
38 array.use_in(ligolw.LIGOLWContentHandler)
39 param.use_in(ligolw.LIGOLWContentHandler)
40 lsctables.use_in(ligolw.LIGOLWContentHandler)
41 from glue.ligolw
import utils
42 from gstlal
import datasource
43 from gstlal
import pipeparts
44 from gstlal
import simplehandler
45 from glue.ligolw.utils
import segments as ligolw_segments
46 from pylal
import series as lalseries
49 # This program will play data in a variety of ways; See gstlal_play for help and usage.
51 # This program will play data in a variety of ways. Its input is anything
52 # supported by datasource.py. You can additionally whiten the data or apply a
53 # band pass filtering. It can direct its output to either your sound card,
54 # various audio file formats, or stderr/stdout in tab delimited ASCII text.
56 # #### Graph of the gsreamer pipeline
58 # - gray boxes are optional and depend on the command line given
66 # node [shape=record fontsize=10 fontname="Verdana"];
67 # edge [fontsize=8 fontname="Verdana"];
71 # "mkbasicsrc()" [URL="\ref datasource.mkbasicsrc()"];
72 # "whitened_multirate_src()" [label="whitened_multirate_src()", URL="\ref multirate_datasource.mkwhitened_multirate_src()", style=filled, color=lightgrey];
73 # "mkresample()" [URL="\ref pipeparts.mkresample()", style=filled, color=lightgrey];
74 # "mkcapsfilter()" [URL="\ref pipeparts.mkcapsfilter()", style=filled, color=lightgrey];
75 # "mkaudiochebband()" [URL="\ref pipeparts.mkaudiochebband()", style=filled, color=lightgrey];
76 # "mkaudiocheblimit()" [URL="\ref pipeparts.mkaudiocheblimit()", style=filled, color=lightgrey];
77 # "mkaudioconvert()" [URL="\ref pipeparts.mkaudioconvert()"];
78 # "mkaudioamplify()" [URL="\ref pipeparts.mkaudioamplify()"];
79 # "mkautoaudiosink()" [URL="\ref pipeparts.mkautoaudiosink()", style=filled, color=lightgrey];
80 # "mkwavenc()" [URL="\ref pipeparts.mkwavenc()", style=filled, color=lightgrey];
81 # "mkflacenc()" [URL="\ref pipeparts.mkflacenc()", style=filled, color=lightgrey];
82 # "mkvorbisenc()" [URL="\ref pipeparts.mkvorbisenc()", style=filled, color=lightgrey];
83 # "mkfilesink()" [URL="\ref pipeparts.mkfilesink()", style=filled, color=lightgrey];
84 # "mknxydumpsink()" [URL="\ref pipeparts.mknxydumpsink()", style=filled, color=lightgrey];
88 # "mkbasicsrc()" -> "mkresample()" [label=" --whiten not provided"];
89 # "mkresample()" -> "mkcapsfilter()";
90 # "mkcapsfilter()" -> "mkaudioconvert()" [label=" neither --low-pass-filter nor --high-pass-filter provided"];
91 # "mkcapsfilter()" -> "mkaudiochebband()" [label=" --low-pass-filter and --high-pass-filter provided"];
92 # "mkcapsfilter()" -> "mkaudiocheblimit()" [label=" --low-pass-filter or --high-pass-filter provided"];
94 # "mkbasicsrc()" -> "whitened_multirate_src()" [label=" --whiten provided"];
95 # "whitened_multirate_src()" -> "mkaudioconvert()" [label=" neither --low-pass-filter nor --high-pass-filter provided"];
96 # "whitened_multirate_src()" -> "mkaudiochebband()" [label=" --low-pass-filter and --high-pass-filter provided"];
97 # "whitened_multirate_src()" -> "mkaudiocheblimit()" [label=" --low-pass-filter or --high-pass-filter provided"];
99 # "mkaudiochebband()" -> "mkaudioconvert()";
100 # "mkaudiocheblimit()" -> "mkaudioconvert()";
102 # "mkaudioconvert()" -> "mkaudioamplify()";
104 # "mkaudioamplify()" -> "mkautoaudiosink()" [label=" --output not provided"];
105 # "mkaudioamplify()" -> "mkwavenc()" [label=" --output ends with '.wav'"];
106 # "mkaudioamplify()" -> "mkflacenc()" [label=" --output ends with '.flac'"];
107 # "mkaudioamplify()" -> "mkvorbisenc()" [label=" --output ends with '.ogg'"];
108 # "mkaudioamplify()" -> "mknxydumpsink()" [label=" --output ends with '.txt' or is /dev/stderr or /dev/stdout"];
109 # "mkwavenc()" -> "mkfilesink()";
110 # "mkvorbisenc()" -> "mkfilesink()";
111 # "mkflacenc()" -> "mkfilesink()";
117 # See datasource.append_options() for additional usage cases for datasource specific command line options
119 # -# Viewing low latency data in stdout (e.g. on CIT cluster)
120 # - Note ctrl+c kills this
122 # gstlal_play --data-source framexmit --channel-name=L1=FAKE-STRAIN --output /dev/stdout
124 # -# Pipe low latency data to an ogg file narrowing in on the sweet spot and
125 # add amplification to make it audible.
126 # - Note ctrl+c kills this
128 # gstlal_play --data-source framexmit --channel-name=L1=FAKE-STRAIN --high-pass-filter 40 --low-pass-filter 1000 --amplification 1e21 --output test.ogg
130 # -# Write injection time series from an xml file into an ASCII delimited text file
132 # gstlal_play --data-source silence --gps-start-time 966383960 --gps-end-time 966384960 --channel-name=L1=FAKE-STRAIN --injections=BNS-MDC1-FIXEDMASS.xml --output test.txt
134 # -# Other things are certainly possible. Please add some!
136 # ### Command line options ###
138 # See datasource.append_options() for common command line options shared among different programs
140 # + `--output` [filename]: Set the filename in which to save the output. If not given, output is sent to the default audio device. The filename's extension determines the format, the following are recognized: .wav, .flac, .ogg, .txt, /dev/stdout, /dev/stderr
141 # + `--rate` [int] (Hz): Downsample input to this sample rate. Default = 4096 Hz. Must be @f$ \leq @f$ input sample rate or else you will get a caps negotiation error.
142 # + `--whiten`: Whiten the time series (default = do not whiten.
143 # + `--reference-psd` [filename]: When whitening, normalize the time series to the spectrum described in this XML file. If this option is not given, the spectrum is measured from the data.
144 # + `--low-pass-filter` [float] (Hz): Low pass filter frequency (default = no low-pass filter). Low-pass filter is applied after whitening.
145 # + `--high-pass-filter` [float] (Hz): High pass filter frequency (default = no high-pass filter). High-pass filter is applied after whitening.
146 # + `--amplification` [float] (dimensionless): Amplify the timeseries this much (default = no amplification). Amplification is applied after low- and high-pass filtering. For unwhitened h(t) that is bandpassed to the most sensitive region you might need to set this to 1e20 to make it audible
147 # + `--verbose`: Be verbose.
151 def parse_command_line():
153 parser = OptionParser(description = __doc__)
156 # First append the datasource common options
161 parser.add_option(
"--output", metavar =
"filename", help =
"Set the filename in which to save the output. If not given, output is sent to the default audio device. The filename's extension determines the format, the following are recognized: .wav, .flac, .ogg, .txt, /dev/stdout, /dev/stderr")
162 parser.add_option(
"--rate", metavar =
"Hz", type =
"int", default = 4096, help =
"Downsample input to this sample rate. Default = 4096 Hz. Must be <= input sample rate or else you will get a caps negotiation error.")
163 parser.add_option(
"--whiten", action =
"store_true", help =
"Whiten the time series (default = do not whiten).")
164 parser.add_option(
"--reference-psd", metavar =
"filename", help =
"When whitening, normalize the time series to the spectrum described in this XML file. If this option is not given, the spectrum is measured from the data.")
165 parser.add_option(
"--low-pass-filter", metavar =
"Hz", type =
"float", help =
"Low pass filter frequency (default = no low-pass filter). Low-pass filter is applied after whitening.")
166 parser.add_option(
"--high-pass-filter", metavar =
"Hz", type =
"float", help =
"High pass filter frequency (default = no high-pass filter). High-pass filter is applied after whitening.")
167 parser.add_option(
"--amplification", metavar =
"factor", type =
"float", default = 1.0, help =
"Amplify the timeseries this much (default = no amplification). Amplification is applied after low- and high-pass filtering. For unwhitened h(t) that is bandpassed to the most sensitive region you might need to set this to 1e20 to make it audible")
168 parser.add_option(
"-v",
"--verbose", action =
"store_true", help =
"Be verbose.")
171 # parse the arguments and sanity check
174 options, filenames = parser.parse_args()
176 if options.low_pass_filter is not None and options.high_pass_filter is not None:
177 if options.low_pass_filter <= options.high_pass_filter:
178 raise ValueError(
"--low-pass-filter must be > --high-pass-filter")
180 if options.whiten and (options.low_pass_filter or options.high_pass_filter):
181 raise ValueError(
"specify either whitening or filtering not both")
183 if options.reference_psd and not options.whiten:
184 raise ValueError(
"--reference-psd requires --whiten")
186 if len(options.channel_name) > 1:
187 raise ValueError(
"only one --channel-name allowed")
189 return options, filenames
192 # parsing and setting up some core structures
193 options, filenames = parse_command_line()
195 gw_data_source_info = datasource.GWDataSourceInfo(options)
196 instrument, = gw_data_source_info.channel_dict
198 if options.reference_psd is not None:
199 psd = lalseries.read_psd_xmldoc(utils.load_filename(options.reference_psd, verbose = options.verbose, contenthandler = ligolw.LIGOLWContentHandler))[instrument]
204 # building the event loop and pipeline
205 gobject.threads_init()
206 mainloop = gobject.MainLoop()
207 pipeline = gst.Pipeline(
"gstlal_play_frames")
208 handler = simplehandler.Handler(mainloop, pipeline)
214 head = datasource.
mkbasicsrc(pipeline, gw_data_source_info, instrument, verbose = options.verbose)
220 head = pipeparts.
mkresample(pipeline, head, quality = 9)
221 head = pipeparts.
mkcapsfilter(pipeline, head,
"audio/x-raw-float, rate=%d" % options.rate)
224 if options.high_pass_filter is not None and options.low_pass_filter is not None:
225 head = pipeparts.
mkaudiochebband(pipeline, head, lower_frequency = options.high_pass_filter, upper_frequency = options.low_pass_filter)
227 if options.high_pass_filter is not None:
228 head = pipeparts.
mkaudiocheblimit(pipeline, head, cutoff = options.high_pass_filter, mode =
"high-pass")
229 elif options.low_pass_filter is not None:
230 head = pipeparts.
mkaudiocheblimit(pipeline, head, cutoff = options.low_pass_filter, mode =
"low-pass")
232 # necessary audio convert and amplify
235 if options.output is None:
237 elif options.output.endswith(
".wav"):
239 elif options.output.endswith(
".flac"):
241 elif options.output.endswith(
".ogg"):
242 head = pipeparts.mkoggmux(pipeline, pipeparts.
mkvorbisenc(pipeline, head))
243 pipeparts.
mkfilesink(pipeline, head, options.output)
244 elif options.output.endswith(
".txt") or options.output in (
"/dev/stdout",
"/dev/stderr"):
247 raise ValueError(
"unrecognized format for --output")
249 # Allow Ctrl+C or sig term to gracefully shut down the program for online
250 # sources, otherwise it will just kill it
251 if gw_data_source_info.data_source in (
"lvshm",
"framexmit"):# what about nds online?
252 simplehandler.OneTimeSignalHandler(pipeline)
255 if pipeline.set_state(gst.STATE_PLAYING) == gst.STATE_CHANGE_FAILURE:
256 raise RuntimeError(
"pipeline failed to enter PLAYING state")