How to change VLC-python output device? - python

In my project, I need to get sound from a radio stream URL and play it. But it needs to be played in a specific output device called "VB-Cable Input (Virtual Audio Cable)".
I couldn't find a way to do it. This is my current code:
import vlc
import time
url = "https://shout25.crossradio.com.br:18156/1"
# Create VLC instance, media player and media
instance = vlc.Instance()
player = instance.media_player_new()
media = instance.media_new(url)
player.set_media(media)
# Get list of output devices
def get_device():
mods = player.audio_output_device_enum()
if mods:
mod = mods
while mod:
mod = mod.contents
# If VB-Cable is found, return it's module and device id
if 'CABLE Input (VB-Audio Virtual Cable)' in str(mod.description):
device = mod.device
module = mod.description
return device,module
mod = mod.next
# Sets the module and device id to VB-Cable
device,module = get_device()
# Sets VB-Cable as output device
player.audio_output_device_set(device,module)
# Trying the function #Random Davis stated, but results still the same
vlc.libvlc_audio_output_device_set(player, module, device)
# It should return VB-Cable as current audio output, but None is returned...
print(player.audio_output_device_get())
# Audio plays in default output device, which is incorrect
player.play()
time.sleep(60)
I searched through StackOverflow and the VLC documentation, but couldn't get the result I expected, I believe I'm using the functions in the wrong way.

After some research, I found this implementation of audio_output_device_set() in GitHub
As #RandomDavis said, the problem was indeed in this function. All I had to do was switching the first argument to None and leave the rest as it is.
This is the correct code:
# Sets VB-Cable as output device
player.audio_output_device_set(None, device)
Still not quite sure why inserting any values other than None makes the function invalid. But the problem is now solved.

Related

Interfacing MIDI device (Akai LPD8 mk2) from Python - works for reading but not sending

