HOME 首頁(yè)
SERVICE 服務(wù)產(chǎn)品
XINMEITI 新媒體代運(yùn)營(yíng)
CASE 服務(wù)案例
NEWS 熱點(diǎn)資訊
ABOUT 關(guān)于我們
CONTACT 聯(lián)系我們
創(chuàng)意嶺
讓品牌有溫度、有情感
專(zhuān)注品牌策劃15年

    alsa景觀設(shè)計(jì)大賽時(shí)間(alsa景觀獲獎(jiǎng)作品)

    發(fā)布時(shí)間:2023-03-07 05:07:01     稿源: 創(chuàng)意嶺    閱讀: 80        問(wèn)大家

    大家好!今天讓小編來(lái)大家介紹下關(guān)于alsa景觀設(shè)計(jì)大賽時(shí)間的問(wèn)題,以下是小編對(duì)此問(wèn)題的歸納整理,讓我們一起來(lái)看看吧。

    創(chuàng)意嶺作為行業(yè)內(nèi)優(yōu)秀的企業(yè),服務(wù)客戶(hù)遍布全球各地,相關(guān)業(yè)務(wù)請(qǐng)撥打電話(huà):175-8598-2043,或添加微信:1454722008(行業(yè)最低價(jià))

    文章目錄列表:

    alsa景觀設(shè)計(jì)大賽時(shí)間(alsa景觀獲獎(jiǎng)作品)

    一、哈佛的景觀設(shè)計(jì)研究生有ALSA認(rèn)證么

    ASLA認(rèn)證吧?景觀設(shè)計(jì)應(yīng)該是這個(gè)。設(shè)計(jì)類(lèi)專(zhuān)業(yè)估計(jì)是很多留學(xué)機(jī)構(gòu)比較少做,懂的人不多。 American Society of Landscape Architects

    二、如何修改alsa支持96khz的pcm輸出

    這里了解一下各個(gè)參數(shù)的含義以及一些基本概念。

    樣本長(zhǎng)度(sample):樣本是記錄音頻數(shù)據(jù)最基本的單位,常見(jiàn)的有8位和16位。

    通道數(shù)(channel):該參數(shù)為1表示單聲道,2則是立體聲。

    楨(frame):楨記錄了一個(gè)聲音單元,其長(zhǎng)度為樣本長(zhǎng)度與通道數(shù)的乘積。

    采樣率(rate):每秒鐘采樣次數(shù),該次數(shù)是針對(duì)楨而言。

    周期(period):音頻設(shè)備一次處理所需要的楨數(shù),對(duì)于音頻設(shè)備的數(shù)據(jù)訪(fǎng)問(wèn)以及音頻數(shù)據(jù)的存儲(chǔ),都是以此為單位。

    交錯(cuò)模式(interleaved):是一種音頻數(shù)據(jù)的記錄方式,在交錯(cuò)模式下,數(shù)據(jù)以連續(xù)楨的形式存放,即首先記錄完楨1的左聲道樣本和右聲道樣本(假設(shè)為立體聲格式),再開(kāi)始楨2的記錄。而在非交錯(cuò)模式下,首先記錄的是一個(gè)周期內(nèi)所有楨的左聲道樣本,再記錄右聲道樣本,數(shù)據(jù)是以連續(xù)通道的方式存儲(chǔ)。不過(guò)多數(shù)情況下,我們只需要使用交錯(cuò)模式就可以了。

    period(周期):硬件中中斷間的間隔時(shí)間。它表示輸入延時(shí)。

    聲卡接口中有一個(gè)指針來(lái)指示聲卡硬件緩存區(qū)中當(dāng)前的讀寫(xiě)位置。只要接口在運(yùn)行,這個(gè)指針將循環(huán)地指向緩存區(qū)中的某個(gè)位置。

    frame size = sizeof(one sample) * nChannels

    alsa中配置的緩存(buffer)和周期(size)大小在runtime中是以幀(frames)形式存儲(chǔ)的。

    period_bytes = frames_to_bytes(runtime, runtime->period_size);

    bytes_to_frames()

    The period and buffer sizes are not dependent on the sample format because they are measured in frames; you do not need to change them.

    ALSA聲音編程介紹

    ALSA表示高級(jí)Linux聲音體系結(jié)構(gòu)(Advanced Linux Sound Architecture)。它由一系列內(nèi)核驅(qū)動(dòng),應(yīng)用程序編譯接口(API)以及支持Linux下聲音的實(shí)用程序組成。這篇文章里,我將簡(jiǎn)單介紹ALSA項(xiàng)目的基本框架以及它的軟件組成。主要集中介紹PCM接口編程,包括您可以自動(dòng)實(shí)踐的程序示例。

    您使用ALSA的原因可能就是因?yàn)樗苄?,但它并不是唯一可用的聲音API。如果您想完成低級(jí)的聲音操作,以便能夠最大化地控制聲音并最大化地提高性能,或者如果您使用其它聲音API沒(méi)有的特性,那么ALSA是很好的選擇。如果您已經(jīng)寫(xiě)了一個(gè)音頻程序,你可能想要為ALSA聲卡驅(qū)動(dòng)添加本地支持。如果您對(duì)音頻不感興趣,只是想播放音頻文件,那么高級(jí)的API將是更好的選擇,比如SDL,OpenAL以及那些桌面環(huán)境提供的工具集。另外,您只能在有ALSA支持的Linux環(huán)境中使用ALSA。

    ALSA歷史

    ALSA項(xiàng)目發(fā)起的起因是Linux下的聲卡驅(qū)動(dòng)(OSS/Free drivers)沒(méi)有得到積極的維護(hù)。并且落后于新的聲卡技術(shù)。Jaroslav Kysela早先寫(xiě)了一個(gè)聲卡驅(qū)動(dòng),并由此開(kāi)始了ALSA項(xiàng)目,隨便,更多的開(kāi)發(fā)者加入到開(kāi)發(fā)隊(duì)伍中,更多的聲卡得到支持,API的結(jié)構(gòu)也得到了重組。

    Linux內(nèi)核2.5在開(kāi)發(fā)過(guò)程中,ALSA被合并到了官方的源碼樹(shù)中。在發(fā)布內(nèi)核2.6后,ALSA已經(jīng)內(nèi)建在穩(wěn)定的內(nèi)核版本中并將廣泛地使用。

    數(shù)字音頻基礎(chǔ)

    聲音由變化的氣壓組成。它被麥克風(fēng)這樣的轉(zhuǎn)換器轉(zhuǎn)換成電子形式。模/數(shù)(ADC)轉(zhuǎn)換器將模擬電壓轉(zhuǎn)換成離散的樣本值。聲音以固定的時(shí)間間隔被采樣,采樣的速率稱(chēng)為采樣率。把樣本輸出到數(shù)/模(DAC)轉(zhuǎn)換器,比如擴(kuò)音器,最后轉(zhuǎn)換成原來(lái)的模擬信號(hào)。

    樣本大小以位來(lái)表示。樣本大小是影響聲音被轉(zhuǎn)換成數(shù)字信號(hào)的精確程度的因素之一。另一個(gè)主要的因素是采樣率。奈奎斯特(Nyquist)理論中,只要離散系統(tǒng)的奈奎斯特頻率高于采樣信號(hào)的最高頻率或帶寬,就可以避免混疊現(xiàn)象。

    ALSA基礎(chǔ)

    ALSA由許多聲卡的聲卡驅(qū)動(dòng)程序組成,同時(shí)它也提供一個(gè)稱(chēng)為libasound的API庫(kù)。應(yīng)用程序開(kāi)發(fā)者應(yīng)該使用libasound而不是內(nèi)核中的ALSA接口。因?yàn)閘ibasound提供最高級(jí)并且編程方便的編程接口。并且提供一個(gè)設(shè)備邏輯命名功能,這樣開(kāi)發(fā)者甚至不需要知道類(lèi)似設(shè)備文件這樣的低層接口。相反,OSS/Free驅(qū)動(dòng)是在內(nèi)核系統(tǒng)調(diào)用級(jí)上編程,它要求開(kāi)發(fā)者提供設(shè)備文件名并且利用ioctrl來(lái)實(shí)現(xiàn)相應(yīng)的功能。為了向后兼容,ALSA提供內(nèi)核模塊來(lái)模擬OSS,這樣之前的許多在OSS基礎(chǔ)上開(kāi)發(fā)的應(yīng)用程序不需要任何改動(dòng)就可以在ALSA上運(yùn)行。另外,libaoss庫(kù)也可以模擬OSS,而它不需要內(nèi)核模塊。

    ALSA包含插件功能,使用插件可以擴(kuò)展新的聲卡驅(qū)動(dòng),包括完全用軟件實(shí)現(xiàn)的虛擬聲卡。ALSA提供一系列基于命令行的工具集,比如混音器(mixer),音頻文件播放器(aplay),以及控制特定聲卡特定屬性的工具。

    ALSA體系結(jié)構(gòu)

    ALSA API可以分解成以下幾個(gè)主要的接口:

    1 控制接口:提供管理聲卡注冊(cè)和請(qǐng)求可用設(shè)備的通用功能

    2 PCM接口:管理數(shù)字音頻回放(playback)和錄音(capture)的接口。本文后續(xù)總結(jié)重點(diǎn)放在這個(gè)接口上,因?yàn)樗情_(kāi)發(fā)數(shù)字音頻程序最常用到的接口。

    3 Raw MIDI接口:支持MIDI(Musical Instrument Digital Interface),標(biāo)準(zhǔn)的電子樂(lè)器。這些API提供對(duì)聲卡上MIDI總線(xiàn)的訪(fǎng)問(wèn)。這個(gè)原始接口基于MIDI事件工作,由程序員負(fù)責(zé)管理協(xié)議以及時(shí)間處理。

    4 定時(shí)器(Timer)接口:為同步音頻事件提供對(duì)聲卡上時(shí)間處理硬件的訪(fǎng)問(wèn)。

    5 時(shí)序器(Sequencer)接口

    6 混音器(Mixer)接口

    設(shè)備命名

    API庫(kù)使用邏輯設(shè)備名而不是設(shè)備文件。設(shè)備名字可以是真實(shí)的硬件名字也可以是插件名字。硬件名字使用hw:i,j這樣的格式。其中i是卡號(hào),j是這塊聲卡上的設(shè)備號(hào)。第一個(gè)聲音設(shè)備是hw:0,0.這個(gè)別名默認(rèn)引用第一塊聲音設(shè)備并且在本文示例中一真會(huì)被用到。插件使用另外的唯一名字。比如plughw:,表示一個(gè)插件,這個(gè)插件不提供對(duì)硬件設(shè)備的訪(fǎng)問(wèn),而是提供像采樣率轉(zhuǎn)換這樣的軟件特性,硬件本身并不支持這樣的特性。

    聲音緩存和數(shù)據(jù)傳輸

    每個(gè)聲卡都有一個(gè)硬件緩存區(qū)來(lái)保存記錄下來(lái)的樣本。當(dāng)緩存區(qū)足夠滿(mǎn)時(shí),聲卡將產(chǎn)生一個(gè)中斷。內(nèi)核聲卡驅(qū)動(dòng)然后使用直接內(nèi)存(DMA)訪(fǎng)問(wèn)通道將樣本傳送到內(nèi)存中的應(yīng)用程序緩存區(qū)。類(lèi)似地,對(duì)于回放,任何應(yīng)用程序使用DMA將自己的緩存區(qū)數(shù)據(jù)傳送到聲卡的硬件緩存區(qū)中。

    這樣硬件緩存區(qū)是環(huán)緩存。也就是說(shuō)當(dāng)數(shù)據(jù)到達(dá)緩存區(qū)末尾時(shí)將重新回到緩存區(qū)的起始位置。ALSA維護(hù)一個(gè)指針來(lái)指向硬件緩存以及應(yīng)用程序緩存區(qū)中數(shù)據(jù)操作的當(dāng)前位置。從內(nèi)核外部看,我們只對(duì)應(yīng)用程序的緩存區(qū)感興趣,所以本文只討論應(yīng)用程序緩存區(qū)。

    應(yīng)用程序緩存區(qū)的大小可以通過(guò)ALSA庫(kù)函數(shù)調(diào)用來(lái)控制。緩存區(qū)可以很大,一次傳輸操作可能會(huì)導(dǎo)致不可接受的延遲,我們把它稱(chēng)為延時(shí)(latency)。為了解決這個(gè)問(wèn)題,ALSA將緩存區(qū)拆分成一系列周期(period)(OSS/Free中叫片斷fragments).ALSA以period為單元來(lái)傳送數(shù)據(jù)。

    一個(gè)周期(period)存儲(chǔ)一些幀(frames)。每一幀包含時(shí)間上一個(gè)點(diǎn)所抓取的樣本。對(duì)于立體聲設(shè)備,一個(gè)幀會(huì)包含兩個(gè)信道上的樣本。圖1展示了分解過(guò)程:一個(gè)緩存區(qū)分解成周期,然后是幀,然后是樣本。圖中包含一些假定的數(shù)值。圖中左右信道信息被交替地存儲(chǔ)在一個(gè)幀內(nèi)。這稱(chēng)為交錯(cuò)(interleaved)模式。在非交錯(cuò)模式中,一個(gè)信道的所有樣本數(shù)據(jù)存儲(chǔ)在另外一個(gè)信道的數(shù)據(jù)之后。

    Over and Under Run

    當(dāng)一個(gè)聲卡活動(dòng)時(shí),數(shù)據(jù)總是連續(xù)地在硬件緩存區(qū)和應(yīng)用程序緩存區(qū)間傳輸。但是也有例外。在錄音例子中,如果應(yīng)用程序讀取數(shù)據(jù)不夠快,循環(huán)緩存區(qū)將會(huì)被新的數(shù)據(jù)覆蓋。這種數(shù)據(jù)的丟失被稱(chēng)為overrun.在回放例子中,如果應(yīng)用程序?qū)懭霐?shù)據(jù)到緩存區(qū)中的速度不夠快,緩存區(qū)將會(huì)"餓死"。這樣的錯(cuò)誤被稱(chēng)為"underrun"。在ALSA文檔中,有時(shí)將這兩種情形統(tǒng)稱(chēng)為"XRUN"。適當(dāng)?shù)卦O(shè)計(jì)應(yīng)用程序可以最小化XRUN并且可以從中恢復(fù)過(guò)來(lái)。

    一個(gè)典型的聲音程序

    使用PCM的程序通常類(lèi)似下面的偽代碼:

    打開(kāi)回放或錄音接口

    設(shè)置硬件參數(shù)(訪(fǎng)問(wèn)模式,數(shù)據(jù)格式,信道數(shù),采樣率,等等)

    while 有數(shù)據(jù)要被處理:

    讀PCM數(shù)據(jù)(錄音)

    或 寫(xiě)PCM數(shù)據(jù)(回放)

    關(guān)閉接口

    我們將在下文中看到一些可以工作的代碼。我建議您在你的Linux系統(tǒng)上測(cè)試運(yùn)行這些代碼。查看輸出并嘗試修改推薦的代碼。和本文相關(guān)的所有實(shí)例清單可以從FTP中獲?。篺tp.ssc.com/pub/lj/listings/issue126/6735.tgz。

    Listing 1. Display Some PCM Types and Formats

    #include <alsa/asoundlib.h>

    int main() {

    int val;

    printf("ALSA library version: %s/n",

    SND_LIB_VERSION_STR);

    printf("/nPCM stream types:/n");

    for (val = 0; val <= SND_PCM_STREAM_LAST; val++)

    printf(" %s/n",

    snd_pcm_stream_name((snd_pcm_stream_t)val));

    printf("/nPCM access types:/n");

    for (val = 0; val <= SND_PCM_ACCESS_LAST; val++)

    printf(" %s/n",

    snd_pcm_access_name((snd_pcm_access_t)val));

    printf("/nPCM formats:/n");

    for (val = 0; val <= SND_PCM_FORMAT_LAST; val++)

    if (snd_pcm_format_name((snd_pcm_format_t)val)

    != NULL)

    printf(" %s (%s)/n",

    snd_pcm_format_name((snd_pcm_format_t)val),

    snd_pcm_format_description(

    (snd_pcm_format_t)val));

    printf("/nPCM subformats:/n");

    for (val = 0; val <= SND_PCM_SUBFORMAT_LAST;

    val++)

    printf(" %s (%s)/n",

    snd_pcm_subformat_name((

    snd_pcm_subformat_t)val),

    snd_pcm_subformat_description((

    snd_pcm_subformat_t)val));

    printf("/nPCM states:/n");

    for (val = 0; val <= SND_PCM_STATE_LAST; val++)

    printf(" %s/n",

    snd_pcm_state_name((snd_pcm_state_t)val));

    return 0;

    }

    清單一顯示了一些ALSA使用的PCM數(shù)據(jù)類(lèi)型和參數(shù)。首先需要做的是包括頭文件。這些頭文件包含了所有庫(kù)函數(shù)的聲明。其中之一就是顯示ALSA庫(kù)的版本。

    這個(gè)程序剩下的部分的迭代一些PCM數(shù)據(jù)類(lèi)型,以流類(lèi)型開(kāi)始。ALSA為每次迭代的最后值提供符號(hào)常量名,并且提供功能函數(shù)以顯示某個(gè)特定值的描述字符串。你將會(huì)看到,ALSA支持許多格式,在我的1.0.15版本里,支持多達(dá)36種格式。

    這個(gè)程序必須鏈接到alsalib庫(kù),通過(guò)在編譯時(shí)需要加上-lasound選項(xiàng)。有些alsa庫(kù)函數(shù)使用dlopen函數(shù)以及浮點(diǎn)操作,所以您可能還需要加上-ldl,-lm選項(xiàng)。

    下面是該程序的Makefile:

    CC=gcc

    TARGET=test

    SRC=$(wildcard *.c)

    OBJECT= ${SRC:.c=.o}

    INCLUDES=-I/usr/include/alsa

    LDFLAGS=-lasound

    all:$(TARGET)

    $(OBJECT):$(SRC)

    $(CC) -c $(INCLUDES) $<

    $(TARGET):$(OBJECT)

    $(CC) -o $@ $< $(LDFLAGS)

    .PHONY:clean

    clean:

    @rm -rf $(OBJECT) $(TARGET) *~

    Listing 2. Opening PCM Device and Setting Parameters

    /*

    This example opens the default PCM device, sets

    some parameters, and then displays the value

    of most of the hardware parameters. It does not

    perform any sound playback or recording.

    */

    /* Use the newer ALSA API */

    #define ALSA_PCM_NEW_HW_PARAMS_API

    /* All of the ALSA library API is defined

    * in this header */

    #include <alsa/asoundlib.h>

    int main() {

    int rc;

    snd_pcm_t *handle;

    snd_pcm_hw_params_t *params;

    unsigned int val, val2;

    int dir;

    snd_pcm_uframes_t frames;

    /* Open PCM device for playback. */

    rc = snd_pcm_open(&handle, "default",

    SND_PCM_STREAM_PLAYBACK, 0);

    if (rc < 0) {

    fprintf(stderr,

    "unable to open pcm device: %s/n",

    snd_strerror(rc));

    exit(1);

    }

    /* Allocate a hardware parameters object. */

    snd_pcm_hw_params_alloca(¶ms);

    /* Fill it in with default values. */

    snd_pcm_hw_params_any(handle, params);

    /* Set the desired hardware parameters. */

    /* Interleaved mode */

    snd_pcm_hw_params_set_access(handle, params,

    SND_PCM_ACCESS_RW_INTERLEAVED);

    /* Signed 16-bit little-endian format */

    snd_pcm_hw_params_set_format(handle, params,

    SND_PCM_FORMAT_S16_LE);

    /* Two channels (stereo) */

    snd_pcm_hw_params_set_channels(handle, params, 2);

    /* 44100 bits/second sampling rate (CD quality) */

    val = 44100;

    snd_pcm_hw_params_set_rate_near(handle,

    params, &val, &dir);

    /* Write the parameters to the driver */

    rc = snd_pcm_hw_params(handle, params);

    if (rc < 0) {

    fprintf(stderr,

    "unable to set hw parameters: %s/n",

    snd_strerror(rc));

    exit(1);

    }

    /* Display information about the PCM interface */

    printf("PCM handle name = '%s'/n",

    snd_pcm_name(handle));

    printf("PCM state = %s/n",

    snd_pcm_state_name(snd_pcm_state(handle)));

    snd_pcm_hw_params_get_access(params,

    (snd_pcm_access_t *) &val);

    printf("access type = %s/n",

    snd_pcm_access_name((snd_pcm_access_t)val));

    snd_pcm_hw_params_get_format(params, &val);

    printf("format = '%s' (%s)/n",

    snd_pcm_format_name((snd_pcm_format_t)val),

    snd_pcm_format_description(

    (snd_pcm_format_t)val));

    snd_pcm_hw_params_get_subformat(params,

    (snd_pcm_subformat_t *)&val);

    printf("subformat = '%s' (%s)/n",

    snd_pcm_subformat_name((snd_pcm_subformat_t)val),

    snd_pcm_subformat_description(

    (snd_pcm_subformat_t)val));

    snd_pcm_hw_params_get_channels(params, &val);

    printf("channels = %d/n", val);

    snd_pcm_hw_params_get_rate(params, &val, &dir);

    printf("rate = %d bps/n", val);

    snd_pcm_hw_params_get_period_time(params,

    &val, &dir);

    printf("period time = %d us/n", val);

    snd_pcm_hw_params_get_period_size(params,

    &frames, &dir);

    printf("period size = %d frames/n", (int)frames);

    snd_pcm_hw_params_get_buffer_time(params,

    &val, &dir);

    printf("buffer time = %d us/n", val);

    snd_pcm_hw_params_get_buffer_size(params,

    (snd_pcm_uframes_t *) &val);

    printf("buffer size = %d frames/n", val);

    snd_pcm_hw_params_get_periods(params, &val, &dir);

    printf("periods per buffer = %d frames/n", val);

    snd_pcm_hw_params_get_rate_numden(params,

    &val, &val2);

    printf("exact rate = %d/%d bps/n", val, val2);

    val = snd_pcm_hw_params_get_sbits(params);

    printf("significant bits = %d/n", val);

    snd_pcm_hw_params_get_tick_time(params,

    &val, &dir);

    printf("tick time = %d us/n", val);

    val = snd_pcm_hw_params_is_batch(params);

    printf("is batch = %d/n", val);

    val = snd_pcm_hw_params_is_block_transfer(params);

    printf("is block transfer = %d/n", val);

    val = snd_pcm_hw_params_is_double(params);

    printf("is double = %d/n", val);

    val = snd_pcm_hw_params_is_half_duplex(params);

    printf("is half duplex = %d/n", val);

    val = snd_pcm_hw_params_is_joint_duplex(params);

    printf("is joint duplex = %d/n", val);

    val = snd_pcm_hw_params_can_overrange(params);

    printf("can overrange = %d/n", val);

    val = snd_pcm_hw_params_can_mmap_sample_resolution(params);

    printf("can mmap = %d/n", val);

    val = snd_pcm_hw_params_can_pause(params);

    printf("can pause = %d/n", val);

    val = snd_pcm_hw_params_can_resume(params);

    printf("can resume = %d/n", val);

    val = snd_pcm_hw_params_can_sync_start(params);

    printf("can sync start = %d/n", val);

    snd_pcm_close(handle);

    return 0;

    }

    三、安卓系統(tǒng)為什么音質(zhì)不好

    Android 基于Linux,我們先來(lái)了解一下Linux的特點(diǎn)。Linux使用ALSA作為其音頻架構(gòu),其全稱(chēng)Advanced Linux Sound Architecture,即高級(jí)Linux聲音架構(gòu)的意思,在2.6核心之后,ALSA成為了Linux系統(tǒng)默認(rèn)的音頻子架構(gòu)。取代了之前的OSS[Open Sound System,開(kāi)放式聲音系統(tǒng)]。

    ALSA并不太好理解,它首先是一個(gè)驅(qū)動(dòng)庫(kù),包含了大量的聲卡設(shè)備的開(kāi)源驅(qū)動(dòng),并提供了核心層API與ALSA庫(kù)通信,而ALSA庫(kù)則是應(yīng)用程序訪(fǎng)問(wèn)和操控音頻硬件的中間層,這個(gè)中間層有標(biāo)準(zhǔn)接口,開(kāi)發(fā)者可以無(wú)須考慮硬件差異性進(jìn)行開(kāi)發(fā),它對(duì)提升開(kāi)發(fā)效率是大有幫助的。ALSA可以向下兼容OSS,因?yàn)镺SS已經(jīng)被淘汰,其兼容的工作模式不再討論。

    這個(gè)體系被繼承到了Android當(dāng)中。在Android2.2[含2,2]之前,系統(tǒng)文件夾中能找到一個(gè)LibAudioALSA.so的文件,這就是ALSA庫(kù)文件,其他應(yīng)用程序調(diào)用它,與聲卡設(shè)備進(jìn)行指令和數(shù)據(jù)通信。Android音頻架構(gòu)與Linux的并無(wú)本質(zhì)區(qū)別。

    在桌面版本的Linux當(dāng)中,為了兼容各類(lèi)聲卡,Linux也設(shè)置了一個(gè)SRC[Sample Rate Converter,采樣頻率轉(zhuǎn)換]的環(huán)節(jié),當(dāng)當(dāng)前采樣率低于48kHz時(shí)強(qiáng)制SRC到48kHz輸出。這個(gè)SRC環(huán)節(jié)位于ALSA的插件模塊中的混音器部分。Android針對(duì)這個(gè)進(jìn)行了改進(jìn)。

    什么是SRC?SRC即Sample Rate Converter,中文意思為采樣頻率轉(zhuǎn)換。它被聲卡愛(ài)好者所關(guān)注,大部分發(fā)燒友視SRC為音質(zhì)殺手。

    Android增加了一個(gè)AudioFinger,這個(gè)可以簡(jiǎn)單的理解為Android的ALSA音頻子系統(tǒng)的標(biāo)準(zhǔn)化的插件模塊,它包含了AudioMixer[混音器]、AudioResampler[重采樣]等子模塊,AudioResampler即我們理解的SRC,Android換了一個(gè)新名稱(chēng)而已。針對(duì)SRC,Android做了改進(jìn),但改進(jìn)并不是以去除SRC為目的,而是修改了默認(rèn)的輸出頻率,Android的SRC目標(biāo)采樣率為44.1kHz,非該值的采樣率都將SRC處理。例如播放48kHz采樣率的信號(hào),輸出的最終是44.1kHz,這對(duì)音質(zhì)將產(chǎn)生負(fù)面影響。

    ALSA是一個(gè)針對(duì)Linux 桌面版本設(shè)計(jì)的音頻架構(gòu),它實(shí)際上是不適合智能終端設(shè)備的,起碼里面大量的開(kāi)源驅(qū)動(dòng)代碼是可以去除的,對(duì)與Android來(lái)說(shuō),這些都是廢代碼。從Android2.3起,啟用了一個(gè)新的音頻架構(gòu)。它放棄了一直使用的ALSA架構(gòu),因此系統(tǒng)文件夾中,也不再有LibAudioALSA.so這個(gè)文件。

    Android2.3起,架構(gòu)已經(jīng)做了修改,在針對(duì)內(nèi)部代碼進(jìn)行了優(yōu)化,去除了冗余代碼,理論上讓系統(tǒng)能變得更加高效,可以將新架構(gòu)理解為一個(gè)精簡(jiǎn)的或者為智能終端設(shè)備定制的ALSA架構(gòu)。遺憾的是,它同樣存在SRC嚴(yán)重劣化的問(wèn)題,通過(guò)測(cè)試可以證明。

    Android 3.0專(zhuān)門(mén)為平板電腦設(shè)計(jì),影音體驗(yàn)變得更加重要了,是不是新系統(tǒng)在音質(zhì)方面會(huì)有新的的進(jìn)步呢,測(cè)試結(jié)果依然是令人失望的。

    Android系統(tǒng)將采樣率同一為44.1kHz輸出,這造成了諸多限制,它將無(wú)法實(shí)現(xiàn)96kHz、192kHz高清音頻節(jié)目的良好回放,大量視頻節(jié)目源自DVD或者藍(lán)光碟,其采用率多為48kHz,Android設(shè)備在回放這些視頻節(jié)目時(shí),音質(zhì)也將大打折扣。

    理論上軟件SRC可以通過(guò)更換算法來(lái)實(shí)現(xiàn)音質(zhì)提升,但卻不太現(xiàn)實(shí),智能終端所采用的CPU多為ARM,ARM芯片的浮點(diǎn)運(yùn)算力有限,而SRC需要大量的浮點(diǎn)運(yùn)算的資源,即便有了高質(zhì)量的SRC算法,其運(yùn)算也是以犧牲設(shè)備性能和耗電量為代價(jià)的,實(shí)用性差。

    從Android的音頻架構(gòu)及流程分析,可以認(rèn)為,播放44.1kHz采樣率的音樂(lè)節(jié)目時(shí),不會(huì)引發(fā)SRC,音質(zhì)因此可以獲得保證,理論上確實(shí)如此。但它同樣存在問(wèn)題,不管是之前的ALSA架構(gòu)還是Android2.3之后改良的架構(gòu),其驅(qū)動(dòng)庫(kù)都位于核心層,也就意味著音頻設(shè)備廠(chǎng)商、用戶(hù)無(wú)法象PC平臺(tái)那樣安裝驅(qū)動(dòng)來(lái)改善音質(zhì)。實(shí)際測(cè)試也表明,Android設(shè)備音質(zhì)普遍偏差,Soomal有大量測(cè)試可以證明。

    我們?cè)侔涯抗馔断騣OS,iOS非常封閉,我們甚至無(wú)法獲知其架構(gòu)的具體構(gòu)成,但iOS設(shè)備不存在硬件設(shè)備多樣性的問(wèn)題,因此要實(shí)現(xiàn)更好音質(zhì)也會(huì)更加簡(jiǎn)單。iOS可以實(shí)現(xiàn)針對(duì)性的開(kāi)發(fā)和改良,以實(shí)現(xiàn)更好的音質(zhì)。實(shí)際情況也是如此,目前為止,還沒(méi)有一款A(yù)ndroid設(shè)備的音質(zhì)可以媲美任意一款iOS設(shè)備,這種差距,我們認(rèn)為不是來(lái)自硬件,而是操作系統(tǒng)。

    Android音頻架構(gòu)的局限性也使得其難以成為優(yōu)質(zhì)的影音平臺(tái),如果你希望設(shè)計(jì)一款基于Android的高清影音播放器,那么首先需要做的不是設(shè)計(jì)硬件,而是去修改現(xiàn)有架構(gòu)的不足,或者干脆設(shè)計(jì)一個(gè)專(zhuān)用的架構(gòu)來(lái)取代Android的通用架構(gòu)。從源代碼分析,Android和原生的Linux底層能支持各種采樣率,開(kāi)源也使得其具有改造基礎(chǔ),因此,在技術(shù)實(shí)力強(qiáng)勁的公司手里,Android也可以烏雞變鳳凰。

    四、請(qǐng)教Linux下ALSA聲道切換

    解各參數(shù)含義及些基本概念

    本度(sample):本記錄音頻數(shù)據(jù)基本單位見(jiàn)8位16位

    通道數(shù)(channel):該參數(shù)1表示單聲道2則立體聲

    楨(frame):楨記錄聲音單元其度本度與通道數(shù)乘積

    采率(rate):每秒鐘采數(shù)該數(shù)針楨言

    周期(period):音頻設(shè)備處理所需要楨數(shù)于音頻設(shè)備數(shù)據(jù)訪(fǎng)問(wèn)及音頻數(shù)據(jù)存儲(chǔ)都單位

    交錯(cuò)模式(interleaved):種音頻數(shù)據(jù)記錄式交錯(cuò)模式數(shù)據(jù)連續(xù)楨形式存放即首先記錄完楨1左聲道本右聲道本(假設(shè)立體聲格式)再始楨2記錄非交錯(cuò)模式首先記錄周期內(nèi)所楨左聲道本再記錄右聲道本數(shù)據(jù)連續(xù)通道式存儲(chǔ)數(shù)情況我需要使用交錯(cuò)模式

    period(周期):硬件斷間間隔間表示輸入延

    聲卡接口指針指示聲卡硬件緩存區(qū)前讀寫(xiě)位置要接口運(yùn)行指針循環(huán)指向緩存區(qū)某位置

    frame size = sizeof(one sample) * nChannels

    alsa配置緩存(buffer)周期(size)runtime幀(frames)形式存儲(chǔ)

    period_bytes = frames_to_bytes(runtime, runtime->period_size);

    bytes_to_frames()

    The period and buffer sizes are not dependent on the sample format because they are measured in frames; you do not need to change them.

    ALSA聲音編程介紹

    ALSA表示高級(jí)Linux聲音體系結(jié)構(gòu)(Advanced Linux Sound Architecture)由系列內(nèi)核驅(qū)應(yīng)用程序編譯接口(API)及支持Linux聲音實(shí)用程序組篇文章我簡(jiǎn)單介紹ALSA項(xiàng)目基本框架及軟件組主要集介紹PCM接口編程包括您自實(shí)踐程序示例

    您使用ALSA原能新并唯用聲音API您想完低級(jí)聲音操作便能夠化控制聲音并化提高性能或者您使用其聲音API沒(méi)特性ALSA選擇您已經(jīng)寫(xiě)音頻程序能想要ALSA聲卡驅(qū)添加本支持您音頻興趣想播放音頻文件高級(jí)API更選擇比SDL,OpenAL及些桌面環(huán)境提供工具集另外您能ALSA支持Linux環(huán)境使用ALSA

    ALSA歷史

    ALSA項(xiàng)目發(fā)起起Linux聲卡驅(qū)(OSS/Free drivers)沒(méi)積極維護(hù)并且落于新聲卡技術(shù)Jaroslav Kysela早先寫(xiě)聲卡驅(qū)并由始ALSA項(xiàng)目隨便更發(fā)者加入發(fā)隊(duì)伍更聲卡支持API結(jié)構(gòu)重組

    Linux內(nèi)核2.5發(fā)程ALSA合并官源碼樹(shù)發(fā)布內(nèi)核2.6ALSA已經(jīng)內(nèi)建穩(wěn)定內(nèi)核版本并廣泛使用

    數(shù)字音頻基礎(chǔ)

    聲音由變化氣壓組麥克風(fēng)轉(zhuǎn)換器轉(zhuǎn)換電形式模/數(shù)(ADC)轉(zhuǎn)換器模擬電壓轉(zhuǎn)換離散本值聲音固定間間隔采采速率稱(chēng)采率本輸數(shù)/模(DAC)轉(zhuǎn)換器比擴(kuò)音器轉(zhuǎn)換原模擬信號(hào)

    本位表示本影響聲音轉(zhuǎn)換數(shù)字信號(hào)精確程度素另主要素采率奈奎斯特(Nyquist)理論要離散系統(tǒng)奈奎斯特頻率高于采信號(hào)高頻率或帶寬避免混疊現(xiàn)象

    ALSA基礎(chǔ)

    ALSA由許聲卡聲卡驅(qū)程序組同提供稱(chēng)libasoundAPI庫(kù)應(yīng)用程序發(fā)者應(yīng)該使用libasound內(nèi)核ALSA接口libasound提供高級(jí)并且編程便編程接口并且提供設(shè)備邏輯命名功能發(fā)者甚至需要知道類(lèi)似設(shè)備文件低層接口相反OSS/Free驅(qū)內(nèi)核系統(tǒng)調(diào)用級(jí)編程要求發(fā)者提供設(shè)備文件名并且利用ioctrl實(shí)現(xiàn)相應(yīng)功能向兼容ALSA提供內(nèi)核模塊模擬OSS前許OSS基礎(chǔ)發(fā)應(yīng)用程序需要任何改ALSA運(yùn)行另外libaoss庫(kù)模擬OSS需要內(nèi)核模塊

    ALSA包含插件功能使用插件擴(kuò)展新聲卡驅(qū)包括完全用軟件實(shí)現(xiàn)虛擬聲卡ALSA提供系列基于命令行工具集比混音器(mixer)音頻文件播放器(aplay)及控制特定聲卡特定屬性工具

    ALSA體系結(jié)構(gòu)

    ALSA API解幾主要接口:

    1 控制接口:提供管理聲卡注冊(cè)請(qǐng)求用設(shè)備通用功能

    2 PCM接口:管理數(shù)字音頻放(playback)錄音(capture)接口本文續(xù)總結(jié)重點(diǎn)放接口發(fā)數(shù)字音頻程序用接口

    3 Raw MIDI接口:支持MIDI(Musical Instrument Digital Interface),標(biāo)準(zhǔn)電樂(lè)器些API提供聲卡MIDI總線(xiàn)訪(fǎng)問(wèn)原始接口基于MIDI事件工作由程序員負(fù)責(zé)管理協(xié)議及間處理

    4 定器(Timer)接口:同步音頻事件提供聲卡間處理硬件訪(fǎng)問(wèn)

    5 序器(Sequencer)接口

    6 混音器(Mixer)接口

    設(shè)備命名

    API庫(kù)使用邏輯設(shè)備名設(shè)備文件設(shè)備名字真實(shí)硬件名字插件名字硬件名字使用hw:i,j格式其i卡號(hào)j塊聲卡設(shè)備號(hào)第聲音設(shè)備hw:0,0.別名默認(rèn)引用第塊聲音設(shè)備并且本文示例真用插件使用另外唯名字比plughw:,表示插件插件提供硬件設(shè)備訪(fǎng)問(wèn)提供像采率轉(zhuǎn)換軟件特性硬件本身并支持特性

    聲音緩存數(shù)據(jù)傳輸

    每聲卡都硬件緩存區(qū)保存記錄本緩存區(qū)足夠滿(mǎn)聲卡產(chǎn)斷內(nèi)核聲卡驅(qū)使用直接內(nèi)存(DMA)訪(fǎng)問(wèn)通道本傳送內(nèi)存應(yīng)用程序緩存區(qū)類(lèi)似于放任何應(yīng)用程序使用DMA自緩存區(qū)數(shù)據(jù)傳送聲卡硬件緩存區(qū)

    硬件緩存區(qū)環(huán)緩存說(shuō)數(shù)據(jù)達(dá)緩存區(qū)末尾重新緩存區(qū)起始位置ALSA維護(hù)指針指向硬件緩存及應(yīng)用程序緩存區(qū)數(shù)據(jù)操作前位置內(nèi)核外部看我應(yīng)用程序緩存區(qū)興趣所本文討論應(yīng)用程序緩存區(qū)

    應(yīng)用程序緩存區(qū)通ALSA庫(kù)函數(shù)調(diào)用控制緩存區(qū)傳輸操作能導(dǎo)致接受延遲我稱(chēng)延(latency)解決問(wèn)題ALSA緩存區(qū)拆系列周期(period)(OSS/Free叫片斷fragments).ALSAperiod單元傳送數(shù)據(jù)

    周期(period)存儲(chǔ)些幀(frames)每幀包含間點(diǎn)所抓取本于立體聲設(shè)備幀包含兩信道本圖1展示解程:緩存區(qū)解周期幀本圖包含些假定數(shù)值圖左右信道信息交替存儲(chǔ)幀內(nèi)稱(chēng)交錯(cuò)(interleaved)模式非交錯(cuò)模式信道所本數(shù)據(jù)存儲(chǔ)另外信道數(shù)據(jù)

    Over and Under Run

    聲卡數(shù)據(jù)總連續(xù)硬件緩存區(qū)應(yīng)用程序緩存區(qū)間傳輸例外錄音例應(yīng)用程序讀取數(shù)據(jù)夠快循環(huán)緩存區(qū)新數(shù)據(jù)覆蓋種數(shù)據(jù)丟失稱(chēng)overrun.放例應(yīng)用程序?qū)懭霐?shù)據(jù)緩存區(qū)速度夠快緩存區(qū)"餓死"錯(cuò)誤稱(chēng)"underrun"ALSA文檔兩種情形統(tǒng)稱(chēng)"XRUN"適設(shè)計(jì)應(yīng)用程序化XRUN并且恢復(fù)

    典型聲音程序

    使用PCM程序通類(lèi)似面?zhèn)未a:

    打放或錄音接口

    設(shè)置硬件參數(shù)(訪(fǎng)問(wèn)模式數(shù)據(jù)格式信道數(shù)采率等等)

    while 數(shù)據(jù)要處理:

    讀PCM數(shù)據(jù)(錄音)

    或 寫(xiě)PCM數(shù)據(jù)(放)

    關(guān)閉接口

    我文看些工作代碼我建議您Linux系統(tǒng)測(cè)試運(yùn)行些代碼查看輸并嘗試修改推薦代碼本文相關(guān)所實(shí)例清單FTP獲?。篺tp.ssc.com/pub/lj/listings/issue126/6735.tgz

    Listing 1. Display Some PCM Types and Formats

    #include asoundlib.h>

    int main() {

    int val;

    printf("ALSA library version: %s/n",

    SND_LIB_VERSION_STR);

    printf("/nPCM stream types:/n");

    for (val = 0; val <= SND_PCM_STREAM_LAST; val++)

    printf(" %s/n",

    snd_pcm_stream_name((snd_pcm_stream_t)val));

    printf("/nPCM access types:/n");

    for (val = 0; val <= SND_PCM_ACCESS_LAST; val++)

    printf(" %s/n",

    snd_pcm_access_name((snd_pcm_access_t)val));

    printf("/nPCM formats:/n");

    for (val = 0; val <= SND_PCM_FORMAT_LAST; val++)

    if (snd_pcm_format_name((snd_pcm_format_t)val)

    != NULL)

    printf(" %s (%s)/n",

    snd_pcm_format_name((snd_pcm_format_t)val),

    snd_pcm_format_description(

    (snd_pcm_format_t)val));

    printf("/nPCM subformats:/n");

    for (val = 0; val <= SND_PCM_SUBFORMAT_LAST;

    val++)

    printf(" %s (%s)/n",

    snd_pcm_subformat_name((

    snd_pcm_subformat_t)val),

    snd_pcm_subformat_description((

    snd_pcm_subformat_t)val));

    printf("/nPCM states:/n");

    for (val = 0; val <= SND_PCM_STATE_LAST; val++)

    printf(" %s/n",

    snd_pcm_state_name((snd_pcm_state_t)val));

    return 0;

    }

    清單顯示些ALSA使用PCM數(shù)據(jù)類(lèi)型參數(shù)首先需要做包括文件些文件包含所庫(kù)函數(shù)聲明其顯示ALSA庫(kù)版本

    程序剩部迭代些PCM數(shù)據(jù)類(lèi)型流類(lèi)型始ALSA每迭代值提供符號(hào)量名并且提供功能函數(shù)顯示某特定值描述字符串看ALSA支持許格式我1.0.15版本支持達(dá)36種格式

    程序必須鏈接alsalib庫(kù)通編譯需要加-lasound選項(xiàng)些alsa庫(kù)函數(shù)使用dlopen函數(shù)及浮點(diǎn)操作所您能需要加-ldl,-lm選項(xiàng)

    面該程序Makefile:

    CC=gcc

    TARGET=test

    SRC=$(wildcard *.c)

    OBJECT= ${SRC:.c=.o}

    INCLUDES=-I/usr/include/alsa

    LDFLAGS=-lasound

    all:$(TARGET)

    $(OBJECT):$(SRC)

    $(CC) -c $(INCLUDES) $<

    $(TARGET):$(OBJECT)

    $(CC) -o $@ $< $(LDFLAGS)

    .PHONY:clean

    clean:

    @rm -rf $(OBJECT) $(TARGET) *~

    Listing 2. Opening PCM Device and Setting Parameters

    /*

    This example opens the default PCM device, sets

    some parameters, and then displays the value

    of most of the hardware parameters. It does not

    perform any sound playback or recording.

    */

    /* Use the newer ALSA API */

    #define ALSA_PCM_NEW_HW_PARAMS_API

    /* All of the ALSA library API is defined

    * in this header */

    #include asoundlib.h>

    int main() {

    int rc;

    snd_pcm_t *handle;

    snd_pcm_hw_params_t *params;

    unsigned int val, val2;

    int dir;

    snd_pcm_uframes_t frames;

    /* Open PCM device for playback. */

    rc = snd_pcm_open(&handle, "default",

    SND_PCM_STREAM_PLAYBACK, 0);

    if (rc < 0) {

    fprintf(stderr,

    "unable to open pcm device: %s/n",

    snd_strerror(rc));

    exit(1);

    }

    /* Allocate a hardware parameters object. */

    snd_pcm_hw_params_alloca(?ms);

    /* Fill it in with default values. */

    snd_pcm_hw_params_any(handle, params);

    /* Set the desired hardware parameters. */

    /* Interleaved mode */

    snd_pcm_hw_params_set_access(handle, params,

    SND_PCM_ACCESS_RW_INTERLEAVED);

    /* Signed 16-bit little-endian format */

    snd_pcm_hw_params_set_format(handle, params,

    SND_PCM_FORMAT_S16_LE);

    /* Two channels (stereo) */

    snd_pcm_hw_params_set_channels(handle, params, 2);

    /* 44100 bits/second sampling rate (CD quality) */

    val = 44100;

    snd_pcm_hw_params_set_rate_near(handle,

    params, &val, &dir);

    /* Write the parameters to the driver */

    rc = snd_pcm_hw_params(handle, params);

    if (rc < 0) {

    fprintf(stderr,

    "unable to set hw parameters: %s/n",

    snd_strerror(rc));

    exit(1);

    }

    /* Display information about the PCM interface */

    printf("PCM handle name = '%s'/n",

    snd_pcm_name(handle));

    printf("PCM state = %s/n",

    snd_pcm_state_name(snd_pcm_state(handle)));

    snd_pcm_hw_params_get_access(params,

    (snd_pcm_access_t *) &val);

    printf("access type = %s/n",

    snd_pcm_access_name((snd_pcm_access_t)val));

    snd_pcm_hw_params_get_format(params, &val);

    printf("format = '%s' (%s)/n",

    snd_pcm_format_name((snd_pcm_format_t)val),

    snd_pcm_format_description(

    (snd_pcm_format_t)val));

    snd_pcm_hw_params_get_subformat(params,

    (snd_pcm_subformat_t *)&val);

    printf("subformat = '%s' (%s)/n",

    snd_pcm_subformat_name((snd_pcm_subformat_t)val),

    snd_pcm_subformat_description(

    (snd_pcm_subformat_t)val));

    snd_pcm_hw_params_get_channels(params, &val);

    printf("channels = %d/n", val);

    snd_pcm_hw_params_get_rate(params, &val, &dir);

    printf("rate = %d bps/n", val);

    snd_pcm_hw_params_get_period_time(params,

    &val, &dir);

    printf("period time = %d us/n", val);

    snd_pcm_hw_params_get_period_size(params,

    &frames, &dir);

    printf("period size = %d frames/n", (int)frames);

    snd_pcm_hw_params_get_buffer_time(params,

    &val, &dir);

    printf("buffer time = %d us/n", val);

    snd_pcm_hw_params_get_buffer_size(params,

    (snd_pcm_uframes_t *) &val);

    printf("buffer size = %d frames/n", val);

    snd_pcm_hw_params_get_periods(params, &val, &dir);

    printf("periods per buffer = %d frames/n", val);

    snd_pcm_hw_params_get_rate_numden(params,

    &val, &val2);

    printf("exact rate = %d/%d bps/n", val, val2);

    val = snd_pcm_hw_params_get_sbits(params);

    printf("significant bits = %d/n", val);

    snd_pcm_hw_params_get_tick_time(params,

    &val, &dir);

    printf("tick time = %d us/n", val);

    val = snd_pcm_hw_params_is_batch(params);

    printf("is batch = %d/n", val);

    val = snd_pcm_hw_params_is_block_transfer(params);

    printf("is block transfer = %d/n", val);

    val = snd_pcm_hw_params_is_double(params);

    printf("is double = %d/n", val);

    val = snd_pcm_hw_params_is_half_duplex(params);

    printf("is half duplex = %d/n", val);

    val = snd_pcm_hw_params_is_joint_duplex(params);

    printf("is joint duplex = %d/n", val);

    val = snd_pcm_hw_params_can_overrange(params);

    printf("can overrange = %d/n", val);

    val = snd_pcm_hw_params_can_mmap_sample_resolution(params);

    printf("can mmap = %d/n", val);

    val = snd_pcm_hw_params_can_pause(params);

    printf("can pause = %d/n", val);

    val = snd_pcm_hw_params_can_resume(params);

    printf("can resume = %d/n", val);

    val = snd_pcm_hw_params_can_sync_start(params);

    printf("can sync start = %d/n", val);

    snd_pcm_close(handle);

    return 0;

    }

    以上就是小編對(duì)于alsa景觀設(shè)計(jì)大賽時(shí)間問(wèn)題和相關(guān)問(wèn)題的解答了,如有疑問(wèn),可撥打網(wǎng)站上的電話(huà),或添加微信。


    推薦閱讀:

    廳堂Hall空間英文名稱(chēng)(廳堂hall空間英文名稱(chēng)是什么)_1

    廳堂Hall空間英文名稱(chēng)(廳堂hall空間英文名稱(chēng)是什么)

    5.22樂(lè)居Morning Call丨本周末6盤(pán)推新 仙林湖速度盤(pán)真的來(lái)了

    年底營(yíng)銷(xiāo)線(xiàn)上線(xiàn)下新聞(營(yíng)銷(xiāo)活動(dòng)線(xiàn)上線(xiàn)下活動(dòng)策劃方案)

    免費(fèi)商標(biāo)查詢(xún)官網(wǎng)(商標(biāo)轉(zhuǎn)讓 中國(guó)商標(biāo)網(wǎng))