CatxFish / obs-v4l2sink

obs studio output plugin for Video4Linux2 device
GNU General Public License v2.0
930 stars 99 forks source link

Virtual Microphone Device? #34

Open EliterScripts opened 4 years ago

EliterScripts commented 4 years ago

Hello, I am using Ubuntu Linux, and am currently using the v4l2sink mod as a workaround to use OBS with my web-based video conferencing software, since it does NOT have a server for accepting SRT/RTMP/FTL streaming.

Is it possible to create a virtual microphone device, and have OBS output to the virtual microphone device? I have been searching for hours on the web for a solution!

Thanks!!!

mjg commented 4 years ago

The only built-in method (besides local streaming) seems to be the monitors: I set up an extra pulse-audio sink with load-module module-null-sink sink_name=OBS sink_properties=device.description="OBS" (in config or manually with pacmd). Then, in obs, I select "Monitor of OBS" as the monitoring device in preferences and activate "output+monitor" for the audio sources in obs that I want to hear somewhere else. Now, using pavucontrol or whatever mixer you prefer, set the record stream of your browser (running the video conference) to record from OBS instead of your microphone. (Personally, I remove all audio devices from obs settings and add them by scene, but the monitoring device is still in obs settings.)

If "OBS the app" vs. "OBS the sink" is confusing then better name the sink differently, such as "OBSmic" or such :-)

This works nicely (filters apply and such) with one known caveat: scene transitions do not fade the audio on the monitor, only on record+stream output. All other niceties seem to work, even ducking.

MarioMey commented 4 years ago

I continue this issue, because I also was hours looking for a similar issue yesterday and I arrived to a simiilar solution. I needed to send OBS audio to Zoom. The problem was that Zoom doesn't read a "sink"... it reads a "source". So, I had to create one and "connect" it to default audio output monitor. After hours trying different things (without the neccessary knowledge) and waiting in #pulseaudio IRC channel for hours (almost noone answer ¯\(ツ)/¯), this is what I needed:

pactl load-module module-remap-source source_name='OUTPUT_FROM_OBS' master=alsa_output.pci-0000_00_14.2.analog-stereo.monitor source_properties=device.description='OUTPUT_FROM_OBS'

It creates a source module by remapping the "analog stereo monitor". Also, you can change it from pavucontrol. So, what I hear from my speakers is the input of that virtual source module and it can be selected from Zoom.

Now, I'm creating a script to set all the volumes and the playback/record streams to their correspondent input/output. I'll paste here when I finish it.

ManuLinares commented 4 years ago

pacmd load-module module-remap-source source_name='OUTPUT_FROM_OBS' master=alsa_output.pci-0000_00_14.2.analog-stereo.monitor source_properties=device.description='OUTPUT_FROM_OBS'

oh wow, I think this and activate "output+monitor" for the MIC in obs, works... I'll try again tomorrow but it just works.. You wrote "pactl" but it needs to be "pacmd" I think.

MarioMey commented 4 years ago

Yes, that's right: you have to activate monitor... and it does work.

I wrote pactl because... I use pactl. Now, I don't know why there are two. The only difference I found is that with "pacmd" you can enter in live shell... that you can't do it with pactl. But they do the same.

I don't use OBS for audio any more, it was not stable. For this reason and because my computer is slow, I wrote a python script to control audio. I use SoX, Play (from SoX), pactl (creating virtual modules, moving sinks, changing volumes, etc, etc).

And OBS, Python script, PureData and MobMuPlat in the tablet, all connected with OSC messages.

I was the last days learning a lot of PulseAudio, if you need something, you can ask me in spanish by inbox. You are from Hurlingham... I'm from Castelar :D.

EliterScripts commented 4 years ago

@MarioMey Thank you so much for your reply!

I don't use OBS for audio any more, it was not stable. For this reason and because my computer is slow, I wrote a python script to control audio. I use SoX, Play (from SoX), pactl (creating virtual modules, moving sinks, changing volumes, etc, etc).

Would you mind sharing this Python script, and licensing it under Creative Commons Universal 1.0, or some other permissive license? @MarioMey

So, I had to create one and "connect"

I feel like I should share this with you, @MarioMey. I found that you can actually create a dummy microphone using this command: sudo modprobe snd-dummy. I have no idea what APT package you have to install/download, but I believe that does the trick if you don't have a physical microphone to use.

MarioMey commented 4 years ago

Would you mind sharing this Python script, and licensing it under Creative Commons Universal 1.0, or some other permissive license? @MarioMey

