Learning AV Foundation(二)AVAudioPlayer

開篇

最近在學(xué)習(xí)AV Foundation 試圖把學(xué)習(xí)內(nèi)容記錄下來 并參考一些博客文章
本期的內(nèi)容是AVAudioPlayer

音頻知識基礎(chǔ)

音頻文件的生成過程是將聲音信息采樣骨宠、量化編碼產(chǎn)生的數(shù)字信號的過程擦剑,人耳所能聽到的聲音凹蜂,最低的頻率是從20Hz起一直到最高頻率20KHZ民镜,因此音頻文件格式的最大帶寬是20KHZ捕捂。根據(jù)奈奎斯特的理論,只有采樣頻率高于聲音信號最高頻率的兩倍時替饿,才能把數(shù)字信號表示的聲音還原成為原來的聲音滤馍,所以音頻文件的采樣率一般在40~50KHZ,比如最常見的CD音質(zhì)采樣率44.1KHZ掸掸。 (所以一般大家都覺得CD音質(zhì)是最好的.) 對聲音進(jìn)行采樣氯庆、量化過程被稱為脈沖編碼調(diào)制(Pulse Code Modulation),簡稱PCM扰付。PCM數(shù)據(jù)是最原始的音頻數(shù)據(jù)完全無損,所以PCM數(shù)據(jù)雖然音質(zhì)優(yōu)秀但體積龐大仁讨,為了解決這個問題先后誕生了一系列的音頻格式羽莺,這些音頻格式運(yùn)用不同的方法對音頻數(shù)據(jù)進(jìn)行壓縮,其中有無損壓縮(ALAC洞豁、APE盐固、FLAC)和有損壓縮(MP3、AAC丈挟、OGG刁卜、WMA)兩種 來源:iOS音頻播放 (一):概述 by 碼農(nóng)人生

我覺得程寅大牛的處理音頻說的很明白
大神列出一個經(jīng)典的音頻播放流程(以MP3為例)

  1. 讀取MP3文件
  2. 解析采樣率、碼率曙咽、時長等信息蛔趴,分離MP3中的音頻幀
  3. 對分離出來的音頻幀解碼得到PCM數(shù)據(jù)
  4. 對PCM數(shù)據(jù)進(jìn)行音效處理(均衡器、混響器等例朱,非必須)
  5. 把PCM數(shù)據(jù)解碼成音頻信號
  6. 把音頻信號交給硬件播放
  7. 重復(fù)1-6步直到播放完成

在iOS系統(tǒng)中apple對上述的流程進(jìn)行了封裝并提供了不同層次的接口


這是CoreAudio的接口層次

下面對其中的中高層接口進(jìn)行功能說明:

  • Audio File Services:讀寫音頻數(shù)據(jù)孝情,可以完成播放流程中的第2步鱼蝉;
  • Audio File Stream Services:對音頻進(jìn)行解碼,可以完成播放流程中的第2步箫荡;
  • Audio Converter services:音頻數(shù)據(jù)轉(zhuǎn)換魁亦,可以完成播放流程中的第3步;
  • Audio Processing Graph Services:音效處理模塊羔挡,可以完成播放流程中的第4步洁奈;
  • Audio Unit Services:播放音頻數(shù)據(jù):可以完成播放流程中的第5步、第6步绞灼;
  • Extended Audio File Services:Audio File Services和Audio
  • Converter services的結(jié)合體利术;
  • AVAudioPlayer/AVPlayer(AVFoundation):高級接口,可以完成整個音頻播放的過程(包括本地文件和網(wǎng)絡(luò)流播放镀赌,第4步除外)氯哮;
  • Audio Queue Services:高級接口,可以進(jìn)行錄音和播放商佛,可以完成播放流程中的第3喉钢、5、6步良姆;
  • OpenAL:用于游戲音頻播放肠虽,暫不討論

