I'm using the Music21 library and want to hear each track in the instrument is should be played in from within my jupyter notebook (IPython).
I can successfully output music on different tracks to a midi file and play through timidity, but am unable to hear any tracks other than the Piano (midiProgram = 0) from the IPython player.
I'm currently on Ubuntu 18.04 using music21 v5.7.0. I've also been able to replicate this on my Mac system.
I've tried writing out a stream to a midi file vs. showing it in the notebook and I can only hear the adjusted instruments in the generated midi file.
from music21 import *
core_corp = corpus.corpora.CoreCorpus()
bach_piece = core_corp.search('bwv120.8-a')[0].parse()
bach_piece.show("midi")
for el in bach_piece.recurse():
if 'Instrument' in el.classes:
el.activeSite.replace(el, instrument.Trumpet())
bach_piece.show("midi")
I expect to hear all trumpet sounds in the second show() call, but still only hear piano. However, when I open up the generated midi file using timidity, I can hear all of them as trumpets.
Digging through some of the source code, I expect it has something to do with the midiPlayer that is generated by the javascript or in the decoding of the base64 encoded midi file, but I don't have any expertise here.
Any help would be greatly appreciated, my current workaround is just to use
!timidity <path to file>
In my notebook in case anyone else is running this problem.
There is a problem about Jupyter Notebook's MIDI player, not about the Music21. You can hear all the instruments' sound when you write&play the MIDI file with correct MIDI player.
For writing MIDI files:
stream1 = converter.parse('d:/musicxml_folder/Rondo_Alla_Turca.xml')
stream1.write('midi', 'd:/musicxml_folder/Rondo_Alla_Turca.mid')
For playing the MIDI file, i use Windows Media Player. When i do this in Pycharm IDE
stream1.show('midi')
Windows Media Player opens up. If you want to hear the sound of MIDI file on another environment you can use a library that can play MIDI files.
Related
I would like to send midi data from my computer (running MacOS) via a port to my digital piano.
I want to be able to play a midi file, play certain notes for certain durations and potentially change instruments.
I have literally no idea on how to achieve this and cannot find any resources online.
Please provide code with your solution as it would help greatly.
I am using USB-to-Host, to send the data --> not sure if this means anything.
Not sure this qualifies as a decent stack overflow question. There's lots of resources on the internet about MIDI.
The search 'Python MIDI' brings up this repo python-midi.
If you read the README it seems it can do what you wish.
Manually with Garageband:
Connect the digital piano to the computer(MacOS) and turn both on.
Open the midi file with Garageband, which is usually available on MacOS devices.
Then go to Gargageband->Preferences->'Audio/Midi'->'Output Device' dropdown
If the Digital Piano is recognized by the computer, it should be listed in the dropdown. Select it.
Click the 'play' icon on Garageband. The midi file should play with output comming from the digital piano.
Programatically with Mido:
Connect the digital piano to the computer(MacOS) and turn both on.
Install the mido python package
pip install mido
Get the list of midi ports that mido recognizes:
python -c "import mido; print(mido.get_output_names())"
The output should be something like:
['Digital Keyboard', 'IAC Driver Bus 1']
In this example 'Digital Keyboard' is your keyboard.
Your actual keyboard probably has a different name.
NOTE: The 'IAC Driver Bus 1' is a standard MacOS midi output port.
Update the following play-midi.py script by changing 'Digital Keyboard' to the name of your keyboard which mido displayed in #3. Update the *.mid file to the name of your *.mid file.
import mido
# This will list the available ports
print(mido.get_output_names())
# Open the midi link to your keyboard
outport = mido.open_output('Digital Keyboard')
# Open the mid file to be played
mid = mido.MidiFile('my_midi_file.mid', clip=True)
# Play the file out to your keyboard
for msg in mid.play():
outport.send(msg)
Run the script. The sound should play out of your keyboard.
Mido Reference resources:
https://github.com/mido/mido
https://mido.readthedocs.io/en/latest/midi_files.html
Background
I'm working on a music player, and want any sort of audio visualizer to be part of it. I've already made a previous post specifically targeting python-vlc, but I guess not too many people know about it. Here is the post in question: How to get audio samples from python-VLC
What I need
I need a way to listen to audio output, preferably by application although it is not completely necessary. It must be a python module.
What I found
A bunch of tutorials regarding audio input from the microphone, which is NOT what I need
Two swhardware.com tutorials with audio visualizers
https://swharden.com/blog/2016-07-19-realtime-audio-visualization-in-python/
Another one from 2013 but I can't find the link
PyAudio tutorials also using the microphone
Listening audio output with python
terminatorX on Linux; it uses the microphone
squishyball on Linux; it doesn't support all audio files
livefft; uses pyqt4 so I couldn't even test it
Alternatives
Any python module or linux terminal that can provide a list of audio samples from any audio file. Similar to pydub.AudioSegement.get_array_of_samples()
What I don't need
More suggestions to use pydub's get_array_of_samples()
Pydub doesn't read the audio file correctly and quickly gets out of sync from already playing audio
More tutorials of how to get audio from the microphone
Answers on how to strip audio from a video file with ffmpeg
Is there a way using python (and not any external software) to play a mp3 file like a microphone input?
For example, I have a mp3 file and with a python script it would play it through my mic so other in a voice room would hear it. As I say it is just an example.
Of course, I have done some research. I found out that I can use a software to create a virtual device and do few things to have the result. But my point is if it is possible without installing software but with some kind of python script?
It is possible but it isn't 100% in python as it requires the installation of other software. (Also from what I know this specific answer only works on Windows, but it should be similar on Linux with PulseAudio instead of VB-Audio Cable, but I'm not a daily Linux user so I don't know.)
First download: https://www.vb-audio.com/Cable/, this will create a "Virtual Audio Cable" where programs can play music to the input device (What looks like a speaker) and it'll pipe it to the output device (What looks like a microphone).
Then run this command in cmd: pip install pygame==2.0.0.dev8 (or py -m pip install pygame==2.0.0.dev8, depending on your installation of python) [Also the reason it's the dev version is that it requires some functions only in sdl2, whereas the main branch uses sdl1)
Then:
>>> from pygame._sdl2 import get_num_audio_devices, get_audio_device_name #Get playback device names
>>> from pygame import mixer #Playing sound
>>> mixer.init() #Initialize the mixer, this will allow the next command to work
>>> [get_audio_device_name(x, 0).decode() for x in range(get_num_audio_devices(0))] #Returns playback devices
['Headphones (Oculus Virtual Audio Device)', 'MONITOR (2- NVIDIA High Definition Audio)', 'Speakers (High Definition Audio Device)', 'Speakers (NVIDIA RTX Voice)', 'CABLE Input (VB-Audio Virtual Cable)']
>>> mixer.quit() #Quit the mixer as it's initialized on your main playback device
>>> mixer.init(devicename='CABLE Input (VB-Audio Virtual Cable)') #Initialize it with the correct device
>>> mixer.music.load("Megalovania.mp3") #Load the mp3
>>> mixer.music.play() #Play it
To stop the music do: mixer.music.stop()
Also, the music doesn't play through your speakers, so you're going to have another python script or thread running that handles playing it through your speakers. (Also if you want it to play on a button press I recommend using the python library keyboard, the GitHub documentation is really good and you should be able to figure it out on your own.)
PS: This took me a while to figure out, your welcome.
PPS: I'm still trying to figure out a way to pipe your own mic through there as well since this method will obviously not pipe your real microphone in too, but looking into the source code of pygame is making my head hurt due to it all being written in C.
If you meant how to play MP3 using Python, well, this is a broad question.
Is it possible, without any dependencies, yes it is, but it is not worth it. Well, playing uncompressed audio is, but MP3, well, I'll explain below.
To play raw audio data from Python without installing pyaudio or pygame or similar, you first have to know the platform on which your script will be run.
Then implement a nice set of functions for choosing an audio device, setting up properties like sample rate, bit rate, mono/stereo..., feeding the stream to audio card and stopping the playback.
It is not hard, but to do it you have to use ctypes on Windows, PyObjC on Mac and Linux is special case as it supports many audio systems (probably use sockets to connect to PulseAudio or pipe to some process like aplay/paplay/mpeg123... or exploit gstreamer.).
But why go through all this just to avoid dependencies, when you have nice libraries out there with simple interfaces to access and use audio devices.
PyAudio is great one.
Well, that is your concern.
But, playing MP3 without external libraries, in real time, from pure Python, well, it's not exactly impossible, but it is very hard to achieve, and as far as I know nobody even tried doing it.
There is pure Python MP3 decoder implementation, but it is 10 times slower than necessary for real-time audio playback. It can be optimized for nearly full speed, but nobody is interested in doing so.
It has mostly educational value and it is used in cases where you do not need real-time speed.
This is what you should do:
Install pygame and use it to play MP3 directly
or:
Install PyAudio and some library that decodes Mp3, there are quite a few of them on pypi.python.org, and use it to decode the MP3 and feed the output to PyAudio.
There are some more possibilities, including pymedia, but I consider these the easiest solutions.
Okay, as we clarified what is really you need here is the answer.
I will leave first answer intact as you need that part too.
Now, you want to play audio to the recording stream, so that any application recording the audio input records the stuff that you are playing.
On Windows, this is called stereo mix and can be found in Volume Control, under audio input.
You choose stereo mix as your default input. Now, when you open an recording app which doesn't select itsown input channel, but uses the selected one (e.g. Skype) , it will record all coming out of your speakers and coming into your mic/line in.
I am not 100% sure whether this option will appear on all Windows or it is a feature of an audio card you have.
I am positive that Creative and Realtek audio cards supports it.
So, research this.
To select that option from Python, you have to connect to winmm.dll using ctypes and call the appropriate function. I do not know which one and with what arguments.
If this option is not present in volume control, there is nothing for it but to install a virtual audio card to do the loopback for you.
There might be such a software that comes packaged in as library so that you can use it from Python or whatever.
On Linux this should be easy using Pulseaudio. I do not know how, but I know that you can do it, redirect the streams etc. There is tutorial out there somewhere.
Then you can call that command from Python, to set to this and reset back to normal.
On Mac, well, I really have no idea, but it should be possible.
If you want your MP3 to be played only to the recording stream, and not on your speakers at all, well on Windows, you will not be able to do that without a loopback audio device.
On Linux, I am sure you will be able to do it, and on Mac it should be possible, but how is the Q.
I currently have no time to sniff around libraries etc. to provide you with some useful code, so you will have to do it yourself. But I hope my directions will help you.
Just an update on #PyPylia's answer for the benefit of anyone who struggled to implement this like I did.
Current Package Version: pygame 2.1.2 (SDL 2.0.18, Python 3.10.6)
Tested Systems: Windows 10 (21H2 - 19044.1288), (Should be the same process on Mac but this is untested as of now...)
First, you'll need to download the VB-Cable Virtual Mic Driver for your respective platform and install it. This provides us with a virtual mic that'll allow us to pass audio we play on our machine as a microphone input when using a video calling software (Google Meet, Microsoft Teams, Zoom). After that, it's all handled through the pygame module's audio package.
To get the audio device list:
from pygame import mixer, _sdl2 as devicer
mixer.init() # Initialize the mixer, this will allow the next command to work
# Returns playback devices, Boolean value determines whether they are Input or Output devices.
print("Inputs:", devicer.audio.get_audio_device_names(True))
print("Outputs:", devicer.audio.get_audio_device_names(False))
mixer.quit() # Quit the mixer as it's initialized on your main playback device
For example, My device returns:
Inputs: ['Microphone (High Definition Audio Device)', 'CABLE Output (VB-Audio Virtual Cable)']
Outputs: ['Speakers (High Definition Audio Device)', 'CABLE Input (VB-Audio Virtual Cable)']
Then, to playback the audio:
import time
from pygame import mixer
mixer.init(devicename = 'CABLE Input (VB-Audio Virtual Cable)') # Initialize it with the correct device
mixer.music.load("Toby Fox - Megalovania.mp3") # Load the mp3
mixer.music.play() # Play it
while mixer.music.get_busy(): # wait for music to finish playing
time.sleep(1)
If you wish to play multiple tracks back to back, add the following code segments to the while loop above:
...
else:
mixer.music.unload() # Unload the mp3 to free up system resources
mixer.music.load("Sleeping at Last - Saturn.wav") # Load the wav
...
Then, on the other end, inside the relevant software, just change the microphone input from the default to CABLE Output (VB-Audio Virtual Cable) to have those on the other end hear the audio from the source.
If you're using a newer version of the package and some of the listed methods don't seem to work because of an AttributeError: module 'pygame' has no attribute {method_name}, use pyup and search for the method in question, to see if there have been any changes to how the method is invoked. This was the main reason #PyPylia's code snippet no longer works unless you use an older version of pygame.
If you want to play an audio file in local directory, you may follow this flow.
#!/usr/bin/env python
import pyaudio
import socket
import sys
import os
CHUNK = 4096
output = os.path.join(BASE_DIR, "speech.wav") #WAV format Output file name
wf = wave.open(output, 'rb')
p = pyaudio.PyAudio()
stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
channels=wf.getnchannels(),
rate=wf.getframerate(),
output=True)
try:
while True:
data = wf.readframes(CHUNK)
stream.write(data)
except KeyboardInterrupt:
pass
print('Shutting down')
s.close()
stream.close()
audio.terminate()
I am trying to make my own music player with Python, and after looking at alternatives, I've settled on using pygame's mixer.music to actually play the audio. (I've used pygame before, just usually for actual games) I was looking at playsound instead until I realized I needed a way to play the next song once one is done, as well as the ability to play and pause the audio. I also need to play mp3 files instead of the wavs that most alternatives require.
I actually got it working perfectly originally, until I tried adding other unrelated features, and now it's saying:
File "main.py", line 66, in playCurrentSong
mixer.load(path.join(museDir, currentSong))
pygame.error: Error reading the stream. (code 18)
(museDir is my variable for the directory that music files are in, and mixer is my variable for pygame.mixer.music as a shorthand)
I cannot figure out for the life of me why it's giving me this error now, as it played the audio perfectly fine before. My code is here: https://pastebin.com/V7nAfmK6
If a solution only works on a certain operating system, my final OS will be Linux, on a Rasperry Pi, but I'm trying to write and test the code on Windows. However, if that's not possible, I understand.
Thank you beforehand for any and all help; this is giving me a headache.
I just found the source of the problem.
Before the error popped up, I had been trying to mess around with the metadata of the mp3 files in order to incorporate a genre system into the player, but nothing was working. I eventually decided to use csv files for that instead.
I must have done something wrong however when I was messing around with that metadata because I looked at the mp3s in File Explorer, and they were all 0 bytes. That's why pygame couldn't read the stream: there wasn't one! I plugged the pygame stuff back in, replaced the mp3s with new ones, and it works just fine now.
Thanks for the help anyway though, Torxed!
Is there a way using python (and not any external software) to play a mp3 file like a microphone input?
For example, I have a mp3 file and with a python script it would play it through my mic so other in a voice room would hear it. As I say it is just an example.
Of course, I have done some research. I found out that I can use a software to create a virtual device and do few things to have the result. But my point is if it is possible without installing software but with some kind of python script?
It is possible but it isn't 100% in python as it requires the installation of other software. (Also from what I know this specific answer only works on Windows, but it should be similar on Linux with PulseAudio instead of VB-Audio Cable, but I'm not a daily Linux user so I don't know.)
First download: https://www.vb-audio.com/Cable/, this will create a "Virtual Audio Cable" where programs can play music to the input device (What looks like a speaker) and it'll pipe it to the output device (What looks like a microphone).
Then run this command in cmd: pip install pygame==2.0.0.dev8 (or py -m pip install pygame==2.0.0.dev8, depending on your installation of python) [Also the reason it's the dev version is that it requires some functions only in sdl2, whereas the main branch uses sdl1)
Then:
>>> from pygame._sdl2 import get_num_audio_devices, get_audio_device_name #Get playback device names
>>> from pygame import mixer #Playing sound
>>> mixer.init() #Initialize the mixer, this will allow the next command to work
>>> [get_audio_device_name(x, 0).decode() for x in range(get_num_audio_devices(0))] #Returns playback devices
['Headphones (Oculus Virtual Audio Device)', 'MONITOR (2- NVIDIA High Definition Audio)', 'Speakers (High Definition Audio Device)', 'Speakers (NVIDIA RTX Voice)', 'CABLE Input (VB-Audio Virtual Cable)']
>>> mixer.quit() #Quit the mixer as it's initialized on your main playback device
>>> mixer.init(devicename='CABLE Input (VB-Audio Virtual Cable)') #Initialize it with the correct device
>>> mixer.music.load("Megalovania.mp3") #Load the mp3
>>> mixer.music.play() #Play it
To stop the music do: mixer.music.stop()
Also, the music doesn't play through your speakers, so you're going to have another python script or thread running that handles playing it through your speakers. (Also if you want it to play on a button press I recommend using the python library keyboard, the GitHub documentation is really good and you should be able to figure it out on your own.)
PS: This took me a while to figure out, your welcome.
PPS: I'm still trying to figure out a way to pipe your own mic through there as well since this method will obviously not pipe your real microphone in too, but looking into the source code of pygame is making my head hurt due to it all being written in C.
If you meant how to play MP3 using Python, well, this is a broad question.
Is it possible, without any dependencies, yes it is, but it is not worth it. Well, playing uncompressed audio is, but MP3, well, I'll explain below.
To play raw audio data from Python without installing pyaudio or pygame or similar, you first have to know the platform on which your script will be run.
Then implement a nice set of functions for choosing an audio device, setting up properties like sample rate, bit rate, mono/stereo..., feeding the stream to audio card and stopping the playback.
It is not hard, but to do it you have to use ctypes on Windows, PyObjC on Mac and Linux is special case as it supports many audio systems (probably use sockets to connect to PulseAudio or pipe to some process like aplay/paplay/mpeg123... or exploit gstreamer.).
But why go through all this just to avoid dependencies, when you have nice libraries out there with simple interfaces to access and use audio devices.
PyAudio is great one.
Well, that is your concern.
But, playing MP3 without external libraries, in real time, from pure Python, well, it's not exactly impossible, but it is very hard to achieve, and as far as I know nobody even tried doing it.
There is pure Python MP3 decoder implementation, but it is 10 times slower than necessary for real-time audio playback. It can be optimized for nearly full speed, but nobody is interested in doing so.
It has mostly educational value and it is used in cases where you do not need real-time speed.
This is what you should do:
Install pygame and use it to play MP3 directly
or:
Install PyAudio and some library that decodes Mp3, there are quite a few of them on pypi.python.org, and use it to decode the MP3 and feed the output to PyAudio.
There are some more possibilities, including pymedia, but I consider these the easiest solutions.
Okay, as we clarified what is really you need here is the answer.
I will leave first answer intact as you need that part too.
Now, you want to play audio to the recording stream, so that any application recording the audio input records the stuff that you are playing.
On Windows, this is called stereo mix and can be found in Volume Control, under audio input.
You choose stereo mix as your default input. Now, when you open an recording app which doesn't select itsown input channel, but uses the selected one (e.g. Skype) , it will record all coming out of your speakers and coming into your mic/line in.
I am not 100% sure whether this option will appear on all Windows or it is a feature of an audio card you have.
I am positive that Creative and Realtek audio cards supports it.
So, research this.
To select that option from Python, you have to connect to winmm.dll using ctypes and call the appropriate function. I do not know which one and with what arguments.
If this option is not present in volume control, there is nothing for it but to install a virtual audio card to do the loopback for you.
There might be such a software that comes packaged in as library so that you can use it from Python or whatever.
On Linux this should be easy using Pulseaudio. I do not know how, but I know that you can do it, redirect the streams etc. There is tutorial out there somewhere.
Then you can call that command from Python, to set to this and reset back to normal.
On Mac, well, I really have no idea, but it should be possible.
If you want your MP3 to be played only to the recording stream, and not on your speakers at all, well on Windows, you will not be able to do that without a loopback audio device.
On Linux, I am sure you will be able to do it, and on Mac it should be possible, but how is the Q.
I currently have no time to sniff around libraries etc. to provide you with some useful code, so you will have to do it yourself. But I hope my directions will help you.
Just an update on #PyPylia's answer for the benefit of anyone who struggled to implement this like I did.
Current Package Version: pygame 2.1.2 (SDL 2.0.18, Python 3.10.6)
Tested Systems: Windows 10 (21H2 - 19044.1288), (Should be the same process on Mac but this is untested as of now...)
First, you'll need to download the VB-Cable Virtual Mic Driver for your respective platform and install it. This provides us with a virtual mic that'll allow us to pass audio we play on our machine as a microphone input when using a video calling software (Google Meet, Microsoft Teams, Zoom). After that, it's all handled through the pygame module's audio package.
To get the audio device list:
from pygame import mixer, _sdl2 as devicer
mixer.init() # Initialize the mixer, this will allow the next command to work
# Returns playback devices, Boolean value determines whether they are Input or Output devices.
print("Inputs:", devicer.audio.get_audio_device_names(True))
print("Outputs:", devicer.audio.get_audio_device_names(False))
mixer.quit() # Quit the mixer as it's initialized on your main playback device
For example, My device returns:
Inputs: ['Microphone (High Definition Audio Device)', 'CABLE Output (VB-Audio Virtual Cable)']
Outputs: ['Speakers (High Definition Audio Device)', 'CABLE Input (VB-Audio Virtual Cable)']
Then, to playback the audio:
import time
from pygame import mixer
mixer.init(devicename = 'CABLE Input (VB-Audio Virtual Cable)') # Initialize it with the correct device
mixer.music.load("Toby Fox - Megalovania.mp3") # Load the mp3
mixer.music.play() # Play it
while mixer.music.get_busy(): # wait for music to finish playing
time.sleep(1)
If you wish to play multiple tracks back to back, add the following code segments to the while loop above:
...
else:
mixer.music.unload() # Unload the mp3 to free up system resources
mixer.music.load("Sleeping at Last - Saturn.wav") # Load the wav
...
Then, on the other end, inside the relevant software, just change the microphone input from the default to CABLE Output (VB-Audio Virtual Cable) to have those on the other end hear the audio from the source.
If you're using a newer version of the package and some of the listed methods don't seem to work because of an AttributeError: module 'pygame' has no attribute {method_name}, use pyup and search for the method in question, to see if there have been any changes to how the method is invoked. This was the main reason #PyPylia's code snippet no longer works unless you use an older version of pygame.
If you want to play an audio file in local directory, you may follow this flow.
#!/usr/bin/env python
import pyaudio
import socket
import sys
import os
CHUNK = 4096
output = os.path.join(BASE_DIR, "speech.wav") #WAV format Output file name
wf = wave.open(output, 'rb')
p = pyaudio.PyAudio()
stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
channels=wf.getnchannels(),
rate=wf.getframerate(),
output=True)
try:
while True:
data = wf.readframes(CHUNK)
stream.write(data)
except KeyboardInterrupt:
pass
print('Shutting down')
s.close()
stream.close()
audio.terminate()