Yes, I will do it. Before, there are some things I have to do with it to avoid error messages... and also I'm working in a almost whole sound restarting.

sudo modprobe snd-dummy

But is it possible to send some audio streams to this dummy and control each volumes? How? And how this would be better than the one I am using (module-remap-sink)?

johnedstone commented 4 years ago

I continue this issue, because I also was hours looking for a similar issue yesterday and I arrived to a simiilar solution. I needed to send OBS audio to Zoom. The problem was that Zoom doesn't read a "sink"... it reads a "source". So, I had to create one and "connect" it to default audio output monitor. After hours trying different things (without the neccessary knowledge) and waiting in #pulseaudio IRC channel for hours (almost noone answer ¯_(ツ)_/¯), this is what I needed:

pactl load-module module-remap-source source_name='OUTPUT_FROM_OBS' master=alsa_output.pci-0000_00_14.2.analog-stereo.monitor source_properties=device.description='OUTPUT_FROM_OBS'

It creates a source module by remapping the "analog stereo monitor". Also, you can change it from pavucontrol. So, what I hear from my speakers is the input of that virtual source module and it can be selected from Zoom.

Now, I'm creating a script to set all the volumes and the playback/record streams to their correspondent input/output. I'll paste here when I finish it.

This is awesome stuff!! I have been looking for a way, on Ubuntu 18.04, to send audio/video through the Zoom audio/video stream, in a similar way that you can do with NDI on Windows (VLC/NDI Plugin). This is awesome and works. I'm still examining if the audio and video are in sync, as it's not as clean as NDI where the audio/video are sent over the same stream. In this case here, as you know, video is coming from the OBS V4L Video Output from this git repo, and the audio is coming from the monitoring of the system sound. But, to my eye, it appears to be in sync! Thanks so much for this discussion. Awesome! Whew!

MarioMey commented 4 years ago

Let me tell you that, for the last 2 weeks, I was looking for a solution to send OBS audio to Zoom and, as I told before, I finally used PulseAudio tools, with Python, bash, PureData. Also, I was trying to make python-osc work inside OBS (with no success) and other different stuff.

Well... almost 90% of the work I've been doing in resarching... I won't use it. It's a shame... but it's for better.

One of the things that I tried was using Jack. As my computer is slow, I saw that creating jack modules to connect to PulseAudio to send/receive to/from Zoom, plus loading Jack, the audio processor... it was a lot of CPU. So, I took for granted that I won't can use it.

Yesterday, someone suggest me again to use Jack. And, before telling him that I already tried it... I realized that, one of the things I did to optimize the sistem, was lo lower the OBS canvas resolution. If I send by webcam, I don't need a HD resolution... so, now it is at 704x396 (*). Almost half vert/horz resolution: supposeddly a little more than a quarter of graphic process. So, I gave Jack a chance.

Yes, Jack was the very most best solution... and not only that. I can configure it with 128 frames: 8ms of latency. I know PureData programming and I can do anything I want with it. And it conects with no problem with Zoom, as the jack modules are sources and sinks.

My advice: use Jack.

(*): there was problems with some resolutions when sending to Zoom: the colors was glitched: Captura de pantalla -2020-05-08 19-18-14 1280x720 was so big, 640x360 was a little small... so, I tried resolution close to 800x450 that has no color glitchs... so, I achieve it with this weird resolution. This could be a bug... I might report it.

MarioMey commented 4 years ago

Would you mind sharing this Python script, and licensing it under Creative Commons Universal 1.0, or some other permissive license? @MarioMey

I will share my scripts with you, maybe it's usefull. I hope you understand everything, because I don't have time to explain them:

init.sh:

#!/bin/bash

reset="\e[39m"
bold="\e[1m"
bold_reset="\e[21m"
rojo="\e[31m"
verde="\e[32m"
amarillo="\e[33m"
azul="\e[34m"
magenta="\e[35m"
cian="\e[36m"
gris_claro="\e[37m"
gris_oscuro="\e[90m"
rojo_claro="\e[91m"
verde_claro="\e[92m"
amarillo_claro="\e[93m"
azul_claro="\e[94m"
magenta_claro="\e[95m"
cian_claro="\e[96m"

# Crea un v4l2loopback en /dev/video10
video10=`ls /dev/video10`
if [ $video10 != '/dev/video10' ]; then
echo -e $amarillo "Crea un v4l2loopback en /dev/video10"
sudo modprobe v4l2loopback \
    devices=1 \
    video_nr=10 \
    card_label="OBS Cam" \
    exclusive_caps=1