可以看到apple提供的接口類型非常豐富,可以滿足各種類別類需求:

  • 如果你只是想實現(xiàn)音頻的播放玛追,沒有其他需求AVFoundation會很好的滿足你的需求税课。它的接口使用簡單、不用關(guān)心其中的細(xì)節(jié)痊剖;

  • 如果你的app需要對音頻進(jìn)行流播放并且同時存儲韩玩,那么AudioFileStreamer加AudioQueue能夠幫到你,你可以先把音頻數(shù)據(jù)下載到本地陆馁,一邊下載一邊用NSFileHandler等接口讀取本地音頻文件并交給AudioFileStreamer或者AudioFile解析分離音頻幀找颓,分離出來的音頻幀可以送給AudioQueue進(jìn)行解碼和播放。如果是本地文件直接讀取文件解析即可叮贩。(這兩個都是比較直接的做法击狮,這類需求也可以用AVFoundation+本地server的方式實現(xiàn),AVAudioPlayer會把請求發(fā)送給本地server益老,由本地server轉(zhuǎn)發(fā)出去彪蓬,獲取數(shù)據(jù)后在本地server中存儲并轉(zhuǎn)送給AVAudioPlayer。另一個比較trick的做法是先把音頻下載到文件中捺萌,在下載到一定量的數(shù)據(jù)后把文件路徑給AVAudioPlayer播放档冬,當(dāng)然這種做法在音頻seek后就回有問題了。)

  • 如果你正在開發(fā)一個專業(yè)的音樂播放軟件,需要對音頻施加音效(均衡器捣郊、混響器)辽狈,那么除了數(shù)據(jù)的讀取和解析以外還需要用到AudioConverter來把音頻數(shù)據(jù)轉(zhuǎn)換成PCM數(shù)據(jù),再由AudioUnit+AUGraph來進(jìn)行音效處理和播放(但目前多數(shù)帶音效的app都是自己開發(fā)音效模塊來坐PCM數(shù)據(jù)的處理呛牲,這部分功能自行開發(fā)在自定義性和擴(kuò)展性上會比較強(qiáng)一些刮萌。PCM數(shù)據(jù)通過音效器處理完成后就可以使用AudioUnit播放了,當(dāng)然AudioQueue也支持直接使對PCM數(shù)據(jù)進(jìn)行播放娘扩。)着茸。下圖描述的就是使用AudioFile + AudioConverter + AudioUnit進(jìn)行音頻播放的流程

image

以上內(nèi)容均轉(zhuǎn)自碼農(nóng)人生 希望大神不要介意 如果有問題 我可立即清除

使用AVAudioPlayer之前對AudioSession簡介

AVAudioSession負(fù)責(zé)管理音頻會話 它是個單例 在應(yīng)用程序和操作系統(tǒng)之間負(fù)責(zé)中間人的角色 AudioSession參考

AVAudioSession主要功能包括以下幾點:

  • app是如何使用的音頻服務(wù) 播放 還是錄制 之類的
  • 控制協(xié)調(diào)app輸入輸出設(shè)備(比如 麥克風(fēng),耳機(jī)琐旁、手機(jī)外放比如藍(lán)牙連接一個外置音響 或airplay)
  • 協(xié)調(diào)你的app的音頻播放和系統(tǒng)以及其他app行為(例如有電話時需要打斷涮阔,電話結(jié)束時需要恢復(fù),按下靜音按鈕時是否歌曲也要靜音等)

注:AVAudioSession iOS6以后使用 以前叫AudioSession

如何使用AVAudioPlayer

在我的博客里面我盡量使用code勝過千言萬語
使用AVAudioPlayer之前需要在AppDelegate里面導(dǎo)入#import <AVFoundation/AVFoundation.h>
并且啟動音頻會話

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    AVAudioSession *session = [AVAudioSession sharedInstance];
    NSError *error;
    if (![session setCategory:AVAudioSessionCategoryPlayback error:&error]) {
        NSLog(@"Category Error: %@", [error localizedDescription]);
    }

    if (![session setActive:YES error:&error]) {
        NSLog(@"Activation Error: %@", [error localizedDescription]);
    }

    return YES;
}

上邊已經(jīng)介紹了AVAudioSession

這里面說一下[session setCategory:AVAudioSessionCategoryPlayback error:&error] 里面的AVAudioSessionCategoryPlayback

音頻會話分類

這是這幾種分類的列表大家可以看下

記得開啟后臺播放


或者在plist里面修改


下面就是創(chuàng)建音頻播放器代碼

