Is there a way to check the volume level of all processes with pipewire/pulseaudio? - linux

I'm trying to find a way to check if i have any desktop audio AND what processes is producing sounds.
After some searching i found a way to list all the sink input in pipewire/pulseaudio using pactl list sink-inputs however i have no idea if that input is muted or not
example output:
Sink Input #512
Driver: protocol-native.c
Owner Module: 9
Client: 795
Sink: 1
Sample Specification: float32le 2ch 48000Hz
Channel Map: front-left,front-right
Format: pcm, format.sample_format = "\"float32le\"" format.rate = "48000" format.channels = "2" format.channel_map = "\"front-left,front-right\""
Corked: yes
Mute: no
Volume: front-left: 43565 / 66% / -10.64 dB, front-right: 43565 / 66% / -10.64 dB
balance 0.00
Buffer Latency: 165979 usec
Sink Latency: 75770 usec
Resample method: speex-float-1
Properties:
media.name = "Polish cow (English Lyrics Full Version) - YouTube"
application.name = "Firefox"
native-protocol.peer = "UNIX socket client"
native-protocol.version = "35"
application.process.id = "612271"
application.process.user = "user"
application.process.host = "host"
application.process.binary = "firefox"
application.language = "en_US.UTF-8"
window.x11.display = ":0"
application.process.machine_id = "93e71eeba04e43789f0972b7ea0e4b39"
application.process.session_id = "2"
application.icon_name = "firefox"
module-stream-restore.id = "sink-input-by-application-name:Firefox"
The obvious thing would be looking at the Mute and Volume line but that is not reliable at all, currently the youtube video is paused but Mute is show as no and Volume is still no different from when the youtube video is actually playing.
I need the solution to be script-able since I'll muting certain thing when there is another process that is making sounds, and play it again when there is no sound, using bash script. If it is not possible on pipewire/pulseaudio but it is possible with another sound server then please do tell me.

Related

Sound activated recording in Julia

I'm recording audio with Julia and want to be able to trigger a 5 second recording after the audio signal exceeds a certain volume. This is my record script so far:
using PortAudio, SampledSignals, LibSndFile, FileIO, Dates
stream = PortAudioStream("HDA Intel PCH: ALC285 Analog (hw:0,0)")
buf = read(stream, 5s)
close(stream)
save(string("recording_", Dates.format(now(), "yyyymmdd_HHMMSS"), ".wav"), buf, Fs = 48000)
I'm new to Julia and signal processing in general. How can I tell this only to start recording once the audio exceeds a specified volume threshold?
You need to test the sound you capture for average amplitude and act on that. Save if loud enough, otherwise rinse and repeat.
using PortAudio, SampledSignals, LibSndFile, FileIO
const hassound = 10 # choose this to fit
suprathreshold(buf, thresh = hassound) = norm(buf) / sqrt(length(buf)) > thresh # power over threshold
stream = PortAudioStream("HDA Intel PCH: ALC285 Analog (hw:0,0)")
while true
buf = read(stream, 5s)
close(stream)
if suprathreshold(buf)
save("recording.wav", buf, Fs = 48000) # should really append here maybe???
end
end

AVCaptureSession audio samples captured at different frequency than AVAudioSession's sample rate

I'm using AVFoundation capture session to output audio buffers through AVCaptureAudioDataOutput. The capture session is using the default application audio session. (ie. captureSession.usesApplicationAudioSession = true). I don't alter the audio session in any way.
The strange behavior is that the capture session returns audio buffers captured at a different frequency than the default audio session's sample rate.
Specifically:
print(AVAudioSession.sharedInstance().sampleRate) \\ 48000
but
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
if connection.audioChannels.first != nil {
print(sampleBuffer.presentationTimeStamp) \\ CMTime(value: 2199812320, timescale: 44100, flags: __C.CMTimeFlags(rawValue: 3), epoch: 0)
delegate?.captureOutput(sampleBuffer: sampleBuffer, mediaType: .audio)
}
}
My expected behavior is that the sample buffer's timescale would also be 48000.
For a little extra info, if I do change the default audio session, for example, change preferred sample rate to 48000, the sample buffer's timescale will change to 48000 as expected. Is this a bug or am I misunderstanding something?
You need to set the capture session's automaticallyConfiguresApplicationAudioSession to false and do your own audio session configuration before starting the capture session.
Like this:
// use audioSession.setPreferredSampleRate() to request desired sample rate
captureSession.automaticallyConfiguresApplicationAudioSession = false
try! AVAudioSession.sharedInstance().setCategory(.playAndRecord) // or just record
try! AVAudioSession.sharedInstance().setActive(true) // worked without this, but feels wrong