else
echo -e $amarillo "/dev/video10 ya está cargado"
fi

# Camara exposicion manual
echo -e $amarillo_claro "Camara exposicion manual"
v4l2-ctl -d /dev/video0 \
    -c exposure_auto=1 \
    -c exposure_absolute=333 \
    -c gain=45

# Pavucontrol
#~ echo -e $azul "Abre dos instancias de Control de Volumen Pulseaudio"
#~ pavucontrol > /dev/null 2>&1 &
#~ pavucontrol > /dev/null 2>&1 &

spk_out='alsa_output.pci-0000_00_14.2.analog-stereo'
spk_out_monitor='alsa_output.pci-0000_00_14.2.analog-stereo.monitor'
hs_out_monitor='alsa_output.usb-Logitech_Logitech_USB_Headset-00.analog-stereo.monitor'
hs_out='alsa_output.usb-Logitech_Logitech_USB_Headset-00.analog-stereo'
hs_mic='alsa_input.usb-Logitech_Logitech_USB_Headset-00.analog-mono'

# Unload Speakers OUT
stream_id=`pactl list | \
    grep -e "Módulo" -e "media.name = \"Speakers OUT\"" | \
    grep -B1 "media.name = \"Speakers OUT\"" | \
    grep "^Módulo #" | sed -e "s/^Módulo #//"`
if [ "$stream_id" != "" ]; then
echo -e $rojo "Kill SoX y Play" $reset
kil sox
kil play
echo -e $rojo_claro "Unload $stream_id / Speakers OUT" $reset
pacmd unload-module $stream_id
else
echo -e $verde "No existe Speakers OUT" $reset
fi

# Unload Headsets OUT
stream_id=`pactl list | \
    grep -e "Módulo" -e "media.name = \"Headsets OUT\"" | \
    grep -B1 "media.name = \"Headsets OUT\"" | \
    grep "^Módulo #" | sed -e "s/^Módulo #//"`
if [ "$stream_id" != "" ]; then
echo -e $rojo_claro "Unload $stream_id / Headsets OUT" $reset
pacmd unload-module $stream_id
else
echo -e $verde "No existe Headsets OUT" $reset
fi

# Unload REMAPEADO
stream_id=`pactl list | \
    grep -e "Módulo" -e "source_name=REMAPEADO" | \
    grep -B1 "source_name=REMAPEADO" | \
    grep "^Módulo #" | sed -e "s/^Módulo #//"`
if [ "$stream_id" != "" ]; then
echo -e $rojo_claro "Unload $stream_id / REMAPEADO" $reset
pacmd unload-module $stream_id
else
echo  -e $verde "No existe REMAPEADO" $reset
fi

echo -e $reset $bold "Sleep 1..." $reset
sleep 1

#~ # Carga source y lo remapea para que Zoom tome de los auriculares.
echo -e $verde_claro "Carga source REMAPEADO y lo remapea para tomar de los speakers" $reset
pactl load-module module-remap-source \
    source_name="REMAPEADO" \
    source_properties=device.description="REMAPEADO" \
    master="$spk_out_monitor"

# Combinado - Musica va a Speakers y al Headset
echo -e $verde_claro "Carga combine-sink para enviar musica a speakers y headphones" $reset
pactl load-module module-combine-sink \
    slaves="$spk_out","$hs_out" \
    sink_name="COMBINADO" \
    sink_properties=device.description="COMBINADO"

echo -e $reset $bold "Sleep 1..." $reset
sleep 1

# Cambia valor de media.name a Speakers OUT
stream_id=`pactl list | \
    grep -e "Sink Input" -e "media.name = \"Simultaneous output on Audio Interno Estéreo Analógico\"" | \
    grep -B1 "media.name = \"Simultaneous output on Audio Interno Estéreo Analógico\"" | \
    grep "^Sink Input #" | sed -e "s/^Sink Input #//"`
pacmd update-sink-input-proplist $stream_id "media.name=\"Speakers OUT\""

# Cambia valor de media.name a Headsets OUT
stream_id=`pactl list | \
    grep -e "Sink Input" -e "media.name = \"Simultaneous output on Clear Chat Comfort USB Headset Estéreo Analógico\"" | \
    grep -B1 "media.name = \"Simultaneous output on Clear Chat Comfort USB Headset Estéreo Analógico\"" | \
    grep "^Sink Input #" | sed -e "s/^Sink Input #//"`
pacmd update-sink-input-proplist $stream_id "media.name=\"Headsets OUT\""

