守望者--AIR技术交流

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

搜索
热搜: ANE FlasCC 炼金术
查看: 1220|回复: 0

[音频分析] 基于特征分析谱估计算法(Capon, MUSIC, ESPRIT)的C++实现

[复制链接]
  • TA的每日心情
    擦汗
    2018-4-10 15:18
  • 签到天数: 447 天

    [LV.9]以坛为家II

    1742

    主题

    2094

    帖子

    13万

    积分

    超级版主

    Rank: 18Rank: 18Rank: 18Rank: 18Rank: 18

    威望
    562
    贡献
    29
    金币
    52584
    钢镚
    1422

    开源英雄守望者

    发表于 2015-8-20 15:43:58 | 显示全部楼层 |阅读模式
    头文件:
    1. /*
    2. * Copyright (c) 2008-2011 Zhang Ming (M. Zhang), zmjerry@163.com
    3. *
    4. * This program is free software; you can redistribute it and/or modify it
    5. * under the terms of the GNU General Public License as published by the
    6. * Free Software Foundation, either version 2 or any later version.
    7. *
    8. * Redistribution and use in source and binary forms, with or without
    9. * modification, are permitted provided that the following conditions are met:
    10. *
    11. * 1. Redistributions of source code must retain the above copyright notice,
    12. *    this list of conditions and the following disclaimer.
    13. *
    14. * 2. Redistributions in binary form must reproduce the above copyright
    15. *    notice, this list of conditions and the following disclaimer in the
    16. *    documentation and/or other materials provided with the distribution.
    17. *
    18. * This program is distributed in the hope that it will be useful, but WITHOUT
    19. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    20. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
    21. * more details. A copy of the GNU General Public License is available at:
    22. * http://www.fsf.org/licensing/licenses
    23. */


    24. /*****************************************************************************
    25. *                                  toeplitz.h
    26. *
    27. * A Toeplitz matrix is defined by one row and one column. A symmetric
    28. * Toeplitz matrix is defined by just one row. Toeplitz generates Toeplitz
    29. * matrices given just the row or row and column description.
    30. *
    31. * Zhang Ming, 2010-11, Xi'an Jiaotong University.
    32. *****************************************************************************/


    33. #ifndef TOEPLITZ_H
    34. #define TOEPLITZ_H


    35. #include <vector.h>
    36. #include <matrix.h>


    37. namespace splab
    38. {
    39.     template<typename Type> Matrix<Type> toeplitz( const Vector<Type>&,
    40.                                                    const Vector<Type>& );
    41.     template<typename Type> Matrix<Type> toeplitz( const Vector<Type>& );


    42.     #include <toeplitz-impl.h>

    43. }
    44. // namespace splab


    45. #endif
    46. // TOEPLITZ_H
    复制代码
    1. /*
    2. * Copyright (c) 2008-2011 Zhang Ming (M. Zhang), zmjerry@163.com
    3. *
    4. * This program is free software; you can redistribute it and/or modify it
    5. * under the terms of the GNU General Public License as published by the
    6. * Free Software Foundation, either version 2 or any later version.
    7. *
    8. * Redistribution and use in source and binary forms, with or without
    9. * modification, are permitted provided that the following conditions are met:
    10. *
    11. * 1. Redistributions of source code must retain the above copyright notice,
    12. *    this list of conditions and the following disclaimer.
    13. *
    14. * 2. Redistributions in binary form must reproduce the above copyright
    15. *    notice, this list of conditions and the following disclaimer in the
    16. *    documentation and/or other materials provided with the distribution.
    17. *
    18. * This program is distributed in the hope that it will be useful, but WITHOUT
    19. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    20. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
    21. * more details. A copy of the GNU General Public License is available at:
    22. * http://www.fsf.org/licensing/licenses
    23. */


    24. /*****************************************************************************
    25. *                              eigenanalysispse.h
    26. *
    27. * Eigenanalysis algorithms for spectrum estimation.
    28. *
    29. * The eigenanalysis algorithms perform eigen decomposition of the signal's
    30. * auto-correlation matrix (ACM) to estimate signal's frequency content. The
    31. * ACM can be decomposed into signal-subspace and noise-subspace, then use
    32. * the orthogonality of the tow subspaces to estimate the signal's spectrum.
    33. *
    34. * These algorithms, such as Pisarenko, MUSIC (MUltiple SIgnal Classification)
    35. * and ESPRIT (Estimation of Signal Parameters by Rotational Invariance
    36. * Techniques) are particularly suitable for signals that are the sum of
    37. * sinusoids with additive white Gaussian noise. The model order (the number
    38. * of complex exponential signal) is estimated by minimizing the MDL criterion.
    39. *
    40. * This file also provide the Capon's maximum likehood method or minimum
    41. * variance method for specturm estimation.
    42. *
    43. * Zhang Ming, 2010-11, Xi'an Jiaotong University.
    44. *****************************************************************************/


    45. #ifndef EIGENANALYSISPSE_H
    46. #define EIGENANALYSISPSE_H


    47. #include <toeplitz.h>
    48. #include <levinson.h>
    49. #include <linequs1.h>
    50. #include <svd.h>
    51. #include <evd.h>


    52. namespace splab
    53. {

    54.     template<typename Type> Vector<Type> caponPSE( const Vector<Type>&,
    55.                                                    int, int );
    56.     template<typename Type> Vector<Type> pisarenkoPSE( const Vector<Type>&,
    57.                                                        int, int, int );
    58.     template<typename Type> Vector<Type> musicPSE( const Vector<Type>&,
    59.                                                    int, int, int );
    60.     template<typename Type> Vector<Type> espritPSE( const Vector<Type>&,
    61.                                                     int, int );

    62.     template<typename Type> int orderEst( const Vector<Type>&, int );


    63.     #include <eigenanalysispse-impl.h>

    64. }
    65. // namespace splab


    66. #endif
    67. // EIGENANALYSISPSE_H
    复制代码


    实现文件:
    1. /*
    2. * Copyright (c) 2008-2011 Zhang Ming (M. Zhang), zmjerry@163.com
    3. *
    4. * This program is free software; you can redistribute it and/or modify it
    5. * under the terms of the GNU General Public License as published by the
    6. * Free Software Foundation, either version 2 or any later version.
    7. *
    8. * Redistribution and use in source and binary forms, with or without
    9. * modification, are permitted provided that the following conditions are met:
    10. *
    11. * 1. Redistributions of source code must retain the above copyright notice,
    12. *    this list of conditions and the following disclaimer.
    13. *
    14. * 2. Redistributions in binary form must reproduce the above copyright
    15. *    notice, this list of conditions and the following disclaimer in the
    16. *    documentation and/or other materials provided with the distribution.
    17. *
    18. * This program is distributed in the hope that it will be useful, but WITHOUT
    19. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    20. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
    21. * more details. A copy of the GNU General Public License is available at:
    22. * http://www.fsf.org/licensing/licenses
    23. */


    24. /*****************************************************************************
    25. *                               toeplitz-impl.h
    26. *
    27. * Implementationfor Toeplitz matrices generator.
    28. *
    29. * Zhang Ming, 2010-11, Xi'an Jiaotong University.
    30. *****************************************************************************/


    31. /**
    32. * Returns a nonsymmetric Toeplitz matrix T having cn as its first column and
    33. * rn as its first row. If the first elements of cn and rn are different, the
    34. * column element is used.
    35. */
    36. template <typename Type>
    37. Matrix<Type> toeplitz( const Vector<Type> &cn, const Vector<Type> &rn )
    38. {
    39.     int M = cn.size(),
    40.         N = rn.size();
    41.     Matrix<Type> T(M,N);

    42.     // main diagonal and below the main diagonal
    43.     for( int d=0; d<M; ++d )
    44.         for( int i=0; i<M-d; ++i )
    45.             T[i+d][i] = cn[d];

    46.     // above the main diagonal
    47.     for( int d=1; d<N; ++d )
    48.         for( int i=0; i<N-d; ++i )
    49.             T[i][i+d] = rn[d];

    50.     return T;
    51. }


    52. /**
    53. * Returns the symmetric or Hermitian Toeplitz matrix formed from vector rn,
    54. * where rn defines the first row of the matrix.
    55. */
    56. template <typename Type>
    57. Matrix<Type> toeplitz( const Vector<Type> &rn )
    58. {
    59.     int N = rn.size();
    60.     Matrix<Type> T(N,N);

    61.     // main diagonal
    62.     for( int i=0; i<N; ++i )
    63.         T[i][i] = rn[0];

    64.     // above and below the main diagonal
    65.     for( int d=1; d<N; ++d )
    66.         for( int i=0; i<N-d; ++i )
    67.             T[i][i+d] = T[i+d][i] = rn[d];

    68.     return T;
    69. }
    复制代码
    1. /*
    2. * Copyright (c) 2008-2011 Zhang Ming (M. Zhang), zmjerry@163.com
    3. *
    4. * This program is free software; you can redistribute it and/or modify it
    5. * under the terms of the GNU General Public License as published by the
    6. * Free Software Foundation, either version 2 or any later version.
    7. *
    8. * Redistribution and use in source and binary forms, with or without
    9. * modification, are permitted provided that the following conditions are met:
    10. *
    11. * 1. Redistributions of source code must retain the above copyright notice,
    12. *    this list of conditions and the following disclaimer.
    13. *
    14. * 2. Redistributions in binary form must reproduce the above copyright
    15. *    notice, this list of conditions and the following disclaimer in the
    16. *    documentation and/or other materials provided with the distribution.
    17. *
    18. * This program is distributed in the hope that it will be useful, but WITHOUT
    19. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    20. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
    21. * more details. A copy of the GNU General Public License is available at:
    22. * http://www.fsf.org/licensing/licenses
    23. */


    24. /*****************************************************************************
    25. *                            parametricpse-impl.h
    26. *
    27. * Implementation for eigenanalysis algorithms for spectrum estimation.
    28. *
    29. * Zhang Ming, 2010-11, Xi'an Jiaotong University.
    30. *****************************************************************************/


    31. /**
    32. * The Capon method (minimum variance method) for spectral estimation.
    33. * xn       : input signal
    34. * M        : the order of the covariance matrix
    35. * L        : the number of estimated spectral samples
    36. * return   : spectral estimates at L frequencies:
    37. *            w = 0, 2*pi/L, ..., 2*pi(L-1)/L
    38. */
    39. template <typename Type>
    40. Vector<Type> caponPSE( const Vector<Type> &xn, int M, int L )
    41. {
    42.     int N = xn.size();

    43.     assert( M < N );

    44.     Vector<Type> rn(M), Ere(M), Eim(M), Tre(M), Tim(M),
    45.                  tn(M), wn(M), Px(L);

    46.     // auto-correlation matrix R
    47.     for( int i=0; i<M; ++i )
    48.     {
    49.         tn[i] = Type(i);
    50.         for( int k=0; k<N-i; ++k )
    51.             rn[i] += xn[k+i]*xn[k];
    52.     }
    53.     rn /= Type(N-M);

    54.     for( int k=0; k<L; ++k )
    55.     {
    56.         // real part and imaginary part of E(omega)
    57.         wn = Type( TWOPI*k/L ) * tn;
    58.         Ere = cos( wn );
    59.         Eim = sin( wn );

    60.         // Tre = inv(R)*Ere, Tim = inv(R)*Eim. Because R is Toeplitz, this can
    61.         // be solved through Levinson algorithm.
    62.         Tre = levinson( rn, Ere );
    63.         Tim = levinson( rn, Eim );

    64.         // denominator of spectrum
    65.         Px[k] = dotProd(Ere,Tre) + dotProd(Eim,Tim);
    66.     }

    67.     return Type(M)/Px;
    68. }


    69. /**
    70. * The MUSIC method for spectral estimation.
    71. * xn       : input signal
    72. * M        : the order of the covariance matrix
    73. * p        : the model order
    74. * L        : the number of estimated spectral samples
    75. * return   : spectral estimates (dB) at L frequencies:
    76. *            w = 0, 2*pi/L, ..., 2*pi(L-1)/L
    77. */
    78. template <typename Type>
    79. Vector<Type> musicPSE( const Vector<Type> &xn, int M, int p, int L )
    80. {
    81.     int N = xn.size();

    82.     assert( M < N );
    83.     assert( p < M );

    84.     Type  Tre, Tim, sum;
    85.     Vector<Type> rm(M), tm(M),
    86.                  Ere(M), Eim(M), Vi(M),
    87.                  wk(M), Px(L);

    88.     // auto-correlation matrix R
    89.     for( int i=0; i<M; ++i )
    90.     {
    91.         tm[i] = Type(i);
    92.         for( int k=0; k<N-i; ++k )
    93.             rm[i] += xn[k+i]*xn[k];
    94.     }
    95.     rm /= Type(N-M);
    96.     Matrix<Type> Rx = toeplitz(rm);

    97.     SVD<Type> svd;
    98.     svd.dec(Rx);
    99.     Matrix<Type> U = svd.getU();

    100.     for( int k=0; k<L; ++k )
    101.     {
    102.         // real part and imaginary part of E(omega)
    103.         wk = Type( TWOPI*k/L ) * tm;
    104.         Ere = cos( wk );
    105.         Eim = sin( wk );

    106.         // Tre = Ere*Vi, Tim = Eim*Vi;
    107.         sum = 0;
    108.         for( int i=p; i<M; ++i )
    109.         {
    110.             Vi = U.getColumn(i);
    111.             Tre = dotProd( Ere, Vi );
    112.             Tim = dotProd( Eim, Vi );
    113.             sum += Tre*Tre + Tim*Tim;
    114.         }

    115.         // spectrum
    116. //        Px[k] = 1 / ( sum );
    117.         Px[k] = -10*log10(sum);
    118.     }

    119.     return Px;
    120. }


    121. /**
    122. * The Pisarenko method for spectral estimation.
    123. * xn       : input signal
    124. * M        : the order of the covariance matrix
    125. * p        : the model order
    126. * L        : the number of estimated spectral samples
    127. * return   : spectral estimates (dB) at L frequencies:
    128. *            w = 0, 2*pi/L, ..., 2*pi(L-1)/L
    129. */
    130. template <typename Type>
    131. Vector<Type> pisarenkoPSE( const Vector<Type> &xn, int M, int p, int L )
    132. {
    133.     int N = xn.size();

    134.     assert( M < N );
    135.     assert( p < M );

    136.     Type  Tre, Tim;
    137.     Vector<Type> rm(M), tm(M),
    138.                  Ere(M), Eim(M), Vi(M),
    139.                  wk(M), Px(L);

    140.     // auto-correlation matrix R
    141.     for( int i=0; i<M; ++i )
    142.     {
    143.         tm[i] = Type(i);
    144.         for( int k=0; k<N-i; ++k )
    145.             rm[i] += xn[k+i]*xn[k];
    146.     }
    147.     rm /= Type(N-M);
    148.     Matrix<Type> Rx = toeplitz(rm);

    149.     SVD<Type> svd;
    150.     svd.dec(Rx);
    151.     Matrix<Type> U = svd.getU();

    152.     for( int k=0; k<L; ++k )
    153.     {
    154.         // real part and imaginary part of E(omega)
    155.         wk = Type( TWOPI*k/L ) * tm;
    156.         Ere = cos( wk );
    157.         Eim = sin( wk );

    158.         // Tre = Ere*Vi, Tim = Eim*Vi;
    159.         Vi = U.getColumn(p);
    160.         Tre = dotProd( Ere, Vi );
    161.         Tim = dotProd( Eim, Vi );

    162.         // spectrum
    163. //        Px[k] = 1 / ( Tre*Tre + Tim*Tim );
    164.         Px[k] = -10*log10(Tre*Tre+Tim*Tim);
    165.     }

    166.     return Px;
    167. }


    168. /**
    169. * The ESPRIT method for spectral estimation.
    170. * xn       : input signal
    171. * M        : the order of the covariance matrix
    172. * p        : the model order
    173. * return   : the esitmated frequency in the interval [-0.5,0.5]
    174. */
    175. template <typename Type>
    176. Vector<Type> espritPSE( Vector<Type> &xn, int M, int p )
    177. {
    178.     int N = xn.size();

    179.     assert( M < N );
    180.     assert( p < M );

    181.     Vector<Type> rm(M), fk(p);
    182.     Matrix<Type> S1(M-1,p), S2(M-1,p);

    183.     // get the auto-correlation matrix
    184.     for( int i=0; i<M; ++i )
    185.         for( int k=0; k<N-i; ++k )
    186.             rm[i] += xn[k+i]*xn[k];
    187.     rm /= Type(N-M);
    188.     Matrix<Type> Rx = toeplitz(rm);

    189.     // get the eigendecomposition of R, use svd because it sorts eigenvalues
    190.     SVD<Type> svd;
    191.     svd.dec(Rx);
    192.     Matrix<Type> U = svd.getU();

    193.     // compute S1 and S2
    194.     for( int i=0; i<M-1; ++i )
    195.         for( int j=0; j<p; ++j )
    196.         {
    197.             S1[i][j] = U[i][j];
    198.             S2[i][j] = U[i+1][j];
    199.         }

    200.     // compute matrix Phi
    201.     Matrix<Type> Phi = choleskySolver( trMult(S1,S1), trMult(S1,S2) );

    202.     // compute eigenvalues of Phi
    203.     EVD<Type> evd;
    204.     evd.dec(Phi);
    205.     Vector<Type> evRe = real( evd.getCD() );
    206.     Vector<Type> evIM = imag( evd.getCD() );

    207.     // compute normalized frequency in the interval [-0.5,0.5]
    208.     for( int i=0; i<p; ++i )
    209.         fk[i] = atan2( evIM[i], evRe[i] ) / Type(TWOPI);

    210.     return fk;
    211. }


    212. /**
    213. * The model order estimation based on minimizing the MDL criterion.
    214. * xn       : input signal
    215. * M        : the order of the covariance matrix
    216. * return   : p ---> the number of sinusoids
    217. */
    218. template <typename Type>
    219. int orderEst( const Vector<Type> &xn, int M )
    220. {
    221.     int N = xn.size();

    222.     assert( M < N );

    223.     int p = 0;
    224.     Type Gp, Ap, Ep, MDL, minMDL;
    225.     Vector<Type> rm(M);

    226.     // auto-correlation matrix R
    227.     for( int i=0; i<M; ++i )
    228.         for( int k=0; k<N-i; ++k )
    229.             rm[i] += xn[k+i]*xn[k];
    230.     rm /= Type(N-M);
    231.     Matrix<Type> Rx = toeplitz(rm);

    232.     SVD<Type> svd;
    233.     svd.dec(Rx);
    234.     Vector<Type> S = svd.getSV();

    235.     minMDL = Type( 0.5*(M-1)*(M+1)*log10(1.0*N) );
    236.     for( int i=0; i<M-2; ++i )
    237.     {
    238.         // compute MDL(i)
    239.         Gp = Type(1);
    240.         Ap = Type(0);
    241.         Ep = Type( 0.5*i*(2*M-i)*log10(1.0*N) );
    242.         for( int j=i+1; j<M; ++j )
    243.         {
    244.             Gp *= S[j];
    245.             Ap += S[j];
    246.         }
    247.         Ap = pow( Ap/(M-i), Type(M-i) );
    248.         MDL = -N*log10(Gp/Ap) + Ep;

    249.         // find the minimum MDL(i)
    250.         if( MDL < minMDL )
    251.         {
    252.             p = i;
    253.             minMDL = MDL;
    254.         }
    255.     }

    256.     return p;
    257. }
    复制代码
    测试代码:
    1. /*****************************************************************************
    2. *                            eigenanalysispse_test.cpp
    3. *
    4. * Eigenanalysis spectrum estimation testing.
    5. *
    6. * Zhang Ming, 2010-11, Xi'an Jiaotong University.
    7. *****************************************************************************/


    8. #define BOUNDS_CHECK

    9. #include <iostream>
    10. #include <iomanip>
    11. #include <cstring>
    12. #include <random.h>
    13. #include <vectormath.h>
    14. #include <eigenanalysispse.h>
    15. #include "engine.h"


    16. using namespace std;
    17. using namespace splab;


    18. typedef double  Type;
    19. const   int     N = 200;
    20. const   int     M = 20;
    21. const   int     L = 200;


    22. int main()
    23. {
    24.     /******************************* [ signal ] ******************************/
    25.     cout << setiosflags(ios::fixed) << setprecision(4);
    26.     int mfn = L/2+1;
    27.     Type amp1 = Type(1.0),
    28.          amp2 = Type(1.0);
    29.     Type f1 = Type(0.2),
    30.          f2 = Type(0.25);
    31.     Type SNR;

    32.     Vector<Type> tn = linspace(Type(0), Type(N-1), N );
    33.     Vector<Type> sn = amp1*sin(Type(TWOPI)*f1*tn) + amp2*sin(Type(TWOPI)*f2*tn);
    34.     Vector<Type> wn = randn( 37, Type(0.0), Type(0.2), N );
    35.     Vector<Type> xn = sn + wn;
    36.     SNR = 20*log10(norm(sn)/norm(wn));
    37.     cout << "The SNR = " << SNR << endl << endl;

    38.     /********************************* [ PSD ] *******************************/
    39.     int p = orderEst( xn, M );
    40.     p = p/2*2;
    41.     cout << "The model order that minimizes the MDL criterion is:   p = "
    42.          << p << endl << endl;

    43. //    Vector<Type> Px = caponPSE( xn, M, L );

    44. //    Vector<Type> Px = pisarenkoPSE( xn, M, p, L );

    45. //    Vector<Type> Px = musicPSE( xn, M, p, L );

    46.     Vector<Type> fk = espritPSE( xn, M, p );
    47.     Vector<Type> Px(mfn);
    48.     for( int k=0; k<p; ++k )
    49.     {
    50.         int index = int( L*abs(fk[k]) + 0.5 );
    51.         if( index != 0 && index != L/2 )
    52.         Px[index] = Type(1.0);
    53.     }

    54.     /******************************** [ PLOT ] *******************************/
    55.     Engine *ep  = engOpen( NULL );
    56.     if( !ep )
    57.     {
    58.         cerr << "Cannot open Matlab Engine!" << endl;
    59.         exit(1);
    60.     }

    61.     mxArray *mxn = mxCreateDoubleMatrix( N, 1, mxREAL );
    62.     mxArray *mPx = mxCreateDoubleMatrix( mfn, 1, mxREAL );
    63.     memcpy( mxGetPr(mxn), xn, N*sizeof(Type) );
    64.     memcpy( mxGetPr(mPx), Px, mfn*sizeof(Type) );
    65.     engPutVariable( ep, "xn", mxn );
    66.     engPutVariable( ep, "Px", mPx );

    67.     const char *mCmd =  " figure('name','FBLPLS Method of Spectrum Estimation'); \
    68.         N = length(xn); mfn = length(Px); \
    69.         subplot(2,1,1); \
    70.             plot((0:N-1), xn); \
    71.             axis([0,N,min(xn),max(xn)]); \
    72.             title('(a)   Signal', 'FontSize',12); \
    73.             xlabel('Samples', 'FontSize',12); \
    74.             ylabel('Amplitude', 'FontSize',12); \
    75.         subplot(2,1,2); \
    76.             h = stem((0:mfn-1)/(mfn-1)/2, Px); \
    77.             axis([0,0.5,min(Px),max(Px)]); \
    78.             set(h,'MarkerFaceColor','blue'); \
    79.             set(gca, 'XTick', 0:0.05:0.5); \
    80.             grid on; \
    81.             title('(b)   Spectrum', 'FontSize',12); \
    82.             xlabel('Normalized Frequency ( f / fs )', 'FontSize',12); \
    83.             ylabel('Amplitude', 'FontSize',12); ";
    84.     engEvalString( ep, mCmd );

    85.     mxDestroyArray( mxn );
    86.     mxDestroyArray( mPx );
    87.     system( "pause" );
    88.     engClose(ep);

    89.     return 0;
    90. }
    复制代码
    运行结果:
    1. The SNR = 14.3399

    2. The model order that minimizes the MDL criterion is:   p = 4


    3. Process returned 0 (0x0)   execution time : 0.062 s
    4. Press any key to continue.
    复制代码
    Capon估计法




    MUSIC估计法




    ESPRIT估计法





    本文来自:http://my.oschina.net/zmjerry/blog/9584

    本帖子中包含更多资源

    您需要 登录 才可以下载或查看,没有帐号?立即注册

    x
    守望者AIR技术交流社区(www.airmyth.com)
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    
    关闭

    站长推荐上一条 /4 下一条

    QQ|手机版|Archiver|网站地图|小黑屋|守望者 ( 京ICP备14061876号

    GMT+8, 2024-3-19 17:55 , Processed in 0.134869 second(s), 36 queries .

    守望者AIR

    守望者AIR技术交流社区

    本站成立于 2014年12月31日

    快速回复 返回顶部 返回列表