0

我在 IronPython 中使用 NAudio 来混合多个音频流以创建环境音频。这对于某些曲目来说效果相当好,但对于其他一些曲目(风,雨),它可能会在循环播放时出现不和谐的停止/开始。

因为我最初不是 100% 确定如何在 python 中实现 LoopStream 类示例,所以我开始只是在十分之一秒或更短的时间内进行位置检查。我知道为什么我在那里有差距。从那以后,我能够弄清楚如何在 python 中重新创建 LoopStream,并且它可以工作,但我仍然像以前一样在播放方面存在差距。我现在正试图将轨道的结尾交叉淡入到同一轨道的开头,当我这样做时,音频会完全出现故障。
这是代码:

class LoopStream(WaveStream):
  def __init__(self,WaveStream,AudioStream):
    self.wavestream = WaveStream
    self.audiostream = AudioStream    
  def get_WaveFormat(self):
    return self.wavestream.WaveFormat
  def get_Length(self):
    return self.wavestream.Length
  def get_Position(self):
    return self.wavestream.Position
  def HasData(count):
    return True

  def Read(self,buf,offset,count):    
    read = 0
    while(read < count):            
      required = count - read
      #print(str(self.audiostream.get_chan_id()) + " reading @ " + str(self.wavestream.Position) + "/" + str(self.wavestream.Length))
      pos = self.wavestream.Position
      readthistime = self.wavestream.Read(buf,offset+read,required)
      if pos == 0:
        self.startbuf = buf      
      if readthistime < required:
        self.wavestream.Position = 0
        #print(len(buf))
        #buf = buf+self.startbuf               
        print(len(buf))
        buf = FadeOut(self,buf,offset,readthistime) + FadeIn(self,self.startbuf,0,required)
        print(len(buf))
        readthistime+=required
        print(str(self.audiostream.get_chan_id()) + " restarting1")
      elif self.wavestream.Position + required > self.wavestream.Length:        
        #read += readthistime
        #readthistime = self.wavestream.Read(buf,self.wavestream.Position,required)
        #print(str(self.audiostream.get_chan_id()) + " restarting2")
        pass
      if self.wavestream.Position >= self.wavestream.Length:
        self.wavestream.Position = 0 
        buf = buf + self.startbuf 
        print(str(self.audiostream.get_chan_id()) + " restarting3")
      read += readthistime
    return read

  def FadeOut(self,buf,offset,count):
    sample = 0
    maxfadesamples = int((self.wavestream.WaveFormat.SampleRate * 75) / 1000)
    fadesamples = 0 
    while sample < count:
      multiplier = 1.0 - (fadesamples / maxfadesamples)
      for i in range(0,self.wavestream.WaveFormat.Channels):       
        buf[offset+sample] *= multiplier
        sample+=1
      fadesamples+=1

      if fadesamples > maxfadesamples:
        for j in range(0,self.wavestream.WaveFormat.Channels):
          while sample < count:
            buf[offset+sample] = 0 
            sample+=1
  def FadeOut(self,buf,offset,count):
    sample = 0    
    maxfadesamples = int((self.wavestream.WaveFormat.SampleRate * 75) / 1000)
    fadesamples = 0 
    while sample < count:
      multiplier = (fadesamples / maxfadesamples)
      for i in range(0,self.wavestream.WaveFormat.Channels):
        buf[offset+sample] *= multiplier
        sample+=1
      fadesamples+=1
4

2 回答 2

0

很抱歉提出愚蠢的问题....应该很明显,我使用的函数没有返回缓冲区,而只是作用于传递给它们的缓冲区。

因此,一旦我收到一条好的错误消息,就很容易确定解决方案

这一行:

buf = FadeOut(self,buf,offset,readthistime) + FadeIn(self,self.startbuf,0,required)

成为:

try:
  self.FadeOut(buf,offset,readthistime) 
  self.FadeIn(self.startbuf,0,required)
  buf = buf = self.startbuf
except Exception, e:
  print(repr(e))

所以问题是我试图从两个没有结果的过程中添加“结果缓冲区”,我只需要运行它们然后添加。交叉淡入淡出使差距明显不那么明显。

于 2017-10-15T01:18:01.097 回答
0

下面是一个比我之前的尝试更好的解决方案,这个解决方案是交叉淡入淡出而不是淡入/淡出,这确实最大限度地减少了差距,但对于某些音轨来说,在重新启动时它仍然是一个明显的故障。