# Cambia valor de media.name a REMAPEADO
stream_id=`pactl list | \
    grep -e "Source Output" -e "media.name = \"Remapped Stream\"" | \
    grep -B1 "media.name = \"Remapped Stream\"" | \
    grep "^Source Output #" | sed -e "s/^Source Output #//"`
pacmd update-source-output-proplist $stream_id "media.name=\"REMAPEADO\""

osc_server.py

#!/usr/bin/env python3
import os, subprocess, argparse, random, math, time

from pythonosc import dispatcher
from pythonosc import osc_server
from pythonosc import udp_client

first_time = True

color = {
    'reset':'\x1b[0m',
    'negro':'\x1b[90m',
    'rojo':'\x1b[91m',
    'verde':'\x1b[92m',
    'amarillo':'\x1b[93m',
    'azul':'\x1b[94m',
    'azul_bold':'\x1b[94;1m',
    'magenta':'\x1b[95m',
    'magenta_bold':'\x1b[95;1m',
    'cian':'\x1b[96m',
    'blanco':'\x1b[97m',

    'error':'\x1b[93;41m',
    'remoto':'\x1b[93;42m',
    'debug':'\x1b[93;44m'
    }

def consola(texto, color_texto='verde'):
    texto = str(texto)
    print(color[color_texto], texto, color['reset'])

spk_out_sink    = 'alsa_output.pci-0000_00_14.2.analog-stereo'
spk_out_monitor = 'alsa_output.pci-0000_00_14.2.analog-stereo.monitor'
hs_out_sink     = 'alsa_output.usb-Logitech_Logitech_USB_Headset-00.analog-stereo'
hs_out_monitor  = 'alsa_output.usb-Logitech_Logitech_USB_Headset-00.analog-stereo.monitor'
hs_mic_source   = 'alsa_input.usb-Logitech_Logitech_USB_Headset-00.analog-mono'
combinado       = 'COMBINADO'

# Sink Inputs (PLAY)
comb_spkout_si_mn = 'Speakers OUT'
comb_spkout_si = "pactl list | \
                        grep -e \"Sink Input\" -e \"\
                        media.name = \\\"%s\\\"\" | \
                        grep -B1 \"\
                        media.name = \\\"%s\\\"\" | \
                        grep \"^Sink Input #\" | \
                        sed -e \"s/^Sink Input #//\"" % (comb_spkout_si_mn, comb_spkout_si_mn)

comb_hsout_si_mn = 'Headsets OUT'
comb_hsout_si  = "pactl list | \
                        grep -e \"Sink Input\" -e \"\
                        media.name = \\\"%s\\\"\" | \
                        grep -B1 \"\
                        media.name = \\\"%s\\\"\" | \
                        grep \"^Sink Input #\" | \
                        sed -e \"s/^Sink Input #//\"" % (comb_hsout_si_mn, comb_hsout_si_mn)

sox_music_si = "pactl list | \
                    grep -e \"Sink Input #\" -e \"Cliente #\" -e \"device.description\" | \
                    grep -B1 \"sox_music\" | \
                    grep \"^Sink Input #\" | \
                    sed -e \"s/^Sink Input #//\""

sox_mic_si   = "pactl list | \
                    grep -e \"Sink Input #\" -e \"Cliente #\" -e \"device.description\" | \
                    grep -B1 \"sox_mic\" | \
                    grep \"^Sink Input #\" | \
                    sed -e \"s/^Sink Input #//\""

zoom_si      = "pactl list | \
                    grep -e \"Sink Input\" -e \"media.name\" | \
                    grep -B1 \"playStream\" | \
                    grep \"^Sink Input #\" | \
                    sed -e \"s/^Sink Input #//\""

vlc_si       = "pactl list | \
                    grep -e \"Sink Input\" -e \"media.name\" | \
                    grep -B1 \"audio stream\" | \
                    grep \"^Sink Input #\" | \
                    sed -e \"s/^Sink Input #//\""

# Source Outputs (REC)
remapped_so_mn = 'REMAPEADO'
remapped_so  = "pactl list | \
                    grep -e \"Source Output\" -e \"media.name\" | \
                    grep -B1 \"%s\" | \
                    grep \"^Source Output #\" | \
                    sed -e \"s/^Source Output #//\"" % remapped_so_mn

desktop_so  = "pactl list | \
                    grep -e \"Source Output\" -e \"media.name\" | \
                    grep -B1 \"Desktop Audio\" | \
                    grep \"^Source Output #\" | \
                    sed -e \"s/^Source Output #//\""

