2015-12-08 4 views
9

मैं मिडी मिलीसेकेंड के टिक्स/डेल्टा समय और पहले से ही कुछ उपयोगी साधन पाया है परिवर्तित करने के लिए कोशिश कर रहा हूँ:मिडी टिक्स को मिलीसेकंड में सही तरीके से कैसे परिवर्तित करें?

  1. MIDI Delta Time Ticks to Seconds
  2. How to convert midi timeline into the actual timeline that should be played
  3. MIDI Time Code spec
  4. MTC

समस्या यह नहीं है कि मुझे नहीं लगता कि मैं इस जानकारी का सही ढंग से उपयोग कर रहा हूं। this test MIDI file से

[ 1 min 60 sec 1 beat  Z clocks ] 
| ------- * ------ * -------- * -------- | = seconds 
[ X beats 1 min Y clocks  1 ] 

मेटाडेटा का उपयोग: मैं सूत्र Nik विस्तार को लागू करने की कोशिश की है

<meta message set_tempo tempo=576923 time=0> 
<meta message key_signature key='Ab' time=0> 
<meta message time_signature numerator=4 denominator=4 clocks_per_click=24 notated_32nd_notes_per_beat=8 time=0> 

तो जैसा:

self.toSeconds = 60.0 * self.t[0][2].clocks_per_click/(self.t[0][0].tempo * self.t[0][2].denominator) * 10 

यह शुरू में ठीक लग रहा है, लेकिन फिर ऐसा लगता है भटकना। यहाँ एक बुनियादी runnable Mido और pygame का उपयोग कर उदाहरण है (यह मानते हुए pygame वापस निभाता है सही ढंग से):

import threading 

import pygame 
from pygame.locals import * 

from mido import MidiFile,MetaMessage 

music_file = "Bee_Gees_-_Stayin_Alive-Voice.mid" 

#audio setup 
freq = 44100 # audio CD quality 
bitsize = -16 # unsigned 16 bit 
channels = 2 # 1 is mono, 2 is stereo 
buffer = 1024 # number of samples 
pygame.mixer.init(freq, bitsize, channels, buffer) 
pygame.mixer.music.set_volume(0.8) 


class MIDIPlayer(threading.Thread): 
    def __init__(self,music_file): 
     try: 
      #MIDI parsing 
      self.mid = MidiFile(music_file) 
      self.t = self.mid.tracks 

      for i, track in enumerate(self.mid.tracks): 
       print('Track {}: {}'.format(i, track.name)) 
       for message in track: 
        if isinstance(message, MetaMessage): 
         if message.type == 'time_signature' or message.type == 'set_tempo' or message.type == 'key_signature': 
          print message 

      self.t0 = self.t[0][3:len(self.t[0])-1] 
      self.t0l = len(self.t0) 
      self.toSeconds = 60.0 * self.t[0][2].clocks_per_click/(self.t[0][0].tempo * self.t[0][2].denominator) * 10 
      print "self.toSeconds",self.toSeconds 
      #timing setup 
      self.event_id = 0 
      self.now = pygame.time.get_ticks() 
      self.play_music(music_file) 
     except KeyboardInterrupt: 
      pygame.mixer.music.fadeout(1000) 
      pygame.mixer.music.stop() 
      raise SystemExit 

    def play_music(self,music_file): 
     clock = pygame.time.Clock() 
     try: 
      pygame.mixer.music.load(music_file) 
      print "Music file %s loaded!" % music_file 
     except pygame.error: 
      print "File %s not found! (%s)" % (music_file, pygame.get_error()) 
      return 
     pygame.mixer.music.play() 
     while pygame.mixer.music.get_busy(): 
      # check if playback has finished 
      millis = pygame.time.get_ticks() 
      deltaMillis = self.t0[self.event_id].time * self.toSeconds * 1000 
      # print millis,deltaMillis 
      if millis - self.now >= deltaMillis: 
       print self.t0[self.event_id].text 
       self.event_id = (self.event_id + 1) % self.t0l 
       self.now = millis 
      clock.tick(30) 

MIDIPlayer(music_file) 

क्या उपरोक्त कोड करना चाहिए मिडी फ़ाइल के आधार पर सही समय पर सही गीत मुद्रित है, फिर भी यह drifts अधिक समय तक।

MIDI डेल्टा समय को सेकंड/मिलीसेकंड में परिवर्तित करने का सही तरीका क्या है?

अद्यतन

सीएल के सहायक जवाब पर मैं हैडर से ticks_per_beat उपयोग करने के लिए कोड को नवीनीकृत किया है के आधार पर। के बाद से वहाँ एक भी set_tempo मेटा संदेश है, मैं भर में इस मूल्य का उपयोग कर रहा:

import threading 

import pygame 
from pygame.locals import * 

from mido import MidiFile,MetaMessage 

music_file = "Bee_Gees_-_Stayin_Alive-Voice.mid" 

#audio setup 
freq = 44100 # audio CD quality 
bitsize = -16 # unsigned 16 bit 
channels = 2 # 1 is mono, 2 is stereo 
buffer = 1024 # number of samples 
pygame.mixer.init(freq, bitsize, channels, buffer) 
pygame.mixer.music.set_volume(0.8) 


class MIDIPlayer(threading.Thread): 
    def __init__(self,music_file): 
     try: 
      #MIDI parsing 
      self.mid = MidiFile(music_file) 
      self.t = self.mid.tracks 

      for i, track in enumerate(self.mid.tracks): 
       print('Track {}: {}'.format(i, track.name)) 
       for message in track: 
        # print message 
        if isinstance(message, MetaMessage): 
         if message.type == 'time_signature' or message.type == 'set_tempo' or message.type == 'key_signature' or message.type == 'ticks_per_beat': 
          print message 

      self.t0 = self.t[0][3:len(self.t[0])-1] 
      self.t0l = len(self.t0) 
      self.toSeconds = 60.0 * self.t[0][2].clocks_per_click/(self.t[0][0].tempo * self.t[0][2].denominator) * 10 
      print "self.toSeconds",self.toSeconds 

      # append delta delays in milliseconds 
      self.delays = [] 

      tempo = self.t[0][0].tempo 
      ticks_per_beat = self.mid.ticks_per_beat 

      last_event_ticks = 0 
      microseconds = 0 

      for event in self.t0: 
       delta_ticks = event.time - last_event_ticks 
       last_event_ticks = event.time 
       delta_microseconds = tempo * delta_ticks/ticks_per_beat 
       microseconds += delta_microseconds 
       print event.text,microseconds/1000000.0 
       self.delays.append(microseconds/1000) 

      #timing setup 
      self.event_id = 0 
      self.now = pygame.time.get_ticks() 
      self.play_music(music_file) 
     except KeyboardInterrupt: 
      pygame.mixer.music.fadeout(1000) 
      pygame.mixer.music.stop() 
      raise SystemExit 

    def play_music(self,music_file): 
     clock = pygame.time.Clock() 
     try: 
      pygame.mixer.music.load(music_file) 
      print "Music file %s loaded!" % music_file 
     except pygame.error: 
      print "File %s not found! (%s)" % (music_file, pygame.get_error()) 
      return 
     pygame.mixer.music.play() 
     while pygame.mixer.music.get_busy(): 
      # check if playback has finished 
      millis = pygame.time.get_ticks() 
      # deltaMillis = self.t0[self.event_id].time * self.toSeconds * 1000 
      deltaMillis = self.delays[self.event_id] 
      # print millis,deltaMillis 
      if millis - self.now >= deltaMillis: 
       print self.t0[self.event_id].text 
       self.event_id = (self.event_id + 1) % self.t0l 
       self.now = millis 
      clock.tick(30) 

MIDIPlayer(music_file) 

संदेशों मैं समय मिलीसेकंड में बदल जाती के आधार पर प्रिंट से समय ज्यादा बेहतर लग रहा है। हालांकि, कुछ सेकंड के बाद भी यह बहती है।

क्या मैं मिडी टिक्स को मिलीसेकंड में सही ढंग से परिवर्तित कर रहा हूं और लूप के दौरान अपडेट में पास मिलीसेकंड का ट्रैक रख सकता हूं?

इस तरह रूपांतरण किया जाता है: self.delays = []

tempo = self.t[0][0].tempo 
    ticks_per_beat = self.mid.ticks_per_beat 

    last_event_ticks = 0 
    microseconds = 0 

    for event in self.t0: 
     delta_ticks = event.time - last_event_ticks 
     last_event_ticks = event.time 
     delta_microseconds = tempo * delta_ticks/ticks_per_beat 
     microseconds += delta_microseconds 
     print event.text,microseconds/1000000.0 
     self.delays.append(microseconds/1000) 

और यह कैसे की जांच अगर एक 'क्यू' समय के रूप में सामना करना पड़ा था गुजरता है:

millis = pygame.time.get_ticks() 
      deltaMillis = self.delays[self.event_id] 
      if millis - self.now >= deltaMillis: 
       print self.t0[self.event_id].text 
       self.event_id = (self.event_id + 1) % self.t0l 
       self.now = millis 
      clock.tick(30) 