SOLVED - Debug Darkice to understand why is not connecting to shoutcast

I'm trying to connect to a shoutcast server from a darkice client using Ubuntu. This is my configuration:
#this section describes general aspects of the live streaming session
[general]
duration = 0 # duration of encoding, in seconds. 0 means forever
bufferSecs = 10 # size of internal slip buffer, in seconds
reconnect = yes # reconnect to the server(s) if disconnected
realtime = no # run the encoder with POSIX realtime priority
rtprio = 3 # scheduling priority for the realtime threads
# this section describes the audio input that will be streamed
[input]
device = hw:CARD=PCH,DEV=0
sampleRate = 44100 # sample rate in Hz. try 11025, 22050 or 44100
bitsPerSample = 16 # bits per sample. try 16
channel = 2 # channels. 1 = mono, 2 = stereo
# this section describes a streaming connection to an IceCast2 server
# there may be up to 8 of these sections, named [icecast2-0] ... [icecast2-7]
# these can be mixed with [icecast-x] and [shoutcast-x] sections
[shoutcast-0]
bitrateMode = cbr
format = mp3
bitrate = 96
quality = 1.0
server = xxxxxxxxxxxxxxx
port = 8020
password = xxxxxxxxxxxxxxx
name = Radio website
url = https://www.mywebsite.it
genre = live
public = no
But when I run
darkice -v 10 -c /etc/darkice-shoutcast.cfg
It only shows this, without errors or similar, but there is no streaming at the url. Using BUTT it works. I've also tested with 8021 instead of 8020 for port (8020 it's the port number given by the provider) but no luck.
DarkIce 1.4 live audio streamer, http://code.google.com/p/darkice/
Copyright (c) 2000-2007, Tyrell Hungary, http://tyrell.hu/
Copyright (c) 2008-2013, Akos Maroy and Rafael Diniz
This is free software, and you are welcome to redistribute it
under the terms of The GNU General Public License version 3 or
any later version.
Using config file: /etc/darkice-shoutcast.cfg
18-May-2021 12:02:28 Using ALSA DSP input device: hw:CARD=PCH,DEV=0
18-May-2021 12:02:28 buffer size: 1764000
18-May-2021 12:02:28 encoding
18-May-2021 12:02:28 MultiThreadedConnector :: transfer, bytes 0
18-May-2021 12:02:28 MultiThreadedConnector :: ThreadData :: threadFunction, was (thread, priority, type): 0x5568a502c010 0 SCHED_OTHER
18-May-2021 12:02:28 MultiThreadedConnector :: ThreadData :: threadFunction, now is (thread, priority, type): 0x5568a502c010 0 SCHED_OTHER
ADDENDUM
I've used tcpdump to understand what could be and I just see something similar to "invalid password"
: Flags [P.], cksum 0xc379 (correct), seq 1:19, ack 3090, win 294, options [nop,nop,TS val 3348978428 ecr 531576376], length 18
E..F1O#.1.3'.}.......T.Jq.k.D......&.y.....
..Z...68Invalid Passwor
Suggestions on how to better debug or fix this?
SOLVED
It seems the error is related to the password and wrong parsing of the config file, so I've written it without spaces
[shoutcast-0]
bitrateMode = cbr
format = mp3
bitrate = 96
quality = 1.0
server = xxxxxxxxxxxxxxx
port = 8020
password=xxxxxxxxxxxxxxx
name = Radio website
url = https://www.mywebsite.it
genre = live
public = no

Linux ALSA Driver using channel count 3