sox_mic_so   = "pactl list | \
                    grep -e \"Source Output #\" -e \"Cliente #\" -e \"device.description\" | \
                    grep -B1 \"sox_mic\" | \
                    grep \"^Source Output #\" | \
                    sed -e \"s/^Source Output #//\""

zoom_so      = "pactl list | \
                    grep -e \"Source Output\" -e \"media.name\" | \
                    grep -B1 \"recStream\" | \
                    grep \"^Source Output #\" | \
                    sed -e \"s/^Source Output #//\""

streams_dict = {}

def crear_dicc(wait=False):
    if wait: time.sleep(.75)
    streams_dict['comb_spkout_si'] = subprocess.check_output([comb_spkout_si.replace('\t', '')], shell=True).decode("utf-8")[:-1]
    streams_dict['comb_hsout_si']  = subprocess.check_output([comb_hsout_si.replace('\t', '')], shell=True).decode("utf-8")[:-1]
    streams_dict['sox_music_si']   = subprocess.check_output([sox_music_si.replace('\t', '')], shell=True).decode("utf-8")[:-1]
    streams_dict['sox_mic_si']     = subprocess.check_output([sox_mic_si.replace('\t', '')], shell=True).decode("utf-8")[:-1]
    streams_dict['zoom_si']        = subprocess.check_output([zoom_si.replace('\t', '')], shell=True).decode("utf-8")[:-1]
    streams_dict['vlc_si']         = subprocess.check_output([vlc_si.replace('\t', '')], shell=True).decode("utf-8")[:-1]
    streams_dict['remapped_so']    = subprocess.check_output([remapped_so.replace('\t', '')], shell=True).decode("utf-8")[:-1]
    streams_dict['desktop_so']     = subprocess.check_output([desktop_so.replace('\t', '')], shell=True).decode("utf-8")[:-1]
    streams_dict['sox_mic_so']     = subprocess.check_output([sox_mic_so.replace('\t', '')], shell=True).decode("utf-8")[:-1]
    streams_dict['zoom_so']        = subprocess.check_output([zoom_so.replace('\t', '')], shell=True).decode("utf-8")[:-1]

def change_volume(stream, num_percent, wait=False):
    if wait: time.sleep(.75)
    stream_id = streams_dict[stream]
    if stream_id == '': return

    if stream.endswith('_si'):
        comando = 'pactl set-sink-input-volume ' + stream_id + ' ' + str(num_percent) + '%'
    elif stream.endswith('_so'):
        comando = 'pactl set-source-output-volume ' + stream_id + ' ' + str(num_percent) + '%'
    os.system(comando)

def change_property(stream, propiedad, valor, wait=False):
    if wait: time.sleep(.75)
    stream_id = streams_dict[stream]
    if stream_id == '': return

    if stream.endswith('_si'):
        comando = 'pacmd update-sink-input-proplist %s \"%s=\\\"%s\\\"\"' % (stream_id, propiedad, valor)
    elif stream.endswith('_so'):
        comando = 'pacmd update-source-output-proplist %s \"%s=\\\"%s\\\"\"' % (stream_id, propiedad, valor)
    os.system(comando)

def change_volume_from_osc(unused_addr, args, num):
    stream = args[0]
    # 'sox_mic_si' y 'remapped_so' son de 0% a 150%
    if stream == 'sox_mic_si' or stream == 'remapped_so' or stream == 'zoom_so':
        num_percent = int(num*150)
    else:
        num_percent = int(num*100)
    change_volume(stream, num_percent)

