def fx_distortion(chunk_p, gain_p, tres_p): if len(chunk_p) != 2 * CHUNK: print('[distortion] chunk size is not %d but %d' % (2 * CHUNK, len(chunk_p))) return chunk_p if tres_p<= 0: print('[distortion] threshold should be > 0!') return chunk_p if tres_p > 1: print('[distortion] threshold should be < 1!') return chunk_p if gain_p < 1: print('[distortion] gain should be > 1!') return chunk_p #do distortion temp1 = audioop.mul(chunk_p, 2, 1 / tres_p) temp2 = audioop.mul(temp1, 2, tres_p) #do gain chunk_p = audioop.mul(temp2, 2, gain_p) del temp1 del temp2 return chunk_p
def mul_stereo(fileName,width,lfactor,rfactor): lsample = audioop.tomono(fileName, width, 1, 0) rsample = audioop.tomono(fileName,width, 0, 1) lsample = audioop.mul(lsample,width,lfactor) rsample = audioop.mul(rsample, width,rfactor) lsample = audioop.tostereo(lsample, width, 1, 0) rsample = audioop.tostereo(rsample, width, 0, 1) return audioop.add(lsample,rsample,width)
def test_mul(self): data2 = [] for d in data: str = '' for s in d: str = str + chr(ord(s)*2) data2.append(str) self.assertEqual(audioop.mul(data[0], 1, 2), data2[0]) self.assertEqual(audioop.mul(data[1],2, 2), data2[1]) self.assertEqual(audioop.mul(data[2], 4, 2), data2[2])
def test_mul(self): data2 = [] for d in data: str = bytearray(len(d)) for i,b in enumerate(d): str[i] = 2*b data2.append(str) self.assertEqual(audioop.mul(data[0], 1, 2), data2[0]) self.assertEqual(audioop.mul(data[1],2, 2), data2[1]) self.assertEqual(audioop.mul(data[2], 4, 2), data2[2])
def convert_wave_data(f_rate,frame_count,sample_width,channels,data): """ Convert wave sample data into pleo format """ if channels==2: data = audioop.tomono(data,sample_width,1,1) data = audioop.mul(data,sample_width,0.97999999999999998) data = audioop.ratecv(data,sample_width,1,f_rate,11025,None,4,4)[0] if sample_width==1: data = audioop.bias(data,1,-128) data = audioop.lin2lin(data,1,2) data = audioop.mul(data,2,(1.0/256)) data = audioop.lin2adpcm(data,2,None)[0] return (11025,frame_count,sample_width,1,data)
def testmul(data): data2 = [] for d in data: str = '' for s in d: str = str + chr(ord(s)*2) data2.append(str) if audioop.mul(data[0], 1, 2) <> data2[0] or \ audioop.mul(data[1],2, 2) <> data2[1] or \ audioop.mul(data[2], 4, 2) <> data2[2]: return 0 return 1
def _play(self, start, length): self.isplaying = True if AudioSegment: millisecondchunk = 50 / 1000.0 playchunk = self.pydubfile[start*1000.0:(start+length)*1000.0] - (60 - (60 * (self.volume/100.0))) self.time = start self.audio.play(playchunk.get_array_of_samples(), blocking=False) # For some reason it does not like the seperated chunks, so we play it non- # We might be able to use self.audio.get_stream().time to improve accuracy for chunks in make_chunks(playchunk, millisecondchunk*1000): self.time += millisecondchunk time.sleep(millisecondchunk) if not self.isplaying: break if self.time >= start+length: break else: startframe = int(round(start * self.wave_reference.getframerate())) samplelen = int(round(length * self.wave_reference.getframerate())) remaining = samplelen chunk = 1024 try: self.wave_reference.setpos(startframe) except wave.Error: self.isplaying = False return if remaining >= 1024: data = audioop.mul(self.wave_reference.readframes(chunk),self.wave_reference.getsampwidth(), self.volume/100.0) remaining -= chunk else: data = audioop.mul(self.wave_reference.readframes(remaining),self.wave_reference.getsampwidth(), self.volume/100.0) remaining = 0 # play stream self.audio.play(data.get_array_of_samples(), blocking=False) while len(data) > 0 and self.isplaying: time.sleep(float(self.wave_reference.getframerate())) self.time = float(self.wave_reference.tell()) / float(self.wave_reference.getframerate()) if remaining >= 1024: data = audioop.mul(self.wave_reference.readframes(chunk),self.wave_reference.getsampwidth(), self.volume/100.0) remaining -= chunk else: data = audioop.mul(self.wave_reference.readframes(remaining),self.wave_reference.getsampwidth(), self.volume/100.0) remaining = 0 self.audio.stop() self.isplaying = False
def testmul(data): if verbose: print 'mul' data2 = [] for d in data: str = '' for s in d: str = str + chr(ord(s)*2) data2.append(str) if audioop.mul(data[0], 1, 2) != data2[0] or \ audioop.mul(data[1],2, 2) != data2[1] or \ audioop.mul(data[2], 4, 2) != data2[2]: return 0 return 1
def get_flush(st, channels, fade=0): """Like soundtouch's flush, don't require that all data comes through, just any. If fade > 0, only allow [fade] samples, and linearly scale volume to 0 over that length""" waiting = st.waiting_count() ready = st.ready_count() result = "" silence = array('h', [0] * 64) while st.ready_count() == ready: st.put_samples(silence) while st.ready_count() > 0: result += st.get_samples(11025) st.clear() if len(result) > 2 * channels * waiting: result = result[0:(2 * channels * waiting)] fade = min(fade, len(result) / 2) if fade > 0: resultstring = "" for ii in xrange(fade / channels): i0 = ii * 2*channels i1 = (ii+1) * 2*channels resultstring += audioop.mul(result[i0:i1], 2, 1 - float(ii) / (fade / channels)) result = resultstring return result
def mix(layers, leftalign=True, boost=2.0): """ mixes N stereo audio strings """ attenuation = 1.0 / len(layers) attenuation *= boost layers.sort(key = len) output_length = flen(layers[-1]) out = pad('', output_length, 0) for layer in layers: padding = output_length - flen(layer) if leftalign: layer = pad(layer, 0, padding) else: layer = pad(layer, padding, 0) layer = audioop.mul(layer, audio_params[1], attenuation) if len(layer) != ftc(output_length) or len(out) != ftc(output_length): dif = int(math.fabs(len(layer) - len(out))) log('unequal'+str(dif)) if len(out) < len(layer): layer = layer[:len(layer) - dif] else: out = out[:len(out) - dif] out = audioop.add(out, layer, audio_params[1]) return out
def __getitem__(self, millisecond): if isinstance(millisecond, slice): start = millisecond.start if millisecond.start is not None else 0 end = millisecond.stop if millisecond.stop is not None \ else len(self) start = min(start, len(self)) end = min(end, len(self)) else: start = millisecond end = millisecond + 1 start = self._parse_position(start) * self.frame_width end = self._parse_position(end) * self.frame_width data = self._data[start:end] # ensure the output is as long as the requester is expecting expected_length = end - start missing_frames = (expected_length - len(data)) / self.frame_width if missing_frames: if missing_frames > self.frame_count(ms=2): raise TooManyMissingFrames("You should never be filling in "\ " more than 2 ms with silence here, missing frames: %s" % \ missing_frames) silence = audioop.mul(data[:self.frame_width], self.sample_width, 0) data += (silence * missing_frames) return self._spawn(data)
def _do_run(self): self.loops = 0 self._start = time.time() while not self._end.is_set(): # are we paused? if not self._resumed.is_set(): # wait until we aren't self._resumed.wait() if not self._connected.is_set(): self.stop() break self.loops += 1 data = self.buff.read(self.frame_size) if self._volume != 1.0: data = audioop.mul(data, 2, min(self._volume, 2.0)) if len(data) != self.frame_size: self.stop() break self.player(data) next_time = self._start + self.delay * self.loops delay = max(0, self.delay + (next_time - time.time())) time.sleep(delay)
def tick(self): self.effect.tick() if self.period == 0 or self.sample is None: return dummy_sample else: data = self.sample.get_data(self.sample_offset, self.samples_per_tick()) self.sample_offset += self.samples_per_tick() return mul(data, 1, self.volume / 64.0)
def __mul__(self, factor): """ Implement audio_data * factor """ if not isinstance(factor, (int, float)): return NotImplemented # passing the job to factor.__rmul__ new_byte_data = audioop.mul(self.BYTE_DATA, self.BIT_WIDTH, factor) return type(self)(new_byte_data, self.SAMPLE_RATE, self.BIT_WIDTH, self.CHANNELS, self.dtype)
def fx_noise_cancel(chunk_p, tres_p): if len(chunk_p) != 2 * CHUNK: print('[echo] chunk size is not %d but %d' % (2 * CHUNK, len(chunk_p))) return chunk_p power = audioop.rms(chunk_p, 2) / float(math.pow(2, 15)) if power < tres_p: chunk_p = audioop.mul(chunk_p, 2, 0) return chunk_p
def amplify_max(self): """Amplify the sample to maximum volume without clipping or overflow happening.""" assert not self.__locked max_amp = audioop.max(self.__frames, self.samplewidth) max_target = 2 ** (8 * self.samplewidth - 1) - 2 if max_amp > 0: factor = max_target/max_amp self.__frames = audioop.mul(self.__frames, self.samplewidth, factor) return self
def mul(self, factor): """ Return frames for which all samples are multiplied by factor. Samples are truncated in case of overflow. :param factor: (int) the factor which will be applied to each sample. :returns: (str) converted frames """ return audioop.mul(self._frames, self._sampwidth, factor)
def echocancel(outputdata, inputdata): pos = audioop.findmax(outputdata, 800) # one tenth second out_test = outputdata[pos*2:] in_test = inputdata[pos*2:] ipos, factor = audioop.findfit(in_test,out_test) prefill = '\0'*(pos+ipos)*2 postfill = '\0'*(len(inputdata)-len(prefill)-len(outputdata)) outputdata = prefill + audioop.mul(outputdata,2-factor) + postfill return audioop.add(inputdata, outputdata,2)
def test_mul(self): for w in 1, 2, 3, 4: self.assertEqual(audioop.mul(b"", w, 2), b"") self.assertEqual(audioop.mul(bytearray(), w, 2), b"") self.assertEqual(audioop.mul(memoryview(b""), w, 2), b"") self.assertEqual(audioop.mul(datas[w], w, 0), b"\0" * len(datas[w])) self.assertEqual(audioop.mul(datas[w], w, 1), datas[w]) self.assertEqual(audioop.mul(datas[1], 1, 2), b"\x00\x24\x7f\x80\x7f\x80\xfe") self.assertEqual(audioop.mul(datas[2], 2, 2), packs[2](0, 0x2468, 0x7FFF, -0x8000, 0x7FFF, -0x8000, -2)) self.assertEqual( audioop.mul(datas[3], 3, 2), packs[3](0, 0x2468AC, 0x7FFFFF, -0x800000, 0x7FFFFF, -0x800000, -2) ) self.assertEqual( audioop.mul(datas[4], 4, 2), packs[4](0, 0x2468ACF0, 0x7FFFFFFF, -0x80000000, 0x7FFFFFFF, -0x80000000, -2) )
def mul(self, factor): """ Return frames that has all samples are multiplied by factor. Samples are truncated in case of overflow. @param factor (int) the factor which will be applied to each sample. @return converted frames """ return audioop.mul(self.frames, self.sampwidth, factor)
def get_32bit_frames(self, scale_amplitude=True): """Returns the raw sample frames scaled to 32 bits. See make_32bit method for more info.""" if self.samplewidth == 4: return self.__frames frames = audioop.lin2lin(self.__frames, self.samplewidth, 4) if not scale_amplitude: # we need to scale back the sample amplitude to fit back into 24/16/8 bit range factor = 1.0/2**(8*abs(self.samplewidth-4)) frames = audioop.mul(frames, 4, factor) return frames
def env(audio_string, wavetable_type="sine", fullres=False, highval=1.0, lowval=0.0): # Very short envelopes are possible... if flen(audio_string) < dsp_grain * 4 or fullres == True: packets = split(audio_string, 1) else: packets = split(audio_string, dsp_grain) wtable = wavetable(wavetable_type, len(packets), highval, lowval) packets = [audioop.mul(packet, audio_params[1], wtable[i]) for i, packet in enumerate(packets)] return ''.join(packets)
def pan(slice, pan_pos=0.5, amp=1.0): amps = pantamp(pan_pos) lslice = audioop.tomono(slice, audio_params[1], 1, 0) lslice = audioop.tostereo(lslice, audio_params[1], amps[0], 0) rslice = audioop.tomono(slice, audio_params[1], 0, 1) rslice = audioop.tostereo(rslice, audio_params[1], 0, amps[1]) slice = audioop.add(lslice, rslice, audio_params[1]) return audioop.mul(slice, audio_params[1], amp)
def mul(fragment, sampwidth, factor): """ Return a fragment that has all samples in the original fragment multiplied by the floating-point value factor. Samples are truncated in case of overflow. @param fragment (string) input frames. @param sampwidth (int) sample width of the frames. @param factor (int) the factor which will be applied to each sample. @return converted frames """ return audioop.mul(fragment, sampwidth, factor)
def _frame_vol(self, frame, mult, *, maxv=2, use_audioop=True): if use_audioop: return audioop.mul(frame, 2, min(mult, maxv)) else: # ffmpeg returns s16le pcm frames. frame_array = array('h', frame) for i in range(len(frame_array)): frame_array[i] = int(frame_array[i] * min(mult, min(1, maxv))) return frame_array.tobytes()
def echocancel(outputdata, inputdata): """Try to identify an echo and remove it. Should contain 2-byte samples""" pos = audioop.findmax(outputdata, 800) out_test = outputdata[pos*2:] in_test = inputdata[pos*2:] ipos, factor = audioop.findfit(in_test, out_test) factor = audioop.findfactor(in_test[ipos*2:ipos*2+len(out_test)], out_test) prefill = '\0'*(pos+ipos)*2 postfill = '\0'*(len(inputdata) - len(prefill) - len(outputdata)) outputdata = prefill + audioop.mul(outputdata, 2, -factor) + postfill return audioop.add(inputdata, outputdata, 2)
def test_mul(self): for w in 1, 2, 3, 4: self.assertEqual(audioop.mul(b'', w, 2), b'') self.assertEqual(audioop.mul(bytearray(), w, 2), b'') self.assertEqual(audioop.mul(memoryview(b''), w, 2), b'') self.assertEqual(audioop.mul(datas[w], w, 0), b'\0' * len(datas[w])) self.assertEqual(audioop.mul(datas[w], w, 1), datas[w]) self.assertEqual(audioop.mul(datas[1], 1, 2), b'\x00\x24\x7f\x80\x7f\x80\xfe') self.assertEqual(audioop.mul(datas[2], 2, 2), packs[2](0, 0x2468, 0x7fff, -0x8000, 0x7fff, -0x8000, -2)) self.assertEqual(audioop.mul(datas[3], 3, 2), packs[3](0, 0x2468ac, 0x7fffff, -0x800000, 0x7fffff, -0x800000, -2)) self.assertEqual(audioop.mul(datas[4], 4, 2), packs[4](0, 0x2468acf0, 0x7fffffff, -0x80000000, 0x7fffffff, -0x80000000, -2))
def raw_read(self): """Return some amount of data as a raw audio string""" buf = self.source.raw_read() if buf is None: self.eof = True return None # Perform the scaling and biasing if self.scale != 1.0: buf = audioop.mul(buf, self.source.raw_width(), self.scale) if self.bias != 0: buf = audioop.bias(buf, self.source.raw_width(), self.bias) return buf
def mixAudio(self): # XXX see the comment above about storing a decaying number for the # volume. For instance, each time round the loop, take the calculated # volume, and the stored volume, and do something like: # newStoredVolume = (oldStoredVolume * 0.33) + (thisPacketVolume * 0.66) import audioop self._audioOut = {} if not self._open: log.msg('mixing closed room %r'%(self,), system='doug') return audioIn = {} for m in self._members: bytes = m.getAudioForRoom() if bytes: audioIn[m] = bytes if CONFDEBUG: print "room %r has %d members"%(self, len(self._members)) print "got %d samples this time"%len(audioIn) print "samples: %r"%(audioIn.items(),) # short-circuit this case if len(self._members) < 2: if CONFDEBUG: print "less than 2 members, no sound" self._audioOutDefault = '' return # Samples is (confsource, audio) samples = audioIn.items() # power is three-tuples of (rms,audio,confsource) power = [ (audioop.rms(x[1],2),x[1], x[0]) for x in samples ] power.sort(); power.reverse() if CONFDEBUG: for rms,audio,confsource in power: print confsource, rms # Speakers is a list of the _maxSpeakers loudest speakers speakers = Set([x[2] for x in power[:self._maxSpeakers]]) # First we calculate the 'default' audio. Used for everyone who's # not a speaker in the room. samples = [ x[1] for x in power[:self._maxSpeakers] ] scaledsamples = [ audioop.mul(x, 2, 1.0/len(samples)) for x in samples ] if scaledsamples: # ooo. a use of reduce. first time for everything... try: combined = reduce(lambda x,y: audioop.add(x, y, 2), scaledsamples) except audioop.error, exc: # XXX tofix! print "combine got error %s"%(exc,) print "lengths", [len(x) for x in scaledsamples] combined = ''
def run(self): while 1: olddata = data = self.iport.readsamps(600) if self.do_ulaw: data = audioop.lin2ulaw(data, 2) data = audioop.ulaw2lin(data, 2) if self.do_adpcm: data, nacstate = audioop.lin2adpcm(data, 2, \ self.acstate) data, dummy = audioop.adpcm2lin(data, 2, \ self.acstate) self.acstate = nacstate if self.do_diff: olddata = audioop.mul(olddata, 2, -1) data = audioop.add(olddata, data, 2) self.oport.writesamps(data) fl.check_forms()
def _callback( self, data, frame_count, time_info, status): # Если микрофон замьючен - ничего не делаем if self.muted : self.buffer.clear() self.ignoreFirstFrame = True return None, pyaudio.paContinue # А еще игнорируем первый фрейм после unmute: if self.ignoreFirstFrame : self.ignoreFirstFrame = False return None, pyaudio.paContinue # Контролируем размер буфера. В режиме ожидания 1с, в активном режиме 3с maxBufferSize = int(CHUNKS_PER_SECOND * (3 if self.active else 1) ) while len(self.buffer)>maxBufferSize : self.buffer.pop(0) data = numpy.fromstring( data, dtype='int16') if self.sampleRate != VOICE_SAMPLING_RATE: data, self.__ratecvState = audioop.ratecv( data.tobytes(), 2, self.channels, self.sampleRate, VOICE_SAMPLING_RATE, self.__ratecvState ) data = numpy.fromstring( data, dtype='int16') #print(f"channels:") # Раскидываем на каналы channels = [0]*self.channels for ch in range(self.channels): channels[ch] = data[ch::self.channels].tobytes() #print(numpy.fromstring(channels[ch], dtype='int16')) # "Оптимальный уровень громкости" if config.micSelection == "rms": # Вариант 2: "наибольший RMS, но без искажений". chBest = -1 rmsBest = 0 maxBest = 0 chGood = -1 rmsGood = 100000 maxGood = 0 for ch in config.microphones: __rms = audioop.rms( channels[ch], self.sampleSize ) __maxpp = audioop.maxpp( channels[ch], self.sampleSize ) if (__rms>rmsBest) and (__rms<5000) and (__maxpp<64000) : rmsBest = __rms maxBest = __maxpp chBest = ch if (chGood<0) or (__rms < rmsGood) : rmsGood = __rms rmsBest = __maxpp chGood = ch #print(f'rms:[{__rms[0]},{__rms[1]}], maxpp:[{__maxpp[0]},{__maxpp[1]}], rmsBest={rmsBest}({chBest}), rmsGood={rmsGood}({chGood})') #print(f'rmsBest={rmsBest}({chBest}), rmsGood={rmsGood}({chGood})') if chBest>=0: self.channel = chBest self.__rms = rmsBest self.__maxpp = maxBest else: self.channel = chGood self.__rms = rmsGood self.__maxpp = maxGood data = channels[self.channel] # "Среднее по микрофонным каналам": else : self.channel = "avg" factor = 1.0 / len(config.microphones) #print(f'factor={factor} ') data = None for ch in config.microphones : if data==None : data = audioop.mul( channels[ch], 2, factor ) else : data = audioop.add( data, audioop.mul( channels[ch], 2, factor ), 2 ) self.__rms = audioop.rms( data, self.sampleSize ) self.__maxpp = audioop.maxpp( data, self.sampleSize ) #print(f"Final data: channel={self.channel}, rms={self.rms}, maxpp={self.maxpp} ") #print(numpy.fromstring(data, dtype='int16')) # Сохранить фрагмент в буфере: self.buffer.append( data ) if not self.active: # Если уровень звука "немного меньше" фонового шума - снизить значение порогового шума if self.__rms + config.noiseThreshold < self.__noiseLevel: self.__noiseLevel = self.__rms + int( config.noiseThreshold / 4 ) # Посчитать VAD index (self.vadLevel) vadFrameSize = int(VOICE_SAMPLING_RATE*self.sampleSize/1000 * VAD_FRAME) p = 0 voiceFrames = 0 totalFrames = 0 while p+vadFrameSize <= len(data): totalFrames += 1 if self.vad.is_speech( data[p:p+vadFrameSize], VOICE_SAMPLING_RATE ): voiceFrames += 1 p += vadFrameSize self.vadLevel = int(voiceFrames*100/totalFrames) isVoice = (totalFrames>0) and (self.vadLevel>=config.vadConfidence) if isVoice and (self.rms > self.triggerLevel): self.active = True else: if self.__rms + config.noiseThreshold > self.__noiseLevel : self.__noiseLevel = self.__rms return None, pyaudio.paContinue
def read(self, volume=None): self.frames += 1 return audioop.mul(super().read(), 2, volume or self.volume)
def create_audio_thread(audio_lib_path: str, audio_lib_type, uri: str, skipto: int = 0, quiet: bool = True, stereo: bool = True, use_reconnect=False): if uri == '': return global_settings.mumble_inst.sound_output.clear_buffer() if global_settings.audio_inst: pid = global_settings.audio_inst.pid global_settings.audio_inst.terminate() try: os.kill(pid, 0) global_settings.audio_inst.kill() except OSError as e: dprint(e) global_settings.audio_inst = None if audio_lib_type.value == AudioLibrary.FFMPEG.value: params = [audio_lib_path] if quiet: params.extend(["-loglevel", "quiet"]) if use_reconnect: params.extend([ "-reconnect", "1", "-reconnect_streamed", "1", "-reconnect_delay_max", "2" ]) params.extend([ "-i", uri, "-ss", f"{skipto}", "-acodec", "pcm_s16le", "-f", "s16le", "-ab", "192k", "-ac", "2" ]) if stereo: params.extend(["-ar", "48000", "-threads", "8", "-"]) else: params.extend(["-ar", "24000", "-threads", "8", "-"]) elif audio_lib_type.value == AudioLibrary.VLC.value: params = [audio_lib_path, uri, '-I', 'dummy'] if quiet: params.extend(["--quiet"]) params.extend(["--one-instance", f"--start-time={skipto}"]) if stereo: params.extend([ "--sout", "#transcode{acodec=s16le, channels=2, samplerate=48000, ab=192, threads=8}:std{access=file, mux=wav, dst=-}" ]) else: params.extend([ "--sout", "#transcode{acodec=s16le, channels=2, samplerate=24000, ab=192, threads=8}:std{access=file, mux=wav, dst=-}" ]) params.extend(["vlc://quit"]) else: return global_settings.audio_inst = sp.Popen(params, stdout=sp.PIPE, bufsize=1024) rutils.unmute() while not global_settings.aud_interface.exit_flag and global_settings.audio_inst: while global_settings.mumble_inst.sound_output.get_buffer_size( ) > 0.5 and not global_settings.aud_interface.exit_flag: sleep(0.01) if global_settings.audio_inst: raw_music = global_settings.audio_inst.stdout.read(1024) if raw_music and global_settings.aud_interface.status.is_playing(): global_settings.mumble_inst.sound_output.add_sound( audioop.mul( raw_music, 2, global_settings.aud_interface.status.get_volume())) else: if global_settings.aud_interface.next_track(): create_audio_thread(audio_lib_path=audio_lib_path, audio_lib_type=audio_lib_type, uri=global_settings.aud_interface. status.get_track().uri, skipto=0, quiet=quiet, stereo=stereo) else: global_settings.aud_interface.reset() return else: return
def loop(self): while not self.exit and self.mumble.is_alive(): while self.thread and self.mumble.sound_output.get_buffer_size( ) > 0.5 and not self.exit: # If the buffer isn't empty, I cannot send new music part, so I wait self._loop_status = f'Wait for buffer {self.mumble.sound_output.get_buffer_size():.3f}' time.sleep(0.01) raw_music = None if self.thread: # I get raw from ffmpeg thread # move playhead forward self._loop_status = 'Reading raw' if self.song_start_at == -1: self.song_start_at = time.time() - self.playhead self.playhead = time.time() - self.song_start_at raw_music = self.thread.stdout.read(self.pcm_buffer_size) self.read_pcm_size += len(raw_music) if self.redirect_ffmpeg_log: try: self.last_ffmpeg_err = self.thread_stderr.readline() if self.last_ffmpeg_err: self.log.debug("ffmpeg: " + self.last_ffmpeg_err.strip("\n")) except: pass if raw_music: # Adjust the volume and send it to mumble self.volume_cycle() if not self.on_interrupting and len( raw_music) == self.pcm_buffer_size: self.mumble.sound_output.add_sound( audioop.mul(raw_music, 2, self.volume_helper.real_volume)) elif self.read_pcm_size == 0: self.mumble.sound_output.add_sound( audioop.mul( self._fadeout(raw_music, self.stereo, fadein=True), 2, self.volume_helper.real_volume)) elif self.on_interrupting or len( raw_music) < self.pcm_buffer_size: self.mumble.sound_output.add_sound( audioop.mul( self._fadeout(raw_music, self.stereo, fadein=False), 2, self.volume_helper.real_volume)) self.thread.kill() self.thread = None time.sleep(0.1) self.on_interrupting = False else: time.sleep(0.1) else: time.sleep(0.1) if not self.is_pause and not raw_music: self.thread = None # bot is not paused, but ffmpeg thread has gone. # indicate that last song has finished, or the bot just resumed from pause, or something is wrong. if self.read_pcm_size < self.pcm_buffer_size \ and var.playlist.current_index != -1 \ and self.last_ffmpeg_err: current = var.playlist.current_item() self.log.error("bot: cannot play music %s", current.format_debug_string()) self.log.error("bot: with ffmpeg error: %s", self.last_ffmpeg_err) self.last_ffmpeg_err = "" self.send_channel_msg( tr('unable_play', item=current.format_title())) var.playlist.remove_by_id(current.id) var.cache.free_and_delete(current.id) # move to the next song. if not self.wait_for_ready: # if wait_for_ready flag is not true, move to the next song. if var.playlist.next(): current = var.playlist.current_item() self.log.debug( f"bot: next into the song: {current.format_debug_string()}" ) try: self.validate_and_start_download(current) self.wait_for_ready = True self.song_start_at = -1 self.playhead = 0 except ValidationFailedError as e: self.send_channel_msg(e.msg) var.playlist.remove_by_id(current.id) var.cache.free_and_delete(current.id) else: self._loop_status = 'Empty queue' else: # if wait_for_ready flag is true, means the pointer is already # pointing to target song. start playing current = var.playlist.current_item() if current: if current.is_ready(): self.wait_for_ready = False self.read_pcm_size = 0 self.launch_music(current, self.playhead) self.last_volume_cycle_time = time.time() self.async_download_next() elif current.is_failed(): var.playlist.remove_by_id(current.id) self.wait_for_ready = False else: self._loop_status = 'Wait for the next item to be ready' else: self.wait_for_ready = False while self.mumble.sound_output.get_buffer_size() > 0: # Empty the buffer before exit time.sleep(0.01) time.sleep(0.5) if self.exit: self._loop_status = "exited" if var.config.getboolean('bot', 'save_playlist', fallback=True) \ and var.config.get("bot", "save_music_library", fallback=True): self.log.info("bot: save playlist into database") var.playlist.save()
def audioseg_adjust_volume(audio: AudioSegment, factor: float): return audio._spawn( data=audioop.mul(audio._data, audio.sample_width, factor))
def fade(self, to_gain=0, from_gain=0, start=None, end=None, duration=None): """ Fade the volume of this audio segment. to_gain (float): resulting volume_change in db start (int): default = beginning of the segment when in this segment to start fading in milliseconds end (int): default = end of the segment when in this segment to start fading in milliseconds duration (int): default = until the end of the audio segment the duration of the fade """ if None not in [duration, end, start]: raise TypeError( "Only two of the three arguments, \"start\", \"end\", and \"duration\" may be specified" ) # no fade == the same audio if to_gain == 0 and from_gain == 0: return self frames = self.frame_count() start = min(len(self), start) end = min(len(self), end) if start is not None and start < 0: start += len(self) if end is not None and end < 0: end += len(self) if duration is not None and duration < 0: raise InvalidDuration("duration must be a positive integer") if duration: if start is not None: end = start + duration elif end is not None: start = end - duration else: duration = end - start from_power = db_to_float(from_gain) output = [] # original data - up until the crossfade portion, as is before_fade = self[:start]._data if from_gain != 0: before_fade = audioop.mul(before_fade, self.sample_width, from_power) output.append(before_fade) gain_delta = db_to_float(to_gain) - from_power scale_step = gain_delta / duration for i in range(duration): volume_change = from_power + (scale_step * i) chunk = self[start + i] chunk = audioop.mul(chunk._data, self.sample_width, volume_change) output.append(chunk) # original data after the crossfade portion, at the new volume after_fade = self[end:]._data if to_gain != 0: after_fade = audioop.mul(after_fade, self.sample_width, db_to_float(to_gain)) output.append(after_fade) return self._spawn(data=output)
def apply_gain(self, volume_change): return self._spawn(data=audioop.mul(self._data, self.frame_width, db_to_float(float(volume_change))))
def read(self): if not self.start_time: self.start_time = time.time() - self.resume_from ret = self.original.read() return audioop.mul(ret, 2, self._volume)
# Normalize audio new_audio_sample = [] audio_sample_recording_list_counter = 0 rms = [] for x in audio_sample: rms.append(audioop.rms(x, 2)) sample_mean = np.mean(rms) scaling_factor = 4000 / sample_mean if debug: print(scaling_factor) for x in audio_sample: new_audio_sample.append(audioop.mul(x, 2, scaling_factor)) audio_sample = new_audio_sample # Write audio audio_data_directory audio_sample_file_path = audio_data_directory + UID + ".wav" audio_sample_file = open(audio_sample_file_path, 'w+') w = wave.open(audio_sample_file_path, "w") w.setnchannels(1) w.setsampwidth(2) w.setframerate(byterate) for x in new_audio_sample:
def fade(self, to_gain=0, from_gain=0, start=None, end=None, duration=None): """ Fade the volume of this audio segment. to_gain (float): resulting volume_change in db start (int): default = beginning of the segment when in this segment to start fading in milliseconds end (int): default = end of the segment when in this segment to start fading in milliseconds duration (int): default = until the end of the audio segment the duration of the fade """ if None not in [duration, end, start]: raise TypeError('Only two of the three arguments, "start", ' '"end", and "duration" may be specified') # no fade == the same audio if to_gain == 0 and from_gain == 0: return self frames = self.frame_count() start = min(len(self), start) if start is not None else None end = min(len(self), end) if end is not None else None if start is not None and start < 0: start += len(self) if end is not None and end < 0: end += len(self) if duration is not None and duration < 0: raise InvalidDuration("duration must be a positive integer") if duration: if start is not None: end = start + duration elif end is not None: start = end - duration else: duration = end - start from_power = db_to_float(from_gain) output = [] # original data - up until the crossfade portion, as is before_fade = self[:start]._data if from_gain != 0: before_fade = audioop.mul(before_fade, self.sample_width, from_power) output.append(before_fade) gain_delta = db_to_float(to_gain) - from_power # fades longer than 100ms can use coarse fading (one gain step per ms), # shorter fades will have audible clicks so they use precise fading (one # gain step per sample) if duration > 100: scale_step = gain_delta / duration for i in range(duration): volume_change = from_power + (scale_step * i) chunk = self[start + i] chunk = audioop.mul(chunk._data, self.sample_width, volume_change) output.append(chunk) else: start_frame = self.frame_count(ms=start) end_frame = self.frame_count(ms=end) fade_frames = end_frame - start_frame scale_step = gain_delta / fade_frames for i in range(int(fade_frames)): volume_change = from_power + (scale_step * i) sample = self.get_frame(int(start_frame + i)) sample = audioop.mul(sample, self.sample_width, volume_change) output.append(sample) # original data after the crossfade portion, at the new volume after_fade = self[end:]._data if to_gain != 0: after_fade = audioop.mul(after_fade, self.sample_width, db_to_float(to_gain)) output.append(after_fade) return self._spawn(data=output)
return db p = pyaudio.PyAudio() volume = [] stream = p.open(format=FORMAT, channels=CHANNELS, rate=RATE, input=True, input_device_index=D_INDEX, frames_per_buffer=CHUNK) print('using device #%d' % D_INDEX) for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)): data = stream.read(CHUNK) data = audioop.mul(data, 2, 10) volume.append(printDB(data)) stream.stop_stream() stream.close() p.terminate() print('writing %d volume samples to file' % len(volume)) target = open(FILENAME, 'w') target.truncate() for i in volume: target.write('%f' % i) target.write(';') target.close()
def write(self, data): data = audioop.mul(data.data, 2, min(self._volume, 2.0)) self.destination.write(data)
def read(self): ret = self.original.read() return audioop.mul(ret, 2, min(self._volume, 2.0))