Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added StarWars60.wav
Binary file not shown.
Binary file added StarWars60.wav.mid
Binary file not shown.
Binary file added __pycache__/averageAmplitude.cpython-38.pyc
Binary file not shown.
13 changes: 13 additions & 0 deletions averageAmplitude.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import numpy as np
from scipy.signal import argrelextrema

def averageAmplitude(numPyArray):
# for local maxima
listOfMaxs = numPyArray[argrelextrema(numPyArray, np.greater)[0]]
print(listOfMaxs)
average = sum(listOfMaxs) / len(listOfMaxs)
print (average)
return average



38 changes: 38 additions & 0 deletions frequency.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import numpy as np
import scipy.io.wavfile
import sys
from aubio import source, pitch

win_s = 4096
hop_s = 512

out = scipy.io.wavfile.read("StarWars60.wav", mmap=False)
a, b = out

your_file = "StarWars60.wav"
samplerate = a


s = source(your_file, samplerate, hop_s)
samplerate = s.samplerate

tolerance = 0.8

pitch_o = pitch("yin", win_s, hop_s, samplerate)
pitch_o.set_unit("midi")
pitch_o.set_tolerance(tolerance)

pitches = []
confidences = []

total_frames = 0
while True:
samples, read = s()
pitch = pitch_o(samples)[0]
pitches += [pitch]
confidence = pitch_o.get_confidence()
confidences += [confidence]
total_frames += read
if read < hop_s: break

print("Average frequency = " + str(np.array(pitches).mean()) + " hz")
7 changes: 7 additions & 0 deletions keyFinder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import music21
import wav2midi2wav.audio_to_midi_melodia as a2m

a2m.audio_to_midi_melodia('StarWars60.wav', 'StarWars60.mid', 120, smooth=0.25, minduration=0.1, savejams=False)
score = music21.converter.parse('StarWars60.mid')
key = score.analyze('key')
print(key.tonic.name, key.mode)
20 changes: 20 additions & 0 deletions makeWaves.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import averageAmplitude
import frequency
import math

def makeWaves(pixels, squareDimension, amplitude, frequency):
dividedRowNum = squareDimension / 3
for j in (0, squareDimension):
i = int(amplitude / 10 * math.sin(frequency * j) + dividedRowNum)
transparencyValue = pixels[i,j][3]
if (transparencyValue in range(129,255)):
transparencyValue = 129
elif (transparencyValue in range(0,128)):
transparencyValue = 128

i = int(amplitude / 10 * math.sin(frequency * j) + dividedRowNum * 2)
transparencyValue = pixels[i,j][3]
if (transparencyValue in range(129,255)):
transparencyValue = 129
elif (transparencyValue in range(0,128)):
transparencyValue = 128
Binary file added reverse2.wav
Binary file not shown.
59 changes: 59 additions & 0 deletions wav2midi2wav/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
### Wav to MIDI to Wav
Extracts the melody notes from an audio file and exports them to MIDI and JAMS files.