def change_scene(unused_addr, args, num):
    global first_time

    # Baja el volumen del mic de 150% a 100%
    if not first_time:
        change_volume('sox_mic_si', 100)

    # REPRODUCCION DE MUSICA Y CONFIGURACION DE MIC
    # Nombre de escena : [ comentario, filtro adicional de SoX, archivo musica ]
    scenes = {
        'nada'  : ['No-imagen / No-musica',   '',                        ''],
        'pre'   : ['Pre - Musica y Mic',      '',                        'prepost-musica.wav'],
        'main1' : ['Main1 - Intro',           '',                        'main-musica-con-intro.wav'],
        'main2' : ['Main2 - Loop',            '',                        'main-musica-directo-loop.wav'],
        'pk'    : ['Pinokio 3.0 - Pitch +900','pitch +900',              'pk-musica.wav'],
        'news'  : ['News - HiPass + Gain',    'highpass 1000',  'news-musica.wav'],
        'space' : ['Space - Echo + Reverb',   'echo 0.8 0.88 500 0.2 1000 .1 1500 .05 reverb 50 10 100', 'space-musica.wav'],
        'dance1': ['Dance1 - Soy Yo',         '',                        'dance-soy-yo.wav'],
        'dance2': ['Dance2 - Baby Shark',     '',                        'dance-baby-shark.wav'],
        'post'  : ['Post - Musica y Mic',     '',                        'prepost-musica.wav'],
        }

    mic_sox_cmd = 'PULSE_SINK=' + spk_out_sink + ' \
                PULSE_PROP=device.description=sox_mic \
                sox --buffer 128 -r 44.1k -c 1 \
                -t pulseaudio default \
                -t pulseaudio default \
                equalizer 47 1.5q -20 \
                equalizer 63 1.5q -20 \
                equalizer 94 1.5q -20'

    music_play_cmd = 'PULSE_SINK=' + combinado + ' \
                    PULSE_PROP=device.description=sox_music \
                    play ../00-audios/'

    repeat = ' repeat -'
    a_null = ' > /dev/null 2>&1 &'

    # Si el source no pertenece a una escena...
    if args not in scenes:
        consola('No existe escena: ' + args, 'error')
        return

    # Muestra comentario
    consola(scenes[args][0])

    # Mata procesos
    if not first_time:
        os.system('kil play')
        os.system('kil sox')

    # Ejecuta SoX para el mic
    os.system(mic_sox_cmd   + ' ' + scenes[args][1]     + a_null)
    # Ejecuta play para la musica
    os.system(music_play_cmd + scenes[args][2] + repeat + a_null)

    # Reinicia streams IDs
    crear_dicc(wait=True)

    # Cambia nombres
    change_property('sox_music_si', 'media.name', 'SoX MUSIC')
    change_property('sox_mic_si', 'media.name', 'SoX MIC')
    change_property('sox_mic_so', 'media.name', 'SoX MIC')

    # Setea volumenes
    change_volume('sox_music_si', 100)
    change_volume('sox_mic_si', 150)
    change_volume('sox_mic_so', 50)

    # OSC -> Pd -> MMP (de 0 a 1)
    client.send_message("/vol_si/vol5", 1)
    client.send_message("/vol_si/vol6", 1)
    client.send_message("/vol_so/vol4", .5)

    first_time = False

def z_main():
    pass

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--ip_server", default="127.0.0.1", help="The ip to listen on")
    parser.add_argument("--port_server", type=int, default=5005, help="The port to listen on")

    parser.add_argument("--ip_client", default="127.0.0.3", help="The ip of the OSC server")
    parser.add_argument("--port_client", type=int, default=8001, help="The port the OSC server is listening on")
    args = parser.parse_args()

    # OSC CLIENT
    client = udp_client.SimpleUDPClient(args.ip_client, args.port_client)
    client.send_message("/init", 1)

    consola('Crea stream_dict{}', 'amarillo')
    crear_dicc(wait=True)
    # Init
    consola('Inicia volumenes', 'magenta')
    change_volume('comb_spkout_si', 50)
    change_volume('comb_hsout_si', 50)
    change_volume('remapped_so', 150)
    change_volume('zoom_so', 150)

    crear_dicc(wait=True)

    # OSC -> Pd -> MMP (de 0 a 1)
    client.send_message("/vol_si/vol1", .5)  # comb_spkout_si
    client.send_message("/vol_si/vol2", .5)  # comb_hsout_si
    client.send_message("/vol_si/vol3", 1)   # zoom_si
    client.send_message("/vol_so/vol1", 1)   # remapped_so
    client.send_message("/vol_so/vol2", 1)   # desktop_so
    client.send_message("/vol_so/vol3", 1)   # zoom_so
    client.send_message("/vol_so/vol4", .5)  # sox_mic_so

    # OSC SERVER
    dispatcher = dispatcher.Dispatcher()
    # ~ dispatcher.map("/*", test)
    dispatcher.map("/vol_si/vol1", change_volume_from_osc, 'comb_spkout_si')
    dispatcher.map("/vol_si/vol2", change_volume_from_osc, 'comb_hsout_si')
    dispatcher.map("/vol_si/vol3", change_volume_from_osc, 'zoom_si')
    dispatcher.map("/vol_si/vol4", change_volume_from_osc, 'vlc_si')
    dispatcher.map("/vol_si/vol5", change_volume_from_osc, 'sox_mic_si')
    dispatcher.map("/vol_si/vol6", change_volume_from_osc, 'sox_music_si')

    dispatcher.map("/vol_so/vol1", change_volume_from_osc, 'remapped_so')
    dispatcher.map("/vol_so/vol2", change_volume_from_osc, 'desktop_so')
    dispatcher.map("/vol_so/vol3", change_volume_from_osc, 'zoom_so')
    dispatcher.map("/vol_so/vol4", change_volume_from_osc, 'sox_mic_so')

    dispatcher.map("/scene",       change_scene)

    server = osc_server.ThreadingOSCUDPServer(
            (args.ip_server, args.port_server), dispatcher)
    consola("Server en {}".format(server.server_address), 'azul_bold')
    server.serve_forever()

