Determine samplerate LMS, squeezelite or like

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • philippe_44
    Senior Member
    • May 2008
    • 9344

    #16
    Originally posted by Roland0
    that's the sample rate of the output device, not the input stream.
    Isn’t it what he wants, the rate at which squeezelite is using the sound card, not the rate of the input stream?
    LMS 8.2 on Odroid-C4 - SqueezeAMP!, 5xRadio, 5xBoom, 2xDuet, 1xTouch, 1xSB3. Sonos PLAY:3, PLAY:5, Marantz NR1603, Foobar2000, ShairPortW, 2xChromecast Audio, Chromecast v1 and v2, Squeezelite on Pi, Yamaha WX-010, AppleTV 4, Airport Express, GGMM E5, RivaArena 1 & 3

    Comment

    • Jesperlykke
      Member
      • Apr 2014
      • 64

      #17
      Thanks both...

      Yes i want to determine the output, or the played samplerate at squeezelite.

      The chain is like this (I sometime confuse myself also) :

      LMS --> squeezelite --> Loopback(alsa) --> camilladsp (DSP Engine) --> Hardware (soundcard, DAC etc...)

      So grapping the samplerate at squeezelite output_alsa.c is useable.
      I "hacked" the code so that it paste played samplerate e.g 44100, 88200, 96000 ... 192000 in a textfile named rate.txt

      Me and my grown up son, did write (okay he did mostly ) a Pythonscript which grab the samplerate from the textfile.
      The Python then choose the right filter (44100 ... 192000) and send via websocket the command to the dsp engine, so that the right filter can be used, for the correct samplerate...

      It's like Alpha version 0.1, but working okay for now... I will have to make some real test on my "main" DAC to make sure it sounds right, and that all samplerates are useful.
      Also there might be a better way of doing this, have to consider but i will try this first

      I drop the Python here just for info! ::

      Jesper.

      from subprocess import *
      import time
      infile = "rate.txt"

      from websocket import create_connection
      ws = create_connection("ws://127.0.0.1:3011")

      fin = open(infile, 'r');
      old = fin.readlines();

      while 1: #While true
      fin = open(infile, 'r');
      current = fin.readlines();
      if(current != old ):
      if int(current[0]) == 44100:
      ws.send("setconfigname:/home/pi/Cfilters/rate_44100.yml")
      ws.send("reload")
      print("Changed to 44100")
      elif int(current[0]) == 48000:
      ws.send("setconfigname:/home/pi/Cfilters/rate_48000.yml")
      ws.send("reload")
      print("Changed to 48000")
      elif int(current[0]) == 88200:
      ws.send("setconfigname:/home/pi/Cfilters/rate_88200.yml")
      ws.send("reload")
      print("Changed to 88200")
      elif int(current[0]) == 96000:
      ws.send("setconfigname:/home/pi/Cfilters/rate_96000.yml")
      ws.send("reload")
      print("Changed to 96000")
      elif int(current[0]) == 176400:
      ws.send("setconfigname:/home/pi/Cfilters/rate_176400.yml")
      ws.send("reload")
      print("Changed to 176400")
      elif int(current[0]) == 192000:
      ws.send("setconfigname:/home/pi/Cfilters/rate_192000.yml")
      ws.send("reload")
      print("Changed to 192000")
      elif int(current[0]) == 352800:
      ws.send("setconfigname:/home/pi/Cfilters/rate_352800.yml")
      ws.send("reload")
      print("Changed to 352800")
      else:
      raise ValueError('Unknown rate detected')
      old = current
      time.sleep(0.05)

      Comment

      • Roland0
        Senior Member
        • Aug 2012
        • 1343

        #18
        Originally posted by Jesperlykke
        Yes i want to determine the output, or the played samplerate at squeezelite.
        The chain is like this (I sometime confuse myself also) :
        LMS --> squeezelite --> Loopback(alsa) --> camilladsp (DSP Engine) --> Hardware (soundcard, DAC etc...)
        As mentioned before, this approach is less than optimal (as it copies the audio data around (into ALSA, from ALSA, into ALSA again... - which introduces delays and potentially conversion issues) . It should be:
        LMS --> squeezelite ----(pipe)---> camilladsp (DSP Engine) --> ALSA -> HW

        Me and my grown up son, did write (okay he did mostly ) a Pythonscript which grab the samplerate from the textfile.
        The Python then choose the right filter (44100 ... 192000) and send via websocket the command to the dsp engine, so that the right filter can be used, for the correct samplerate...
        Polling the file like this is not only costly in terms of resources, it also introduces a (variable) delay.
        Use something like pygtail or watchdog
        Or even better, use a FIFO (named pipe) (or socket) instead of the file.
        If you insist on polling, at least do it based on the file modification time.

        Also: use a dict lookup to avoid the if...elif
        or check if rate is valid and:
        ws.send("setconfigname:/home/pi/Cfilters/rate_{}.yml".format(current[0]) )
        Various SW: Web Interface | Text Interface | Playlist Editor / Generator | Music Classification | Similar Music | Announce | EventTrigger | Ambient Noise Mixer | DB Optimizer | Image Enhancer | Chiptunes | LMSlib2go | ...
        Various HowTos: build a self-contained LMS | Bluetooth/ALSA | Control LMS with any device | ...

        Comment

        • Jesperlykke
          Member
          • Apr 2014
          • 64

          #19
          Morning...

          RolandO, the chain you pasted is more correct thanks ::
          As mentioned before, this approach is less than optimal (as it copies the audio data around (into ALSA, from ALSA, into ALSA again... - which introduces delays and potentially conversion issues) . It should be:
          LMS --> squeezelite ----(pipe)---> camilladsp (DSP Engine) --> ALSA -> HW
          I know it's not the best and right solution i found here, but i have to say something through.
          I think this hack i did in alsa_output is only executed everytime player switches number & samplerate. (I can see that the file is only updated there).

          We will change the script to youre suggestions :
          Also: use a dict lookup to avoid the if...elif
          or check if rate is valid and:
          ws.send("setconfigname:/home/pi/Cfilters/rate_{}.yml".format(current[0]) )
          --

          Polling the file like this is not only costly in terms of resources, it also introduces a (variable) delay.
          Use something like pygtail or watchdog
          Or even better, use a FIFO (named pipe) (or socket) instead of the file.
          If you insist on polling, at least do it based on the file modification time.
          You mean when we read (poll) the textfile from the python right ?
          If right i should try to use another way like pygtail or watchdog or FIFO right ?

          Regarding the polling, you said i should do it on the file modification time? - Can you explain for newbies ?

          Also, if you should make a hack/script which should do this, how would you do it RolandO ??

          Rgds; and thanks for the time you use on this; Jesper.
          Last edited by Jesperlykke; 2020-03-25, 06:16.

          Comment

          • Paul Webster
            Senior Member
            • Apr 2005
            • 10347

            #20
            Originally posted by Jesperlykke
            Regarding the polling, you said i should do it on the file modification time? - Can you explain for newbies ?
            That part of the suggestion was to put an IF in there somewhere that is based on the return from https://docs.python.org/library/os.p....path.getmtime

            Logic would be something like ...
            Code:
            oldfilecontents = ""
            oldlastmodifiedtime = 0
            newlastmodifiedtime = 0
            
            
            oldlastmodifiedtime = last-modified-time (if file exists)
            
            loop:
                get last-modified-time (if file exists)
            
                if last-modified-time not-equal to saved last-modified-time then
                    oldlastmodifiedtime = newlastmodifiedtime
                    read file contents
                    close file
            
                    if new file contents not same as old file contents then
                        do the stuff
                    endif
                end-if
            
                sleep
            endloop:
            Paul Webster
            Author of "Now Playing" plugins covering Radio France (FIP etc), PlanetRadio (Bauer - Kiss, Absolute, Scala, JazzFM etc), KCRW, ABC Australia and CBC/Radio-Canada
            and, via the extra "Radio Now Playing" plugin lots more - see https://forums.slimdevices.com/showt...Playing-plugin

            Comment

            • Jesperlykke
              Member
              • Apr 2014
              • 64

              #21
              Originally posted by Paul Webster
              That part of the suggestion was to put an IF in there somewhere that is based on the return from https://docs.python.org/library/os.p....path.getmtime

              Logic would be something like ...
              Code:
              oldfilecontents = ""
              oldlastmodifiedtime = 0
              newlastmodifiedtime = 0
              
              
              oldlastmodifiedtime = last-modified-time (if file exists)
              
              loop:
                  get last-modified-time (if file exists)
              
                  if last-modified-time not-equal to saved last-modified-time then
                      oldlastmodifiedtime = newlastmodifiedtime
                      read file contents
                      close file
              
                      if new file contents not same as old file contents then
                          do the stuff
                      endif
                  end-if
              
                  sleep
              endloop:
              Hi...

              Sry. I'am lost, cant't figure out what that means? I see it in my head as to compare when rate.txt file was changed last time and then if it's not the same time execute again?

              It just makes no sense in my head now ...

              I think that the alsa_output.c is only changing my rate.txt when samplerate really is changing (i can see that in the rate.txt file) so i cant see why it is doing heavy work or do delay the stream whatsoever... But i'am properly wrong???

              Please explain; Jesper

              Comment

              • Paul Webster
                Senior Member
                • Apr 2005
                • 10347

                #22
                Originally posted by Jesperlykke
                Please explain; Jesper
                In your current code you are opening the file each time you go around the loop - and that loop has a sleep of 0.01 (very short).
                It is more work for the operating system to open the file each time. It is less work to check the modified time of the file first and only open it if the modified time has changed.

                In any case - since this is more of a proof of concept then it does not matter very much if it is inefficient - unless it has an effect on the sound quality.

                So - i think that the next thing to do is to do some listening tests and see if you are getting the hoped for improvement in sound.
                Paul Webster
                Author of "Now Playing" plugins covering Radio France (FIP etc), PlanetRadio (Bauer - Kiss, Absolute, Scala, JazzFM etc), KCRW, ABC Australia and CBC/Radio-Canada
                and, via the extra "Radio Now Playing" plugin lots more - see https://forums.slimdevices.com/showt...Playing-plugin

                Comment

                • Jesperlykke
                  Member
                  • Apr 2014
                  • 64

                  #23
                  Originally posted by Paul Webster
                  In your current code you are opening the file each time you go around the loop - and that loop has a sleep of 0.01 (very short).
                  It is more work for the operating system to open the file each time. It is less work to check the modified time of the file first and only open it if the modified time has changed.

                  In any case - since this is more of a proof of concept then it does not matter very much if it is inefficient - unless it has an effect on the sound quality.

                  So - i think that the next thing to do is to do some listening tests and see if you are getting the hoped for improvement in sound.
                  Perfect... thanks now i understand... silly me ofcause... thanks again!

                  Jesper.

                  Comment

                  • Roland0
                    Senior Member
                    • Aug 2012
                    • 1343

                    #24
                    Originally posted by Jesperlykke
                    I know it's not the best and right solution i found here, but i have to say something through.
                    I think this hack i did in alsa_output is only executed everytime player switches number & samplerate. (I can see that the file is only updated there).
                    Not sure if we are on the same page - the chain described above is about how the audio data is routed through the various components. It has nothing to do how often the sample rate is written to a file.

                    I'm also still baffled why you insist on modifying squeezelite to write the sample rate to a file, even though it can already be configured to do so out of the box (see post #15).

                    Originally posted by Jesperlykke
                    You mean when we read (poll) the textfile from the python right ?
                    If right i should try to use another way like pygtail or watchdog or FIFO right ?
                    Regarding the polling, you said i should do it on the file modification time? - Can you explain for newbies ?
                    When your script checks the rate file every 0.05s, it can miss a change for up to 0.04999s. Processing the change and switching camilladsp's config will add to this delay. During this time, squeezelite and camilladsp will operate at different sample rates (Note that with your setup, you actually have 3 ALSA devices ( squeezelite PCM playback device, camilladsp PCM capture device and PCM playback device))
                    Checking the file modification time is faster than reading the content.
                    Using pygtail or watchdog avoids polling altogether by waiting for a notification sent by the OS that the file has changed.
                    A name pipe is even better, since the python script simply waits until the sample rate can be read from it.

                    Originally posted by Jesperlykke
                    Also, if you should make a hack/script which should do this, how would you do it Roland ??
                    Either:
                    - chain is LMS --> squeezelite ----(pipe)---> camilladsp (DSP Engine) --> ALSA -> HW
                    - add code to squeezelite to write sample rate to a named pipe for each new track
                    - python script waits until the sample rate is written to pipe, sends "change config" command to camilladsp via web socket if sample rate changes
                    Or avoid all of this by processing everything on the server, as BruteFIR does (see post #6).

                    If I actually wanted to use camilladsp, I'd check if the BruteFIR plugin works on my setup, and then modify it to use camilladsp instead.
                    Or (more likely), just configure squeezelite to up-sample and pipe the data directly to camilladsp and be done with it.
                    Various SW: Web Interface | Text Interface | Playlist Editor / Generator | Music Classification | Similar Music | Announce | EventTrigger | Ambient Noise Mixer | DB Optimizer | Image Enhancer | Chiptunes | LMSlib2go | ...
                    Various HowTos: build a self-contained LMS | Bluetooth/ALSA | Control LMS with any device | ...

                    Comment

                    • philippe_44
                      Senior Member
                      • May 2008
                      • 9344

                      #25
                      Originally posted by Roland0
                      I'm also still baffled why you insist on modifying squeezelite to write the sample rate to a file, even though it can already be configured to do so out of the box (see post #15).
                      Probably b/c I pushed him to do so. Don't blame him I'm so used to have my modified version of squeezelite for the bridges and squeezeesp32 that I forgot some of the native logs
                      LMS 8.2 on Odroid-C4 - SqueezeAMP!, 5xRadio, 5xBoom, 2xDuet, 1xTouch, 1xSB3. Sonos PLAY:3, PLAY:5, Marantz NR1603, Foobar2000, ShairPortW, 2xChromecast Audio, Chromecast v1 and v2, Squeezelite on Pi, Yamaha WX-010, AppleTV 4, Airport Express, GGMM E5, RivaArena 1 & 3

                      Comment

                      • bpa
                        Senior Member
                        • Oct 2005
                        • 22880

                        #26
                        If you want/need to do some development rather than have scripts or modded squeezelite, wouldn't it be better for camilla DSP to accept a WAV input (sample rate,width in header in line with data) rather than plain PCM so that from each data stream it can determine on the fly the audio format. It would then just be a drop into LMS conf file.

                        Comment

                        • Jesperlykke
                          Member
                          • Apr 2014
                          • 64

                          #27
                          Originally posted by bpa
                          If you want/need to do some development rather than have scripts or modded squeezelite, wouldn't it be better for camilla DSP to accept a WAV input (sample rate,width in header in line with data) rather than plain PCM so that from each data stream it can determine on the fly the audio format. It would then just be a drop into LMS conf file.
                          Look's like a good idea... I can ask if it would be possible!

                          What do you mean when writing
                          It would then just be a drop into LMS conf file
                          ... can you explain ?

                          Thanks all... really appreciate all the input's here... Thanks; Rgds; Jesper.

                          Comment

                          • bpa
                            Senior Member
                            • Oct 2005
                            • 22880

                            #28
                            Originally posted by Jesperlykke
                            Look's like a good idea... I can ask if it would be possible!
                            Why not.

                            The WAV header is a fixed number (44?) of bytes at start of file or stream. All camilladsp would have to do is on startup read the WAV header, analyse it ( https://jawadsblog.wordpress.com/201...v-file-format/ ) and then use the values as if they are default or from command line / config file.
                            Audio DSP processing then starts at byte 45. Chunk length should be zero to indicate infinity - it is not required in a streaming environment.

                            You might even ask the developer to do it as a command line option (e.g. -w) ?


                            What do you mean when writing ... can you explain ?

                            Thanks all... really appreciate all the input's here... Thanks; Rgds; Jesper.
                            Then, disable all "native" so that transcoding to Flac is preferred. All you need is have conf rules which have an additional step to current ones - say to flac
                            For example the AAC to FLAC change
                            Code:
                            aac flc * *
                            	# IF
                            	[faad] -q -w -f 1 $FILE$ | [flac] -cs --totally-silent --compression-level-0 --ignore-chunk-sizes -
                            changes to convert conf file ( use a custom-convert.conf to make it easy to upgrade).
                            Code:
                            aac flc * *
                            	# IF
                            	[faad] -q -w -f 1 $FILE$ | camilladsp  camillaoptions -w  | [flac] -cs --totally-silent --compression-level-0 --ignore-chunk-sizes -

                            Comment

                            • Jesperlykke
                              Member
                              • Apr 2014
                              • 64

                              #29
                              Good explanation !!

                              I asked the dev. of the camilladsp the suggestion you wrote...

                              Excited to see what he say's about that ...

                              Everyone is so helpfull, so i am pleased

                              Jesper.

                              Comment

                              • Jesperlykke
                                Member
                                • Apr 2014
                                • 64

                                #30
                                If I actually wanted to use camilladsp, I'd check if the BruteFIR plugin works on my setup, and then modify it to use camilladsp instead.
                                Or (more likely), just configure squeezelite to up-sample and pipe the data directly to camilladsp and be done with it.
                                Hello again here.

                                While still thinking and ran into other issues with this i need, i tried the route as RolandO also wrote about!
                                I've been installing the BrutefirDRC plugin and getting it to work as basic now.
                                ======== brutefirwrapper rev 12 starting at 1585476309 2020-03-29 10:05:09
                                Traceback (most recent call last):
                                File "/usr/share/squeezeboxserver/Plugins/BrutefirDrc/Bin/brutefirwrapper", line 471, in read_tail
                                age = time.time()-os.stat(tail_filename).st_mtime
                                OSError: [Errno 2] No such file or directory: '/tmp/.BrutefirDrc-110/tail-dc_a6_32_18_8f_ab.pcm'
                                Output follows (0.026s)
                                TEST_output
                                44100
                                TEST_1
                                Output complete (0.027s)

                                BruteFIR v1.0m (November 2013) (c) Anders Torger

                                Internal resolution is 32 bit floating point.
                                Creating 4 FFTW plans of size 32768...finished.
                                Loading 3 coefficient sets...finished.
                                Warning: no support for clock cycle counter on this platform.
                                Timers for benchmarking may be unreliable.
                                Filters in process 0: 0 2
                                Filters in process 1: 1 3
                                Creating inverse inplace FFTW plan of size 65536 using wisdom...
                                As one can see, i pasted in som line in the brutefirwrapper code, to see if i can make changes, which is possible.
                                I can now see the samplerate and some point in code and so...

                                Next step is to find where the audio actually is played in the code, and let this stream to the camilladsp capture [loopback].
                                Then if possible let squeezelite grab the output of camilladsp and play it directly on the hardware.

                                Output follows (0.026s)
                                TEST_output
                                44100
                                TEST_1
                                Output complete (0.027s) <----- I think brutefirdrc should stop here and let camilladsp handle it from here ?
                                ----------------------------------------------------------------------------------------------------------------------------------------------
                                BruteFIR v1.0m (November 2013) (c) Anders Torger

                                Internal resolution is 32 bit floating point.
                                Creating 4 FFTW plans of size 32768...finished.
                                Loading 3 coefficient sets...finished.
                                Warning: no support for clock cycle counter on this platform.
                                Timers for benchmarking may be unreliable.
                                Filters in process 0: 0 2
                                Filters in process 1: 1 3
                                Creating inverse inplace FFTW plan of size 65536 using wisdom... <------ This takes ~34 seconds on my RPI4 to there actually is music !!
                                I don't think i can use the Brutefirdrc as it is, due to the latency (red marks above)

                                Is it a good idea or is it nogo

                                Jesper.

                                Comment

                                Working...