import os, random, re from fractions import Fraction from midiutil.MidiFile import MIDIFile import streamlit as st import mido, openai if 'path' not in st.session_state: st.session_state['path'] = os.path.realpath(os.path.dirname(__file__)) if 'sessionID' not in st.session_state: st.session_state['sessionID'] = random.randint(0,99999999) if 'history' not in st.session_state: st.session_state['history'] = [] if 'downloadable' not in st.session_state: st.session_state['downloadable'] = False notes = [['C'], ['Db', 'C#'], ['D'], ['Eb', 'D#'], ['E'], ['F'], ['Gb', 'F#'], ['G'], ['Ab', 'G#'], ['A'], ['Bb', 'A#'], ['B']] monsters = [r'(?=6 else noteDur if nIndex: midOut.append('-'.join([notes[msg.note%12][0]+str(msg.note//12-1), noteDur, str(round(noteTime,3))])) else: midOut.append('-'.join([notes[msg.note%12][0]+str(msg.note//12-1), noteDur])) del opens[msg.note] if msg.type == 'note_on': opens[msg.note] = globalT return ', '.join(midOut) st.markdown('# GPT-4 2 Midi\n#### AI Generated Polyphonic Music\n##### plus conversion tools for use with Chat-GPT\napp by [d3nt](https://github.com/d3n7/)') notation = st.selectbox('Notation', ('Polyphonic', 'Monophonic')) main, m2t, t2m = st.tabs(['GPT4-To-Midi', 'Midi-2-Text', 'Text-2-Midi']) with main: userPrompt = st.text_input('Prompt', 'Full piece of sad music with multiple parts. Plan out the structure beforehand, including chords, parts (soprano, alto, tenor, bass), meter, etc.') with st.expander('System Prompt'): sysPrompt = st.text_input('', 'You are MusicGPT, a music creation and completion chat bot that. When a user gives you a prompt, you return them a song showing the notes, durations, and times that they occur. Respond with just the music.') openaikey = st.text_input('OpenAI API Key', type='password') modelV = st.selectbox('Model', ('GPT-4', 'GPT-3.5-Turbo')) col1, col2 = st.columns(2) with col1: newSession = st.checkbox('New Session', True) with col2: showOutput = st.checkbox('Show Output', True) uploadMidi = st.file_uploader('Upload a midi file (OPTIONAL)') col3, col4 = st.columns(2) with col3: if st.button('Ask GPT'): if userPrompt != '' and sysPrompt != '' and openaikey != '': notationIndex = int(notation=='Polyphonic') if newSession: st.session_state['history'] = [{'role': 'system', 'content': sysPrompt+examples[notationIndex]}] prompt = userPrompt if uploadMidi: filename = ''.join(uploadMidi.name.split('.')[:-1])+str(st.session_state['sessionID'])+'.'+''.join(uploadMidi.name.split('.')[-1]) midiPath = os.path.join(st.session_state['path'], filename) with open(midiPath, 'wb') as f: f.write(uploadMidi.getbuffer()) prompt += '\n'+midiToStr(midiPath, notationIndex) os.remove(midiPath) st.session_state['history'].append({'role': 'user', 'content': prompt}) openai.api_key = openaikey with st.spinner('Talking to OpenAI...'): r = openai.ChatCompletion.create( model=modelV.lower(), messages=st.session_state['history'] ) response = r['choices'][0]['message']['content'] st.session_state['history'].append({'role': 'assistant', 'content': response}) noteInfo = [] for i in re.findall(monsters[notationIndex], response): n = i.split('-') if notationIndex: noteInfo.append([noteToInt(n[0]), float(Fraction(n[1]))*4, float(n[2])]) #note, duration, time else: noteInfo.append([noteToInt(n[0]), float(Fraction(n[1]))*4]) # note, duration song = MIDIFile(1, deinterleave=False) time = 0 for i in noteInfo: if notationIndex: pitch, dur, time = i else: pitch, dur = i song.addNote(0, 0, pitch, time, dur, 100) if not notationIndex: time += dur with open(os.path.join(st.session_state['path'], 'out.mid'), 'wb') as f: song.writeFile(f) if not st.session_state['downloadable']: st.session_state['downloadable'] = True else: st.warning('Make sure OpenAI key, prompt, and system prompt are entered', icon='⚠️') with col4: if st.session_state['downloadable']: with open(os.path.join(st.session_state['path'], 'out.mid'), 'rb') as f: st.download_button('Download Midi', f, file_name='song.mid', key='main') if showOutput: with st.container(): for i in st.session_state['history']: st.text(i['role']+': '+i['content']+'\n') with m2t: inMidi = st.file_uploader('Input') if st.button('Convert', key='1'): if inMidi: filename = ''.join(inMidi.name.split('.')[:-1]) + str(st.session_state['sessionID']) + '.' + ''.join(inMidi.name.split('.')[-1]) midiPath = os.path.join(st.session_state['path'], filename) with open(midiPath, 'wb') as f: f.write(inMidi.getbuffer()) st.text_area('Output', midiToStr(midiPath, notation=='Polyphonic')) os.remove(midiPath) with t2m: inText = st.text_input('Input') if st.button('Convert', key='2'): notationIndex = int(notation=='Polyphonic') noteInfo = [] for i in re.findall(monsters[notationIndex], inText): n = i.split('-') if notationIndex: noteInfo.append([noteToInt(n[0]), float(Fraction(n[1])) * 4, float(n[2])]) # note, duration, time else: noteInfo.append([noteToInt(n[0]), float(Fraction(n[1])) * 4]) # note, duration song = MIDIFile(1, deinterleave=False) time = 0 for i in noteInfo: if notationIndex: pitch, dur, time = i else: pitch, dur = i song.addNote(0, 0, pitch, time, dur, 100) if not notationIndex: time += dur with open(os.path.join(st.session_state['path'], 't2m.mid'), 'wb') as f: song.writeFile(f) with open(os.path.join(st.session_state['path'], 't2m.mid'), 'rb') as f: st.download_button('Download Midi', f, file_name='song.mid', key='t2m')