#import "ViewController.h"
#import <Masonry/Masonry.h>
#import "THControlKnob.h"
#import "THPlayButton.h"
#import <AVFoundation/AVFoundation.h>
@interface ViewController ()
//三個控制推子
@property (weak, nonatomic) IBOutlet THOrangeControlKnob *panKnob;
@property (weak, nonatomic) IBOutlet THOrangeControlKnob *volumnKnob;
@property (weak, nonatomic) IBOutlet THGreenControlKnob *rateKnob;
@property (weak, nonatomic) IBOutlet THPlayButton *playButton;
//音樂播放器
@property (nonatomic, strong) AVAudioPlayer *musicPlayer;
@property (nonatomic, getter = isPlaying) BOOL playing; //播放狀態(tài)
//無關(guān)代碼
@property (weak, nonatomic) IBOutlet UILabel *LeftRightRoundDec;
@property (weak, nonatomic) IBOutlet UILabel *voiceDec;
@property (weak, nonatomic) IBOutlet UILabel *rateDec;
@property (weak, nonatomic) IBOutlet UILabel *trackDescrption;
@end

導(dǎo)入幾個第三方控件的類用于音樂播放

這上邊的三個旋鈕就是導(dǎo)入的開源庫

下面創(chuàng)建播放器AVAudioPlayer
創(chuàng)建時需要一個NSURL代表要播放的文件路徑 這里簡單從bundle中拖了一首歌進(jìn)去了

#pragma mark -
#pragma mark - 創(chuàng)建AVAudioPlayer與播放狀態(tài)控制
/**

 創(chuàng)建音樂播放器

 @param fileName 文件名
 @param fileExtension 文件擴(kuò)展名
 @return 播放器實例
 */
- (AVAudioPlayer *)createPlayForFile:(NSString *)fileName
                       withExtension:(NSString *)fileExtension{
    NSURL *url = [[NSBundle mainBundle] URLForResource:fileName withExtension:fileExtension];
    NSError *error = nil;
    AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
    if (audioPlayer) {
        audioPlayer.numberOfLoops = -1; //-1無限循環(huán)
        audioPlayer.enableRate = YES; //啟動倍速控制
        [audioPlayer prepareToPlay];
    } else {
        NSLog(@"Error creating player: %@",[error localizedDescription]);
    }
    return audioPlayer;
}

numberOfLoops = -1; 代表本首歌 無限循環(huán) 其它常數(shù)代表循環(huán)次數(shù)
enableRate 代表是否啟用倍速調(diào)節(jié) 0.5x 1.0x 2.0x 等倍速 1.0代表正常速度

這里說一下[audioPlayer prepareToPlay]
調(diào)用這個函數(shù)是為了取得需要的音頻硬件并預(yù)加載Audio Queue的緩沖區(qū). 當(dāng)然也可以不調(diào)用這個方法直接調(diào)用 [audioPlayer play]灰殴,但當(dāng) 調(diào)用play方法時也會隱性激活,調(diào)用prepareToPlay是為了減少 創(chuàng)建播放器時預(yù)設(shè)加載和聽到聲音輸出之間的延時

@implementation ViewController
- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        if (self.musicPlayer == nil) {
            self.musicPlayer = [self createPlayForFile:@"384551_1438267683" withExtension:@"mp3"];
        }
        [self setupNotifications];
    }
    return self;
}
- (void)awakeFromNib{
    [super awakeFromNib];
    if (self.musicPlayer == nil) {
        self.musicPlayer = [self createPlayForFile:@"384551_1438267683" withExtension:@"mp3"];
    }
    [self setupNotifications];
}

initWithNibNameawakeFromNib時候調(diào)用一下創(chuàng)建播放器的代碼
這個[self setupNotifications];后面說

先添加一些常見的方法封裝 比如 播放敬特、暫停、停止

- (void)play {
    if (self.musicPlayer == nil) { return; }
    if (!self.playing) {
        NSTimeInterval delayTime = [self.musicPlayer deviceCurrentTime] + 0.01;
        [self.musicPlayer playAtTime:delayTime];
        self.playing = YES;
    }

    self.trackDescrption.text = [self.musicPlayer.url absoluteString];
    [self configNowPlayingInfoCenter]; //配置后臺播放的頁面信息
}
- (void)stop {
    if (self.musicPlayer == nil) { return; }
    if (self.playing) {
        [self.musicPlayer stop];
        self.musicPlayer.currentTime = 0.0f;
        self.playing = NO;
    }
}
- (void)pause {
    if (self.musicPlayer == nil) { return; }
    if (self.playing) {
        [self.musicPlayer pause];
        self.playing = NO;
    }
}

