diff --git a/revid/audio.go b/revid/audio.go index 2177d146..5fe6aaef 100644 --- a/revid/audio.go +++ b/revid/audio.go @@ -1,46 +1,7 @@ -/* -NAME - audio-netsender - NetSender client for sending audio to NetReceiver - -AUTHORS - Alan Noble - Trek Hopton - -ACKNOWLEDGEMENTS - A special thanks to Joel Jensen for his Go ALSA package. - -LICENSE - audio-netsender is Copyright (C) 2018 the Australian Ocean Lab (AusOcean). - - It is free software: you can redistribute it and/or modify them under - the terms of the GNU General Public License as published by the - Free Software Foundation, either version 3 of the License, or (at your - option) any later version. - - It is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - for more details. - - You should have received a copy of the GNU General Public License - along with https://bitbucket.org/ausocean/iot/src/master/gpl.txt. - If not, see http://www.gnu.org/licenses. -*/ - -// audio-netsender is a NetSender client for sending audio to -// NetReceiver. Audio is captured by means of an ALSA recording -// device, specified by the NetReceiver "source" variable. It sent via -// HTTP to NetReceiver in raw audio form, i.e., as PCM data, where it -// is stored as BinaryData objects. Other NetReceiver variables are -// "rate", "period", "channels" and "bits", for specifiying the frame -// rate (Hz), audio period (seconds), number of channels and sample -// bit size respectively. For a description of NetReceiver see -// http://netreceiver.appspot.com/help. -package main +package revid import ( "errors" - "flag" "io" "strconv" "sync" @@ -51,38 +12,33 @@ import ( "bitbucket.org/ausocean/av/codec/pcm" "bitbucket.org/ausocean/iot/pi/netsender" "bitbucket.org/ausocean/iot/pi/sds" - "bitbucket.org/ausocean/iot/pi/smartlogger" "bitbucket.org/ausocean/utils/logger" "bitbucket.org/ausocean/utils/ring" ) const ( - progName = "audio-netsender" - logPath = "/var/log/netsender" - retryPeriod = 5 * time.Second - defaultFrameRate = 48000 - defaultPeriod = 5 // seconds - defaultChannels = 2 - defaultBits = 16 - rbDuration = 300 // seconds - rbTimeout = 100 * time.Millisecond - rbNextTimeout = 100 * time.Millisecond + defaultRate = 48000 + defaultPeriod = 5 // seconds + defaultChannels = 2 + defaultBits = 16 + rbDuration = 300 // seconds + rbTimeout = 100 * time.Millisecond + rbNextTimeout = 100 * time.Millisecond ) -// audioClient holds everything we need to know about the client. +// audioInput holds everything we need to know about the audio input stream. // NB: At 44100 Hz frame rate, 2 channels and 16-bit samples, a period of 5 seconds // results in PCM data chunks of 882000 bytes! A longer period exceeds datastore's 1MB blob limit. -type audioClient struct { - mu sync.Mutex // mu protects the audioClient. +type audioInput struct { + mu sync.Mutex // mu protects the audioInput. parameters // internals - dev *alsa.Device // audio input device - ab alsa.Buffer // ALSA's buffer - rb *ring.Buffer // our buffer - ns *netsender.Sender // our NetSender - vs int // our "var sum" to track var changes + dev *alsa.Device // audio input device + ab alsa.Buffer // ALSA's buffer + rb *ring.Buffer // our buffer //TODO: change this to output stream, doesn't have to be ring buffer + vs int // our "var sum" to track var changes } type parameters struct { @@ -94,32 +50,8 @@ type parameters struct { bits int // sample bit size, 16 by default } -var log *logger.Logger - -func main() { - var logLevel int - flag.IntVar(&logLevel, "LogLevel", int(logger.Debug), "Specifies log level") - flag.Parse() - - validLogLevel := true - if logLevel < int(logger.Debug) || logLevel > int(logger.Fatal) { - logLevel = int(logger.Info) - validLogLevel = false - } - - logSender := smartlogger.New(logPath) - log = logger.New(int8(logLevel), &logSender.LogRoller) - log.Log(logger.Info, "log-netsender: Logger Initialized") - if !validLogLevel { - log.Log(logger.Error, "Invalid log level was defaulted to Info") - } - - var ac audioClient - var err error - ac.ns, err = netsender.New(log, nil, sds.ReadSystem, nil) - if err != nil { - log.Log(logger.Fatal, "netsender.Init failed", "error", err.Error()) - } +func NewAudioInput() { + var ac audioInput // Get audio params and store the current var sum. vars, err := ac.ns.Vars() @@ -144,11 +76,13 @@ func main() { go ac.input() ac.output() + + return stream } // params extracts audio params from corresponding NetReceiver vars and returns true if anything has changed. -// See audioClient for a description of the params and their limits. -func (ac *audioClient) params(vars map[string]string) bool { +// See audioInput for a description of the params and their limits. +func (ac *audioInput) params(vars map[string]string) bool { // We are the only writers to this field // so we don't need to lock here. p := ac.parameters @@ -166,7 +100,7 @@ func (ac *audioClient) params(vars map[string]string) bool { } val, err := strconv.Atoi(vars["rate"]) if err != nil { - val = defaultFrameRate + val = defaultRate } if p.rate != val { p.rate = val @@ -209,7 +143,7 @@ func (ac *audioClient) params(vars map[string]string) bool { // open or re-open the recording device with the given name and prepare it to record. // If name is empty, the first recording device is used. -func (ac *audioClient) open() error { +func (ac *audioInput) open() error { if ac.dev != nil { log.Log(logger.Debug, "Closing", "source", ac.source) ac.dev.Close() @@ -279,11 +213,11 @@ func (ac *audioClient) open() error { // If no easily divisible rate is found, then use the default rate. if !foundRate { log.Log(logger.Warning, "No available device sample-rates are divisible by the requested rate. Default rate will be used. Resampling may fail.", "rateRequested", ac.rate) - _, err = ac.dev.NegotiateRate(defaultFrameRate) + _, err = ac.dev.NegotiateRate(defaultRate) if err != nil { return err } - log.Log(logger.Debug, "Sample rate set", "rate", defaultFrameRate) + log.Log(logger.Debug, "Sample rate set", "rate", defaultRate) } var fmt alsa.FormatType @@ -318,7 +252,7 @@ func (ac *audioClient) open() error { // Spends a lot of time sleeping in Paused mode. // ToDo: Currently, reading audio and writing to the ringbuffer are synchronous. // Need a way to asynchronously read from the ALSA buffer, i.e., _while_ it is recording to avoid any gaps. -func (ac *audioClient) input() { +func (ac *audioInput) input() { for { ac.mu.Lock() mode := ac.mode @@ -369,7 +303,7 @@ func (ac *audioClient) input() { // since cycling more frequently is pointless. // Finally while audio data is sent every audio period, other data is reported only every monitor period. // This function also handles NetReceiver configuration requests and updating of NetReceiver vars. -func (ac *audioClient) output() { +func (ac *audioInput) output() { // Calculate the size of the output data based on wanted channels and rate. outLen := (((len(ac.ab.Data) / ac.ab.Format.Channels) * ac.channels) / ac.ab.Format.Rate) * ac.rate buf := make([]byte, outLen) @@ -510,7 +444,7 @@ func read(rb *ring.Buffer, buf []byte) (int, error) { // formatBuffer returns an ALSA buffer that has the recording data from the ac's original ALSA buffer but stored // in the desired format specified by the ac's parameters. -func (ac *audioClient) formatBuffer() alsa.Buffer { +func (ac *audioInput) formatBuffer() alsa.Buffer { var err error ac.mu.Lock() wantChannels := ac.channels