Preface
I have an Akai LPD8 mk2 which I would like to interface from Python.
There are two projects that (I believe) provide interfaces for the predecessor (mk1):
https://github.com/zetof/LPD8
https://github.com/DrLuke/python-lpd8
I found I needed to make a few small changes to get the reading to work with these libs (*example below, not sure how relevant this is and it could easily be because I misunderstand something).
However, no matter what I tried, I could not get the sending to the device to work. Specifically, changing the state (on/off) of the pads of the device from Python. This state is indicated by the color of the pads' lighting.
Question
A minimal example of what I thought should work is the following:
With the very nice mido library, I would like to read a few state changes of the pads. Then send them back with a small delay (similar idea here: https://stackoverflow.com/a/29501455/655404):
from time import sleep
import mido
def get_ioport_name(search):
# see footnote **
names = mido.get_ioport_names()
names = set(n for n in names if search in n)
assert len(names) == 1
return names.pop()
def get_messages(port):
return list(port.iter_pending())
name = get_ioport_name("LPD8")
port = mido.open_ioport(name) # see footnote ***
# ensure that there are no messages in the queue
get_messages(port)
#pause
input("Press a few pads, then press Enter to continue...")
msgs = get_messages(port)
print(f"Recorded these messages:\n{msgs}\n")
print("Echoing these messages:")
for m in msgs:
print(m)
port.send(m)
sleep(1)
This runs and gives no error messages. However, the expected color changes on the pads do not happen.
What am I doing wrong?
I have tried with different backends for mido (rtmidi, portmidi, pygame) as well as using rtmidi directly, all with the same result.
Footnotes
*Example for changes needed:
I had to change the constant's block here https://github.com/zetof/LPD8/blob/master/lpd8/lpd8.py#L16 to:
NOTE_ON = 144+9
NOTE_OFF = 128+9
CTRL = 176
PGM_CHG = 192+9
**Port names
names = mido.get_ioport_names() gives:
['Midi Through:Midi Through Port-0 14:0',
'LPD8 mk2:LPD8 mk2 MIDI 1 20:0',
'Midi Through:Midi Through Port-0 14:0',
'LPD8 mk2:LPD8 mk2 MIDI 1 20:0']
and set(names) gives:
{'LPD8 mk2:LPD8 mk2 MIDI 1 20:0', 'Midi Through:Midi Through Port-0 14:0'}
The result is also the same for mido.get_input_names() and mido.get_output_names().
***Ports in mido
From what I understand mido has three port classes:
open_input() # only receive
open_output() # only send
open_ioport() # both (and what I used above)
If I change from ioport to using an input/output pair, the result is the same:
port_in = mido.open_input(name)
port_out = mido.open_output(name)
# ensure that there are no messages in the queue
get_messages(port_in)
#pause
input("Press a few pads, then press Enter to continue...")
msgs = get_messages(port_in)
print(f"Recorded these messages:\n{msgs}\n")
print("Echoing these messages:")
for m in msgs:
print(m)
port_out.send(m)
sleep(1)

Trying to understand win32api, win32con - python [duplicate]

So, I'm trying to make myself a Python script which goes through the selected Music folder and tells the user if specific album doesn't have an album cover. It basically goes through all the files and checks if file[-4:] in (".jpg",".bmp",".png"), if true, it found a picture file. Just to make it clear, the structure of my folders is:
Music folder
Arctic Monkeys
Humbug (2009)
Suck it and see (2011)
Morphine
Cure For Pain (1993)
.. and so on. I'm testing the script to find if there's a missing cover in my Arctic Monkeys directory, and my script goes through the "Humbug (2009)" folder and finds AlbumArtSmall.jpg which doesn't show up in the command prompt so I tried "Show hidden files/folders" and still nothing. However, the files show up once I uncheck "Hide protected operating system files", so that's kinda weird.
My question is - how do I tell Python to skip searching the hidden/protected files?
I checked out the How to ignore hidden files using os.listdir()? but the solution I found there only works for files starting with ".", and that's not what I need.
Cheers!
Edit - so here's the code:
import os
def findCover(path, band, album):
print os.path.join(path, band, album)
coverFound = False
for mFile in os.listdir(os.path.join(path, band, album)):
if mFile[-4:] in (".jpg",".bmp",".png"):
print "Cover file found - %s." % mFile
coverFound = True
return coverFound
musicFolder = "E:\Music" #for example
noCovers = []
for band in os.listdir(musicFolder): #iterate over bands inside the music folder
if band[0:] == "Arctic Monkeys": #only Arctic Monkeys
print band
bandFolder = os.path.join(musicFolder, band)
for album in os.listdir(bandFolder):
if os.path.isdir(os.path.join(bandFolder,album)):
if findCover(musicFolder, band, album): #if cover found
pass #do nothing
else:
print "Cover not found"
noCovers.append(band+" - "+album) #append to list
else: #if bandFolder is not actually a folder
pass
print ""
You can use with the pywin32 module, and manually test for FILE_ATTRIBUTE_HIDDEN or any number of attributes
FILE_ATTRIBUTE_ARCHIVE = 32
FILE_ATTRIBUTE_ATOMIC_WRITE = 512
FILE_ATTRIBUTE_COMPRESSED = 2048
FILE_ATTRIBUTE_DEVICE = 64
FILE_ATTRIBUTE_DIRECTORY = 16
FILE_ATTRIBUTE_ENCRYPTED = 16384
FILE_ATTRIBUTE_HIDDEN = 2
FILE_ATTRIBUTE_NORMAL = 128
FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 8192
FILE_ATTRIBUTE_OFFLINE = 4096
FILE_ATTRIBUTE_READONLY = 1
FILE_ATTRIBUTE_REPARSE_POINT = 1024
FILE_ATTRIBUTE_SPARSE_FILE = 512
FILE_ATTRIBUTE_SYSTEM = 4
FILE_ATTRIBUTE_TEMPORARY = 256
FILE_ATTRIBUTE_VIRTUAL = 65536
FILE_ATTRIBUTE_XACTION_WRITE = 1024
like so:
import win32api, win32con
#test for a certain type of attribute
attribute = win32api.GetFileAttributes(filepath)
#The file attributes are bitflags, so you want to see if a given flag is 1.
# (AKA if it can fit inside the binary number or not)
# 38 in binary is 100110 which means that 2, 4 and 32 are 'enabled', so we're checking for that
## Thanks to Nneoneo
if attribute & (win32con.FILE_ATTRIBUTE_HIDDEN | win32con.FILE_ATTRIBUTE_SYSTEM):
raise Exception("hidden file") #or whatever
#or alter them
win32api.SetFileAttributes(filepath, win32con.FILE_ATTRIBUTE_NORMAL) #or FILE_ATTRIBUTE_HIDDEN
After you alter a file, take a look in the folder, it won't be hidden anymore.
Found this information here and here: Checking file attributes in python
Alternatively, you can try to use the os.stat function, whose docs here and then use the stat module to further understand what you're looking at.
Found these relevant questions. (python) meaning of st_mode and How can I get a file's permission mask?

Python bindings for libVLC - cannot change audio output device

VLC 2.2.3, python-vlc 1.1.2.
I have a virtual audio output and am trying to get libVLC to output on it. So far, I can see the virtual output appearing in libVLC, but selecting it makes audio play on the default output (i.e. the speakers).
This is the relevant part of what I have:
self.Instance = vlc.Instance()
self.player = self.Instance.media_player_new()
devices = []
mods = self.player.audio_output_device_enum()
if mods:
mod = mods
while mod:
mod = mod.contents
devices.append(mod.device)
mod = mod.next
vlc.libvlc_audio_output_device_list_release(mods)
# this is the part I change on each run of the code.
self.player.audio_output_device_set(None, devices[0])
I've run the code multiple times, changing the device ID as per the code comment. However, the output device doesn't actually change. This is a headache for two reasons:
1) audio_output_device_set() doesn't return anything. I can't tell if I'm actually accomplishing anything when I run this function.
2) I can't even run audio_output_device_get() to check if the set function is doing anything as this is only for libvlc 3. I would prefer for my program to work with 2.2.3.
So, what I did next was install VLC 3.0 and run the above code with it. Now, audio_output_device_get() works and I can see that the set function is actually changing the output device to the virtual output. But sound STILL plays on the speakers.
What's going on? How do I fix this?
I asked at the VLC forums and got a singularly unhelpful reply telling me to 'check logs and documentation'. That's it. I've been superglued to the rather lacking documentation to get this far. Even though I doubt it can help, I've decided to try logging. I thought it would be as simple as calling libvlc_log_set_file but it needs a libVLC file pointer, and I don't know how to create one with a name and mode as in Python.
tl;dr:
1) How do I successfully change the audio output device?
2) How do I set up maximum verbosity logging?
1) For some reason, I had to pause and unpause before VLC would register my change.
This code fixes things:
[... rest of GUI class ...]
self.p.play()
self.root.after(350, self.DeviceSet)
def DeviceSet(self):
self.p.audio_output_device_set(None, self.audiodevice)
self.p.pause()
self.root.after(10)
self.p.pause()
2) Initialise VLC as follows:
self.instance = vlc.Instance('--verbose 9')
Here is a full example of how to switch to different audio device.
Remember: don't call player.stop() after player.audio_output_device_set(), otherwise the set operation won't work!!
import time
from typing import List
import vlc
def vlc_set_device_test(filename: str):
# creating a vlc instance
vlc_instance: vlc.Instance = vlc.Instance()
player: vlc.MediaPlayer = vlc_instance.media_player_new()
media: vlc.Media = vlc_instance.media_new(filename)
player.set_media(media)
# list devices
device_ids: List[bytes] = []
mods = player.audio_output_device_enum()
if mods:
index = 0
mod = mods
while mod:
mod = mod.contents
desc = mod.description.decode('utf-8', 'ignore')
print(f'index = {index}, desc = {desc}')
device_ids.append(mod.device)
mod = mod.next
index += 1
# free devices
vlc.libvlc_audio_output_device_list_release(mods)
# hard code device
pc_speaker = device_ids[1]
headset = device_ids[3]
# play music to default device
player.play()
time.sleep(3)
# set output device
player.audio_output_device_set(None, headset)
# don't call player.stop()!!
player.pause()
# now music is playing from headset
player.play()
time.sleep(10)
player.stop()
if __name__ == '__main__':
vlc_set_device_test(r'D:\cheer up.mp3')