這里看到[self.musicPlayer deviceCurrentTime] + 0.01 加了 -0.01的延時, 是為了以后大家做播放器的時候 有可能暫臀眨或者歌曲切換時 有可能 向前向后做片段銜接, 也是為了使用 playAtTime去播放 指定位置的音樂用于 意外暫臀袄或者播放上次播放的配置信息使用 這里看到我寫了一個
[self configNowPlayingInfoCenter];配置后臺播放的頁面信息
這個主要用于播放音樂在后臺時 鎖屏顯示的屏幕信息 請看下面代碼

//設(shè)置鎖屏狀態(tài),顯示的歌曲信息
-(void)configNowPlayingInfoCenter{
    if (NSClassFromString(@"MPNowPlayingInfoCenter")) {
        NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];

        //歌曲名稱
        [dict setObject:@"歌曲名稱" forKey:MPMediaItemPropertyTitle];

        //演唱者
        [dict setObject:@"演唱者" forKey:MPMediaItemPropertyArtist];

        //專輯名
        [dict setObject:@"專輯名" forKey:MPMediaItemPropertyAlbumTitle];

        //專輯縮略圖
        UIImage *image = [UIImage imageNamed:@"sunyazhou"];
        MPMediaItemArtwork *artwork = [[MPMediaItemArtwork alloc] initWithImage:image];
        [dict setObject:artwork forKey:MPMediaItemPropertyArtwork];

        //音樂剩余時長
        [dict setObject:@20 forKey:MPMediaItemPropertyPlaybackDuration];

        //音樂當(dāng)前播放時間 在計時器中修改
       // [dict setObject:[NSNumber numberWithDouble:100.0] forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime];

        //設(shè)置鎖屏狀態(tài)下屏幕顯示播放音樂信息
        [[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:dict];
    }
}

如果需要在計時器中不斷刷新鎖屏狀態(tài)下的播放進(jìn)度條請寫如下代碼

//計時器修改進(jìn)度
- (void)changeProgress:(NSTimer *)sender{
    if(self.player){
        //當(dāng)前播放時間
        NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithDictionary:[[MPNowPlayingInfoCenter defaultCenter] nowPlayingInfo]];
        [dict setObject:[NSNumber numberWithDouble:self.player.currentTime] forKey:MPNowPlayingInfoPropertyElapsedPlaybackTime]; //音樂當(dāng)前已經(jīng)過時間
        [[MPNowPlayingInfoCenter defaultCenter] setNowPlayingInfo:dict];
    }
}

下面我們來介紹一下
[self setupNotifications];注冊監(jiān)聽 音頻意外中斷和耳機(jī)拔出時要暫停音樂播放
實現(xiàn)代碼如下

/**
 播放的通知處理
 */
- (void)setupNotifications {
    NSNotificationCenter *nsnc = [NSNotificationCenter defaultCenter];

    //添加意外中斷音頻播放的通知
    [nsnc addObserver:self
             selector:@selector(handleInterruption:)
                 name:AVAudioSessionInterruptionNotification
               object:[AVAudioSession sharedInstance]];

    //添加線路變化通知
    [nsnc addObserver:self
             selector:@selector(hanldeRouteChange:)
                 name:AVAudioSessionRouteChangeNotification
               object:[AVAudioSession sharedInstance]];
}

注:記得在delloc里面[[NSNotificationCenter defaultCenter] removeObserver:self]

意外中斷音頻發(fā)生的場景 例如 聽歌過程中來電話或者 按住home鍵使用siri

下面是具體方法實現(xiàn)

/**
 音頻意外打斷處理
 @param notification 通知信息
 */
