Which audio I/F?

Hi,
I’m close to get a raspberry pi 4, and to try out Sonic Pi, but I wonder which audio interface would be recommended ? My use would be to use Sonic Pi as on the fly effect for vocals, so I would need at least 1 input, and of course 1 output, at least 44.1 kHz 16 bit. What I found up to now :

  • Hifiberry : very compact but seems to be dedicated to audio streaming (small RCA connectors without any input)

  • Pi sound : very expensive and way too high performances for my application

I would like to find some small and reliable solution… :thinking: Any advice ?

Thanks :grinning_face:

I think it’s a good question. There are many inexpensive USB audio interface options that are guaranteed to work with Pi, but I would like to know whether more fully featured USB audio interfaces such as from Focusrite’s Scarlett range, would work well.

Many such devices offer excellent fidelity and can be found in the used market at affordable prices.

From my research, and in the absence of such interfaces being tested by Pi communicators (and I don’t understand why not), the key for such a USB device is that it is “class-compliant”.

1 Like

Hi, I have a basic Clarett2Pre+ from Focusrite - Sonic Pi finds it in the I/O automatically. Also picked up an old 2ndhand Thunderbolt one. It needed a new driver because of a change in Mac security.

Haven’t tried live input into Sonic Pi yet. Will have a go, next chance I get :slight_smile:

Edit: tried live_audio and Focusrite Clarett2Pre. It was fun!

I’ve read the tutorials and looked on here, but can’t find many examples. @robin.newman is your Frere Jacques still up somewhere? Would love to hear/see.

Is live_audio similar to live_loop, just inputting the live instrument? Does it need a sleep line or sync, or does it just stay constantly open? Sorry if these are basic Qs! Thanks.

1 Like

I think this may be what you are looking for Frere Jaques Records my violin and generates a four part round in Sonic Pi.
The program is shown here

