diff --git a/submodules/TelegramUI/TelegramUI/Bridge Audio/TGBridgeAudioEncoder.m b/submodules/TelegramUI/TelegramUI/Bridge Audio/TGBridgeAudioEncoder.m index b96776b012..0177caaffa 100644 --- a/submodules/TelegramUI/TelegramUI/Bridge Audio/TGBridgeAudioEncoder.m +++ b/submodules/TelegramUI/TelegramUI/Bridge Audio/TGBridgeAudioEncoder.m @@ -81,13 +81,13 @@ typedef enum { NSDictionary *outputSettings = @ { - AVFormatIDKey: @(kAudioFormatLinearPCM), - AVSampleRateKey: @(TGBridgeAudioEncoderSampleRate), - AVNumberOfChannelsKey: @1, - AVLinearPCMBitDepthKey: @16, - AVLinearPCMIsFloatKey: @false, - AVLinearPCMIsBigEndianKey: @false, - AVLinearPCMIsNonInterleaved: @false + AVFormatIDKey: @(kAudioFormatLinearPCM), + AVSampleRateKey: @(TGBridgeAudioEncoderSampleRate), + AVNumberOfChannelsKey: @1, + AVLinearPCMBitDepthKey: @16, + AVLinearPCMIsFloatKey: @false, + AVLinearPCMIsBigEndianKey: @false, + AVLinearPCMIsNonInterleaved: @false }; _readerOutput = [AVAssetReaderAudioMixOutput assetReaderAudioMixOutputWithAudioTracks:asset.tracks audioSettings:outputSettings]; @@ -114,9 +114,9 @@ typedef enum { static ATQueue *queue = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^ - { - queue = [[ATQueue alloc] initWithName:@"org.telegram.opusAudioEncoderQueue"]; - }); + { + queue = [[ATQueue alloc] initWithName:@"org.telegram.opusAudioEncoderQueue"]; + }); return queue; } @@ -126,74 +126,74 @@ static const int encoderPacketSizeInBytes = TGBridgeAudioEncoderSampleRate / 100 - (void)startWithCompletion:(void (^)(NSString *, int32_t))completion { [[TGBridgeAudioEncoder processingQueue] dispatch:^ - { - _oggWriter = [[TGOggOpusWriter alloc] init]; - if (![_oggWriter beginWithDataItem:_tempFileItem]) - { - [self cleanup]; - return; - } - - [_assetReader startReading]; - - while (_assetReader.status != AVAssetReaderStatusCompleted) - { - if (_assetReader.status == AVAssetReaderStatusReading) - { - CMSampleBufferRef nextBuffer = [_readerOutput copyNextSampleBuffer]; - if (nextBuffer) - { - AudioBufferList abl; - CMBlockBufferRef blockBuffer; - CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(nextBuffer, NULL, &abl, sizeof(abl), NULL, NULL, kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment, &blockBuffer); - - [[TGBridgeAudioEncoder processingQueue] dispatch:^ - { - [self _processBuffer:&abl.mBuffers[0]]; - - CFRelease(nextBuffer); - CFRelease(blockBuffer); - }]; - } - else - { - [[TGBridgeAudioEncoder processingQueue] dispatch:^ - { - if (_tailLength > 0) { - [_oggWriter writeFrame:(uint8_t *)_audioBuffer.bytes frameByteCount:(NSUInteger)_tailLength]; - } - }]; - break; - } - } - } - - [[TGBridgeAudioEncoder processingQueue] dispatch:^ - { - TGFileDataItem *dataItemResult = nil; - NSTimeInterval durationResult = 0.0; - - NSUInteger totalBytes = 0; - - if (_assetReader.status == AVAssetReaderStatusCompleted) - { - NSLog(@"finished"); - if (_oggWriter != nil) - { - dataItemResult = _tempFileItem; - durationResult = [_oggWriter encodedDuration]; - totalBytes = [_oggWriter encodedBytes]; - } - - [self cleanup]; - } - - //TGLog(@"[TGBridgeAudioEncoder#%x convert time: %f ms]", self, (CFAbsoluteTimeGetCurrent() - startTime) * 1000.0); - - if (completion != nil) - completion(dataItemResult.path, (int32_t)durationResult); - }]; - }]; + { + _oggWriter = [[TGOggOpusWriter alloc] init]; + if (![_oggWriter beginWithDataItem:_tempFileItem]) + { + [self cleanup]; + return; + } + + [_assetReader startReading]; + + while (_assetReader.status != AVAssetReaderStatusCompleted) + { + if (_assetReader.status == AVAssetReaderStatusReading) + { + CMSampleBufferRef nextBuffer = [_readerOutput copyNextSampleBuffer]; + if (nextBuffer) + { + AudioBufferList abl; + CMBlockBufferRef blockBuffer; + CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(nextBuffer, NULL, &abl, sizeof(abl), NULL, NULL, kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment, &blockBuffer); + + [[TGBridgeAudioEncoder processingQueue] dispatch:^ + { + [self _processBuffer:&abl.mBuffers[0]]; + + CFRelease(nextBuffer); + CFRelease(blockBuffer); + }]; + } + else + { + [[TGBridgeAudioEncoder processingQueue] dispatch:^ + { + if (_tailLength > 0) { + [_oggWriter writeFrame:(uint8_t *)_audioBuffer.bytes frameByteCount:(NSUInteger)_tailLength]; + } + }]; + break; + } + } + } + + [[TGBridgeAudioEncoder processingQueue] dispatch:^ + { + TGFileDataItem *dataItemResult = nil; + NSTimeInterval durationResult = 0.0; + + NSUInteger totalBytes = 0; + + if (_assetReader.status == AVAssetReaderStatusCompleted) + { + NSLog(@"finished"); + if (_oggWriter != nil && [_oggWriter writeFrame:NULL frameByteCount:0]) + { + dataItemResult = _tempFileItem; + durationResult = [_oggWriter encodedDuration]; + totalBytes = [_oggWriter encodedBytes]; + } + + [self cleanup]; + } + + //TGLog(@"[TGBridgeAudioEncoder#%x convert time: %f ms]", self, (CFAbsoluteTimeGetCurrent() - startTime) * 1000.0); + + if (completion != nil) + completion(dataItemResult.path, (int32_t)durationResult); + }]; + }]; } - (void)_processBuffer:(AudioBuffer const *)buffer @@ -306,11 +306,11 @@ static const int encoderPacketSizeInBytes = TGBridgeAudioEncoderSampleRate / 100 [_queue dispatch:^ - { - _fileName = filePath; - _length = [[[NSFileManager defaultManager] attributesOfItemAtPath:_fileName error:nil][NSFileSize] unsignedIntegerValue]; - _fileExists = [[NSFileManager defaultManager] fileExistsAtPath:_fileName]; - }]; + { + _fileName = filePath; + _length = [[[NSFileManager defaultManager] attributesOfItemAtPath:_fileName error:nil][NSFileSize] unsignedIntegerValue]; + _fileExists = [[NSFileManager defaultManager] fileExistsAtPath:_fileName]; + }]; } return self; } @@ -322,38 +322,38 @@ static const int encoderPacketSizeInBytes = TGBridgeAudioEncoderSampleRate / 100 - (void)moveToPath:(NSString *)path { [_queue dispatch:^ - { - [[NSFileManager defaultManager] moveItemAtPath:_fileName toPath:path error:nil]; - _fileName = path; - }]; + { + [[NSFileManager defaultManager] moveItemAtPath:_fileName toPath:path error:nil]; + _fileName = path; + }]; } - (void)remove { [_queue dispatch:^ - { - [[NSFileManager defaultManager] removeItemAtPath:_fileName error:nil]; - }]; + { + [[NSFileManager defaultManager] removeItemAtPath:_fileName error:nil]; + }]; } - (void)appendData:(NSData *)data { [_queue dispatch:^ - { - if (!_fileExists) - { - [[NSFileManager defaultManager] createFileAtPath:_fileName contents:nil attributes:nil]; - _fileExists = true; - } - NSFileHandle *file = [NSFileHandle fileHandleForUpdatingAtPath:_fileName]; - [file seekToEndOfFile]; - [file writeData:data]; - [file synchronizeFile]; - [file closeFile]; - _length += data.length; - - [_data appendData:data]; - }]; + { + if (!_fileExists) + { + [[NSFileManager defaultManager] createFileAtPath:_fileName contents:nil attributes:nil]; + _fileExists = true; + } + NSFileHandle *file = [NSFileHandle fileHandleForUpdatingAtPath:_fileName]; + [file seekToEndOfFile]; + [file writeData:data]; + [file synchronizeFile]; + [file closeFile]; + _length += data.length; + + [_data appendData:data]; + }]; } - (NSData *)readDataAtOffset:(NSUInteger)offset length:(NSUInteger)length @@ -361,14 +361,14 @@ static const int encoderPacketSizeInBytes = TGBridgeAudioEncoderSampleRate / 100 __block NSData *data = nil; [_queue dispatch:^ - { - NSFileHandle *file = [NSFileHandle fileHandleForUpdatingAtPath:_fileName]; - [file seekToFileOffset:(unsigned long long)offset]; - data = [file readDataOfLength:length]; - if (data.length != length) - //TGLog(@"Read data length mismatch"); - [file closeFile]; - } synchronous:true]; + { + NSFileHandle *file = [NSFileHandle fileHandleForUpdatingAtPath:_fileName]; + [file seekToFileOffset:(unsigned long long)offset]; + data = [file readDataOfLength:length]; + if (data.length != length) + //TGLog(@"Read data length mismatch"); + [file closeFile]; + } synchronous:true]; return data; } @@ -377,9 +377,9 @@ static const int encoderPacketSizeInBytes = TGBridgeAudioEncoderSampleRate / 100 { __block NSUInteger result = 0; [_queue dispatch:^ - { - result = _length; - } synchronous:true]; + { + result = _length; + } synchronous:true]; return result; } @@ -420,11 +420,11 @@ static const int encoderPacketSizeInBytes = TGBridgeAudioEncoderSampleRate / 100 static ATQueue *queue = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^ - { - queue = [[ATQueue alloc] init]; - queue->_nativeQueue = dispatch_get_main_queue(); - queue->_isMainQueue = true; - }); + { + queue = [[ATQueue alloc] init]; + queue->_nativeQueue = dispatch_get_main_queue(); + queue->_isMainQueue = true; + }); return queue; } @@ -434,9 +434,9 @@ static const int encoderPacketSizeInBytes = TGBridgeAudioEncoderSampleRate / 100 static ATQueue *queue = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^ - { - queue = [[ATQueue alloc] initWithNativeQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)]; - }); + { + queue = [[ATQueue alloc] initWithNativeQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)]; + }); return queue; } @@ -446,9 +446,9 @@ static const int encoderPacketSizeInBytes = TGBridgeAudioEncoderSampleRate / 100 static ATQueue *queue = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^ - { - queue = [[ATQueue alloc] initWithNativeQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)]; - }); + { + queue = [[ATQueue alloc] initWithNativeQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)]; + }); return queue; } diff --git a/submodules/TelegramUI/TelegramUI/ManagedAudioRecorder.swift b/submodules/TelegramUI/TelegramUI/ManagedAudioRecorder.swift index c6fafa8e81..dc8217c1de 100644 --- a/submodules/TelegramUI/TelegramUI/ManagedAudioRecorder.swift +++ b/submodules/TelegramUI/TelegramUI/ManagedAudioRecorder.swift @@ -319,7 +319,7 @@ final class ManagedAudioRecorderContext { self.idleTimerExtensionDisposable = (Signal { subscriber in return pushIdleTimerExtension() - } |> delay(5.0, queue: queue)).start() + } |> delay(5.0, queue: queue)).start() } deinit { @@ -406,19 +406,19 @@ final class ManagedAudioRecorderContext { strongSelf.audioSessionAcquired(headset: state.isHeadsetConnected) } } - }, deactivate: { [weak self] in - return Signal { subscriber in - queue.async { - if let strongSelf = self { - strongSelf.hasAudioSession = false - strongSelf.stop() - strongSelf.recordingState.set(.stopped) - subscriber.putCompletion() - } + }, deactivate: { [weak self] in + return Signal { subscriber in + queue.async { + if let strongSelf = self { + strongSelf.hasAudioSession = false + strongSelf.stop() + strongSelf.recordingState.set(.stopped) + subscriber.putCompletion() } - - return EmptyDisposable } + + return EmptyDisposable + } }) } } @@ -592,53 +592,57 @@ final class ManagedAudioRecorderContext { } func takeData() -> RecordedAudioData? { - var scaledSamplesMemory = malloc(100 * 2)! - var scaledSamples: UnsafeMutablePointer = scaledSamplesMemory.assumingMemoryBound(to: Int16.self) - defer { - free(scaledSamplesMemory) - } - memset(scaledSamples, 0, 100 * 2); - var waveform: Data? - - let count = self.compressedWaveformSamples.count / 2 - self.compressedWaveformSamples.withUnsafeMutableBytes { (samples: UnsafeMutablePointer) -> Void in - for i in 0 ..< count { - let sample = samples[i] - let index = i * 100 / count - if (scaledSamples[index] < sample) { - scaledSamples[index] = sample; + if self.oggWriter.writeFrame(nil, frameByteCount: 0) { + var scaledSamplesMemory = malloc(100 * 2)! + var scaledSamples: UnsafeMutablePointer = scaledSamplesMemory.assumingMemoryBound(to: Int16.self) + defer { + free(scaledSamplesMemory) + } + memset(scaledSamples, 0, 100 * 2); + var waveform: Data? + + let count = self.compressedWaveformSamples.count / 2 + self.compressedWaveformSamples.withUnsafeMutableBytes { (samples: UnsafeMutablePointer) -> Void in + for i in 0 ..< count { + let sample = samples[i] + let index = i * 100 / count + if (scaledSamples[index] < sample) { + scaledSamples[index] = sample; + } } - } - - var peak: Int16 = 0 - var sumSamples: Int64 = 0 - for i in 0 ..< 100 { - let sample = scaledSamples[i] - if peak < sample { - peak = sample + + var peak: Int16 = 0 + var sumSamples: Int64 = 0 + for i in 0 ..< 100 { + let sample = scaledSamples[i] + if peak < sample { + peak = sample + } + sumSamples += Int64(sample) } - sumSamples += Int64(sample) - } - var calculatedPeak: UInt16 = 0 - calculatedPeak = UInt16((Double(sumSamples) * 1.8 / 100.0)) - - if calculatedPeak < 2500 { - calculatedPeak = 2500 + var calculatedPeak: UInt16 = 0 + calculatedPeak = UInt16((Double(sumSamples) * 1.8 / 100.0)) + + if calculatedPeak < 2500 { + calculatedPeak = 2500 + } + + for i in 0 ..< 100 { + let sample: UInt16 = UInt16(Int64(scaledSamples[i])) + let minPeak = min(Int64(sample), Int64(calculatedPeak)) + let resultPeak = minPeak * 31 / Int64(calculatedPeak) + scaledSamples[i] = Int16(clamping: min(31, resultPeak)) + } + + let resultWaveform = AudioWaveform(samples: Data(bytes: scaledSamplesMemory, count: 100 * 2), peak: 31) + let bitstream = resultWaveform.makeBitstream() + waveform = AudioWaveform(bitstream: bitstream, bitsPerSample: 5).makeBitstream() } - for i in 0 ..< 100 { - let sample: UInt16 = UInt16(Int64(scaledSamples[i])) - let minPeak = min(Int64(sample), Int64(calculatedPeak)) - let resultPeak = minPeak * 31 / Int64(calculatedPeak) - scaledSamples[i] = Int16(clamping: min(31, resultPeak)) - } - - let resultWaveform = AudioWaveform(samples: Data(bytes: scaledSamplesMemory, count: 100 * 2), peak: 31) - let bitstream = resultWaveform.makeBitstream() - waveform = AudioWaveform(bitstream: bitstream, bitsPerSample: 5).makeBitstream() + return RecordedAudioData(compressedData: self.dataItem.data(), duration: self.oggWriter.encodedDuration(), waveform: waveform) + } else { + return nil } - - return RecordedAudioData(compressedData: self.dataItem.data(), duration: self.oggWriter.encodedDuration(), waveform: waveform) } } diff --git a/submodules/TelegramUI/third-party/opusenc/opusenc.m b/submodules/TelegramUI/third-party/opusenc/opusenc.m index 2cd7fdea65..d29eaa10ad 100644 --- a/submodules/TelegramUI/third-party/opusenc/opusenc.m +++ b/submodules/TelegramUI/third-party/opusenc/opusenc.m @@ -426,39 +426,24 @@ static inline int writeOggPage(ogg_page *page, TGDataItem *fileItem) pages_out++; } - op.packet = (unsigned char *)_packet; - op.bytes = nbBytes; - op.b_o_s = 0; - op.granulepos = enc_granulepos; - if (op.e_o_s) - { - /* We compute the final GP as ceil(len*48k/input_rate). When a resampling - decoder does the matching floor(len*input/48k) conversion the length will - be exactly the same as the input. - */ - op.granulepos = ((total_samples * 48000 + rate - 1) / rate) + header.preskip; - } - op.packetno = 2 + _packetId; - ogg_stream_packetin(&os, &op); - last_segments += size_segments; + if (framePcmBytes != NULL) { + op.packet = (unsigned char *)_packet; + op.bytes = nbBytes; + op.b_o_s = 0; + op.granulepos = enc_granulepos; - /* The downside of early reading is if the input is an exact - multiple of the frame_size you'll get an extra frame that needs - to get cropped off. The downside of late reading is added delay. - If your ogg_delay is 120ms or less we'll assume you want the - low delay behavior. - */ - /*if ((!op.e_o_s) && max_ogg_delay > 5760) - { - nb_samples = inopt.read_samples(inopt.readdata, input, frame_size); - total_samples += nb_samples; - if (nb_samples < frame_size) - eos = 1; - if (nb_samples == 0) - op.e_o_s = 1; + if (op.e_o_s) + { + /* We compute the final GP as ceil(len*48k/input_rate). When a resampling + decoder does the matching floor(len*input/48k) conversion the length will + be exactly the same as the input. + */ + op.granulepos = ((total_samples * 48000 + rate - 1) / rate) + header.preskip; + } + op.packetno = 2 + _packetId; + ogg_stream_packetin(&os, &op); + last_segments += size_segments; } - else - nb_samples = -1;*/ // If the stream is over or we're sure that the delayed flush will fire, go ahead and flush now to avoid adding delay while ((op.e_o_s || (enc_granulepos + (frame_size * 48000 / coding_rate) - last_granulepos > max_ogg_delay) ||