मैं मुझे यकीन नहीं है कि क्या यह कार्यान्वयन एमआईडीआई डेल्टा टिक्स को मिलीसेकंड में गलत तरीके से परिवर्तित करता है, गलत तरीके से जांच करें कि मिलीसेकंद आधारित देरी पास या दोनों हैं या नहीं।

उत्तर

5

सबसे पहले, आपको यह सुनिश्चित करने के लिए सभी ट्रैकों को मर्ज करना होगा कि टेम्पो परिवर्तन ईवेंट ठीक से संसाधित हो जाएं।(यह संभवतः आसान है अगर आप पहली बार पूर्ण टिक मूल्यों के लिए डेल्टा बार परिवर्तित;। अन्यथा, आप डेल्टा बार जब भी एक घटना एक और ट्रैक की घटनाओं के बीच डाला जाता है recompute होगा)

तो फिर तुम गणना करने के लिए, के लिए है प्रत्येक घटना, अंतिम घटना के सापेक्ष समय, जैसा कि निम्नलिखित छद्म कोड में है। यह महत्वपूर्ण है कि गणना सापेक्ष बार क्योंकि गति को किसी भी समय बदल गया होगा का उपयोग करना चाहिए:

tempo = 500000  # default: 120 BPM 
ticks_per_beat = ... # from the file header 

last_event_ticks = 0 
microseconds = 0 
for each event: 
    delta_ticks = event.ticks - last_event_ticks 
    last_event_ticks = event.ticks 
    delta_microseconds = tempo * delta_ticks/ticks_per_beat 
    microseconds += delta_microseconds 
    if event is a tempo event: 
     tempo = event.new_tempo 
    # ... handle event ... 
+0

उत्तर के लिए धन्यवाद। थोड़ा सा कोशिश करेंगे। सिर्फ दो बार जांचना चाहता था: '' 'tocks_per_click'''' 'ticks_per_beat''' जैसा ही है? (मुझे मेटा संदेशों में '' ticks_per_beat''' नहीं मिल रहा है) –

+0

घटनाओं के समय को निर्धारित करने के लिए समय हस्ताक्षर घटनाएं * आवश्यक नहीं हैं; वे केवल नोटेशन के लिए उपयोगी हैं। मिडो में, संपत्ति जो प्रति बीट टिक निर्दिष्ट करती है उसे 'ticks_per_beat' कहा जाता है। –

+0

क्षमा करें, मुझे नहीं लगता कि मैंने बहुत अच्छी तरह से समझाया है। आप परीक्षण MIDI फ़ाइल के MIDI संदेशों को देख सकते हैं जो मैं टेक्स्ट [यहां] (http://lifesine.eu/so/Bee_Gees_-_Stayin_Alive-Voice.txt) के रूप में उपयोग कर रहा हूं। ऐसा लगता है कि '' ticks_per_beat''' नामक कोई संदेश नहीं है। क्या एमआईडीआई फाइल के लिए यह जानकारी याद आ सकती है? यदि हां, तो क्या किया जा सकता है? धन्यवाद (+1) –

1

आप फ्रेम दर को बढ़ाने के लिए चाहते हो सकता है। मेरे सिस्टम पर, clock.tick(30) से बढ़कर clock.tick(300) अच्छे परिणाम देता है। आप मुद्रण कितना अपने समय बंद है के द्वारा इस उपाय कर सकते हैं:

print self.t0[self.event_id].text, millis - self.now - deltaMillis 

30 के साथ टिक्स संकेत 20 से 30 मिलीसेकंड से पीछे रहे हैं। 300 टिकों के साथ वे 2 मिलीसेकंड पीछे हैं। आप इसे और भी आगे बढ़ाना चाहते हैं।

बस सुरक्षित होने के लिए आपको -u के साथ पाइथन चलाने के लिए stdout को बफरिंग से रोकने के लिए स्विच करना चाहिए (यह अनावश्यक हो सकता है, क्योंकि लाइनें नई लाइन के साथ समाप्त होती हैं)।

मुझे समय निर्धारित करने में कठिनाई होती है, लेकिन "आह हा हा हा" से निर्णय लेने से यह इन परिवर्तनों के साथ सही लगता है।

+0

ओएमजी! मैं बहुत करीब था (अभी तक अब तक) इतना आसान! मेरे लिए इसे तोड़ने के लिए धन्यवाद। मैं सीएल के बिना यहां नहीं मिला होता। और आपकी मदद –

संबंधित मुद्दे

 संबंधित मुद्दे