- (void)handleInterruption:(NSNotification *)notification {
    NSDictionary *info = notification.userInfo;
    AVAudioSessionInterruptionType type = [info[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
    if (type == AVAudioSessionInterruptionTypeBegan) {
        //Handle AVAudioSessionInterruptionTypeBegan
        [self pause];
    } else {
        //Handle AVAudioSessionInterruptionTypeEnded
        AVAudioSessionInterruptionOptions options = [info[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
        NSError *error = nil;
        //激活音頻會話 允許外接音響
        [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback
                                         withOptions:AVAudioSessionCategoryOptionAllowBluetooth error:nil];
        [[AVAudioSession sharedInstance] setActive:YES withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:&error];
        if (options == AVAudioSessionInterruptionOptionShouldResume) {
            [self play];
        } else {
            [self play];
        }

        self.playButton.selected = YES;

        if (error) {
            NSLog(@"AVAudioSessionInterruptionOptionShouldResume失敗:%@",[error localizedDescription]);
        }
    }
}

先說handleInterruption意外情況下中斷比如我按住home鍵使用siri
我會收到意外打斷的通知當(dāng) type == AVAudioSessionInterruptionTypeBegan時 我們停止音樂播放或者暫停.
當(dāng)type != AVAudioSessionInterruptionTypeBegan的時候一定是AVAudioSessionInterruptionTypeEnded這個時候notification.userInfo里面包含一個AVAudioSessionInterruptionOptions值來表明音頻會話是否已經(jīng)重新激活以及是否可以再次播放

注:這個地方遇到個坑 當(dāng)意外中斷時候有時音頻會話會很不靈敏 后來發(fā)現(xiàn)這種情況下需要重新激活會話 如下代碼:

[[AVAudioSession sharedInstance] setActive:YES withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:&error];

這里AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation是為了通知其它應(yīng)用會話被我激活了 很多播放器開發(fā)者很不講究 每次從來不用這個方法導(dǎo)致每次別人播放完音頻 自己都收不到音頻重新播放的信息 建議大家以和為貴, 寫良心代碼.

因為我外接的小米藍(lán)牙音響發(fā)現(xiàn)還是不好使 最后又補(bǔ)上了AVAudioSessionCategoryOptionAllowBluetooth這個

激活音頻會話 允許外接音響

[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionAllowBluetooth error:nil];

就好使了

下面說一下耳機(jī)插拔或者USB麥克風(fēng)斷開 Apple有個什么Human Interface Guidelines(HIG)相關(guān)定義 意思是說當(dāng)硬件耳機(jī)拔出時建議 暫停播放音樂或者麥克風(fēng)斷開時掰伸。就是處于靜音狀態(tài)皱炉。是為了保密播放內(nèi)容不被外界聽到,不管蘋果啥規(guī)定 我們都得照辦 否則就得被拒。

- (void)hanldeRouteChange:(NSNotification *)notification {
    NSDictionary *info = notification.userInfo;
    AVAudioSessionRouteChangeReason reason = [info[AVAudioSessionRouteChangeReasonKey] unsignedIntegerValue];
    //老設(shè)備不可用
    if (reason == AVAudioSessionRouteChangeReasonOldDeviceUnavailable) {
        AVAudioSessionRouteDescription *previousRoute = info[AVAudioSessionRouteChangePreviousRouteKey];
        AVAudioSessionPortDescription *previousOutput = previousRoute.outputs[0];
        NSString *portType = previousOutput.portType;
        if ([portType isEqualToString:AVAudioSessionPortHeadphones]) {
            [self stop];
            self.playButton.selected = NO;
        }
    }
}

這需要用AVAudioSessionRouteChangeReasonKey取出線路切換的原因AVAudioSessionRouteChangeReason 原因有這么多

typedef NS_ENUM(NSUInteger, AVAudioSessionRouteChangeReason)
{
    AVAudioSessionRouteChangeReasonUnknown = 0,
    AVAudioSessionRouteChangeReasonNewDeviceAvailable = 1,
    AVAudioSessionRouteChangeReasonOldDeviceUnavailable = 2,
    AVAudioSessionRouteChangeReasonCategoryChange = 3,
    AVAudioSessionRouteChangeReasonOverride = 4,
    AVAudioSessionRouteChangeReasonWakeFromSleep = 6,
    AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory = 7,
    AVAudioSessionRouteChangeReasonRouteConfigurationChange NS_ENUM_AVAILABLE_IOS(7_0) = 8
} NS_AVAILABLE_IOS(6_0);

我們需要這個AVAudioSessionRouteChangeReasonOldDeviceUnavailable 判斷是否是舊設(shè)備
通過AVAudioSessionRouteChangePreviousRouteKey拿出

AVAudioSessionRouteDescription描述信息
previousRoute 在通過
previousRoute.outputs[0]拿出AVAudioSessionPortDescription

拿出NSString *portType = previousOutput.portType

如果[portType isEqualToString:AVAudioSessionPortHeadphones]

如果是耳機(jī)AVAudioSessionPortHeadphones則暫停播放

以上就是中斷和線路切換的一些代碼邏輯

下面我介紹一些好玩的

前面說的一些后臺設(shè)置信息顯示的內(nèi)容就是上圖所示 在鎖屏的時候顯示

但是大家一定很奇怪的是怎么實現(xiàn)接收 鎖屏狀態(tài)下 點擊 上一曲 暫停/播放 下一曲等操作

需要在AppDelegate里面寫上

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    AVAudioSession *session = [AVAudioSession sharedInstance];
    NSError *error;
    if (![session setCategory:AVAudioSessionCategoryPlayback error:&error]) {
        NSLog(@"Category Error: %@", [error localizedDescription]);
    }

    if (![session setActive:YES error:&error]) {
        NSLog(@"Activation Error: %@", [error localizedDescription]);
    }

    [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
    [self becomeFirstResponder];
    return YES;
}

[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
行代碼 以及調(diào)用自己為 [self becomeFirstResponder];第一響應(yīng)者 這樣寫是為了應(yīng)用響應(yīng)音頻播放 后臺切換或者中斷的時候更靈敏.

- (BOOL)canBecomeFirstResponder {
    return YES;
}

然后 寫上如下代碼 處理鎖屏狀態(tài)下 點擊 上一曲 暫停/播放 下一曲等操作

- (void)remoteControlReceivedWithEvent:(UIEvent *)event {
    if (event.type == UIEventTypeRemoteControl) {
        switch (event.subtype) {
            case UIEventSubtypeRemoteControlPlay:
                NSLog(@"暫停播放");
                break;
            case UIEventSubtypeRemoteControlPause:

                NSLog(@"繼續(xù)播放");
                break;
            case UIEventSubtypeRemoteControlNextTrack:
                NSLog(@"下一曲");
                break;
            case UIEventSubtypeRemoteControlPreviousTrack:
                NSLog(@"上一曲");
                break;
            default:
                break;
        }
    }
}

剩余邏輯大家自己填充吧我就不介紹了.

好了AVAudioPlayer就到這吧狮鸭!有啥疑問大家可以評論留言都能看到或者指正我的錯誤合搅。我會及時改正.

全文完

文章最終的Demo獲取:加iOS高級技術(shù)交流群:624212887歧蕉,獲取Demo,以及更多iOS學(xué)習(xí)資料

文章來源于網(wǎng)絡(luò)灾部,如有侵權(quán)請聯(lián)系小編刪除

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市惯退,隨后出現(xiàn)的幾起案子梳猪,更是在濱河造成了極大的恐慌,老刑警劉巖蒸痹,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異呛哟,居然都是意外死亡叠荠,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門扫责,熙熙樓的掌柜王于貴愁眉苦臉地迎上來榛鼎,“玉大人,你說我怎么就攤上這事≌哂椋” “怎么了抡笼?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長黄鳍。 經(jīng)常有香客問我推姻,道長,這世上最難降的妖魔是什么框沟? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任藏古,我火速辦了婚禮,結(jié)果婚禮上忍燥,老公的妹妹穿的比我還像新娘拧晕。我一直安慰自己,他們只是感情好梅垄,可當(dāng)我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布厂捞。 她就那樣靜靜地躺著,像睡著了一般队丝。 火紅的嫁衣襯著肌膚如雪靡馁。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天炭玫,我揣著相機(jī)與錄音奈嘿,去河邊找鬼。 笑死吞加,一個胖子當(dāng)著我的面吹牛裙犹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播衔憨,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼叶圃,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了践图?” 一聲冷哼從身側(cè)響起掺冠,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎码党,沒想到半個月后德崭,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡揖盘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年眉厨,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片兽狭。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡憾股,死狀恐怖鹿蜀,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情服球,我是刑警寧澤茴恰,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站斩熊,受9級特大地震影響往枣,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜座享,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一婉商、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧渣叛,春花似錦丈秩、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至箫攀,卻和暖如春肠牲,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背靴跛。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工缀雳, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人梢睛。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓肥印,卻偏偏與公主長得像,于是被迫代替她去往敵國和親绝葡。 傳聞我的和親對象是個殘疾皇子深碱,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,577評論 2 353