Midi - how to get all notes for specific second(s) - python

I am in the middle of a project and sadly don't know much about MIDI files. I am using python library for MIDI files.
The main question is how to get all notes (messages) for a specific time && track ( for example 1:20 to 1:21)
The other issue is: in some MIDI files, we have different tempo speeds. I can solve this issue by counting the time, and velocity, but I don't know how to convert these numbers to Second and apply different tempos.

Instead of that library (which seems to be currently unmaintained), you may use another one providing times in seconds out of the box for each event, for instance using pretty-midi - docs (python 3):
import pretty_midi
midi_data = pretty_midi.PrettyMIDI('example.mid')
print("duration:",midi_data.get_end_time())
print(f'{"note":>10} {"start":>10} {"end":>10}')
for instrument in midi_data.instruments:
print("instrument:", instrument.program);
for note in instrument.notes:
print(f'{note.pitch:10} {note.start:10} {note.end:10}')
You can filter the note events by note.start and note.end times already computed in seconds.

Related

How to get audio samples from python-VLC

What I need
I am working on a music player with a sample analyzer. My issue is that I am currently using pydub to get the samples from the song, but as the song gets longer, the more out of sync the two become. I have made the two literally in sync, pydub just so happens to read out of it [I even started them at the same time, the two actually progressively de-synced with audio alone and more so as the song continues playing].
Basically, is there a python-vlc equivalent to pydub.AudioSegment.get_array_of_samples()?
You can look at the python-vlc docs here: https://www.olivieraubert.net/vlc/python-ctypes/doc/
What I've tried
python-vlc has sound.get_time() which returns the last updated time in milliseconds. The issue with this command is not that it only updates every half a second or so [I've found a way around that] but that it doesn't return the accurate time. I can start a timer from when it starts playing using time.monotonic(). As time progresses, the get_time() wildly varies from the timer. I've gotten a difference of 195ms, 294ms and 217ms.
I tried using an external timer using the threading module. It appears that pydub.AudioSegment[index] is not in sync whatsoever.
Scaling from sound.get_time() in py-vlc to len(sound) in pydub. This doesn't work as expected. I cannot tell you why but it is still desynced.
Using sound.get_time() with an average offset that increases over time. It appears that pydub.AudioSegment[index] doesn't line up properly.
Using aubio. It only reads WAV files, and for a live analyzer, converting to a WAV first would take too long.
Things I found out
I've looked at how long each song is in milliseconds, len(sound) for pydub and sound.get_length() for python-vlc is different usually by 10 or so seconds.
Things that won't work
Using pydub's play() command. I don't like it because it's very limiting.
Using something else than py-vlc to play the audio, VLC has many features that just cannot be replicated.
Previous suggestions
Use How can I extract audio from video with ffmpeg?
This won't work because I am not looking to extract audio from an existing video file, I'm trying to get samples like pydub.AudioSegment.get_array_of_samples()
I'm curious
Is there a python module for Audacity? I've been looking and couldn't find it. I can also use some other command line tool that can interact with Audacity if possible [I know the built in command line utility doesn't do much at all]
You probably want to use ffmpeg directly for this.

Bad timing when playing audio files with PyGame

When I play a sound every 0.5 second with PyGame:
import pygame, time
pygame.mixer.init()
s = pygame.mixer.Sound("2.wav")
for i in range(8):
pygame.mixer.Channel(i).play(s)
time.sleep(0.5)
it doesn't respect the timing correctly at all.
It's like there are pause of 0.2 sec than 0.7 sec then 0.2 sec again, it's very irregular.
Notes:
I know that time.sleep() is not the most accurate in the world, but even with the more accurate solutions from here, the problem is still present
Tested on a RaspberryPi
The problem is still there if I play many different files s[i].play(), with i in a big range. So the problem doesn't come from the fact it tries to replay the same file
Here is the reason:
Even if we decrease the audio buffer to the minimum supported by the soundcard (1024 or 512 samples instead of pygame's default 4096), the differences will still be there, making irregulat what should be a "metronome beat".
I'll update with a working solution as soon as I find one. (I have a few ideas in this direction).
As you wrote in your own answer, the reason for the timing problems very likely is the fact that the audio callback runs decoupled from the rest of the application.
The audio backend typically has some kind of a clock which is accessible from both inside the callback function and outside of it.
I see two possible solutions:
use a library that allows you to implement the callback function yourself, calculate the starting times of your sounds inside the callback function, compare those times with the current time of the "audio clock" and write your sound to the output at the appropriate position in the output buffer.
use a library that allows you to specify the exact time (in terms of the "audio clock") when to start playing your sounds. This library would do the steps of the previous point for you.
For the first option, you could use the sounddevice module. The callback function (which you'll have to implement) will get an argument named time, which has an attribute time.outputBufferDacTime, which is a floating point value specifying the time (in seconds) when the first sample of the output buffer will be played back.
Full disclosure: I'm the author of the sounddevice module, so my recommendation is quite biased.
Quite recently, I've started working on the rtmixer module, which can be used for the second option.
Please note that this is in very early development state, so use it with caution.
With this module, you don't have to write a callback function, you can use the function rtmixer.Mixer.play_buffer() to play an audio buffer at a specified time (in seconds). For reference, you can get the current time from rtmixer.Mixer.time.

Is it possible to make writing to files/reading from files safe for a questionnaire type website?

My web app asks users 3 questions and simple writes that to a file, a1,a2,a3. I also have real time visualization of the average of the data (reads real time from file).
Must I use a database to ensure that no/minimal information is lost? Is it possible to produce a queue of read/writes>(Since files are small I am not too worried about the execution time of each call). Does python/flask already take care of this?
I am quite experienced in python itself, but not in this area(with flask).
I see a few solutions:
read /dev/urandom a few times, calculate sha-256 of the number and use it as a file name; collision is extremely improbable
use Redis and command like LPUSH, using it from Python is very easy; then RPOP from right end of the linked list, there's your queue

Python Intercepting/reading audio output level in python/linux

I'm trying to write something that catches the audio being played to the speakers/headphones/soundcard and see whether it is playing and what the longest silence is. This is to test an application that is being executed and to see if it stops playing audio after a certain point, as such i don't actually need to really know what the audio itself is, just whether or not there is audio playing.
I need this to be fully programmatic (so not requiring the use of GUI tools or the like, to set up an environment). I know applications like projectM do this, I just can't for the life of me find anything anywhere that denotes how.
An audio level meter would also work for this, as would ossiliscope data or the like, really would take any recommendation.
Here is a very similar question: record output sound in python
You could try to route your output to a new device with jack and record this with portaudio. There are Python Bindings for portaudio called pyaudio and for jack called PyJack. I have never used the latter one but pyaudio works great.

How to use `wx.ProgressDialog` with my own method?

How can I use the wx.ProgressDialog to time my method called imgSearch? The imgSearch method finds image files on the user's pc. How can I make the wx.ProgressDialog run while imgSearch is still running and display how long the imgSearch is taking?
Here's my code:
def onFind (self,event)# triggered by a button click
max = 80
dlg = wx.ProgressDialog("Progress dialog example","An informative message",parent=self, style = wx.PD_CAN_ABORT| wx.PD_APP_MODAL| wx.PD_ELAPSED_TIME| wx.PD_REMAINING_TIME)
keepGoing = True
count = 0
imageExtentions = ['*.jpg', '*.jpeg', '*.png', '*.tif', '*.tiff']
selectedDir = 'C:\\'
imgSearch.findImages(imageExtentions, selectedDir)# my method
while keepGoing and count < max:
count += 1
wx.MilliSleep(250)
if count >= max / 2:
(keepGoing, skip) = dlg.Update(count, "Half-time!")
else:
(keepGoing, skip) = dlg.Update(count)
dlg.Destroy()
The Problem
Your question is vague but after looking at your code I am going to guess that your problem is that imgSearch.findImages() completes before the dialog ever even opens. I'd appreciate if you could edit your question so I know if this is correct.
I see you are using code from this tutorial to show you how to use wx.ProgressDialog. Unfortunately, you are taking a rather naive approach to this. The purpose of that tutorial is to introduce readers to a wide range of built-in dialogs. As such, the wx.ProgressDialog example is a simple example simply to show you the methods, not to be used as an implementation.
Background Information
First, you need to know what multithreading is and how it works. Everything a computer does is broken down into a series of mathematical operations. The computer processor can only do one operation at a time. Therefore, the computer can only do one thing at a time. Now you might be thinking "well that isn't true. I'm listening to an MP3 while browsing the internet. That's at least two things". Well you're right and you're wrong. Imagine a chef. A chef can prepare many dishes at once but he can only pay attention to them one at a time. Therefore, he must switch between the dishes, performing some small task, until they are all complete. That's how a computer multitasks. Instead of "dishes", computers switch between "threads".
The Solution
Step 1: Threading
By default wxPython is only a single thread (this isn't exactly true, but just go with it). wxPython literally runs your code line-by-line such that one line must finish completely before the next line is run. Therefore, while imgSearch.findImages() is running no other code is executing and it must complete before the wx.ProgressDialog can even be created. The good news is that there are several ways to fix this. The bad news is that fixing this can become quite complicated. I'm not an expert but I suggest doing a search for "wxpython threading" and trying to find some tutorials. I found this tutorial but it is fairly old. The main point is that you will need to run imgSearch.findImages() in it's own thread so that it does not block the GUI.
Step 2: Metrics
So now, how do you make the wx.ProgressDialog reflect the actual status of imgSearch.findImages()? Well, that depends. You need to find some metric to help you measure how much has been done and how much more is left to do. In your code the metric is time because the progress is programmed to take 80 * 250ms = 20s. Time probably isn't what you want. You will need to decide what is best for your program but I will use the example of "number of folders" because it's easy to understand. I will count the total number of folders as well as the number of folders I have completely scanned. Now there's two ways to initialize your metric. You can calculate the max you need if the number is small or easy to calculate (such as time) or you can take a dynamic approach. In my case, calculating "number of folders" exactly would require almost as much time as the image search itself so dynamic is the best option. So, I would modify imgSearch.findImages() so that it keeps a count of the number of folders it has seen as well as the number of folders it has scanned (e.g. if my starting location has 3 folders I start with 3/0. I begin scanning the first folder which itself contains 2 more folders, so I now have 5/0. Every time I completely scan a folder I add 1 to the number of scanned folders, i.e. 5/1 after scanning one folder.). Knowing how many folders I have scanned and how many folders I have left to scan I can estimate my percentage completion using # of folders scanned / total # of folders. 1 / 5 = 0.20, therefore I estimate that I am 20% complete (note that if the next folder I scan contains 5 more folders then this number is automatically decreased to 10%. That is why I called 20% an estimate)
Step 3: Updating
So by now we should have imgSearch.findImages() in it's own thread and it should be able to estimate how far from completion it is. Now we need to update the wx.ProgresDialog. My favourite method for these types of problems is using the pubsub library. Basically, the imgSearch.findImages() thread must occasionally send a message which says how close to completion it is. Maybe at the end of each folder. Maybe ever three folders. It's up to you. Next, you will need to create a method in your main GUI's thread which can read these messages and update wx.ProgressDialog accordingly.
It's a lot of work but I wish you the best of luck.

Categories

Resources