Also, I was using this plugin that sends OSC messages from OBS to PureData (no audio). So, PureData receives simple messages about the actual scene, it also receives OSC mesages from the tablet, and send OSC messages to osc_server.py (file above). If you need, I can share the PureData patch file... tell me.

johnedstone commented 4 years ago

Thank you for posting that script. There is a lot of good stuff in there. I'm studying it now to understand more about all the options - great!. Just to be clear, if all that one wants to do, is to send OBS audio to Zoom, then all it takes is the following:

My one concern, although it seems to be working exactly right, is "Is there a delay in the audio/video sync because you are 'monitoring' an audio source?" My impression is "no" because it is in real time, but I am not sure. I am using this sort of video, after downoading, along with kdenlive, after zoom recording, to look at the frames to see if the audio/video on the zoom platform is in sync.

MarioMey commented 4 years ago

Theorically no, there would be no such unsynch. Buuuut... do all the tests you need to be sure. If there is a delay, you can correct with some features:

Thanks for that test video! It's great.

johnedstone commented 4 years ago

Good, I'm glad you agree that theoretically it will be in sync. I've been reading this link, to understand what remap-source means. And from this link, especially this when the sink reads a chunk from its sink inputs, it also writes this chunk to the sink monitor., so it makes me think it is in sync:

Monitor device

Sink monitor is a special kind of virtual source associated with a sink.

Every sink automatically gets a sink monitor, named as “<sink_name>.monitor”. Every time when the sink reads a chunk from its sink inputs, it also writes this chunk to the sink monitor.

Typical usage of the sink monitor is capturing all sound that was sent to speakers and duplicating it somewhere else. PulseAudio automatically creates a sink monitor for every sink

Thanks for the screenshot of the adjusting the video sync in OBS. I had found the audio sync adjustment, but didn't know the video one existed

MarioMey commented 4 years ago

The "Modules" link was open in Firefox all the time in last two weeks. And the other (Pulse under the Hood) I didn't read it completly (obviously), but I bookmarked with the name of "everything about PulseAudio".

But I repeat, I learnt a lot of PulseAudio... but now I'm a bit dissapointed with it. It is possible to do a lot of things... but finally it consumed a lot of CPU. Creating Jack modules and working inside PureData... audio is limitless. Music, voice, filters, FXs, conections, pipes, delays... everything. I'm very familiar with PureData, so it's pleasurable to work with it.

Again, my advice: use Jack. You don't need to use PureData, maybe you can use Patchage or some other soft to conect modules and add filters with, for example, "Calf Pack Plugin for Jack", VLC with Jack plugin, etc. Also, connect OBS to Jack.

Good luck.

PS: take a look to my MEH-SYSTEM, my very big project built with PureData. It's in spanish, sorry.

EliterScripts commented 4 years ago

Thank you all for your input!

Yes, these all do take the input from the desktop applications, and put them into whatever sink/virtual microphone, but the problem I keep having is that I cannot combine BOTH desktop application sound AND physical microphone sound.

How do you combine the two?

Thanks a lot!!!

johnedstone commented 4 years ago

Thank you all for your input!

Yes, these all do take the input from the desktop applications, and put them into whatever sink/virtual microphone, but the problem I keep having is that I cannot combine BOTH desktop application sound AND physical microphone sound.

How do you combine the two?

Thanks a lot!!!

Ubuntu 18.04: Regarding two input streams, going into one sink, .... In OBS Studio, this is no problem. Maybe you are trying to do this outside of OBS Studio. Within OBS, as seen in the attached image, you can see that there are two audio sources, the green lines, and above that, the Advanced Audio Properties is set to Monitor and Output. This setting "Monitor and Output", in effect sends these two audio streams to one sink, they system sink. Hope that makes sense. OBS_2_audio sources_sent_to_1_sink

EliterScripts commented 4 years ago

@johnedstone Thank you for the reply! However, I do not see how I can get to the screen you are referring to. I am using Ubuntu Linux LTS 18.04 and Ubuntu Linux 16.04 on another computer. Are we using the same operating system? How do I get to the screen you are referring to?

