def m4a_to_mp3(input_file_path): # パスから、拡張子と名前を分ける root, ext = os.path.splitext(input_file_path) logger.info('start m4a to mp3 convert {}'.format(input_file_path)) # s3にアップロード # upload_s3.sign_s3('/tmp/analize_log//{}'.format(logging_file),'log_2nd/{}'.format(logging_file)) if ext not in ['.m4a', '.mp4']: logger.debug('input file: {}'.format(str(input_file_path))) return if os.path.exists('tmp/') is not True: logger.info('make directory to mp3_files') # upload_s3.sign_s3( # 'tmp/analize_log//{}'.format(logging_file), 'log_2nd/{}'.format(logging_file)) os.mkdir('tmp/') # 変換するmp3ファイルの名前 input_file_path_mp3 = '%s.mp3' % root # set commands for m4a to mp3 using ffmpeg cmd = 'ffmpeg -i %s -ab 64k -ar 16000 %s' % (input_file_path, input_file_path_mp3) # upload_s3.sign_s3('/tmp/analize_log//{}'.format(logging_file),'log_2nd/{}'.format(logging_file)) logger.info('converted mp3 file: {}'.format(input_file_path_mp3)) logger.info(cmd) # do m4a to mp3(どちらもバイナリではなくパスを指定すること) status, output = subprocess.getstatusoutput(cmd) if status != 0: logger.error('failed convert {0}, {1}'.format(status, output)) return input_file_path_mp3 # S3にアップロード # upload_s3.sign_s3(input_file_path_mp3, # 'mp3_2nd/{}.mp3'.format(root.strip('/tmp/'))) logger.info('Done converted\nstatus: {0}\noutput: {1}'.format( status, output)) # mp3ファイルパスを返す return input_file_path_mp3
def detect_chords(input_file): """ 音声データからコードを取得する """ endpoint = 'analyze/chords' # コード解析APIのエンドポイント # 必要なパラメータをセットする url, datas, files = set_param_to_api(endpoint, input_file) logger.info('set param: url: {0}, datas: {1}, file: {2}'.format( url, datas, files)) # Sonic APIに音声データを投げる res = r.post(url=url, data=datas, files=files, verify=False) logger.info('analyzed result: {}'.format(res)) return res.text
def set_response_chord_analize(analize_chord_s): """ :param analize_chord: :return: chord_analize_response """ logger.info('Do make for response: {}'.format(analize_chord_s)) # str型を辞書型に変換 analize_chord_j = json.loads(analize_chord_s) logger.info('str count of result'.format(len(str(analize_chord_s)))) # 総検出数 num_chords = analize_chord_j['chords_result']['num_chords'] # コードのみを格納する辞書を用意する chords_list = [] # 取りたい項目を決める(今回は'chords') for key, value in enumerate(analize_chord_j['chords_result']['chords']): if value['chord'] == 'N': continue # コードのみ抽出 chord = value['chord'] # リストに格納 chords_list.append(chord) logger.info( 'Result Analized number of chord: {0}, Chord Progression{1}'.format( num_chords, chords_list)) logger.info('number of string count in Response: {}'.format( len(str(chords_list)))) if len(str(chords_list)) > 2000: chords_list = str(chords_list)[0:1999] return chords_list
def youtube2midi(self, youtube_url, user_id): # youtubeのurlをmp4ファイルにダウンロード mp4 = youtube2mp3(youtube_url, user_id) # mp4ファイルをmp3に変換 mp3 = m4a_to_mp3(mp4) logger.info("mp3 file: {}".format(mp3)) # mp3のコード解析 chord_analyze_str = detect_chords(str(mp3)) # 辞書型に変換 chord_analyze_dict = ast.literal_eval(chord_analyze_str) logger.info("change type {0} to {1}".format(type(chord_analyze_str), type(chord_analyze_dict))) # midiファイルに変換 chords2midi(chord_analyze_dict, user_id)
def youtube2mp3(youtube_url, line_userid): logger.info('youtube url: {}'.format(youtube_url)) print('youtube url: {}'.format(youtube_url)) # S3にアップロード # upload_s3.sign_s3(youtube_url, 'youtube_url/{}'.format(youtube_url)) # youtube動画を抜き出す yt = YouTube(youtube_url) # 動画の情報を出力 for lis in yt.streams.all(): logger.info(lis) # get_bu_itagtodownloadメソッドででダウンロードができる yt2mp3 = yt.streams.get_by_itag(140).download('/tmp/') logger.info('youtube video converted to mp4 {}'.format(yt2mp3)) # LINEのuseridにrenameする input_file_path = '/tmp/{}.mp4'.format(line_userid) os.rename(yt2mp3, input_file_path) # mp3ファイルを5分にカットする mp4 = AudioSegment.from_file(input_file_path, format='mp4') # 0~500sec(300000ms)にカット mp4_1min = mp4[0:60000] # logger.info('rename: {}'.format(os.rename(yt2mp3, 'tmp/{}.mp4'.format(line_userid)))) logger.info('Receive mp4 file name: {}'.format(input_file_path)) # with open(yt2mp3, 'wb') as fb: # fb.write(yt2mp3) return input_file_path
def callback(): # get X-Line-Signature header value signature = request.headers['X-Line-Signature'] # get request body as text body = request.get_data(as_text=True) logger.info('Request body: ' + str(body)) upload_s3.sign_s3('/tmp/analize_log//{}'.format(logging_file), 'log_2nd/{}'.format(logging_file)) # hadle webhook body try: handler.handle(body, signature) except InvalidSignatureError: abort(400) return 'OK'
async def basic_async(num, sleepingtime): """ 処理の中核 :return: """ logger.info('basic async') # awaitでsleeptingの処理が終わるのを待ってる for t in task: r = await sleeping(order, t) logger.info('await sleeping') logger.info("{0}'s {1} is finished".format(num, r)) index = +1 logger.info('index {}'.format(index)) time.sleep(5) logger.info('basic_async time {} [sec]\n'.format(sleepingtime)) return True
def m4a_to_mp3(input_file_path): # パスから、拡張子と名前を分ける root, ext = os.path.splitext(input_file_path) logger.info('start m4a to mp3 convert {}'.format(input_file_path)) upload_s3.sign_s3('/tmp/analize_log//{}'.format(logging_file), 'mico/log/{}'.format(logging_file)) if ext not in ['.m4a', '.mp4']: logger.debug('input file: {}'.format(str(input_file_path))) return if os.path.exists('/tmp/') is not True: logger.info('make directory to mp3_files') upload_s3.sign_s3('/tmp/analize_log//{}'.format(logging_file), 'mico/log/{}'.format(logging_file)) os.mkdir('/tmp/') # 変換するmp3ファイルの名前 input_file_path_mp3 = '%s.mp3' % root # set commands for m4a to mp3 using ffmpeg cmd = 'ffmpeg -i %s -ab 64k -ar 16000 %s' % (input_file_path, input_file_path_mp3) upload_s3.sign_s3('/tmp/analize_log//{}'.format(logging_file), 'mico/log/{}'.format(logging_file)) logger.info('converted mp3 file: {}'.format(input_file_path_mp3)) logger.info(cmd) # do m4a to mp3(どちらもバイナリではなくパスを指定すること) status, output = subprocess.getstatusoutput(cmd) if status != 0: logger.error('failed convert {0}, {1}'.format(status, output)) return input_file_path_mp3 # S3にアップロード upload_s3.sign_s3(input_file_path_mp3, 'mico/mp3/{}.mp3'.format(root.strip('/tmp/'))) logger.info('Done converted\nstatus: {0}\noutput: {1}'.format( status, output)) upload_s3.sign_s3('/tmp/analize_log//{}'.format(logging_file), 'mico/log/{}'.format(logging_file)) # 保存 # with open(input_file_path_mp3, 'rw') as fb: # fb.write(input_file_path_mp3) # # mp3ファイルを5分にカットする # mp3 = AudioSegment.from_file(input_file_path_mp3, format='mp3') # # 0~500sec(300000ms)にカット # mp3_5min = mp3[0:300000] # mp3ファイルパスを返す return input_file_path_mp3
async def basic_async_2(msg, sleepingtime): """ 処理の中核 :return: """ index = 0 while True: logger.info('\nbasic async') r = await sleeping(msg, sleepingtime) logger.info('await sleeping') # これをやってる間に logger.info("SUB task count {}".format(index)) time.sleep(sleepingtime) logger.info('2basic_async time {} [sec]\n'.format(sleepingtime)) index = +1
async def sleeping(order, time_limit, hook=None): """ 「重いけど処理順は問わない」処理。 重たい処理(m4aの読み込み。mp3に変換)はここで行う。 """ logger.info('sleeping 10sec') # awaitが行われるということは何か重たい処理が始まるということ await asyncio.sleep(time_limit) logger.info('await asyncio.sleep') if hook: hook(order) a = time.sleep(2) logger.info('sleeping time {} [sec]\n'.format(2)) return order
def youtube2mp3(youtube_url, line_userid): logger.info('youtube url: {}'.format(youtube_url)) # S3にアップロード # upload_s3.sign_s3(youtube_url, 'youtube_url/{}'.format(youtube_url)) # youtube動画を抜き出す yt = YouTube(youtube_url) logger.info(yt) # 動画の情報を出力 for lis in yt.streams.all(): logger.debug(lis) # get_bu_itagtodownloadメソッドででダウンロードができる yt2mp3 = yt.streams.get_by_itag(140).download('tmp/') logger.info('youtube video converted to mp4 {}'.format(yt2mp3)) # LINEのuseridにrenameする input_file_path = 'tmp/{}.mp4'.format(line_userid) os.rename(yt2mp3, input_file_path) # logger.info('rename: {}'.format(os.rename(yt2mp3, 'tmp/{}.mp4'.format(line_userid)))) logger.info('Receive mp4 file name: {}'.format(input_file_path)) # with open(yt2mp3, 'wb') as fb: # fb.write(yt2mp3) return input_file_path
async def sleeping(t, sleepingtime, hook=None): """ 「重いけど処理順は問わない」処理。 重たい処理(m4aの読み込み。mp3に変換)はここで行う。 """ # logger.info('sleeping {} sec') logger.info('sleeping: {}'.format(t)) # awaitが行われるということは何か重たい処理が始まるということ await asyncio.sleep(t) logger.info('await asyncio.sleep TASK: {}'.format(t)) if hook: hook(order) time.sleep(sleepingtime) logger.info('sleeping time {} [sec]\n'.format(sleepingtime)) return order
def handle_message(event): if event.message.type is not 'audio': # 入ってきたものがaudio以外だったら、デフォルトメッセージを返す start_youtube_time = time.time() for i in range(1, MAX_RETRY): try: line_bot_api.reply_message( event.reply_token, # トークンとテキストで紐づけてる TextSendMessage(text='あなたと一緒にコード解析するよー!\n' 'LINEで録音して送ってみてね。\n' '容量の小さいmp3ファイルも解析できるよ')) break except LineBotApiError as e: logger.error('LineBotApiError: {}'.format(e)) logger.error('retry: {0}/{1}'.format(i, MAX_RETRY)) line_bot_api.reply_message( event.reply_token, # トークンとテキストで紐づけてる TextSendMessage(text='あなたと一緒にコード解析するよー!\n' 'LINEで録音して送ってみてね\n' '容量の小さいmp3ファイルも解析できるよ\n')) return 'ok' logger.info('Message ID: {}'.format(str(event.message.id))) upload_s3.sign_s3('/tmp/analize_log//{}'.format(logging_file), 'log_2nd/{}'.format(logging_file)) # オーディオデータ(バイナリ形式。'audio/x-m4a')を取得する message_content = line_bot_api.get_message_content(event.message.id) # tmpディレクトリに保存 input_file_path = '/tmp/{}.m4a'.format(event.message.id) logger.info('Receive m4a file name: {}'.format(str(input_file_path))) upload_s3.sign_s3('/tmp/analize_log//{}'.format(logging_file), 'log_2nd/{}'.format(logging_file)) if os.path.exists('/tmp/') is not True: logger.info('make temporary directory') os.mkdir('/tmp/') # line_bot_api.push_message( # event.source.user_id, # トークンとテキストで紐づけてる # TextSendMessage(text='解析してみるよー!\n' # '終わったら話しかけるねー。\n' # '1分経ってもお返事が来なかったら、もう少し短いファイルを送ってみてくれるかな?') # ) start_chunk = time.time() with open(input_file_path, 'wb') as fd: for i in range(MAX_RETRY): # m4aバイナリの書き込みに失敗したら、5回までリトライ処理入れる try: for chunk in message_content.iter_content(): fd.write(chunk) break except: logger.error('LineBotApiError: {}'.format( traceback.format_exc())) logger.error('retry: {0}/{1}'.format(i, MAX_RETRY)) sleep(i * 5) end_chunk = time.time() - start_chunk logger.info('chunk time: {}'.format(end_chunk)) start_conv_mp3 = time.time() # S3にアップロード upload_s3.sign_s3(input_file_path, 'm4a_2nd/{}.m4a'.format(event.message.id)) # m4aバイナリファイルをローカルに保存し、mp3バイナリファイルに変換する chunk_mp3 = song_upload.m4a_to_mp3(input_file_path) end_conv_mp3 = time.time() - start_conv_mp3 logger.info('converted time: {} [sec]'.format(end_conv_mp3)) start_analize = time.time() chord_analize_response = mp3_to_response(chunk_mp3) end_analize = time.time() - start_analize logger.info('Analize time: {} [sec]'.format(end_analize)) # push API使えないバージョン # line_bot_api.reply_message( # event.reply_token, # トークンとテキストで紐づけてる # TextSendMessage( # text= # str(chord_analize_response) # ) # ) line_bot_api.reply_message( event.reply_token, # トークンとテキストで紐づけてる TextSendMessage(text=str(chord_analize_response))) logger.info( 'Success! Sent response for user: {}'.format(chord_analize_response)) # S3にアップロード upload_s3.sign_s3('/tmp/analize_log//{}'.format(logging_file), 'log_2nd/{}'.format(logging_file)) return 'ok'
def check_timeout(push, loop=None): logger.info('check timeout') if loop is None: loop = asyncio.get_event_loop() loop.stop() # イベントループをとめて制御をもどす処理を追加
""" 処理の中核 :return: """ logger.info('basic async') # awaitでsleeptingの処理が終わるのを待ってる for t in task: r = await sleeping(order, t) logger.info('await sleeping') logger.info("{0}'s {1} is finished".format(num, r)) index = +1 logger.info('index {}'.format(index)) time.sleep(5) logger.info('basic_async time {} [sec]\n'.format(sleepingtime)) return True if __name__ == "__main__": # 処理の実行を担うのがこのイベントグループ。 # これが「ノンブロッキングなスレッド」 loop = asyncio.get_event_loop() logger.info('make loop') # 2のタスクを生成する。 # 一つの処理がawaitに入ったら次の処理をしてを繰り返す # while True: asyncio.ensure_future(basic_async(1, 2)) logger.info('done basic_async 1') asyncio.ensure_future(basic_async(2, 5)) logger.info('done basic_async 2') # 2つの処理が終わるまで繰り返す loop.run_forever()
def chords2midi(chords_dict, file_name): # pretty midiオブジェクトを作る piano_chord = pretty_midi.PrettyMIDI() # 楽器名を入れると、対応するgeneral midi program numberを返す piano_program = pretty_midi.instrument_name_to_program('Acoustic Grand Piano') # Instrument instanceをcelloとして作成 piano = pretty_midi.Instrument(program=piano_program) # 解析したコードを単音のリストにバラす for i, chord in enumerate(chords_dict["chords_result"]["chords"]): logger.info(i, chord) # Nの時はスキップ(ノートに書き込まない) if chord['chord'] == "N": continue chords_list = key2chord(chord) logger.info("chords list: {}".format(chords_list)) logger.info("start time: {}".format(chord["time"])) logger.info("end time: {}".format(chords_dict["chords_result"]["chords"][i+1]["time"]-0.1)) # print("time: {}".format(float(chords_dict["chords_result"]["chords"][i+1]["time"])-float(chord["time"]))) for note_name in chords_list: # コードの名前を数字に変換 note_number = pretty_midi.note_name_to_number(note_name) logger.info("chord name {0}, chord num {1}".format(note_name, note_number)) # velocityを定義する try: note = pretty_midi.Note( velocity=100, pitch=note_number, start=chord["time"], end=chords_dict["chords_result"] ["chords"][i+1]["time"]-0.1 ) # print(note) # 上記で作成したnoteをchelloインスタンスに加える piano.notes.append(note) except IndexError: # 最後の章は0.5秒とする logger.info("note last code") note = pretty_midi.Note( velocity=100, pitch=note_number, start=chord["time"], end=chord["time"]+0.5 ) # 上記で作成したnoteをchelloインスタンスに加える piano.notes.append(note) # PrettyMIDIオブジェクトに加える piano_chord.instruments.append(piano) print(piano) print(piano_chord) midi_file = "midi/" + str(file_name) + ".mid" # MIDIファイルとして書き出す piano_chord.write(midi_file) # Load MIDI file into PrettyMIDI object midi_data = pretty_midi.PrettyMIDI(midi_file) # Print an empirical estimate of its global tempo print(midi_data.estimate_tempo())
def mp3_to_response(data_mp3): logger.info('Do analizing mp3 data: {}'.format(data_mp3)) # mp3を引数にして、コード解析APIに投げる analize_chord = chord_analize.detect_chords(input_file=data_mp3) # str型なので、JSON形式に変換する analize_chord_j = json.loads(analize_chord) logger.info('Response Header: {}'.format(analize_chord)) if analize_chord_j['status']['code'] == (200 or 201): # 得られたレスポンスを成形する chord_analize_response = set_response.set_response_chord_analize( analize_chord) logger.info('Made Response:{} '.format(str(chord_analize_response))) return chord_analize_response elif analize_chord_j['errors']: if analize_chord_j['errors'][0]['error_code'] == '23': chord_analize_response = 'オーディオファイルが短すぎるみたい。\n15秒以上にしてもう一回送ってみてね!' logger.info('Made Response: '.format(chord_analize_response)) return chord_analize_response elif analize_chord_j['errors'][0]['error_code'] == '22': chord_analize_response = '私の知らないファイル形式かも。\n' \ 'mp3形式かaac形式しか今はわからないんだ、、。\nあなたは物知りで凄いなぁ' logger.info('Made Response: '.format(chord_analize_response)) return chord_analize_response else: chord_analize_response = 'ちょっと調子が悪いみたい。\nもう一回試してみてもらえるかな?' logger.info('Made Response: '.format(chord_analize_response)) return chord_analize_response