class LoopStream(WaveStream):
  def __init__(self,WaveStream,AudioStream):   
    self.wavestream = WaveStream
    self.originalstream = self.wavestream
    self.audiostream = AudioStream

    if (float(self.get_Length()) / self.WaveFormat.AverageBytesPerSecond) > 6:
      self.CFSeconds = 3.1
    else:
      #self.CFSeconds = (self.LSeconds / 2.0) + 0.1
      self.CFSeconds = ((float(self.get_Length()) / self.WaveFormat.AverageBytesPerSecond)/2) - 0.1
    self.CFBytes = int(round(self.WaveFormat.AverageBytesPerSecond*self.CFSeconds))
    #determine bytes per section of audio
    temp = (self.WaveFormat.BitsPerSample / 8) * self.WaveFormat.Channels
    #determine how many bytes we are over target
    temp = self.CFBytes % temp
    #subtract bits to get to target 
    self.CFBytes-=temp

    self.startbuf = Array.CreateInstance(System.Byte,self.CFBytes)
    self.endbuf = Array.CreateInstance(System.Byte,self.CFBytes)
    self.wavestream.Read(self.startbuf,0,self.CFBytes)
    self.wavestream.Position = self.Length - self.CFBytes
    self.wavestream.Read(self.endbuf,0,self.CFBytes)

    self.wavestream.Position = 0 

    #self.startbuf = self.buf[:int(round(self.WaveFormat.AverageBytesPerSecond*CFSeconds))]        
    self.FadeIn(self.startbuf,0,int(round(self.WaveFormat.AverageBytesPerSecond*self.CFSeconds)))    

    #self.endbuf = self.buf[self.Length-int(round(self.WaveFormat.AverageBytesPerSecond*CFSeconds)):]
    self.FadeOut(self.endbuf,0,int(round(self.WaveFormat.AverageBytesPerSecond*self.CFSeconds)))
    self.FirstPlay = True 

    self.startstream = RawSourceWaveStream(self.startbuf,0,self.CFBytes,self.WaveFormat)
    self.endstream = RawSourceWaveStream(self.endbuf,0,self.CFBytes,self.WaveFormat)
    self.crossfadestream = MixingWaveProvider32() #self.startstream,self.endstream)
    self.crossposition = 0 
    self.crossfadestream.AddInputStream(self.startstream)
    self.crossfadestream.AddInputStream(self.endstream)
    self.CFBuffer = Array.CreateInstance(System.Byte,self.CFBytes)
    self.crossfadestream.Read(self.CFBuffer,0,self.CFBytes)

    print(self.audiostream.chan_id,"initialized")
  def get_WaveFormat(self):
    return self.wavestream.WaveFormat
  def get_Length(self):
    return self.wavestream.Length
  def get_Position(self):
    return self.wavestream.Position
  def HasData(count):
    return True
  def LSeconds(self):
    return float(self.get_Length()) / self.WaveFormat.AverageBytesPerSecond 
  def PSeconds(self):
    return float(self.get_Position()) / self.WaveFormat.AverageBytesPerSecond
  def Read(self,buf,offset,count):    
    read = 0
    while(read < count):            
      required = count - read
      readthistime = 0 
      if self.FirstPlay == True:
        if (int(self.Position) + read >= self.CFBytes) or (int(self.Position) + read >= int(self.Length) - self.CFBytes):
          self.FirstPlay = False

      if self.FirstPlay == True or ((int(self.Position) + read) < (int(self.Length) - self.CFBytes) and (int(self.Position) + read > self.CFBytes)):                
        try:
          #print(self.audiostream.chan_id,"Normal")
          readthistime = self.wavestream.Read(buf,offset+read,required)          
        except Exception, e:
          exc_type, exc_obj, exc_tb = sys.exc_info()
          fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
          print(exc_type,fname,exc_tb.tb_lineno)
      else:       
        try:        
          #print(self.audiostream.chan_id,"Crossfade")
          buf = self.CFBuffer
          self.wavestream.Position = self.CFBytes + ((self.WaveFormat.BitsPerSample/8)*self.WaveFormat.Channels)
          read += self.CFBytes          
          return read
        except Exception, e:
          exc_type, exc_obj, exc_tb = sys.exc_info()
          fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
          print(exc_type,fname,exc_tb.tb_lineno)
      read += readthistime
    return read

  def FadeOut(self,buf,offset,count):    
    sample = 0
    maxfadesamples = int((self.WaveFormat.SampleRate * self.CFSeconds))
    fadesamples = 0 
    while sample < count:
      multiplier = 1.0 - (fadesamples / maxfadesamples)
      for i in range(0,self.wavestream.WaveFormat.Channels):       
        try:          
          buf[offset+sample] *= multiplier        
        except Exception, e:
          exc_type, exc_obj, exc_tb = sys.exc_info()
          fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
          print(sample,exc_type,fname,exc_tb.tb_lineno)
          return
        sample+=1
      fadesamples+=1

      if fadesamples > maxfadesamples:        
        while sample < count:          
          buf[offset+sample] = 0           
          sample+=1
        break
  def FadeIn(self,buf,offset,count):    
    sample = 0    
    maxfadesamples = int((self.WaveFormat.SampleRate * self.CFSeconds))
    fadesamples = 0 
    while sample < count:    
      multiplier = (fadesamples / maxfadesamples)
      for i in range(0,self.wavestream.WaveFormat.Channels):
        buf[offset+sample] *= multiplier
        sample+=1
      fadesamples+=1
      if fadesamples>maxfadesamples:
        break
于 2017-10-16T06:14:36.483 回答