EliterScripts commented 4 years ago

Alright, so I found where advanced audio properties is. It is under edit > advanced audio properties.

The problem is that there is a 3-5 second delay between when the user sees the video, and when the user sees the audio. I tested this out. There is unoticeable video delay between my video actions on my computer, and the person on the other end of the video conference call (on Jitsi); I use a web browser piano, and counted between the hit, and hearing it on the other end. It is 3-5 second delay.

The other thing is that the "Desktop Audio" in OBS lights up immediately when I press the piano key, but it delays to light up in pavucontrol, and Jitsi/Chromium (the web video conference software).

I also have an immediate response when using the microphone as well, which is strange. So the desktop audio

I used pactl load-module module-null-sink sink_name=OBS sink_properties=device.description="OBS"

Then, under Settings > Audio > Advanced > Monitoring Device, I chose "Monitor of OBS" I hit apply, then OK. image

Then, I go to Edit > Advanced Audio Properties and I make sure it looks like this: image

Then I also go into pavucontrol, and changed the "recording from" to "Monitor of OBS". image

Here is what Jitsi, the web-browser-based conference software uses for the audio: image

And here is what my scene looks like: image

johnedstone commented 4 years ago

Note that I am not using the null sink module. If you'd like to set up a zoom meeting, I can demonstrate this, in a zoom meeting and you can tell me if it is in sync - no problem

MarioMey commented 4 years ago

Yes, these all do take the input from the desktop applications, and put them into whatever sink/virtual microphone, but the problem I keep having is that I cannot combine BOTH desktop application sound AND physical microphone sound.

Is this resolved?

The other thing is that the "Desktop Audio" in OBS lights up immediately when I press the piano key, but it delays to light up in pavucontrol, and Jitsi/Chromium (the web video conference software).

That was only one reason why I switched to Jack. It is what I called "unstable". OBS audio issues that I had:

My advice: use Jack :).

johnedstone commented 4 years ago

I'm done. From my viewpoint, getting a virtual microphone with module-remap-source and enabling monitoring in OBS took care of my concerns, thanks!

TRPB commented 3 years ago

@MarioMey do you have an example of setting this up using Jack? I've never used it before, but despite trying all the pulseaudio solutions here, I have a 1-2 second delay in the audio when doing this with whatever I try.

MarioMey commented 3 years ago

I don't have time to explain step by step... but I could give you some advices:

I think you can do whatever you need with this softwares. I use PurrData (a fork of PureData)... but it is a bit complicated to learn.

TRPB commented 3 years ago

I've got jack up and running but there doesn't seem to be a way to route the audio coming out of OBS? Could you paste a screenshot of how you have Patchage set up? Here's what I get:

image

When OBS is launched, it comes up as "JACK Input Client" but there's no way to then route the sound out of OBS back to a dummy microphone. I've only just started playing with this but it doesn't look like it'll quite do what we were trying to achieve with pulse Microphone -> OBS -> Apply Filters -> Virtual Microphone -> Zoom/Skype/etc

TRPB commented 3 years ago

Ok, I actually think there may be a bug in OBS.

If I create the virtual devices:

pactl load-module module-null-sink sink_name=Virtual-Speaker sink_properties=device.description=Virtual-Speaker
pactl load-module module-remap-source source_name=Remap-Source master=Virtual-Speaker.monitor

Then set the monitor in OBS settings, I get a 1-2 second delay on the audio.

If, however, I leave it set to default and then change the monitor in pagraphcontrol or KDE system settings (change the output of OBS-Monitor to the virtual speaker) there is no delay!

MarioMey commented 3 years ago

I've got jack up and running but there doesn't seem to be a way to route the audio coming out of OBS?

You have to create a Jack sink module with Pulse Audio tools:

pactl load-module module-jack-sink channels=2 sink_name=OBSOUT client_name=OBSOUT connect=false

... and then use pavucontrol to redirect every "out" of OBS to this "OBSOUT" created module.

imagen

Could you paste a screenshot of how you have Patchage set up? Here's what I get:

imagen

OBSOUT: above explained. ZoomOUT: another Jack module to connect Zoom speakers out to PureData (in). ZoomIN: the same as ZoomOUT, but to connect PureData (out) to Zoom (in) JACK-OBS: it is created by OBS ("JACK Input Client") OBSIN: I used to use this before I discover Jack Input Client. Now, I have it as a backup. One day, the Jack Input Client didn't work well and I had to use it (luckily, I had it connected yet) system out and in: phisical mic and spk.