Am running my ALSA Driver on Ubuntu 14.04, 64bit, 3.16.0-30-generic Kernel.
Hardware is proprietary hardware, hence cant give much details.
Following is the existing driver implementation:
Driver is provided sample format, sample rate, channel_count as input via module parameter. (Due to requirements need to provide inputs via module parameters)
Initial snd_pcm_hardware structure for playback path.
#define DEFAULT_PERIOD_SIZE (4096)
#define DEFAULT_NO_OF_PERIODS (1024)
static struct snd_pcm_hardware xxx_playback =
{
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_SYNC_START,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = (SNDRV_PCM_RATE_8000 | \
SNDRV_PCM_RATE_16000 | \
SNDRV_PCM_RATE_48000 | \
SNDRV_PCM_RATE_96000),
.rate_min = 8000,
.rate_max = 96000,
.channels_min = 1,
.channels_max = 1,
.buffer_bytes_max = (DEFAULT_PERIOD_SIZE * DEFAULT_NO_OF_PERIODS),
.period_bytes_min = DEFAULT_PERIOD_SIZE,
.period_bytes_max = DEFAULT_PERIOD_SIZE,
.periods_min = DEFAULT_NO_OF_PERIODS,
.periods_max = DEFAULT_NO_OF_PERIODS,
};
Similar values for captures side snd_pcm_hardware structure.
Please, note that the following below values are replaced in playback open entry point, based on the current audio test configuration:
(user provides audio format, audio rate, ch count via module parameters as inputs to the driver, which are refilled in snd_pcm_hardware structure)
xxx_playback.formats = user_format_input
xxx_playback.rates = xxx_playback.rate_min, xxx_playback.rate_max = user_sample_rate_input
xxx_playback.channels_min = xxx_playback.channels_max = user_channel_input
Similarly values are re-filled for capture snd_pcm_hardware structure in capture open entry point.
Hardware is configured for clocks based on channel_count, format, sample_rate and driver registers successfully with ALSA layer
Found aplay/arecord working fine for channel_count = 1 or 2 or 4
During aplay/arecord, in driver when "runtime->channels" value is checked, it reflects the channel_count configured, which sounds correct to me.
Record data matches with played, since its a loop back test.
But when i use channel_count = 3, Both aplay or arecord reports
"Broken configuration for this PCM: no configurations available"!! for a wave file with channel_count '3'
ex: Playing WAVE './xxx.wav' : Signed 16 bit Little Endian, Rate 48000 Hz, Channels 3
ALSA lib pcm_params.c:2162:(snd1_pcm_hw_refine_slave) Slave PCM not usable
aplay: set_params:1204: Broken configuration for this PCM: no configurations available
With Following changes I was able to move ahead a bit:
.........................
Method1:
Driver is provided channel_count '3' as input via module parameter
Modified Driver to fill snd_pcm_hardware structure as payback->channels_min = 2 & playback->channels_min = 3; Similar values for capture path
aplay/arecord reports as 'channel count not available', though the wave file in use has 3 channels
ex: aplay -D hw:CARD=xxx,DEV=0 ./xxx.wav Playing WAVE './xxx.wav' : Signed 16 bit Little Endian, Rate 48000 Hz, Channels 3
aplay: set_params:1239: Channels count non available
Tried aplay/arecord with plughw, and aplay/arecord moved ahead
arecord -D plughw:CARD=xxx,DEV=0 -d 3 -f S16_LE -r 48000 -c 3 ./xxx_rec0.wav
aplay -D plughw:CARD=xxx,DEV=0 ./xxx.wav
Recording WAVE './xxx_rec0.wav' : Signed 16 bit Little Endian, Rate 48000 Hz, Channels 3
Playing WAVE './xxx.wav' : Signed 16 bit Little Endian, Rate 48000 Hz, Channels 3
End of Test
During aplay/arecord, In driver when "runtime->channels" value is checked it returns value 2!!! But played wavefile has ch count 3...
When data in recorded file is checked its all silence
.........................
Method2:
Driver is provided channel_count '3' as input via module parameter
Modified Driver to fill snd_pcm_hardware structure as playback->channels_min = 3 & playback->channels_min = 4; Similar values for capture path
aplay/arecord reports as 'channel count not available', though the wave file in use has 3 channels
Tried aplay/arecord with plughw, and aplay/arecord moved ahead
During aplay/arecord, In driver when "runtime->channels" value is checked it returns value 4!!! But played wavefile has ch count 3...
When data in recorded file is checked its all silence
.........................
So from above observations, the runtime->channels is either 2 or 4, but never 3 channels was used by alsa stack though requested. When used Plughw, alsa is converting data to run under 2 or 4 channel.
Can anyone help why am unable to use channel count 3.
Will provide more information if needed.
Thanks in Advance.
A period (and the entire buffer) must contain an integral number of frames, i.e., you cannot have partial frames.
With three channels, one frame has six bytes. The fixed period size (4096) is not divisible by six without remainder.
Thanks CL.
I used period size 4092 for this particular test case with channel count 3, and was able to do loop back successfully (without using plughw).
One last question, when I used plughw earlier, and when runtime->channels was either 2 or 4, why was the recorded data not showing?