The script extracts the melody from an audio file using the [Melodia](http://mtg.upf.edu/technologies/melodia) algorithm, and then segments the continuous pitch sequence into a series of quantized notes, and exports to MIDI using the provided BPM.

### Usage
```bash
python main.py [--folder FOLDER_NAME] [--bpm BPM] [--smooth SMOOTH] [--minduration MINDURATION] [--jams]
```
For example:
```bash
python main.py --folder assets/COGNIMUSE/ --bpm 146 --smooth 0.25 --minduration 0.1 --jams
```

### Notes
* *wav* to *mp3*:
```
ffmpeg -i assets/test/10.wav assets/test/10.mp3
```
* Find out *bpm* (146 in my case):
```
bpm-tag assets/test/10.mp3
```
* *wav* to *mid*:
```
python audio_to_midi_melodia.py assets/test/10.wav assets/test/10.mid 146 --smooth 0.25 --minduration 0.1 --jams
```
* *mid* to *wav* [converter](https://www.zamzar.com/convert/midi-to-wav/)
```bash
timidity 10.mid -Ow -o 10_recovered.wav
```

### Dependencies
* Install dependencies:
```bash
pip install vamp jams numpy scipy
```
* Librosa
```bash
git clone https://github.com/librosa/librosa
cd dependencies/librosa
python setup.py build
python setup.py install
```
* [MidiUtil](https://code.google.com/p/midiutil/)
```bash
cd dependencies/MIDIUtil-0.89
python setup.py install
```
* Download [Melodia plugin](http://mtg.upf.edu/technologies/melodia) and copy all files to */usr/local/lib/vamp*
* Audio tools
```bash
pip install pydub
apt-get install ffmpeg bpm-tools
apt-get install timidity timidity-interfaces-extra
```

### Credit
[audio_to_midi_melodia](https://github.com/justinsalamon/audio_to_midi_melodia)
2 changes: 2 additions & 0 deletions wav2midi2wav/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# CREATED: 11/11/15 11:22 AM by Justin Salamon <justin.salamon@nyu.edu>
__version__ = '0.0.0'
Binary file added wav2midi2wav/__pycache__/__init__.cpython-38.pyc
Binary file not shown.
Binary file not shown.
Binary file added wav2midi2wav/assets/test/10.mid
Binary file not shown.
Binary file added wav2midi2wav/assets/test/10.wav
Binary file not shown.
Binary file added wav2midi2wav/assets/test/10_recovered.wav
Binary file not shown.
Binary file added wav2midi2wav/assets/test/14.mid
Binary file not shown.
Binary file added wav2midi2wav/assets/test/14.wav
Binary file not shown.
Binary file added wav2midi2wav/assets/test/14_recovered.wav
Binary file not shown.
Binary file added wav2midi2wav/assets/test/23.mid
Binary file not shown.
Binary file added wav2midi2wav/assets/test/23.wav
Binary file not shown.
Binary file added wav2midi2wav/assets/test/23_recovered.wav
Binary file not shown.
217 changes: 217 additions & 0 deletions wav2midi2wav/audio_to_midi_melodia.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
# CREATED: 11/9/15 3:57 PM by Justin Salamon <justin.salamon@nyu.edu>

import librosa
import vamp
import argparse
import os
import numpy as np
from midiutil.MidiFile import MIDIFile
from scipy.signal import medfilt
import jams
import wav2midi2wav

'''
Extract the melody from an audio file and convert it to MIDI.

The script extracts the melody from an audio file using the Melodia algorithm,
and then segments the continuous pitch sequence into a series of quantized
notes, and exports to MIDI using the provided BPM. If the --jams option is
specified the script will also save the output as a JAMS file. Note that the
JAMS file uses the original note onset/offset times estimated by the algorithm
and ignores the provided BPM value.

Note: Melodia can work pretty well and is the result of several years of
research. The note segmentation/quantization code was hacked in about 30
minutes. Proceed at your own risk... :)

usage: audio_to_midi_melodia.py [-h] [--smooth SMOOTH]
[--minduration MINDURATION] [--jams]
infile outfile bpm


Examples:
python audio_to_midi_melodia.py --smooth 0.25 --minduration 0.1 --jams
~/song.wav ~/song.mid 60
'''


def save_jams(jamsfile, notes, track_duration, orig_filename):

# Construct a new JAMS object and annotation records
jam = jams.JAMS()

# Store the track duration
jam.file_metadata.duration = track_duration
jam.file_metadata.title = orig_filename

midi_an = jams.Annotation(namespace='pitch_midi',
duration=track_duration)
midi_an.annotation_metadata = \
jams.AnnotationMetadata(
data_source='audio_to_midi_melodia.py v%s' % __init__.__version__,
annotation_tools='audio_to_midi_melodia.py (https://github.com/'
'justinsalamon/audio_to_midi_melodia)')

# Add midi notes to the annotation record.
for n in notes:
midi_an.append(time=n[0], duration=n[1], value=n[2], confidence=0)

# Store the new annotation in the jam
jam.annotations.append(midi_an)

# Save to disk
jam.save(jamsfile)


def save_midi(outfile, notes, tempo):

track = 0
time = 0
midifile = MIDIFile(1)

# Add track name and tempo.
midifile.addTrackName(track, time, "MIDI TRACK")
midifile.addTempo(track, time, tempo)

channel = 0
volume = 100

for note in notes:
onset = note[0] * (tempo/60.)
duration = note[1] * (tempo/60.)
# duration = 1
pitch = note[2]
midifile.addNote(track, channel, pitch, onset, duration, volume)

# And write it to disk.
binfile = open(outfile, 'wb')
midifile.writeFile(binfile)
binfile.close()


def midi_to_notes(midi, fs, hop, smooth, minduration):

# smooth midi pitch sequence first
if (smooth > 0):
filter_duration = smooth # in seconds
filter_size = int(filter_duration * fs / float(hop))
if filter_size % 2 == 0:
filter_size += 1
midi_filt = medfilt(midi, filter_size)
else:
midi_filt = midi
# print(len(midi),len(midi_filt))

notes = []
p_prev = None
duration = 0
onset = 0
for n, p in enumerate(midi_filt):
if p == p_prev:
duration += 1
else:
# treat 0 as silence
if p_prev > 0:
# add note
duration_sec = duration * hop / float(fs)
# only add notes that are long enough
if duration_sec >= minduration:
onset_sec = onset * hop / float(fs)
notes.append((onset_sec, duration_sec, p_prev))

# start new note
onset = n
duration = 1
p_prev = p

# add last note
if p_prev > 0:
# add note
duration_sec = duration * hop / float(fs)
onset_sec = onset * hop / float(fs)
notes.append((onset_sec, duration_sec, p_prev))

return notes


def hz2midi(hz):

# convert from Hz to midi note
hz_nonneg = hz.copy()
idx = hz_nonneg <= 0
hz_nonneg[idx] = 1
midi = 69 + 12*np.log2(hz_nonneg/440.)
midi[idx] = 0

# round
midi = np.round(midi)

return midi


def audio_to_midi_melodia(infile, outfile, bpm, smooth=0.25, minduration=0.1,
savejams=False):

# define analysis parameters
fs = 44100
hop = 128

# load audio using librosa
print("Loading audio...")
data, sr = librosa.load(infile, sr=fs, mono=True)

# extract melody using melodia vamp plugin
print("Extracting melody f0 with MELODIA...")
melody = vamp.collect(data, sr, "mtg-melodia:melodia",
parameters={"voicing": 0.2})

# hop = melody['vector'][0]
pitch = melody['vector'][1]

# impute missing 0's to compensate for starting timestamp
pitch = np.insert(pitch, 0, [0]*8)

# debug
# np.asarray(pitch).dump('f0.npy')
# print(len(pitch))

# convert f0 to midi notes
print("Converting Hz to MIDI notes...")
midi_pitch = hz2midi(pitch)

# segment sequence into individual midi notes
notes = midi_to_notes(midi_pitch, fs, hop, smooth, minduration)

# save note sequence to a midi file
print("Saving MIDI to disk...")
save_midi(outfile, notes, bpm)

if savejams:
print("Saving JAMS to disk...")
jamsfile = outfile.replace(".mid", ".jams")
track_duration = len(data) / float(fs)
save_jams(jamsfile, notes, track_duration, os.path.basename(infile))

print("Conversion complete.")


if __name__ == "__main__":

parser = argparse.ArgumentParser()
parser.add_argument("infile", default="assets/10.wav", help="Path to input audio file.")
parser.add_argument("outfile", default="assets/10.mid", help="Path for saving output MIDI file.")
parser.add_argument("bpm", type=int, default=146, help="Tempo of the track in BPM.")
parser.add_argument("--smooth", type=float, default=0.25,
help="Smooth the pitch sequence with a median filter "
"of the provided duration (in seconds).")
parser.add_argument("--minduration", type=float, default=0.1,
help="Minimum allowed duration for note (in seconds). "
"Shorter notes will be removed.")
parser.add_argument("--jams", action="store_const", const=True,
default=False, help="Also save output in JAMS format.")

args = parser.parse_args()

audio_to_midi_melodia(args.infile, args.outfile, args.bpm,
smooth=args.smooth, minduration=args.minduration,
savejams=args.jams)
Loading