Pipsta Printer and Printing a list

I'm trying to modify the simple python script provided with my Pipsta Printer so that instead of printing a single line of text, it prints out a list of things.
I know there are probably better ways to do this, but using the script below, could somebody please tell me what changes I need to make around the "txt = " part of the script, so that I can print out a list of items and not just one item?
Thanks.
# BasicPrint.py
# Copyright (c) 2014 Able Systems Limited. All rights reserved.
'''This simple code example is provided as-is, and is for demonstration
purposes only. Able Systems takes no responsibility for any system
implementations based on this code.
This very simple python script establishes USB communication with the Pipsta
printer sends a simple text string to the printer.
Copyright (c) 2014 Able Systems Limited. All rights reserved.
'''
import argparse
import platform
import sys
import time
import usb.core
import usb.util
FEED_PAST_CUTTER = b'\n' * 5
USB_BUSY = 66
# NOTE: The following section establishes communication to the Pipsta printer
# via USB. YOU DO NOT NEED TO UNDERSTAND THIS SECTION TO PROGRESS WITH THE
# TUTORIALS! ALTERING THIS SECTION IN ANY WAY CAN CAUSE A FAILURE TO COMMUNICATE
# WITH THE PIPSTA. If you are interested in learning about what is happening
# herein, please look at the following references:
#
# PyUSB: http://sourceforge.net/apps/trac/pyusb/
# ...which is a wrapper for...
# LibUSB: http://www.libusb.org/
#
# For full help on PyUSB, at the IDLE prompt, type:
# >>> import usb
# >>> help(usb)
# 'Deeper' help can be trawled by (e.g.):
# >>> help(usb.core)
#
# or at the Linux prompt, type:
# pydoc usb
# pydoc usb.core
PIPSTA_USB_VENDOR_ID = 0x0483
PIPSTA_USB_PRODUCT_ID = 0xA053
def parse_arguments():
'''Parse the arguments passed to the script looking for a font file name
and a text string to print. If either are mssing defaults are used.
'''
txt = 'Hello World from Pipsta!'
parser = argparse.ArgumentParser()
parser.add_argument('text', help='the text to print',
nargs='*', default=txt.split())
args = parser.parse_args()
return ' '.join(args.text)
def main():
"""The main loop of the application. Wrapping the code in a function
prevents it being executed when various tools import the code.
"""
if platform.system() != 'Linux':
sys.exit('This script has only been written for Linux')
# Find the Pipsta's specific Vendor ID and Product ID
dev = usb.core.find(idVendor=PIPSTA_USB_VENDOR_ID,
idProduct=PIPSTA_USB_PRODUCT_ID)
if dev is None: # if no such device is connected...
raise IOError('Printer not found') # ...report error
try:
# Linux requires USB devices to be reset before configuring, may not be
# required on other operating systems.
dev.reset()
# Initialisation. Passing no arguments sets the configuration to the
# currently active configuration.
dev.set_configuration()
except usb.core.USBError as ex:
raise IOError('Failed to configure the printer', ex)
# The following steps get an 'Endpoint instance'. It uses
# PyUSB's versatile find_descriptor functionality to claim
# the interface and get a handle to the endpoint
# An introduction to this (forming the basis of the code below)
# can be found at:
cfg = dev.get_active_configuration() # Get a handle to the active interface
interface_number = cfg[(0, 0)].bInterfaceNumber
# added to silence Linux complaint about unclaimed interface, it should be
# release automatically
usb.util.claim_interface(dev, interface_number)
alternate_setting = usb.control.get_interface(dev, interface_number)
interface = usb.util.find_descriptor(
cfg, bInterfaceNumber=interface_number,
bAlternateSetting=alternate_setting)
usb_endpoint = usb.util.find_descriptor(
interface,
custom_match=lambda e:
usb.util.endpoint_direction(e.bEndpointAddress) ==
usb.util.ENDPOINT_OUT
)
if usb_endpoint is None: # check we have a real endpoint handle
raise IOError("Could not find an endpoint to print to")
# Now that the USB endpoint is open, we can start to send data to the
# printer.
# The following opens the text_file, by using the 'with' statemnent there is
# no need to close the text_file manually. This method ensures that the
# close is called in all situation (including unhandled exceptions).
txt = parse_arguments()
usb_endpoint.write(b'\x1b!\x00')
# Print a char at a time and check the printers buffer isn't full
for x in txt:
usb_endpoint.write(x) # write all the data to the USB OUT endpoint
res = dev.ctrl_transfer(0xC0, 0x0E, 0x020E, 0, 2)
while res[0] == USB_BUSY:
time.sleep(0.01)
res = dev.ctrl_transfer(0xC0, 0x0E, 0x020E, 0, 2)
usb_endpoint.write(FEED_PAST_CUTTER)
usb.util.dispose_resources(dev)
# Ensure that BasicPrint is ran in a stand-alone fashion (as intended) and not
# imported as a module. Prevents accidental execution of code.
if __name__ == '__main__':
main()
Pipsta uses linux line-feed ('\n', 0x0A, 10 in decimal) to mark a new line. For a quick test change BasicPrint.py as follows:
#txt = parse_arguments()
txt = 'Recipt:\n========\n1. food 1 - 100$\n2. drink 1 - 200$\n\n\n\n\n'
usb_endpoint.write(b'\x1b!\x00')
for x in txt:
usb_endpoint.write(x)
res = dev.ctrl_transfer(0xC0, 0x0E, 0x020E, 0, 2)
while res[0] == USB_BUSY:
time.sleep(0.01)
res = dev.ctrl_transfer(0xC0, 0x0E, 0x020E, 0, 2)
I commented out parameter parsing and injected a test string with multi line content.