RaspBerry pi B rev2 - Issue while sampling a LM335 (temp. sensor) thru a MCP3208 ADC via SPI in Python 3

I tried to interface a RaspBerry pi with a LM335 temperature sensor this week-end. I'm using a MCP 3208 micro controller (channel 0) to interface the sensor. My goal is to collect samples data in SPI mode with python 3 scripts (classes).
I've checked the wiring and everything seems OK for me, I'but I'am a beginner, not really aware of Electronic concepts.
On the software side , I've installed quick2wire that claims to be python 3 compatible. In fact I want to lead the micro-controller with Python 3 API's (not thru shell calls)
Components
Raspberry pi REV2 model B with Rasbian-wheezy / Quick2wire installed. /dev/spix.y devices are listed.
MCP3208 ADC : 12 bits ADC / SPI. I'm using CS0 from the GPIO. The sensor is connected to channel 0 (B). see datasheet.
LM335 : temperature sensor. Outputs 10mV / °K. Min 5muA / Max 5 mA. It's connected to the MCP3208 channel #0 (A). see datasheet
220 ohms resistor (C). set up regarding LM335 outputs and desired temperature range coverage with my own calculations : May be a problem ...
Schematics extract
The LM335 (zener diode like) is connected as :
Wiring
Components are wired as shown bellow. Note that the yellow link is connected behind the cobbler kit on the CS0 SPI channel.
Quick2wire
I use the bellow script to query the CS0/Channel 0 GPIO interface. Unfortunately, I've not found usefull informations on the quick2wire-python-api API's. I've just copy/paste an example found as it was written in the same goal. I'm not sure if it really works :
#!/usr/bin/env python3
from quick2wire.spi import *
import sys, time
try:
channel = int(sys.argv[1])
except:
channel = 0
MCP3208 = SPIDevice(channel, 0)
while True:
try:
response = MCP3208.transaction(writing_bytes(0x41, 0x13), reading(1))
print ("output = %i" % ord(response[0]))
time.sleep(1)
except KeyboardInterrupt:
break
The script outputs :
output = 0
output = 0
output = 0
output = 0
output = 0
....
The result is the same with the channel 1 ( with argv = 1)
As the MCP3208 Din (probe output) receives voltage (see bellow) quick2wire should read at 18°C (rawghly my home inside temperature today)
3,3 V / 2^12 = 805 muA as I understand as "digital step"
18°C + 273°C = 291 => 2,91 V on the micro controller Din pin
and then return 2 910 / 0.805 = 3 615
Am I wrong ?
Controls
I've no oscilloscope, the only measures I can read are :
Voltage is 2.529 V at B checkpoint and 0,5 V (+/-5%) on the other MCP3208 channels
Note : the adjust pin is not used on the LM335 so results way not be accurate but voltage is here !
Seems to be a problem on the quick2wire side I think. But which ?
Code
The quick2wire.spi.SPIDevice class lakes of détails on the transfers parameter in terms of structure, content and output response format.
def transaction(self, *transfers):
"""
Perform an SPI I/O transaction.
Arguments:
*transfers -- SPI transfer requests created by one of the reading,
writing, writing_bytes, duplex or duplex_bytes
functions.
Returns: a list of byte sequences, one for each read or duplex
operation performed.
"""
transfer_count = len(transfers)
ioctl_arg = (spi_ioc_transfer*transfer_count)()
# populate array from transfers
for i, transfer in enumerate(transfers):
ioctl_arg[i] = transfers[i].to_spi_ioc_transfer()
ioctl(self.fd, SPI_IOC_MESSAGE(transfer_count), addressof(ioctl_arg))
return [transfer.to_read_bytes() for t in transfers if t.has_read_buf]
Another question :
how to set SPI configuration values like mode, clock speed, bits per word, LSB ... and so on.
Thanks in advance for your help.
I know you probably intend to learn how to use the ADC, an so this isn't really an answer to your question (I will use your very rich post for sure - thanks), but I'm aware of temperature sensors that already pack data in GPIO serial line, that are best suited for the raspberry.
You really have to read this awesome tutorial, if you haven't already.

Resources