live_audio is essentially just a switch which enables sound input as specified in the parameters. Once starterd it is active until a live_audio :stop command is issued with the same name parameter.
I use live_audio :violin,input: 2 to start it running, :violin is just the name I assign to the input stream, and input: 2 was the input channel I used (only recording mono here from the pickup. Input channels are numbered according to the number of input channels you have enabled. This depends on your audio input card.
Later I stop the input stream using live_audio :violin, :stop
The duration of the buffer is set by the time between these two commands
I use fx :record to specify where the input stream is recorded using three buffers named :v0,:v1,:v2 and :v3 to store the four seperate recordings.
At the beginning I played some tones (in my headphones) to hep me get the tempo and the point at which to start playinfg.

It doesn’t have to be a violin. You can record any instrument or even just sing into a mic. Let me know if you need more info to get going.

3 Likes

Wow - so helpful - thanks @robin.newman! I had no idea Sonic Pi can do this, couldn’t find much info. Also have never used buffers in this way before. I got some FX including ring_mod working on live input over SP backdrop last night, wasn’t sure how to end it. Look forward to having a go. @simon1618 hope this info is useful for you, too :slight_smile:

2 Likes

Thanks for all these useful information and sorry for my late answer !

Finally I’ve choosen a hifiberry dac+adc soundcard and it works.

I am building my code (looper project) step by step and this way of indexing buffers at each iteration is a great hint thanks !

I hope to be able to share a descent code soon… :sweat_smile:

1 Like

Getting back to the question of audio interface for the Pi computer, I guess @simon1618 bought this, as opposed to the HiFiBerry DAC2 ADC Pro. How is the quality?

My problem with HAT boards is that they require re-doing the casing, and they’re not portable to other computers and operating systems. Also in this category there is Pi Sound, which has DAC, ADC and MIDI, and a bunch of boards from Waveshare and others.

A cheap option without these downsides would be this USB Audio Adapter tested for Pi, from Pi Hut. Who knows how good the quality is though for recording.

I am keeping a lookout for well-reviewed studio quality cross-compatible USB audio interfaces that work with Pi, examples of which are few. One guy has posted a video about Pi 5 audio, with a Volt 176 and a follow-up Pi5 music-making one. I haven’t come across much else featuring external USB audio interfaces on a Pi as yet.

Hi @TKonan,
Yes that’s right I got an hifiberry dac+adc, the quality is good but for the moment I can’t manage to change parameters such as rate and block size through “audio-config” file. Also I don’t know how to make SP and/or pipewire remember audio I/Os. I have to reconnect them at each boot… So I guess still some basics to figure out :sweat_smile:

To be more specific here is the pipewire connection I have to redo at each reboot :

Here is the “audio-settings.toml” configuration I tried (only reducing buffsize from 1024 to 512) :

And here is the “daemon.log” file :

As the 512 config seems to be firstly taken into account (row#22) my guess is that something goes wrongs afterwards that make SP come back to 1024 default value. I’m thinking maybe pipewire path is wrong (row#24) because the path where I find pipewire is “/lib/aarch64-linux-gnu/pipewire-0.3/jack” and not “/lib/pipewire-0.3/jack”.

Maybe there is some way to setup right “LD_LIBRARY_PATH” ? Maybe with “scsynth_opts” ?

(sorry drifting from initial thread topic :face_in_clouds:)

Hi Simon. It is possible to switch the output audio other than by using qpwgraph. For most users that is the most convenient to use. Since Sonic Pi doesn’t know your audio set up on a Raspberry Pi it automatically selects hdmi for the audio output route, as every Pi has that route. Some for example do not have built in audio jack output eg all Pi5
The hdmi selection is hard coded into Sonic Pi. however you can use a terminal and the terminal utility pw-link to alter the output selection. I developed for my own use some ruby code which will run in a Sonic Pi buffer which lets you easily alter whether you are using hdmi, usb or buetooth output. Slight caveat is that it will select the first usb or first bluetooth output that it detects but in most cases you will only have one connected anyway.
The routines are shown below. For initial use I suggest you just post them into a spare buffer once run they will remain available until Sonic Pi is rebooted. Then add the commands gohdmi, gousb and gobluez to select the output route (assuming you have the relevant hardware connected). You can also use getCurrentData to show the current connection or deleteSCout to disconnect all SuperCollider output connections.

It is also possible to include the code in the init.rb file in the .sonic-pi/config folder. If you want to automatically connect a certain output route when you start Sonic Pi you can do a slight bodge by adding

in_thread do
 sleep 2
 gousb #or which ever connection you want
end

at the end of the the functions. This makes sure that the internal hardcoded routine is selected BEFORE you try and change it, Otherwise you can end up with both usb and hdmi being connected.
Th code was always intended for experimentation and it could be developed further but you may find it useful.

#functions to aid connecting and disconnectiog SuperCollider under pipewire
define :getCurrentData do
  #first input ports
  inputs =  `pw-link -iI`.lines
  set :hdmiL,inputs.grep(/hdmi.*playback_FL$/).first.to_i
  set :hdmiR,inputs.grep(/hdmi.*playback_FR$/).first.to_i
  set :usbL,inputs.grep(/usb.*playback_FL$/).first.to_i
  set :usbR,inputs.grep(/usb.*playback_FR$/).first.to_i
  set :bluezL,inputs.grep(/bluez.*playback_FL$/).first.to_i
  set :bluezR,inputs.grep(/bluez.*playback_FR$/).first.to_i
  set :avJackL,inputs.grep(/audio.*playback_FL$/).first.to_i
  set :avJackR,inputs.grep(/audio.*playback_FR$/).first.to_i
  #now output ports
  outputs = `pw-link -oI`.lines
  scOutPorts=[]
  16.times do |i|
    scOutPorts[i] = outputs.grep(/SuperCollider:out_#{i+1}$/).first.to_i
  end
  set :scOutPorts,scOutPorts
  #now get all current links ids
  links = `pw-link -lI`.lines
  #extract SuperCollider:out links
  linkOutputs=links.grep(/-.*SuperCollider:out/)
  #extract id of each of these links to scLinks
  scLinks=[]
  linkOutputs.length.times do |i|
    scLinks[i]=linkOutputs[i].to_i
  end
  set :scLinks,scLinks
end
define :displayCurrentID do
  #update current data
  getCurrentData
  #display current link id#
  puts "Current data. Note: a 0 signifies no ID found"
  puts "hdmi L and R",get(:hdmiL),get(:hdmiR)
  puts "usb L and R",get(:usbL),get(:usbR)
  puts "avJack L and R",get(:avJackL),get(:avJackR)
  puts "bluez L and R",get(:bluezL),get(:bluezR)
  puts "sc output ports (16 max)",get(:scOutPorts)
  puts "current SuperCollider output link Ids2",get(:scLinks)
end
#display the data
displayCurrentID
###################################################
#function to connect or disconnect SuperCollider
define :connectStereo do |output,input,type=1|
  #puts "data",output,input,type
  portlist=get(:scOutPorts)
  case output
  when :usb
    if get(:usbL) == ""
      puts "not available"
      return
    end
    o1=get(:usbL);o2=get(:usbR)
  when :hdmi
    if get(:hdmiL) == ""
      puts "not available"
      return
    end
    o1=get(:hdmiL);o2=get(:hdmiR)
  when :avjack
    if get(:avJackL) == ""
      puts "not available"
      return
    end
    o1=get(:avJackL);o2=get(:avJackR)
  when :bluez
    if get(:bluezL) == ""
      puts "not available"
      return
    end
    o1=get(:bluezL);o2=get(:bluezR)
    #puts "bluez",o1,o2
  else
    puts "not available"
    return
  end
  if input == ""
    puts "input not available"
    return
  end
  if portlist[input-1] == "9" or portlist[input] == "0"
    puts "outputs not available"
    return
  end
  i1=portlist[input-1];i2=portlist[input]
  action=["pw-link -d ","pw-link -L "]
  cmd = action[type]+i1.to_s+" "+o1.to_s
  puts cmd
  system(cmd)
  cmd = action[type]+i2.to_s+" "+o2.to_s
  puts cmd
  system(cmd)
end
#function to delete all SuperCollider output connections
define :deleteSCout do
  getCurrentData
  links=get(:scLinks)
  return if links.length==0
  links.each do |n|
    cmd= "pw-link -d #{n}"
    puts cmd
    system(cmd)
  end
end
###########################################################
#four functions that let you easily swap the main Sonic Pi output audio routing
define :gohdmi do
  getCurrentData #they may have changed
  deleteSCout
  connectStereo :hdmi,1,1
end
define :gousb do
  getCurrentData#they may have changed
  deleteSCout
  connectStereo :usb,1,1
end
define :gobluez do
  getCurrentData#they may have changed
  deleteSCout
  connectStereo :bluez, 1,1
end
define :goavjack do
  getCurrentData#they may have changed
  deleteSCout
  connectStereo :avjack, 1,1
end
#PLACE BELOW WHERE YOU WANT SONIC PI TO CONNECT WHEN YOU RUN THE CODE
#IF YOU HAVE MORE THAN ONE USB OR BLUETOOTH IT WILL CONNECT THE FIRST ONE IT FINDS
# choose from gohdmi, gousb, goavjack or gobluez (no audiojack on Pi5 or Pi400)
gohdmi

If you use this last command in init.tb then put it in a thread with a 2 beat delay as discussed above.

1 Like

Wow thanks a lot @robin.newman, this will definitly help !

What about GPIO hat cards, are they also considered as usb ?

Thanks again :+1:

Good question. Not sure how they appear. What is the description of yours on the qpwgragh screen? I think I have a hat somewhere. I’ll see if I can try it out. Might have to add a ‘hat’ section of code.

DONE SEE POST BELOW

I tried a pisound board I had handy. I added code so that it could be recognised, adding the new command gocard in order to connect to it. If you have a different plugin card and it doesn’t work you should be able to add different search text to locate it,

# Sonic Pi init file
# added code for PIsound card. may work with other eg hifiberry
# Code in here will be evaluated on launch.
#functions to aid connecting and disconnectiog SuperCollider under pipewire
define :getCurrentData do
  #first input ports
  inputs =  `pw-link -iI`.lines
  set :hdmiL,inputs.grep(/hdmi.*playback_FL$/).first.to_i
  set :hdmiR,inputs.grep(/hdmi.*playback_FR$/).first.to_i
  set :usbL,inputs.grep(/usb.*playback_FL$/).first.to_i
  set :usbR,inputs.grep(/usb.*playback_FR$/).first.to_i
  set :bluezL,inputs.grep(/bluez.*playback_FL$/).first.to_i
  set :bluezR,inputs.grep(/bluez.*playback_FR$/).first.to_i
  set :avJackL,inputs.grep(/audio.*playback_FL$/).first.to_i
  set :avJackR,inputs.grep(/audio.*playback_FR$/).first.to_i
  set :cardL,inputs.grep(/platform-soc_sound.*playback_FL$/).first.to_i
  set :cardR,inputs.grep(/platform-soc_sound.*playback_FR$/).first.to_i
  
  #now output ports
  outputs = `pw-link -oI`.lines
  scOutPorts=[]
  16.times do |i|
    scOutPorts[i] = outputs.grep(/SuperCollider:out_#{i+1}$/).first.to_i
  end
  set :scOutPorts,scOutPorts
  #now get all current links ids
  links = `pw-link -lI`.lines
  #extract SuperCollider:out links
  linkOutputs=links.grep(/-.*SuperCollider:out/)
  #extract id of each of these links to scLinks
  scLinks=[]
  linkOutputs.length.times do |i|
    scLinks[i]=linkOutputs[i].to_i
  end
  set :scLinks,scLinks
end

define :displayCurrentID do
  #update current data
  getCurrentData
  #display current link id#
  puts "Current data. Note: a 0 signifies no ID found"
  puts "hdmi L and R",get(:hdmiL),get(:hdmiR)
  puts "usb L and R",get(:usbL),get(:usbR)
  puts "avJack L and R",get(:avJackL),get(:avJackR)
  puts "bluez L and R",get(:bluezL),get(:bluezR)
  puts "card L and R",get(:cardL),get(:cardR)
  puts "sc output ports (16 max)",get(:scOutPorts)
  puts "current SuperCollider output link Ids2",get(:scLinks)
end

#display the data
displayCurrentID

###################################################
#function to connect or disconnect SuperCollider
define :connectStereo do |output,input,type=1|
  #puts "data",output,input,type
  portlist=get(:scOutPorts)
  case output
  when :usb
    if get(:usbL) == ""
      puts "not available"
      return
    end
    o1=get(:usbL);o2=get(:usbR)
  when :hdmi
    if get(:hdmiL) == ""
      puts "not available"
      return
    end
    o1=get(:hdmiL);o2=get(:hdmiR)
  when :avjack
    if get(:avJackL) == ""
      puts "not available"
      return
    end
    o1=get(:avJackL);o2=get(:avJackR)
  when :bluez
    if get(:bluezL) == ""
      puts "not available"
      return
    end
    o1=get(:bluezL);o2=get(:bluezR)
    #puts "bluez",o1,o2
  when :card
    if get(:cardL) == ""
      puts "not available"
      return
    end
    o1=get(:cardL);o2=get(:cardR)
  else
    puts "not available"
    return
  end
  if input == ""
    puts "input not available"
    return
  end
  if portlist[input-1] == "9" or portlist[input] == "0"
    puts "outputs not available"
    return
  end
  i1=portlist[input-1];i2=portlist[input]
  action=["pw-link -d ","pw-link -L "]
  cmd = action[type]+i1.to_s+" "+o1.to_s
  puts cmd
  system(cmd)
  cmd = action[type]+i2.to_s+" "+o2.to_s
  puts cmd
  system(cmd)
end

#function to delete all SuperCollider output connections
define :deleteSCout do
  getCurrentData
  links=get(:scLinks)
  return if links.length==0
  links.each do |n|
    cmd= "pw-link -d #{n}"
    puts cmd
    system(cmd)
  end
end

###########################################################
#four functions that let you easily swap the main Sonic Pi output audio routing

define :gohdmi do
  getCurrentData #they may have changed
  deleteSCout
  connectStereo :hdmi,1,1
end
define :gousb do
  getCurrentData#they may have changed
  deleteSCout
  connectStereo :usb,1,1
end
define :gobluez do
  getCurrentData#they may have changed
  deleteSCout
  connectStereo :bluez, 1,1
end
define :goavjack do
  getCurrentData#they may have changed
  deleteSCout
  connectStereo :avjack, 1,1
end
define :gocard do
  getCurrentData#they may have changed
  deleteSCout
  connectStereo :card, 1,1
end
#PLACE BELOW WHERE YOU WANT SONIC PI TO CONNECT WHEN IT STARTS UP
#IF YOU HAVE MORE THAN ONE USB OR BLUETOOTH IT WILL CONNECT THE FIRST ONE IT FINDS
# choose from gohdmi, gousb, goavjack, gobluez or :gocard (no audiojack on Pi5 or Pi400)

as in the first version add a thread at the bottom to activate the card on boot. in this case using gocard as the command.

ADDENDUM just tried with a hifiberry based card and the gocard command works with this too.

Hi @robin.newman, it works great for the outputs :ok_hand:
… but unfortunately not for the inputs

I’m on a Pi 4B maybe this input address is to be adjusted ?

  set :cardL,inputs.grep(/platform-soc_sound.*playback_FL$/).first.to_i
  set :cardR,inputs.grep(/platform-soc_sound.*playback_FR$/).first.to_i

I was testing on a P4b myself. Can you manually (on qpwgraph) connect the sonic-pi outputs to the inputs on the sound card you are using (and check it works), and open a terminal window and type

pw-link -li ( thats - lowercase l lowercase i)

in my case this gives:

Midi-Bridge:Midi Through:(playback_0) Midi Through Port-0
Midi-Bridge:RtMidi Input Client:(playback_0) RtMidi Input
alsa_output.usb-0c76_USB_PnP_Audio_Device-00.analog-stereo:playback_FL
alsa_output.usb-0c76_USB_PnP_Audio_Device-00.analog-stereo:playback_FR
alsa_output.platform-bcm2835_audio.stereo-fallback:playback_FL
alsa_output.platform-bcm2835_audio.stereo-fallback:playback_FR
alsa_output.platform-fef00700.hdmi.hdmi-stereo:playback_FL
alsa_output.platform-fef00700.hdmi.hdmi-stereo:playback_FR
alsa_output.platform-soc_sound.stereo-fallback:playback_FL
  |<- SuperCollider:out_1
alsa_output.platform-soc_sound.stereo-fallback:playback_FR
  |<- SuperCollider:out_2
SuperCollider:in_1
SuperCollider:in_2
SuperCollider:in_3
SuperCollider:in_4

and let me know what it shows in your case
you can see it shows the superColloder out_1 and out_2 are connected to the alsa_output.platform-soc_sound.stereo-fallback:playback_FL and FR inputs of the sound card. If the texts are different you need to alter what the grep is trying to find

alsa_input.platform-soc_sound.stereo-fallback:capture_FL

Hmm that is an input device alsa_input.platform-soc_sound.stereo-fallback:capture_FL you can see it is described as capturing from Front Left input.
you should get a whole range of lines with pw-link -li
you can show both input and output with pw-link -lio
Are you using a sound card that has both input and output?
MY code is for connecting the sound output of Supercollider to a playback device eg hdmi speaker usb speaker bluetooth speaker.
are you trying to select the input from a microphone connected to your sound card and connect that to a supercollider input?
you can see below that the OUTPUT of super-collider is being connected to a playback device (playback_FL)
alsa_output.platform-soc_sound.stereo-fallback:playback_FL |<- SuperCollider:out_1

Similar code could be developed I suppose for connecting to the supercollider input ports,

Yes it has 1x stereo input + 1x stereo output (hifiberry dac+ adc).

No problem thanks for your precious help !

My project is to code a looper that would be like a standalone effect pedal. So that’s the reason why I’m trying to automate config at boot.

I will share my looper code soon, it’s beginning to work…

OK. code can certainly be written to automatically connect your inputs to sonic-pi. (I’d missed your screenshot at the top. I can have a look at it later next week.

Hi @robin.newman,

Finally it works by only keeping below code into “init.rb” file :

`pw-link alsa_input.platform-soc_sound.stereo-fallback:capture_FL SuperCollider:in_1`
`pw-link alsa_input.platform-soc_sound.stereo-fallback:capture_FR SuperCollider:in_2`
`pw-link SuperCollider:out_1 alsa_output.platform-soc_sound.stereo-fallback:playback_FL`
`pw-link SuperCollider:out_2 alsa_output.platform-soc_sound.stereo-fallback:playback_FR`

Now I’m trying to find out how to configure the soundcard as “audio-settings.toml” wouldn’t do. I would like to manage to get 96kHz/512b automatically (instead 48kHz/1024b) for latency reducing purpose as you can imagine