Play mp4 video with python and gstreamer

I'm trying to play video in mp4 format but not working.
In console I execute this line and it works:
gst-launch playbin uri=rtmp://localhost:1935/files/video.mp4
But if I change to version 1.0 only works the audio:
gst-launch-1.0 playbin uri=rtmp://localhost:1935/files/video.mp4
in python I have the following code:
self.player = Gst.Pipeline.new("player")
source = Gst.ElementFactory.make("filesrc", "file-source")
demuxer = Gst.ElementFactory.make("mp4mux", "demuxer")
demuxer.connect("pad-added", self.demuxer_callback)
self.video_decoder = Gst.ElementFactory.make("x264enc", "video-decoder")
self.audio_decoder = Gst.ElementFactory.make("vorbisdec", "audio-decoder")
audioconv = Gst.ElementFactory.make("audioconvert", "converter")
audiosink = Gst.ElementFactory.make("autoaudiosink", "audio-output")
videosink = Gst.ElementFactory.make("autovideosink", "video-output")
self.queuea = Gst.ElementFactory.make("queue", "queuea")
self.queuev = Gst.ElementFactory.make("queue", "queuev")
colorspace = Gst.ElementFactory.make("videoconvert", "colorspace")
self.player.add(source)
self.player.add(demuxer)
self.player.add(self.video_decoder)
self.player.add(self.audio_decoder)
self.player.add(audioconv)
self.player.add(audiosink)
self.player.add(videosink)
self.player.add(self.queuea)
self.player.add(self.queuev)
self.player.add(colorspace)
source.link(demuxer)
self.queuev.link(self.video_decoder)
self.video_decoder.link(colorspace)
colorspace.link(videosink)
self.queuea.link(self.audio_decoder)
self.audio_decoder.link(audioconv)
audioconv.link(audiosink)
but I get this error:
Error: Error in the internal data flow. gstbasesrc.c(2865): gst_base_src_loop (): /GstPipeline:player/GstFileSrc:file-source:
streaming task paused, reason not-linked (-1)
What can be happening? think I am no good decoding
You are missing linking the demuxer pads to your queues. Demuxers have 'sometimes' pads so you need to listen to the pad-added signal of them and link in this callback. Remember to check the pad caps once you get them and link to the appropriate branch of your pipeline.
You can read about dynamic pads here: http://gstreamer.freedesktop.org/data/doc/gstreamer/head/manual/html/chapter-pads.html#section-pads-dynamic
You have in your code:
demuxer = Gst.ElementFactory.make("mp4mux", "demuxer")
demuxer.connect("pad-added", self.demuxer_callback)
I hope this is a cut/paste error, as demuxing with a mux will not work. I believe for an .mp4 file, the normal demuxer (if you are choosing one by hand) is qtdemux.
You could also use decodebin to decode the file for you.

Categories